Nessun risultato. Prova con un altro termine.
Guide
Notizie
Software
Tutorial

XChat

Tutorial per la realizzazione di una chat usando il paradigma Ajax
Tutorial per la realizzazione di una chat usando il paradigma Ajax
Link copiato negli appunti

Aspetti teorici

Prima di iniziare a sviluppare l'applicazione è opportuno soffermarsi su alcune considerazioni teoriche. Vediamo quali sono i "passi" fondamentali per sviluppare un'applicazione di questo genere con protocolli non basati su socket che non hanno stato (l'HTTP è uno di questi).

Tre diversi comportamenti

All'interno dell'applicazione è possibile identificare tre diversi comportamenti, relativi alle tre diverse azioni che si possono verificare.

Fase di login

Definiamo il comportamento dell'applicazione durante la fase di login:

  1. L'utente si collega al sito e riceve in risposta la pagina HTML contenente tutti gli script necessari in futuro
  2. L'utente effettua il login sulla chat e il browser invia il nick tramite l'oggetto XmlHttpRequest
  3. Il server riceve il nick, gli assegna un id univoco (un session id), salva il nome nel database e restitituisce l'id generato al browser che lo salva in una variabile globale per averlo sempre a disposizione. Da questo momento l'utente non sarà più identificato dal nick, ma dall'id che sarà univoco per ciascun utente collegato alla chat.

Refresh della pagina

Il secondo comportamento è il refresh della pagina, attivato ad intervalli regolari (pochi secondi) dal client (browser) una volta effettuato il login:

  1. Il client invia una richiesta di refresh al server specificando il proprio session id e l'id dell'ultimo messaggio ricevuto (richiede solo messaggi per lui nuovi)
  2. Il server risponde con l'elenco dei nuovi messaggi e degli utenti ancora collegati
  3. Il client aggiorna l'interfaccia sulla base dei dati ricevuti

L'invio di un messaggio

L'ultimo comportamento è l'invio di un messaggio. Questo comportamento è simile al precedente ma introduce due aggiunte:

  • Il client invia oltre al proprio id e all'ultimo messaggio ricevuto anche il messaggio scritto dal proprio utente
  • Il server prima di rispondere, salverà sul database questo nuovo messaggio. La risposta è identica a quella del comportamento numero 2, contiene quindi tutte le informazioni "nuove" per il client.
Tabella riassuntiva dei tre comportamenti
Comportamento Informazioni inviate (client) Informazioni ricevute (client)
Login Nickname Session id
Elenco utenti
Id ultimo messaggio
Refresh Session id
Id ultimo messaggio
Elenco utenti
Elenco nuovi messaggi
Id ultimo messaggio
Invio MSG Nuovo messaggio
Session id
id ultimo messaggio
Elenco utenti
Elenco nuovi messaggi
Id ultimo messaggio

La persistenza degli utenti

HTTP è un protocollo senza stato, quindi non è possibile sapere in modo preciso quando un utente è collegato o meno ad una applicazione. Nel nostro caso abbiamo però bisogno di sapere quali e quanti utenti sono collegati in un intervallo di tempo. Per questo, la XChat sfrutta una tecnica abbastanza comune: la tecnica del timeout.

Sul server è salvato, l'istante preciso nel quale ogni utente ha fatto l'ultima richiesta (login,refresh o invio). Al momento di una nuova richiesta, questo valore viene sostituito con il timestamp attuale. Se il tempo trascorso dall'ultima richiesta è maggiore del timeout (ad esempio 5 secondi) l'utente viene considerato scollegato.

Questa non è una soluzione efficientissima in quanto non è precisa al secondo, ma è il meglio che si possa ottenere tramite il protocollo http che non è stato pensato per applicazioni di questo tipo.

Caratteristiche dell'applicazione

Dopo la breve introduzione teorica, esaminiamo le tecniche e le tecnologie usate per lo sviluppo di questa applicazione.

Per la parte server sono state utilizzate le seguenti tecnologie:

  • PHP
  • 2 librerie a supporto:
    • Database.class.php (libreria sviluppata da me per semplificare l'interfacciamento con il database MySQL)
    • Json.class.php (libreria utile per generare codice JSON a partire da qualsiasi tipo di variabile o di oggetto php)
  • MySQL

Come si può intuire il modello applicativo utilizzato per lo scambio di dati è il JSON-RPC.

Per la parte client invece le tecnologie utilizzate sono state:

  • JavaScript
  • 1 libreria a supporto:
    • net.ContentLoader (libreria sviluppata da me per semplificare la gestione dell'oggetto XmlHttpRequest).

Note relative al codice

Prima di entrare nel vivo del tutorial è utile un breve chiarimento sulla notazione nel codice: nell'elencare eventuali metodi o funzioni scritti in PHP, mettiamo in evidenza i tipi di dati ritornati e passati come parametro, questo solo per migliorare la comprensione e il modo d'uso del codice. È noto che PHP non è affatto strongly-type, quindi attenzione al copia/incolla.

L'applicazione server-side

Le librerie utilizzate

Per la realizzazione degli aspetti server, usiamo due librerie per semplificare e rendere più leggero il lavoro. Esaminiamone il contenuto.

La libreria Database.class

Questa libreria creata da me, si occupa di gestire in maniera totalmente trasparente la gestione del database MySQL. Il suo utilizzo è molto semplice: basta infatti definire alcune costanti relative alla connessione al database e creare un istanza dell'oggetto database. Una volta creato questo oggetto i metodi utilizzabili sono pochissimi:

metodo descrizione
public database() costruisce l'oggetto e si connette al database secondo le costanti definite nel file PHP
public array doQuery(string $query) esegue la query e ritorna (in caso di SELECT) le righe in un array bidomensionale associativo
public void close() chiude la connessione al database

Usare questa libreria è molto semplice e non dovrebbe creare particolari problemi. Maggiori informazioni possono essere trovate sul breve articolo pubblicato sul mio sito web.

La libreria JSON.class

Questa libreria, realizzata da Jack Sleight (www.reallyshiny.com) si occupa di convertire ciascuna entità PHP (sia essa stringa, intero, array, oggetto) nella corrispondente notazione JSON.

Il JSON può essere definito come un particolare modo di rappresentare gli oggetti in formato testuale, facilmente leggibile da un umano, ma anche dal motore JavaScript, in grado di creare un oggetto a partire da una stringa JSON in tempi rapidissimi. Questa libreria permette di convertire rapidamente ciascuna entità PHP in un'entità identica in JavaScript.

Per approfondire l'argomento JSON, vi rimando all'articolo su Ajax e JSON oppure al sito web www.json.org.

L'utilizzo di questa libreria è elementare: una volta identificata la stringa relativa all'oggetto sarà necessario stamparla e inviarla al client che si occuperà di "parsarla" in un modo praticamente indolore. Riportiamo brevemente i metodi più utilizzati:

metodo descrizione
public json() costruisce l'oggetto
public string encode(object $entity) codifica l'oggetto in una stringa

L'oggetto Response

Al momento dell'implementazione, la prima cosa che ho ritenuto utile sviluppare è stata la classe Response, che si occupa di contenere tutti i dati da spedire al client ed i metodi necessari per andare a recuperare questi dati interrogando il database. Analizziamone le proprietà:

proprietà descrizione
int $sessionId; sessionId del client corrente
int $lastId; id dell'ultimo messaggio inviato al client
array $messaggi = array(); array contenente i nuovi messaggi
array $utenti = array(); array contenente gli utenti correntemente online nella chat

Queste sono le informazioni che ad ogni refresh vengono mandate al client, aggiornando la sua situazione con tutte le "news".

All'interno dell'array $messaggi, andremo a inserire una collezione di oggetti Messaggio, mentre nell'array $utenti, inseriremo semplicemente i nick degli utenti.

La struttura della classe messaggio è la seguente, ed ha uno scopo solamente strutturale, di divisione dei dati:

Listato 1. La classe Messaggio

class Messaggio {

  //nick del utente che ha scritto il messaggio

  var $utente;

  //contenuto del messaggio

  var $messaggio;

  //data di invio

  var $data;


  //costruttore che toglie eventuali slash alle stringe e rende la data leggibile

  function messaggio($u,$m,$d,$t) {
    $this->utente = stripslashes($u);
    $this->messaggio = stripslashes($m);
    $this->data = date("G:i:s",$d);
    $this->type = $t;
  }
}

Una volta definite le strutture di contenimento dei dati, andiamo ora ad analizzare l'intera classe Response, composta anche da metodi che permettono di definire tutte le proprietà viste in precedenza.

Listato 2. La classe Response

class Response {
  var $sessionId;
  var $lastId;
  var $messaggi = array();
  var $utenti = array();


  // imposta l'id passato come parametro*

  function setSessionId($id) {
    $this->sessionId = $id;
  }


  // aggiunge gli utenti all'array

  function setAllUtenti() {
    global $db;
    $query = "SELECT nick FROM xchat_users";
    $r = $r = $db->doQuery($query);
    foreach($r as $utente)
      $this->utenti[] = stripslashes($utente['nick']);
  }


  // setta l'ultimo id relative all'ultimo messaggio inviato

  function setLastId() {
    global $db;
    $query = "SELECT MAX(id) AS last FROM xchat_mex";
    $r = $db->doQuery($query);
    $this->lastId = $r[0]['last'];
  }


  //aggiunge nuovi messaggi all'array

  function setAllMessaggi($lastMex) {
    global $db;

    $query = "SELECT nick, mex, UNIX_TIMESTAMP(t) AS tt, type FROM xchat_mex WHERE id > '$lastMex' ORDER BY id";
    $r = $db->doQuery($query);
  
    foreach($r as $m)
      $this->messaggi[] = new Messaggio($m['nick'], $m['mex'], $m['tt'], $m['type']);
  }
}

Oltre alle proprietà viste in precedenza, notiamo 4 metodi che si occupano semplicemente di andare a impostare gli attributi dell'oggetto in questione. Tranne il primo metodo alquanto banale, gli altri utilizzano la variabile globale $db (la creeremo nella pagina principale) per eseguire query al database utilizzando la libreria Database vista in precedenza e impostano i vari risultati alle proprietà dell'oggetto.

La funzione updateMyTimeout

Prima di passare ai diversi comportamenti che l'applicazione deve gestire, vorrei aprire una piccola parentesi su questa funzione, importantissima per la gestione e la persistenza degli utenti della chat.

Questa funzione si occupa di aggiornare la "situazione" dell'utente corrente, incrementando il suo timeout di 5 secondi (in parole povere, dandogli altri 5 secondi di vita nell'applicazione) e sconnettendo gli utenti con un timeout superato. Il tutto viene fatto semplicemente tramite due query:

Listato 3. Metodo updateMyTimeout()

function updateMyTimeout($userId) {
  global $db;

  //incremento il mio timeout

  $query = "UPDATE xchat_users SET t = NOW() + INTERVAL 5 SECOND WHERE id = '$userId'";
  $db->doQuery($query);
  

  //cancello gli utenti scollegati

  $query = "DELETE FROM xchat_users WHERE t < NOW()";
  $db->doQuery($query);
}

Una volta compresa la parte teorica dell'approccio, la sua implementazione è relativamente semplice. Da notare solamente la prima query che utilizza le funzioni temporali messe a disposizione da MySQL (NOW() + INTERVAL 5 SECOND).

I tre casi d'uso dell'applicazione

Una volta definito l'accesso ai dati, è giunto il momento di gestire i tre casi d'uso visti in precedenza (login, refresh "puro", refresh con invio di un nuovo messaggio). Lo strumento per identificare in quale caso d'uso il server dovrà agire, è la variabile $_POST che contiene i parametri inviati dal client, in particolare $_POST[‘action'], che conterrà l'azione da eseguire.

In base a questa informazione ed alle informazioni richieste dal client, l'oggetto Response dovrà essere configurato in tre modi differenti. La gestione delle varie azioni viene implementata tramite uno switch che si occupa di delegare le varie azioni.

Analizziamo questo listato. Dopo aver creato i tre oggetti necessari e parsato l'array $_POST, entra in azione lo switch, che separa l'esecuzione in tre blocchi in base all'azione richiesta dal client.

Nel caso di login, lo script aggiunge l'utente al database e gli associa l'id di sessione (a partire dal timestamp corrente), oltre che assegnare all'oggetto $response le dovute proprietà. Nel caso di nuovo messaggio, lo script salva lo stesso nel database e riempie l'oggetto $response. Infine nel caso di un semplice refresh della pagina, lo script si occupa solamente di aggiornare l'oggetto $response.

Una volta terminato lo script, viene encodato e stampato l'oggetto $response e viene chiusa la connessione con il database.

Conclusioni

La parte server-side è conclusa. Basta semplicemente salvare il file php contenente lo switch principale e includervi, oltre alla definizione delle classi Messaggio e Response, anche le librerie esterne Database.class e JSON.class.

Passando all'implementazione della parte client dobbiamo solamente ricordarci i tre casi d'uso con i relativi comandi (da passare tramite POST nel parametro action) e l'insieme dei dati inviati e ricevuti (vedi tabella riassuntiva precedente).

L'applicazione client-side

A differenza della parte lato server, in questa sezione si deve tener conto non solo del "motore" del programma, ma anche dell'interfaccia utente, gestendo quindi eventi, funzioni callback e modifiche runtime della pagina.

In questa sezione non mi occuperò di aspetti estetici e grafici della pagina, ma mi soffermerò solo sugli aspetti strettamente funzionali.

Le libreria utilizzate

Per la realizzazione di questo aspetto, ho utilizzato una libreria, sempre sviluppata da me, che si occupa di gestire in maniera totalmente trasparente ed astratta la gestione dell'oggetto XmlHttpRequest: la libreria net.ContentLoader.

La libreria net.ContentLoader

Questa libreria dunque permette di sfruttare l'oggetto XmlHttpRequest all'interno di una pagina per eseguire richieste asincrone (AJAX) in maniera totalmente automatica.

L'idea di questa libreria risale alla lettura del libro AJAX in Action (pubblicato da Manning) e prevede solamente la costruzione dell'oggetto net.ContentLoader seguendo un determinato costruttore:

net.ContentLoader = function(url, onload, params)

I parametri da passare sono relativamente semplici:

  • url identifica l'indirizzo della pagina,
  • onload identifica una funzione callback e
  • params identifica i parametri da passare al server.

Istanziando un oggetto di questo tipo, la libreria crea una connessione verso l'url specificato, passandogli eventualmente parametri, e, in caso di successo, esegue la funzione onload passandole come parametro il contenuto della pagina presente sul server.

Il DOM della pagina

La pagina assume durante il ciclo di vita dell'applicazione due diverse interfacce: una "pre-login" e una "post-login".

L'aspetto pre-login è composto semplicemente da un div che comprende un campo di testo, un input e un bottone:

Listato 4. Aspetto "pre-login"

<div id="form">
  <span id="nickname">Nickname:</span>
  <input type="text" id="nickInput" value="nick"/>
  <button id="nickButton">Login!</button>
</div>

Una volta effettuato il login, viene generata automaticamente la struttura della seconda interfaccia:

Listato 5. Template dell'interfaccia post-login

<div id="form">
  <span>Messaggio:</span>
  <input id="mexInput" type="text"/>
  <button>Invia messaggio</button>
</div>
<div id="online">
  <b>Utenti online:</b>
  <!-- elenco utenti online -->

</div>

<div id="mex”>
  <!-- elenco messaggi -->

</div>

Le variabili globali dell'applicazione

All'interno dell'applicazione client ho istanziato 3 variabili globali che in questo modo vengono rese accessibili da tutte le funzioni disponibili.

Variabile Utilizzo
var userId; contiene l'userId definito dal server
var lastMex; contiene l'id dell'ultimo messaggio ricevuto
var userList = new Array(); contiene l'elenco degli utenti correntemente loggati nella chat
var timeout; permette di bloccare il timeout da qualsiasi funzione

I tre casi d'uso dell'applicazione

Anche in questo ambito, l'intero script fa capo ai tre diversi casi d'uso che, ricordo, sono il login, l'invio di un nuovo messaggio e il refresh temporizzato della pagina. Le funzioni in questo caso sono molto più distinte a seconda del caso d'uso e quindi verranno analizzate in diversi paragrafi.

Le funzioni di login

Il primo caso d'uso dell'applicazione è legato al login dell'utente. Esso avviene tramite la casella di testo con id "nickInput" e al bottone con id "nickButton". La prima cosa da fare è quindi creare una funzione login e associarla all'evento onclick del pulsante:

Listato 6. Funzioni per il login

// imposto la funzione login come gestore dell'evento onclick

window.onload = function() {
  document.getElementById("nickButton").onclick = login;
}

// definisco la funzione login

function login() {
  document.getElementById("nickButton").disabled = true;
  var nick = document.getElementById("nickInput").value;
  if(nick = validateNick(nick)) {
    var param = "action=login&nick="+nick;
    new net.ContentLoader("request.php", loginCallback,
param);
  }
}

// definisco la funzione che permette di validare il nick inserito

function validateNick(nick) {
  nick = trim(nick);

  if(nick.length==0) alert("Completa il campo nick!");
  else if(nick.length>20) alert("Nick troppo lungo!");
  else return nick;

  document.getElementById("nickButton").disabled = false;
  return false;  
}

La funzione login esegue alcune semplici operazioni. Anzitutto disabilita il pulsante per evitare lo scomodo "multi-submit". Successivamente estrae il nick dal campo input e, con la funzione validateNick, controlla che esso sia compatibile con l'applicazione.

Se la validazione va a buon fine, lo script costruisce la variabile param impostando come action la stringa login e come nick la stringa inserita dall'utente.

L'ultima operazione che la funzione esegue è quella di istanziare un oggetto net.ContentLoader passandogli come url "request.php", come funzione callback loginCallback e la strimga di parametri appena costruita.

Questo oggetto quindi si occupa di gestire la connessione verso l'url e, una volta ricevuti correttamente i dati, esegue la funzione impostata come callback, in questo caso loginCallback.

Listato 7. La funzione loginCallBack

function loginCallback(txt) {
  response = eval('(' + txt + ')');
  userId = response.sessionId;

  var mexText = document.createElement("SPAN");
  mexText.innerHTML = "Messaggio: ";
  var mexInput = document.createElement("INPUT");
  mexInput.setAttribute("id","mexInput");
  mexInput.setAttribute("type","text");
  mexInput.onkeyup = keyListener;
  
  var mexButton = document.createElement("BUTTON");
  mexButton.onclick = sendMex;
  mexButton.innerHTML = "Invia messaggio";

  var div = document.getElementById("form");
  div.removeChild(document.getElementById("nickInput"));
  div.removeChild(document.getElementById("nickButton"));
  div.removeChild(document.getElementById("nickname"));
  div.appendChild(mexText);
  div.appendChild(mexInput);
  div.appendChild(mexButton);

  var divOnline = document.createElement("DIV");
  divOnline.setAttribute("id","online");
  divOnline.innerHTML = "<b>Utenti online: </b>";
  document.body.appendChild(divOnline);

  var divMex = document.createElement("DIV");
  divMex.setAttribute("id","mex");
  document.body.appendChild(divMex);
  
  refreshCallback(txt);
  chat();
  mexFocus();
}

La maggior parte delle linee di codice costruiscono elementi del DOM e su queste non vale la pena soffermarsi, notiamo invece il parametro txt, obbligatorio per ciascuna funzione callback per l'oggetto net.ContentLoader. Esso contiene le informazioni ricevute dal server.

Grazie al comando eval è possibile convertire la stringa txt (che ricordo essere un oggetto PHP codificato in JSON) ed ottenere un oggetto JavaScript, con le stesse proprietà definite in PHP accessibili con la notazione oggetto.proprietà.

Lo script, dopo aver salvato nella variabile globale il "sessionId" contenuto nell'oggetto response, modifica il DOM della pagina grazie alle funzioni messe a disposizione dal motore JavaScript.

Una volta aggiornata la struttura della pagina, viene invocata la funzione refreshCallback (passandogli come argomento lo stesso argomento txt della funzione loginCallback), la funzione chat e la funzione mexFocus.

  • refreshCallback è la funzione callback invocata dal metodo refresh e che si occupa di gestire il refresh della pagina.
  • chat si occupa di impostare il timeout permettendo il refresh della pagina ogni 3 secondi.
  • mexFocus, semplicemente applica il focus alla casella di testo che conterrà i messaggi scritti dall'utente.

In questa parte dell'articolo continuiamo a studiare i comportamenti dell'applicazione lato client, in particolare esaminiamo le funzioni per il refresh e per l'invio dei messaggi.

Le funzioni di refresh

Listato 8. Funzione chat()

function chat() {
  refresh();
  timeout = setTimeout("chat()",3000);
}

Questa funzione si occupa di impostare il timeout ed invoca semplicemente ogni 3 secondi (3000 millisecondi) la funzione refresh:

Listato 9. Funzione refresh()

function refresh() {
  param = "action=refresh&userId="+userId+"&lastMex="+lastMex;
  new net.ContentLoader("request.php", refreshCallback, param);
}

Questa seconda funzione può essere assimilata alla funzione login: infatti si occupa solamente di creare una stringa di parametri e di istanziare una nuova connessione al server utilizzando come callback la funzione refreshCallback (che è la stessa che viene invocata nella funzione loginCallback) che si occupa di gestire i messaggi e gli utenti.

Listato 10. Funzione refreshCallback e le funzioni correlate

//funzione callback che delega i vari compiti ad altre sotto-funzioni

function refreshCallback(txt) {
  var response = eval('(' + txt + ')');  
  updateNickList(response.utenti);
  updateMexList(response.messaggi);
  lastMex = response.lastId;  
}

//gestisce l'elenco dei nick collegati

function updateNickList(utenti) {
  for(i in utenti) {
    nuovo = utenti[i];
    if(!inArray(userList,nuovo)) addUserOnline(nuovo);
}
  for(i in userList) {
    vecchio = userList[i];
    if(!inArray(utenti,vecchio)) removeUserOnline(vecchio);
}
  printUserOnline(utenti);
  userList = utenti;
}

// gestisce e stampa l'elenco dei nuovi messaggi
function updateMexList(messaggi) {  
  for(i=0; i<messaggi.length;i++) {
    document.getElementById("mex").innerHTML+="<b>"+
        messaggi[i].data+" "+messaggi[i].utente+":</b>"+
        messaggi[i].messaggio+"<br/>";
}
  var dv = document.getElementById("mex");
  dv.scrollTop = dv.offsetHeight;
}

// notifica l'arrivo di un nuovo utente
function addUserOnline(username) {
  document.getElementById("mex").innerHTML +=
      "<span class='newUser'><b>E' entrato l'utente "+username+"</span><br/>";
}

// notifica l'uscita di un utente già presente
function removeUserOnline(username) {
  document.getElementById("mex").innerHTML +=
      "<span class='newUser'><b>L'utente "+username+" ci ha lasciato</span><br/>";
}

//si occupa di stampare l'elenco degli utenti nella pagina
function printUserOnline(utenti) {
  document.getElementById("online").innerHTML = "<b>Utenti online:</b> ";

  for(u in utenti)
    document.getElementById("online").innerHTML+=utenti[u]+" ";
}

//funzione di utilità: permette di sapere se value è contenuto in ar
function inArray(ar,value) {
  presente = false;
  for(a in ar)
    if(ar[a] == value) presente = true;

  return presente;
}

Una volta effettuato l'eval per ottenere un oggetto conforme a JavaScript, questa funzione delega i vari compiti (gestione utenti e gestione messaggi) ad altre due funzioni e aggiorna la variabile globale lastId con l'id ricevuto.

La gestione utenti viene effettuata da updateNickList la quale riceve un array di utenti ed esegue 4 diversi compiti confrontando la nuova lista con la vecchia lista presente nella variabile globale userList vista in precedenza:

  • controlla eventuali nuovi utenti presenti (invocando la funzione addUserOnline incaricata di notificare questa nuova presenza)
  • controlla eventuali uscite di utenti già presenti (invocando la funzione removeUserOnline incaricata di notificare l'uscita)
  • stampa l'elenco degli utenti correntemente collegati (invocando la funzione printUserOnline)
  • aggiorna la variabile globale userList con la nuova lista di utenti della chat.

Grazie alla funzione chat e al timeout, tutte i metodi di refresh vengono invocati periodicamente in maniera automatica, dando alla chat quel senso di continuità.

Le funzioni di invio dei messaggi

L'ultimo comportamento della nostra applicazione è quello relativo all'invio di un nuovo messaggio. Questo comportamento è molto simile al precedente in quanto, come vedremo, la sua richiesta remota non ha un proprio callback, ma si appoggia al callback delle funzioni di refresh.

Listato 11. Funzioni relative all'invio dei messaggi

//funzione callback dell’evento onclick sul pulsante di invio
function sendMex() {
  var mex = document.getElementById("mexInput").value;
  disableMexInput();
  clearTimeout(timeout);
  if(mex = validateMex(mex)) {    
    var param = "action=sendMex&userId="+userId+"&mex="+mex+"&lastMex="+lastMex;
    new net.ContentLoader("request.php", sendMexCallback, param);
  }
}

//funzione callback che richiama il refreshCallback
function sendMexCallback(txt) {
  refreshCallback(txt);
  mexFocus();
  timeout = setTimeout("chat()",3000);
  activateMexInput();
}

//funzione che impediscono l’invio multiplo di messaggi
function disableMexInput() {
  document.getElementById("mexInput").disable = true;
}
function activateMexInput() {
  document.getElementById("mexInput").disable = false;
}

//funzione di validazione per il messaggio
function validateMex(mex) {
  mex = trim(mex);
  if(mex.length==0) {
    activateMexInput();
    timeout = setTimeout("chat()",3000);
    return false;
  } else return mex;
}

//imposta il focus sul campo input del messaggio, cancellandone il
contenuto

function mexFocus() {
  document.getElementById("mexInput").value = "";
  document.getElementById("mexInput").focus();
}

//associa alla pressione del tasto invio, la funzione sendMex
function keyListener(e) {
  if(!e) e=window.event;
  if(e.keyCode==13) sendMex();
}

Rispetto alle precedenti, queste funzioni sono più semplici. Tutto parte dalla funzione sendMex() che viene invocata (come è possibile vedere nella loginCallback) nel momento in cui l'utente preme il pulsante per l'invio del messaggio.

Questa funzione una volta disabilitato il timeout (per evitare richieste sovrapposte al server) e il pulsante per l'invio, di occupa di costruire la stringa param includendo, oltre ai parametri standard, anche il nuovo messaggio correttamente validato.

Una volta ricevuta la risposta positiva dal server, la funzione sendMexCallback() si occupa di richiamare la refreshCallback, che si occuperà di refreshare la pagina anche con il nuovo messaggio inviato, e di rendere attivo sia il timeout, precedentemente interrotto, e di riabilitare l'invio di un nuovo messaggio.

Le ultime due funzioni (mexFocus e keyListener) sono semplici funzioni per migliorare l'interfaccia con l'utente. La prima permettendo di avere il focus sempre sul campo input del messaggio mentre la seconda associando alla pressione del tasto invio (con codice 13) la funzione sendMex, delegata di inviare il messaggio.

Conclusioni

L'applicazione client-side è sicuramente più complessa di quella server-side, e questo perchè si occupa anche di gestire al meglio l'interfacciamento con l'utente, attività che non viene mai svolta da un applicazione server-side.

Anche in questo caso però i tre diversi comportamenti dell'applicazione sono ben indipendenti e distinti tra di loro.

Ti consigliamo anche