codice:
	// (C) Andrea Giammarchi
// closure per non avere problemi di ambiguità con la funzione dollaro
// inviata come variabile globale jQuery
(function($){$(function(){
	// funzione fulcro della gestione upload
	// il this è riferito alla form del caso
	function APCQuery(){
		var	i = 0,		// indice
			ids = [];	// lista di id univoci creati per inviare al server
					// dei valori "affidabili"
		// per ogni input di tipo file ...
		$(this).find("input").filter("[type=file]").each(function(){
			// si aggiunge un campo hidden immediatamente prima
			// utilizzando il nome APC_UPLOAD_PROGRESS ed un valore presumibilmente e preferibilmente univoco
			$(this).before(
				'<input type="hidden" name="APC_UPLOAD_PROGRESS[]" value="' +
					// valore salvato nella lista di id
					(ids[i++] = i + Math.random()) +
				'" />'
			);
		});
		
		// l'iframe utilizzato come target non è ancora presente
		// è fondamentale inserirlo in un nodo DOM esterno al form
		// altrimenti non verrebbe accettato come target
		$(this.parentNode).append(
			'<iframe class="apcquery" name="' +
				// il nome dell'iframe deve essere univoco e per far si che questo avvenga
				// lo si assegna sfruttando un prefisso fittizio "apcqueryiframe"
				// più l'insieme degli id univoci concatenati tramite un carattere qualunque,
				// possibilmente ASCII standard, in questo caso il carattere "-"
				(this.target = "apcqueryiframe" + ids.join("-")) +
			'" id="'
				// per compatibilità si assegna anche l'id utilizzando
				// lo stesso nome univoco scelto per il name
				+ this.target +
			'" src="' +
				// per evitare problemi (avvisi) in ambiente sicuro (SSL)
				// è sempre meglio mettere un source all'iframe
				// (Internet Explorer chiederebbe una conferma codice insicuro se omesso)
				(this.action = this.action.split("?")[0]) +
			'"></iframe>'
		);
		// da notare che durante la creazione dell'iframe sono stati riassegnati
		// sia il target che l'action della form.
		// Nel primo caso, il target, si aggiunge l'iframe per evitare che il submit
		// della form vada all'altra pagina mentre nel secondo, l'action,
		// si elimina la querystring dedicata al redirect e quindi 
		// agli utenti che non hanno JavaScript abilitato o che non sono
		// compatibili con questo codice
		
		// non resta che assegnare l'evento "onsubmit" della form ...
		$(this).submit(function(){
		
			// per prima cosa l'evento deve capire se effettivamente c'è qualcosa da fare ... 
			// dato che si sfrutta il client per l'upload, tanto vale sfruttarlo anche per diminuire le chiamate al server
			var	result = !!$(this).find("input").filter("[type=file]").filter(function(){return !!this.value}).length;
			
			// se tra i vari input di tipo file almeno uno è stato specificato ed ha quindi un valore
			// si procede con il submit
			if(result){
				var	delay = 500,		// tempo, in millisecondi, ogni quanto il client chiede informazioni al server
					timeout = 0,		// intervallo assegnato dalla funzione setTimeout
					apcquery = $(this).find("div.apcquery"),	// contenitore informazioni all'interno della form
					iframe = $(this.parentNode).find("iframe[name=" + this.target + "]").get(0),	// l'iframe utilizzato come target del form
					self = this,		// riferimento alla form stessa, utile per le funzioni innestate in intervalli e/o altro
					onload = function(){	// callback richiamata ad iframe caricato
						// si annullano gli eventi precedentemente assegnati all'iframe
						iframe.onload = iframe.onreadystatechange = function(){};
						
						// si annulla il timeout precedentemente assegnato
						clearTimeout(timeout);
						
						// si elabora il risultato presente, grazie alla classe PHP5, nella window dell'iframe e non nel documento
						// (più compatibilità per meno operazioni)
						APCResult(apcquery, iframe.contentWindow.response, function(){
							// si mostra per il tempo scelto le informazioni complete ...
							setTimeout(function(){
								// si riabilita il bottone submit
								$(self).find("input[type=submit]").each(function(){
									this.disabled = false;
								});
								// e si rinascondono le informazioni
								apcquery.css("visibility", "hidden");
							}, delay);
						});
					};
				
				// proprietà utilizzata esclusivamente da Explorer ma se presente, anche da Opera
				iframe.onreadystatechange = function(){
					// se lo stato dell'iframe è caricato o completo
					if(/loaded|complete/i.test(iframe.readyState))
						// si richiama la callback onload
						onload();
				};
				
				// evento utilizzato da tutti i browsers tranne che da Explorer (al solito insomma)
				iframe.onload = onload;
				
				// da notare che non è possibile assegnare la callback onload direttamente ad iframe.onload
				// per poi richiamarla all'onreadystatechange per il semplice motivo che Explorer non darà
				// errore ... ma non effettuerà nemmeno la chiamata (al solito insomma ...)
				
				// tentativo di evitare doppio upload, stress inutile per il server
				$(this).find("input[type=submit]").each(function(){
					// si disabilita il submit della form (uno o più di uno se presenti)
					this.disabled = true;
				});
				
				// si assegna il valore in percentuale allo span dedicto a mostrare lo stato sotto forma di barra (zero inizialmente)
				apcquery.find(".percent").css("width", "0%");
				
				// si mostra il contenitore di informazioni e si assegna il valore zero a tutti i campi
				apcquery.css("visibility", "visible").find(".total,.loaded,.rate").text(APCSize(0));
				
				// si assegna un timeout per cominciare a richiedere informazioni al server
				// ... è totalmente inutile farlo immediatamente, l'upload deve ancora cominciare ...
				timeout = setTimeout(function(){
					var	i = 0,				// indice array
						result = [],			// array risultati
						callee = arguments.callee;	// questa stesa funzione
					
					// per ogni input che invia l'informazione APC_UPLOAD_PROGRESS ...
					$(self).find("input")
					.filter(function(){
						return	this.name === "APC_UPLOAD_PROGRESS[]"
					})
					// si assegna ala lista risultati la chiave APC_PK contenente il valore dell'input
					// gestiti entrambi dalla classe PHP5
					.each(function(){
						result[i++] = 'APC_PK[]=' + this.value;
					});
					
					// si richiama la pagina server, in questo caso la classe PHP5, concatenando
					// le chiavi ed i rispettivi valori
					$.getJSON(self.action + "?" + result.join("&"),
					// una volta ricevuto il risultato ...
					function(data){
						// lo si invia alla funzione dedicata al fine di mostrare quanto
						// ricevuto dal server
						APCResult(apcquery, data, function(){
							// questa callback serve per evitare di intasare il server di chiamate
							// Se si utilizza un intervallo infatti il client
							// continuerà impoerterrito a richiedere informazioni mentre
							// l'opzione più ovvia è attendere che almeno l'ultima richiesta
							// sia stata elaborata.
							// tutto questo è gestito nella funzione APCResult
							timeout = setTimeout(callee, delay);
						})
					});
				}, delay);
			};
			
			// si restituisce il risultato per attivare o disattivare il submit
			// (se non c0è alcun input di tipo file selezionato o con un valore restituisce false, non accade niente)
			return	result;
		});
	};
	
	// funzione di gestione risultati
	function APCResult(div, data, callback){
		var	loaded = 0,	// totale bytes inviati
			total = 0,	// totale bytes necessari per terminare l'upload
			i = 0;		// indice array (data)
		
		// è necessario avere un riferimento per gestire
		// il rate, in questa pillola presente fin da subito
		// e non solo ad upload terminato, come previsto da APC
		if(!div.__APCRate)
			div.__APCRate = {
				time:new Date,	// tempo richiesta
				value:0		// dati scaricati
			};
		while(i < data.length){
		
			// se data[i] non contiene il valore false,
			// default quando il server non sa cosa fare,
			// si assegnano tutti i caricati e tutti i totali
			if(data[i]){
				loaded += data[i].current;
				total += data[i].total;
			};
			++i;
		};
		
		// per avere un rate verosimile si sottrae il totale dati caricati
		// a quelli precedenti per poter gestire un rapporto upload / tempo trascorso
		div.__APCRate.value = loaded - div.__APCRate.value;
		
		// si scrive il totale tramite la funzione APCSize
		div.find(".total").text(APCSize(total));
		
		// si scrive il totale tramite la funzione APCSize
		div.find(".loaded").text(APCSize(loaded));
		
		// si scrive il rate eliminando la dicitura bytess (convertita in bps)
		div.find(".rate").text(APCSize(parseInt((div.__APCRate.value * 100) / (new Date - div.__APCRate.time)) || 0).replace(/bytes/, "b") + "ps");
		
		// si assegna la percentuale in base al rapporto bytes uploadati / totale bytes da uploadare
		div.find(".percent").css("width", (Math.round((loaded * 100) / total) || 0)+ "%");
		
		// si aggiorna il tempo per il rate successivo
		div.__APCRate.time = new Date;
		
		// se è stata specificata una callback la si richiama
		// ovvero si assegna il prossimo setTimeout per avere informazioni
		// o si esce dal "programma" - caso onload
		if(callback)
			callback();
	};
	
	// resittuisce un risultato compresinbile, da bytes a Kb, Mb o altro
	function APCSize(value){
		var	type = ["bytes", "Kb", "Mb", "Gb", "Tb", "Zb"],
			i = 0;
		while(value > 1024 && ++i)
			value /= 1024;
		return	(i ? value.toFixed(2) : Math.ceil(value)) + " " + type[i];
	};
	
	// si avvia il programma automatizzato scegliendo solo
	// le form della pagina che contengono un input di tipo file
	$("form").each(function(){
		if(0 < $(this).find("input").filter("[type=file]").length)
			APCQuery.call(this);
	});
	
	// normalizzazione numerica, funzione toFixed non presente in IE5.5 o inferiore
	if(!Number.prototype.toFixed)
		Number.prototype.toFixed = function(max){
			var	result = Math.pow(10, parseInt(max)), tmp;
			result = String(Math.round(this * result) / result);
			if(max > 0){
				tmp = result.split(".");
				tmp[1] = (tmp[1] || "") + new Array(++max - (tmp[1] ? tmp[1].length : 0)).join("0");
				result = tmp.join(".");
			};
			return	result;
		};
})})(jQuery);