Share |

venerdì 24 giugno 2011

HTML5 : I Web Workers dedicati

Continuo la saga degli articoli su HTML5 spiegando cos'è un Web Worker e mostrandone l'utilizzo.

L'esempio utilizzato in questo articolo,che puoi provare tu stesso al seguente link, consiste nel creare ed istanziare un web worker che, ad intervalli regolari, dopo aver incrementato la proprietà contatore (definita counter) di un oggetto con un valore variabile (definito step e appartenente all'oggetto stesso) invia alla pagina per la quale gira in background un messaggio per comunicare il nuovo valore di counter; la pagina, ricevuto il messaggio, scrive lo stato dell'oggetto ricevuto.

Per mostrare come è facile creare una comunicazione bidirezionale, è possibile modificare il valore della proprietà step intervenendo dalla pagina HTML in maniera che sia la pagina stessa ad inviare un messaggio al web worker, il quale al successivo incremento utilizzerà il nuovo valore di step.

Questo web worker si definisce dedicato perchè lavora esclusivamente per una sola pagina, con la quale nasce, ne condivide i dati e termina.

Cos'è un Web Worker?

Sostanzialmente come un thread, i web workers offrono la possibilità di eseguire scripts all'interno di una finestra di un browser senza bloccarne il caricamento e senza interferire con le attività dell'utente poichè i suddetti scripts girano in background.

Quando utilizzare un Web Worker?

Proprio per via della loro somiglianza con i threads, è consigliato delegare ai web workers l'esecuzione di scripts lunghi o onerosi in termini di risorse, che altrimenti costringerebbero ad una lunga attesa l'utente.

Scope & Security di un Web Worker

Dal punto di vista della sicurezza le API per l'utilizzo dei web workers sembrano avere le carte in regola.

La sicurezza in questo caso è garantita dalla scarsa area di validità (o scope) di cui gode il componente: questo infatti non ha accesso alla gran parte dei componenti ed oggetti javascript caricati nella pagina in cui gira e deve essere fornito tramite un file js esterno.

Un web worker può accedere ai seguenti oggetti e componenti:

  • oggetto self: rappresenta il web worker stesso;
  • oggetto navigator: rappresenta il browser; ma al worker è consentita la sola lettura delle seguenti proprietà:
    • appName;
    • appVersion;
    • platform;
    • userAgent;
  • oggetto location: rappresenta la barra di navigazione della finestra; ma al worker è consentita la sola lettura delle seguenti proprietà:
    • hash;
    • host;
    • hostname;
    • href;
    • pathname;
    • port;
    • protocol;
    • search;

I web workers non hanno la possibilità di accedere e manipolare gli oggetti window e document quindi non possono utilizzare le API per la gestione del DOM.

Comunicare con un Web Worker

Dato che i web workers in alcun modo possono modificare il contenuto della pagina in cui girano bisogna capire come comunicare con il componente al fine di scambiarsi dati.

Con i web workers è possibile scambiare valori primitivi ed oggetti, ma non functions e ne metodi.

La comunicazione con il componente può essere sia mono che bidirezionale, ovvero può avvenire esclusivamente dal worker verso la pagina o viceversa (monodirezionale) oppure possono interagire flussi di dati provenienti da entrambe le sorgenti (bidirezionale).

Lo scambio di dati da e verso un web worker è basato sull'invio e la ricezione di messaggi.

E' più semplice comprendere il funzionamento osservando un esempio: di seguito è mostrato come inviare e ricevere dati facendo interagire una pagina e un web worker.

Il web worker, in background, ogni 2 secondi incrementa counter con il valore di step e tramite una chiamata al metodo postMessage() invia un messaggio con il nuovo valore incrementato alla pagina che lo mostra.
L'esempio permette di modificare il valore della proprietà step dalla pagina inviando al web worker il nuovo valore con cui incrementare counter.

//Codice HTML della pagina principale.


<!DOCTYPE html>
<html>
  <head>
    <title>Esempio di utilizzo di un Web Worker Dedicato</title>
    <meta name="description" content="A smart and easy HTML5 Dedicated Web Worker." />
    <meta name="author" content="Mirko Agrati" />
    <script type="text/javascript">
        /**
         * Istanza dell'oggetto che viene scambiato
         * con il web worker
         */
        var token = null;
        
        /**
         * web worker: il costruttore riceve
         * l'URI dello script che contiene la logica.
         */
        var worker = new Worker("lib/dedicatedwebworker.js");
        
        /**
         * Implementazione del metodo onmessage(event):
         * questo metodo viene richiamato ogni volta
         * che il web worker riceve un messaggio.
         *
         * Ricevuto il messaggio con il nuovo
         * valore incrementato esegue una chiamata
         * alla funzione trace().
         */
        worker.onmessage = function(event){
            token = event.data;
            trace();
        }
        
        /**
         * In caso si manifesti un errore bloccante
         * lo si può gestire con il metodo onerror(event).
         *
         * Segnalato l'errore interrompe il funzionamento
         * del web worker.
         */
        worker.onerror = function(event){
            alert('ERRORE:\ndesc: ' + event.message
                + '\nfilename: ' + event.filename
                + '\nline number: ' + event.lineno);
            stopWorker();
        }
        
        /**
         * Interrompe l'esecuzione del web worker.
         */
        function stopWorker(){
            worker.terminate();
        }
        
        /**
         * Traccia all'interno della pagina lo stato
         * dell'ultima operazione di incremento
         * eseguita del web worker.
         */
        function trace(){
            var text = 'counter = ' + token.counter
                + '; step = ' + token.step + '
'; document.getElementById("result").innerHTML += text; } /** * Modifica il valore della proprietà step dell'oggetto token * e richiama la funzione sendMessage() per comunicare al * web worker che il valore incrementale è variato. function updateStep(){ token.step = parseInt(document.getElementById('newstep').value); sendMessage(); } /** * Invia un messaggio al web worker contenente * l'istanza dell'oggetto token. */ function sendMessage(){ worker.postMessage(token); } </script></head> <body> <header> <h1>HTML5 Dedicated Web Worker MA Test Page</h1></header> <article id="content"> <input type="number" value="1" id="newstep"> <input type="button" value="cambia step" onclick="updateStep();"> <input type="button" value="Stop" onclick="stopWorker();"> <br> <div id="result"></div></article> <footer> <p>© Copyright 2011 by Mirko Agrati</p></footer></body></html>
//Codice sorgente del Web Worker. 
//File URI 'lib/dedicatedwebworker.js'

/**
 * Classe che viene istanziata
 * per l'invio e la ricezione
 * dei dati alla pagina.
 * 
 * Conserva lo stato dell'ultima
 * operazione compiuta.
 */
function Token(){
    this.counter = 0;
    this.step = 1;
}

//Istanza di classe Token.
var token = new Token();

/**
 * Ogni volta che il worker riceve un messaggio
 * viene scatenata una chiamata a questo metodo.
 *
 * Aggiorna l'istanz di Token con quella ricevuta.
 */
onmessage = function(event){
  token = event.data;
}

/**
 * Incrementa il valore della proprietà
 * counter e invia il nuovo valore alla pagina.
 */
function reply(){
  token.counter += token.step;
  postMessage(token);  
}

/**
 * Ogni 2 secondi esegue una chiamata
 * alla funzione reply().
 */
setInterval(reply,2000);

Anche se il codice sorgente è ottimamente commentato :) è il caso di fare qualche chiarimento.

Prima di tutto, al caricamento della pagina viene creata un'istanza della classe Worker che, caricando lo script passato al costruttore, avvia il meccanismo eseguendo l'istruzione setInterval(reply,2000). Questa richiama ogni 2 secondi il metodo reply() del worker che incrementa il contatore e lo comunica alla pagina per tracciare l'operazione.

Quindi ogni 2 secondi la pagina principale riceve un messaggio dal worker con il nuovo valore incrementato di step unità. Ogni volta che questo si verifica viene eseguita una call al metodo onmessage(event) del worker che provvede a tracciare l'operazione eseguendo la funzione trace().

Qualora l'utente modificasse il valore della proprietà step dell'oggetto token tramite l'apposito FORM, si verificherebbe una chiamata alla funzione updateStep() che registrato il nuovo valore invierà un messaggio al worker per notificargli la variazione del valore di incremento.

Qui sono disponibili i sorgenti dell'esempio, in formato .zip .

Alla prossima,
MA

lunedì 20 giugno 2011

HTML5 : geolocation & mappe online

In questo articolo HTML5 sarà spiegato il funzionamento delle API per la geolocalizzazione e verrà mostrato come utilizzare questo nuovo standard per localizzare la posizione di un utente e mostrarla su una mappa di Google.

A questo indirizzo è possibile leggere un articolo introduttivo a riguardo nel quale è mostrato come verificare se un browser supporta le API per la geolocalizzazione.

Geolocalizzazione

Geolocalizzare un browser utente significa identificare le coordinate geografiche, ovvero latitudine e longitudine, della posizione, o area, dalla quale arriva la richiesta HTTP dello stesso client.

Questo è possibile attraverso l'analisi dell'indirizzo IP del client connesso.

Una premessa

Le specifiche di sicurezza delle attuali API prevedono che i browser implementino un sistema di notifica per richiedere all'utente il consenso a rilevare le proprie coordinate.

L'oggetto Geolocation

Tramite l'implementazione dell'interfaccia navigator esposta dal browser (solo quelli che supportano le API in discussione) è possibile accedere ad un'istanza di classe che implementa l'interfaccia Geolocation.
E' grazie a quest'ultima che si recuperano le coordinate geografiche del client e si gestisco eventuali errori di diversa natura.

Al fine di recuperare la posizione geografica, l'oggetto geolocation espone il metodo getCurrentPosition(successCallback,errorCallback,options), i tre parametri in ingresso sono:

  1. successCallback: il nome della funzione di callback che verrà invocata qualora non vi siano errori nella rilevazione della posizione. Alla funzione verrà passato un oggetto di tipo Position il quale espone anche i valori di latitudine e longitudine.
  2. errorCallback: il nome della funzione di callback che verrà invocata qualora si verificassero errori nella rilevazione della posizione al fine di gestire l'inconveniente. Alla funzione verrà passato un oggetto di tipo PositionError il quale espone il codice identificativo dell'errore.
  3. options: un'istanza della classe PositionOptions grazie alla quale è possibile configurare alcuni parametri della richiesta come ad esempio l'accuratezza della rilevazione.

Siccome questo articolo non si prefigge l'analisi e la spiegazione completa dell'interfaccia Geolocation, per qualunque approfondimento fate riferimento alla documentazione ufficiale W3C o alla documentazione ufficiale delle implementazioni offerte da ogni produttore di browser.

Posizionare un client su una mappa

Di seguito un esempio dell'utilizzo delle sopracitate API nel quale al caricamento della pagina, composta di un IFRAME, viene eseguita una chiamata alla funzione getEmbeddedGMap() la quale, dopo aver rilevato le coordinate geografiche del browser, si occuperà di comporre la query per richiamare il servizio Google Maps e di fare apparire la mappa, sulla quale è posizionato il client, nell'elemento iframe.

La funzione getEmbeddedGMap() è in grado di gestire eventuali errori durante il recupero delle coordinate geografiche del browser utente.

Siccome alcuni browser (soprattutto Chrome) non eseguono la localizzazione se la pagina è locale, a questo indirizzo è possibile eseguire lo script dell'esempio.

<!DOCTYPE html>
<html>
<head>
    <title>Mappa Google con HTML5 Geolocation</title>
    <script type="text/javascript">
        /**
         * Interfaccia le API per localizzare il browser.
         * Se l'operazione ha successo visualizza la mappa
         * su cui è posizionato il client, altrimenti gestirà l'errore.
         */
        function getEmbeddedGMap(){
            if(testGeo())
                navigator.geolocation.getCurrentPosition(
                      createMap, manageError, {enableHighAccuracy:true});
        }

        /**
         * Verifica che il browser utente supporta le API.
         *
         * @return boolean
         */
        function testGeo(){
            if(!!navigator.geolocation)
                return true;
            
            alert('Il browser non supporta la Geolocalizzazione.');
            return false;
        }
        
        /**
         * Accede ai valori di latitudine e longitudine del client
         * e compone la query per richiamare il servizio 
         * di mappe online di Google e mostrare il risultato
         * nell'IFRAME.
         * 
         * @param position istanza della classe Position.
         */
        function createMap(position){
            var qry = 'http://maps.google.it/maps?hl=it&ie=utf8&q='
                    + position.coords.latitude + ',' 
                    + position.coords.longitude
                    + '&z=17&output=embed';
            document.getElementById('gMapPlace').src = 'qry';
        }
        
        /**
         * Gestisce eventuali errori durante la rilevazione 
         * delle coordinate geografiche del browser.
         * Accede al codice di errore dell'oggetto ricevuto 
         * e lo decodifica.
         * 
         * @param err istanza della classe PositionError.
         */
        function manageError(err){
            switch(err.code){
                case 1:
                    alert('PERMISSION DENIED');
                    break;
                case 2:
                    alert('POSITION ANAVAILABLE');
                    break;
                case 3:
                    alert('TIMEOUT');
                    break;
                default:
                    alert('UNKNOWN ERROR');
            }
        }
    </script>
</head>
<body onload="getEmbeddedGMap();">
    <h2>Posizione sulla mappa</h2>
    <iframe id="gMapPlace" style="width:400px; height: 400px;"></iframe>
</body>
</html>

Il codice sorgente è ben organizzato, ben commentato e di facile comprensione quindi ritengo che non ci sia nient'altro da spiegare; in compenso è il caso di sottolineare quanto sia stato facile eseguire questa operazione, il che è sicuramente merito del team di sviluppo che ha progettato e realizzato queste API.

Alla prossima,
MA.

sabato 18 giugno 2011

HTML5 : Microdata & semantica

In questo articolo sarà spiegato il concetto di microdata e verrà illustrato come utilizzare questo nuovo standard introdotto da HTML5 al fine di migliorare l'aspetto semantico del web.

Dal punto di vista della semantica HTML5 ha introdotto anche altri nuovi tags quali article, nav, header, footer ecc..

Non trattandosi di tags,i microdata sono gruppi di coppie e valori che estendono il markup di HTML5.

Gruppi di microdati

Poter descrivere un oggetto con tutte le sue caratteristiche è chiaramente lo scopo di un gruppo, o item, di microdati.

Per creare un item è necessario utilizzare l'attributo HTML5 itemscope, che essendo abbinato uno specifico elemento HTML (di cui è attributo) segna il contesto, ed il confine, nel quale valgono le informazioni, o proprietà, aggiuntive.

Al fine di caratterizzare meglio il soggetto descritto è utile aggiungere all'item le proprietà che maggiormente gli donano significato: questo è possibile utilizzando l'attributo itemprop valorizzandolo con il nome della proprietà descritta.

Di seguito un esempio sull'utilizzo dei microdata per la descrizione di una persona.

<!DOCTYPE html>
<html lang="it">
<head>
  <meta charset="utf-8" />
  <title>HTML5 microdata</title>
  <meta name="author" content="Mirko Agrati" /> 
</head>
<body>
  <div itemscope>
    <p>
    Benvenuti sul mio blog!
    Mi chiamo <span itemprop="nome">Mirko</span> 
    <span itemprop="cognome">Agrati</span>, mi diverte 
    <span itemprop="hobby">pubblicare articoli tecnici</span> 
    su questo blog e <span itemprop="hobby">giocare a calcetto</span> 
    con gli amici.
    I miei colori preferiti sono quelli della squadra di calcio che tifo: 
    <span itemprop="colore-pref squadra-pref">nero e azzurro</span> 
    </p>
  </div>
</body>

Come è possibile notare tutte le proprietà sono contrassegnate dall'attributo itemprop e sono racchiuse all'interno del recinto tracciato dal DIV con attributo itemscope.

Sempre osservando l'esempio, è possibile assegnare alla stessa proprietà più di un valore che la descrive (es: hobby) ma anche assegnare contemporaneamente lo stesso valore a più proprietà (es: colore-pref e squadra-pref).

Definire il tipo di dato e fornirne il dizionario

Per definire al massimo il tipo di dato da rappresentare tramite microdata è possibile crearne la definizione in una pagina HTML separata e referenziarla all'occorrenza tramite l'attributo itemtype. Questo va utilizzato insieme all'attributo itemscope.

<!DOCTYPE html>
<html lang="it">
<head>
  <meta charset="utf-8" />
  <title>HTML5 microdata</title>
  <meta name="author" content="Mirko Agrati" /> 
</head>
<body>
  <div itemscope itemtype="www.esempio-microdata.it/MA_Person">
    <p>
    Benvenuti sul mio blog!
    .................
    </p>
  </div>
</body>

Per farsi un'idea riguardo la definizione di un tipo di dato è possibile dare uno sguardo alle tipologie che Google utilizza e che ha pubblicato all'indirizzo http://www.data-vocabulary.org/.

A questo indirizzo invece è possibile capire come Google utilizza le informazioni microdata per la creazione dei famosi rich snippets.

Alla prossima,
MA.

giovedì 2 giugno 2011

HTML5 : Tag input e nuovi type

In questo articolo saranno trattati brevemente i nuovi tipi dell'elemento <input> introdotti da HTML5 e sarà mostrato come verificarne, tramite Javascript, il supporto offerto dai browsers.

Non essendo ancora HTML5 uno standard supportato da tutti i browsers è buona norma testare le nuove features senza darne per scontato il supporto.

HTML 4.1 dispone di poche tipologie di campi input, tanto che per qualunque personalizzazione era necessario estendere le loro funzionalità e comportamenti tramite javascript. Esempio di ciò può essere il classico calendario a comparsa da cliccare per valorizzare un campo di testo con una data ben formattata.

Le tipologie di <input> in HTML4.1

Fino ad oggi HTML4 offre le seguenti tipologie per l'elemento <input>:

  • text per campi di testo generici;
  • password per campi di testo che mascherano una password;
  • submit per bottoni di invio form HTML;
  • reset per cancellare le modifiche apportate ai campi di un form HTML;
  • button per bottoni generici;
  • checkbox per caselle di scelta multipla;
  • radio per caselle di scelta singola;
  • file per la scelta di un file da uploadare;

Di seguito la sintassi HTML per indicare la tipologia di un elemento input.

<-- Crea un bottone generico con attributo id -->
<input type="botton" id="idelemento">

Le nuove tipologie offerte da HTML5

HTML5 introduce una varietà di tipologie decisamente utili ed interessanti, ovviamente estendendo l'offerta di HTML4 senza nulla rimuovere.

Di seguito l'elenco delle nuove features riguardanti l'elemento <input>.

  • color per scegliere un colore e recuperare il suo valore esadecimale;
  • date per scegliere una data da un calendario o date picker;
  • datetime come date con l'aggiunta di un campo di testo de/incrementabile per scegliere un orario;
  • datetime-local come datetime con l'aggiunta del fuso orario;
  • email per campi di testo in cui inserire indirizzi e-mail;
  • month come date, ma nel campo di testo saranno riportati solo mese ed anno;
  • number per campi di testo in cui inserire valori numerici che possono essere de/incrementati tramite pulsanti;
  • range crea barre di scorrimento per la selezione di valori all'interno di un intervallo o range;
  • search per campi di testo con cui eseguire una ricerca;
  • tel per campi di testo in cui inserire un numero di telefono;
  • time per campi data/ora;
  • url per campi di testo in cui inserire indirizzi http;
  • week per campi di testo in cui inserire il numero della settimana relativa ad un anno;

Test del supporto alle nuove features

Purtroppo non esiste maniera di poter testare il supporto dei browsers a tutte le tipologie se non testandone una per ogniuna.

Quindi, per comodità, è bene creare una function javascript che dinamicamente possa testare il supporto offerto ad ogni singola tipologia.

Di seguito un esempio banale.

<!DOCTYPE html>
<html lang="it">
<head>
  <meta charset="utf-8" />
  <title>HTML5 Smart Template</title>
  <meta name="author" content="Mirko Agrati" />
  <script type="text/javascript">
  
  /** 
   * Array di tutte le tipologie 
   * su cui ciclare ed eseguire il test.
   */
  arr = ['color','date','datetime',
        'datetimelocal','email','month',
        'number','range','search','tel','time','url','week'];
  
  /**
   * Richiamata sull'evento onLoad del body.
   */
  function testInputType(){
    var el= document.createElement('input');
    for(var i=0; i<arr.length; i++){
      el.setAttribute('type', arr[i]);
      var ctx = document.getElementById('content');
      ctx.innerHTML += 'Test tipologia <i>' + arr[i] + 
          '</i>: supportata <i>' + (el.type == arr[i]) + 
          '</i><br>';
    }
  }
  </script>
</head>
<body onload="testInputType();">
<header>
  <h1>HTML5 MA Test Page</h1>
</header>
<article id="content">
  <h3>
    Test del supporto alle tipologie di <i><input></i>
  </h3>
</article>
<footer>
 <p>© Copyright 2011 by Mirko Agrati</p>
</footer>
</body>
</html>

Il codice è semplice e ben commentato: parla da sé.

A questo indirizzo è presente una pagina per testare le funzionalità dei browsers:
è una piccola unit test in fase di realizzazione che permette rapidamente di verificare il supporto offerto da qualunque browser ai nuovi componenti ed eventualmente vederne il rendering grafico. Potrete testare anche il supporto alla geo-localizzazione ed a Canvas.

E' infatti bene ricordare che HTML5 è supportato anche dai browsers di apparecchi mobili quali BlackBerry, iPhone e dispositivi Android e che ognuno di questi offre una propria personalizzazione grafica dei diversi componenti.

Alla prossima,
MA.