<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0"
	xmlns:content="http://purl.org/rss/1.0/modules/content/"
	xmlns:wfw="http://wellformedweb.org/CommentAPI/"
	xmlns:dc="http://purl.org/dc/elements/1.1/"
	xmlns:atom="http://www.w3.org/2005/Atom"
	xmlns:sy="http://purl.org/rss/1.0/modules/syndication/"
	xmlns:slash="http://purl.org/rss/1.0/modules/slash/"
	>

<channel>
	<title>Recoding</title>
	<atom:link href="http://www.recoding.it/?feed=rss2" rel="self" type="application/rss+xml" />
	<link>http://www.recoding.it</link>
	<description>Sviluppo web e quant'altro</description>
	<lastBuildDate>Sun, 21 Feb 2010 17:12:01 +0000</lastBuildDate>
	<language>en</language>
	<sy:updatePeriod>hourly</sy:updatePeriod>
	<sy:updateFrequency>1</sy:updateFrequency>
	<generator>http://wordpress.org/?v=3.0</generator>
		<item>
		<title>Items su CodeCanyon</title>
		<link>http://www.recoding.it/?p=316</link>
		<comments>http://www.recoding.it/?p=316#comments</comments>
		<pubDate>Sun, 21 Feb 2010 17:10:55 +0000</pubDate>
		<dc:creator>eros</dc:creator>
				<category><![CDATA[Asp.Net]]></category>
		<category><![CDATA[Custom Control]]></category>

		<guid isPermaLink="false">http://www.recoding.it/?p=316</guid>
		<description><![CDATA[Recoding su CodeCanyon!]]></description>
			<content:encoded><![CDATA[<p>Per chiunque fosse interessato, segnalo l&#8217;apertura di un <a href="http://codecanyon.net/user/recoding/portfolio" target="_blank">nostro account su CodeCanyon</a>, il marketplace online di Envato, dedicato alla programmazione.<br />
Attualmente contiene due soli controlli, ma altri e più interessanti sono già in lavorazione, seguiteci!</p>
]]></content:encoded>
			<wfw:commentRss>http://www.recoding.it/?feed=rss2&amp;p=316</wfw:commentRss>
		<slash:comments>2</slash:comments>
		</item>
		<item>
		<title>Costruire un handler per immagini in Asp.Net</title>
		<link>http://www.recoding.it/?p=270</link>
		<comments>http://www.recoding.it/?p=270#comments</comments>
		<pubDate>Thu, 17 Sep 2009 12:19:38 +0000</pubDate>
		<dc:creator>eros</dc:creator>
				<category><![CDATA[Asp.Net]]></category>
		<category><![CDATA[Handler]]></category>
		<category><![CDATA[HttpHandler]]></category>
		<category><![CDATA[Images]]></category>

		<guid isPermaLink="false">http://www.recoding.it/?p=270</guid>
		<description><![CDATA[Tutorial per la costruizione di un httphandler in asp.net per la manipolazione delle immagini.]]></description>
			<content:encoded><![CDATA[<p><a class="codiceLink" href='http://www.recoding.it/wp-content/uploads/2009/09/DemoImgHandler.zip'><img src="wp-content/themes/charcoal/images/box_zip.gif" alt="" />download esempio</a><br />
<br />
In questo articolo illustreremo come costruire un handler in grado di manipolare un&#8217;immagine richiesta dal client.</p>
<h4>Il problema</h4>
<p>Quando inseriamo un&#8217;immagine (tipicamente la miniatura di un&#8217;immagine più grande) in una pagina web spesso riduciamo la dimensione dell&#8217;immagine utilizzando le proprietà css <code>width</code> e <code>height</code></p>
<pre class="html:nogutter" name="code">&lt;img src="immagine.jpg" alt="bla bla" style="width:200px; height: 200px;" /&gt;</pre>
<p>il che porta al resize operato dal browser che, più o meno, è una schifezza.<br />
L&#8217;alternativa è produrre la miniatura utilizzando programmi come Photoshop e Gimp, che scalano e ricampionano correttamente l&#8217;immagine.<br />
Spesso però non è possibile costruirsi a mano la miniatura. Innanzitutto perchè è una rottura, e quantomeno duplica il numero di immagini da gestire nel nostro markup. Spesso non possiamo nemmeno sapere a priori quali saranno le immagini, ad esempio nella visualizzazione di gallerie fotografiche uppate da altri utenti.</p>
<h4>La soluzione</h4>
<p>Asp.Net ci viene in aiuto con la tecnica degli HttpHandler generici.<br />
Senza dilungarci troppo nella presentazione degli handler diciamo semplicemente che un handler è una parte di codice che si occupa di intercettare una particolare richiesta inviata al server e di costruire una risposta adatta.<br />
Possiamo quindi immaginare di costruire una classe che implementa l&#8217;interafccia <code>IHttpHandler</code>, in grado di processare un&#8217;immagine richiesta in base ad alcuni parametri e riversare nello stream di output una versione modificata della stessa immagine (ovviamente senza modificare l&#8217;immagine originale).</p>
<p>La richiesta potrebbe includere una semplice scalatura dell&#8217;immagine, un ritaglio di una particolare sezione, l&#8217;arrotondamento degli angoli, ecc&#8230;</p>
<h4>Veniamo al codice</h4>
<p>La costruzione di un handler prevede 2 passi fondamentali:<br />
- Scrivere la classe<br />
- Aggiungere l&#8217;handler all&#8217;insieme degli handlers del nostro sito web.</p>
<p>Un primo abbozzo della classe (che chiameremo imghandler) potrebbe essere il seguente:</p>
<pre class="csharp:nogutter" name="code">
namespace Recoding.Web
{
	using System;
	using System.Web;
	using System.Drawing;
	using System.Drawing.Imaging;
	using System.Drawing.Drawing2D;

	public class imghandler : IHttpHandler
	{
		public void ProcessRequest(HttpContext context)
		{

		}

		public bool IsReusable { get { return false; } }
	}
}
</pre>
<p>Il metodo <code>ProcessRequest</code> è il cuore dell&#8217;handler e conterrà il comportamento da seguire per soddisfare la richiesta.<br />
I namespace <code>System.Drawing</code>, <code>System.Drawing.Imaging</code> e <code>System.Drawing.Drawing2D</code> costituiscono l&#8217;accesso alle librerie GDI+ che saranno indispensabili per la manipolazione dell&#8217;immagine (per una panoramica generale leggete <a href="http://msdn.microsoft.com/en-us/library/system.drawing%28VS.71%29.aspx" target="_blank">quì</a>).</p>
<p>Ora dobbiamo definire l&#8217;handler nel web.config del nostro sito web, per fare in modo che le richieste specifiche vengano dirottate a questa classe:</p>
<pre class="html:nogutter" name="code">
&lt;httpHandlers>
    &lt;remove verb="*" path="*.asmx"/>
    &lt;add verb="*" path="imghandler.ashx" type="Recoding.Web.imghandler"/>
&lt;/httpHandlers>
</pre>
<p>In questo modo ogni richiesta alla risorsa <code>imghandler.ashx</code> passerà per il nostro <code>ProcessRequest</code>, e saremo in grado di manipolarla.</p>
<h4>Definizione dei parametri</h4>
<p>Come prima cosa dobbiamo definire cosa l&#8217;handler è in grado di accettare come richiesta.<br />
Prevediamo quindi un set di costanti e attributi:</p>
<pre class="csharp:nogutter" name="code">
...
public class imghandler : IHttpHandler
{
	public static string _PARAM_IMAGEPATH = "imagepath";
	public static string _PARAM_WIDTH = "width";
	public static string _PARAM_HEIGHT = "height";
	public static string _PARAM_CROP = "crop";
	public static string _PARAM_VERTICALALIGN = "valign";
	public static string _PARAM_HORIZONTALALIGN = "halign";
	public static string _PARAM_DEFAULTIMAGEPATH = "defaultimagepath";
	public static string _PARAM_ROUNDED = "rounded";
	public static string _PARAM_RADIUS = "radius";

	public string imagepath { get; set; }
	public string defaultimagepath { get; set; }
	public string physical_imagepath { get; set; }
	private string imgExtension { get; set; }
	public int width { get; set; }
	public int height { get; set; }
	public bool crop { get; set; }
	public string valign { get; set; }
	public string halign { get; set; }
	public bool rounded { get; set; }
	public int radius { get; set; }

	public void ProcessRequest(HttpContext context) { ... }
...
</pre>
<p>Con questi parametri una possibile richiesta al nostro handler potrebbe essere costruita nella forma:</p>
<pre class="html:nogutter" name="code">
&lt;img alt="" src="imghandler.ashx?imagepath=immagine1.jpg&#038;width=200&#038;height=200" />
</pre>
<p>ad indicare che vogliamo ottenere l&#8217;immagine1.jpg scalata (o croppata) a 200 pixel per 200.</p>
<p>Ora possiamo procedere alla scrittura del corpo del metodo ProcessRequest, che come prima cosa dovrà ricavare i parametri passati in querystring, dopodichè procederà all&#8217;elaborazione vera e propria, che demanderemo ad altri 2 metodi:</p>
<pre class="csharp:nogutter" name="code">
public void ProcessRequest(HttpContext context)
{
	string sImg = context.Request.QueryString[_PARAM_IMAGEPATH];
	physical_imagepath = context.Server.MapPath(sImg.StartsWith("~/") ? sImg : "~/" + sImg);

	width = GenericConverter.TryGetValue&lt;int>(context.Request.QueryString[_PARAM_WIDTH], 0);
	height = GenericConverter.TryGetValue&lt;int>(context.Request.QueryString[_PARAM_HEIGHT], 0);
	crop = GenericConverter.TryGetValue&lt;bool>(context.Request.QueryString[_PARAM_CROP], false);
	rounded = GenericConverter.TryGetValue&lt;bool>(context.Request.QueryString[_PARAM_ROUNDED], false);
	radius = GenericConverter.TryGetValue&lt;int>(context.Request.QueryString[_PARAM_RADIUS], 8);

	defaultimagepath = "";
	if (context.Request.QueryString[_PARAM_DEFAULTIMAGEPATH] != null)
		defaultimagepath = context.Request.QueryString[_PARAM_DEFAULTIMAGEPATH].ToString();

	try
	{
		FileInfo fileImage = new FileInfo(physical_imagepath);
		if (!fileImage.Exists)
		{
			sImg = defaultimagepath;
			physical_imagepath = context.Server.MapPath(sImg.StartsWith("~/") ? sImg : "~/" + sImg);
		}
	}
	catch
	{
		sImg = defaultimagepath;
		physical_imagepath = context.Server.MapPath(sImg.StartsWith("~/") ? sImg : "~/" + sImg);
	}

	// Forzo l'estenzione a png nel caso sia richiesta la stondatura dell'immagine
	// ciò mi consente di avere lo sfondo trasparente nei punti non coperti
	imgExtension = rounded ? ".PNG" : Path.GetExtension(sImg);

	ProcessImage(context);
}
</pre>
<p>Come si può vedere, la prima operazione è costruire il percorso fisico dell&#8217;immagine nel server, se l&#8217;immagine non viene trovata si utilizzerà un&#8217;immagine di default rappresentata dal parametro <code>_PARAM_DEFAULTIMAGEPATH</code>, che potrebbe ad esempio essere il logo del sito.<br />
Al termine della raccolta parametri viene invocato il metodo ProcessImage, che si occuperà dell&#8217;effettiva elaborazione.</p>
<p>NOTA: la classe <code>GenericConverter</code> è un helper contenente dei metodi in grado di tentare una conversione di un dato in un certo tipo, se la conversione riesce viene restituito il valore, altrimenti viene restituito un valore di default passato come parametro. Ho sviluppato la classe all&#8217;interno del namespace <code>Recoding.Convert</code>, che occorre aggiungere tra gli using della classe. Il codice dell&#8217;helper è il seguente, è piuttosto banale e non mi dilungherò in spiegazioni:</p>
<pre class="csharp:nogutter" name="code">
namespace Recoding.Convert
{
    using System;
    using System.Collections.Generic;
    using System.Linq;
    using System.Text;
    using System.Globalization;

    public class GenericConverter
    {
        public static T TryGetValue&lt;T>(object p, object defaultValue) where T : IConvertible
        {
            return TryGetValue&lt;T>(p, defaultValue, CultureInfo.CurrentCulture);
        }

        public static T TryGetValue&lt;T>(object p, object defaultValue, IFormatProvider fp) where T : IConvertible
        {
            try
            {
                return (T)Convert.ChangeType(p, typeof(T), fp);
            }
            catch
            {
                return (T)defaultValue;
            }
        }
    }
}
</pre>
<h4>ProcessImage</h4>
<p>Uno stub iniziale del metodo prevederà la creazione di una Bitmap dal percorso fisico del file richiesto (indicato nell&#8217;attributo <code>physical_imagepath</code>) e si dividerà in due tronconi: la scalatura dell&#8217;immagine e il suo ritaglio. La strada da intraprendere viene indicata dal valore dell&#8217;attributo <code>crop</code>.</p>
<pre class="csharp:nogutter" name="code">
...
public void ProcessImage(HttpContext context)
{
	System.Drawing.Image img = System.Drawing.Image.FromFile(physical_imagepath);

	// Bitmap di partenza
	Bitmap original_image = (Bitmap)img;

	// Seleziono il metodo di elaborazione
	if (crop)
	{
		#region -- crop --
                // TODO
		#endregion
	}
	else
	{
		#region -- scalatura --
                // TODO
		#endregion
	}
}
...
</pre>
<p>Una piccola nota sul processo di crop.<br />
Solitamente il crop porta ad una selezione di un&#8217;area all&#8217;interno dell&#8217;immagine che può anche essere separata da tutti e 4 i bordi.</p>
<p><img src="http://www.recoding.it/wp-content/uploads/2009/09/crop1.jpg" alt="crop1" title="normale crop" width="450" height="301" class="aligncenter size-full wp-image-300" style="margin:auto;" /><small style="display:block; text-align:center; color:#787878;">Immagini gentilmente concesse da <a href="http://www.riccardovandoni.com/" target="_blank">Riccardo Vandoni</a></small></p>
<p>Il crop che ho deciso di implementare, invece, ridimensiona l&#8217;immagine per far si che almeno una coppia di bordi tocchi sempre l&#8217;area selezionata.<br />
In sostanza se abbiamo un&#8217;immagine di w500 e h200 e decidiamo di visualizzare una miniatura croppata di w100 e h100 l&#8217;handler scalerà proporzionalmente l&#8217;immagine per ottenere una miniatura alta 100 e larga 250, taglierà fuori i 150 pixel in eccesso, posizionando il viewport orizzontalmente sulla base del parametro <code>halign</code>.</p>
<p><img src="http://www.recoding.it/wp-content/uploads/2009/09/crop2.jpg" alt="crop2" title="crop con adattamento" width="450" height="301" class="aligncenter size-full wp-image-300" style="margin:auto;" /></p>
<p>Ho preferito questo approccio perchè l&#8217;handler è stato concepito per la visualizzazione di miniature di foto, e non avrebbe senso selezionare un&#8217;area all&#8217;interno della foto senza eseguire una scalatura, col rischio quindi di inquadrare un&#8217;area priva di significato.<br />
Non escludo comunque la possibilità di implementare in futuro l&#8217;altra possibilità.</p>
<h4>La scalatura</h4>
<p>Ora vediamo come scalare l&#8217;immagine, la cosa più semplice.<br />
Partiamo dal presupposto che la scalatura può essere effettuata in 2 modi: modificando una delle due dimensioni dell&#8217;immagine e ricalcolando la restante, lasciando invariato il rapporto base/altezza, oppure ridefinendo entrambe le dimensioni, e ciò può portare ad uno stretch dell&#8217;immagine.</p>
<pre class="csharp:nogutter" name="code">
...
#region -- scalatura --

if (width > 0 || height > 0)
{
	//se solo height=0, l'altezza viene ridotta proporzionalmente alla larghezza
	if (height == 0)
		height = Convert.ToInt32(((float)(width) / (float)(original_image.Width)) * original_image.Height);

	//se solo width=0, l'altezza viene ridotta proporzionalmente alla larghezza
	if (width == 0)
		width = Convert.ToInt32(((float)(height) / (float)(original_image.Height)) * original_image.Width);

	//Ora viene creata la thumbnail e salvata in un output di stream
	using (Bitmap oThumb = new Bitmap(original_image, width, height))
	{
		if (rounded)
		{
			TextureBrush texture = new TextureBrush(oThumb);

			Graphics graphic = Graphics.FromImage(oThumb);
			graphic.Clear(Color.Transparent);
			graphic.CompositingQuality = CompositingQuality.HighQuality;
			graphic.SmoothingMode = SmoothingMode.HighQuality;
			graphic.InterpolationMode = InterpolationMode.HighQualityBicubic;
			graphic.FillRoundedRectangle(texture, 0, 0, width, height, radius);
			graphic.Save();
		}
		else
		{
			using (Graphics oGraphic = Graphics.FromImage(oThumb))
			{
				oGraphic.InterpolationMode = System.Drawing.Drawing2D.InterpolationMode.HighQualityBicubic;
				oGraphic.PixelOffsetMode = System.Drawing.Drawing2D.PixelOffsetMode.HighQuality;
				oGraphic.SmoothingMode = System.Drawing.Drawing2D.SmoothingMode.HighQuality;

				Rectangle oRectangle = new Rectangle(0, 0, width, height);

				oGraphic.DrawImage(original_image, oRectangle);
			}
		}

		//salvo in uno stream di output
		StreamOut(oThumb, context);
	}
}
else
{
	// Se width e height sono entrambi uguali a 0, l’immagine viene salvata nello stream cosi com’è
	StreamOut(original_image, context);
}

#endregion
...
</pre>
<p>Come si può vedere la prima cosa che facciamo è controllare il valore dei due attributi width e height e ricalcolare se necessario l&#8217;attributo mancante.<br />
Se nessuno dei due è valorizzato il processo viene bypassato e verrà restituita in output l&#8217;immagine originale.<br />
Se sono presenti dei valori l&#8217;immagine viene alterata (proporzionalmente o no), viene quindi generato un oggetto Bitmap che rappresenta la nuova miniatura.</p>
<p>Viene fatto un ulteriore controllo: se l&#8217;attributo <code>rounded</code> è uguale a <code>true</code> si procede alla costruzione degli angoli stondati.<br />
Per fare ciò mi sono avvalso del fantastico namespace Plasmoid, sviluppato da Arun Reginald, e che trovate pubblicato su CodeProject a <a href="http://www.codeproject.com/KB/GDI-plus/ExtendedGraphicsII.aspx" target="_blank">questo indirizzo</a>.<br />
Il namespace contiene dei metodi di estenzione del namespace System.Drawing, uno dei quali è appunto <code>FillRoundedRectangle</code>.</p>
<p>Non mi dilungherò sull&#8217;utilizzo delle librerie GDI+, una completa documentazione la trovate all&#8217;indirizzo indicato precedentemente.</p>
<h4>Il crop</h4>
<p>Per quel che riguarda il ritaglio dell&#8217;immagine le cose si complicano un pò.<br />
Se ad esempio la miniatura che vogliamo generare ha un rapporto base/altezza diverso da quello dell&#8217;immagine originale avremo una delle due dimensioni &#8220;esuberante&#8221; rispetto alla superficie visualizzata, dovremo operare quindi un riallineamento del viewport.<br />
Potremmo ad esempio voler sempre visualizzare la parte centrale, la sinistra, la destra, e così via, escludendo ciò che eccede il nostro nuovo formato.</p>
<p>Per definire questi allineamenti ci avvarremo degli enumeratori <code>VerticalAlign</code> e <code>HorizontalAlign</code>, comuni ai WebControl, i cui valori saranno intercettati dai parametri <code>_PARAM_VERTICALALIGN</code> e <code>_PARAM_HORIZONTALALIGN</code>.</p>
<p>Il codice della nostra sezione crop sarà il seguente:</p>
<pre class="csharp:nogutter" name="code">
...
#region -- crop --
// Se una delle due dimensioni è mancante le rendo uguali
if (width == 0 || height == 0) { width = height = Math.Max(width, height); }

using (Bitmap output_image = new Bitmap(width, height))
{
	// Ricavo dalla request gli allineamenti richiesti.
	// Ovviamente ha senso solo per le immagini croppate.
	valign = context.Request.Params[_PARAM_VERTICALALIGN] ?? VerticalAlign.NotSet.ToString();
	halign = context.Request.Params[_PARAM_HORIZONTALALIGN] ?? HorizontalAlign.NotSet.ToString();

	int new_width, new_height;

	// Calcolo il ratio dell'immagine originale e quello dell'immagine di destinazione
	// Mi occorrono per capire in che asse l'immagine originale risulta esuberante rispetto alla nuova proporsione
	float target_ratio = (float)width / (float)height;
	float image_ratio = (float)original_image.Width / (float)original_image.Height;

	if (target_ratio < image_ratio)
	{
		new_height = height;
		new_width = (int)Math.Floor(image_ratio * (float)height);
	}
	else
	{
		new_height = (int)Math.Floor((float)width / image_ratio);
		new_width = width;
	}

	new_width = new_width > width ? new_width : width;
	new_height = new_height > height ? new_height : height;

	#region -- Offset degli assi, assegno i valori di default (NotSet e Middle/Center) --
	int paste_x = (width - new_width) / 2;
	int paste_y = (height - new_height) / 2;

	// Applico gli allineamenti verticali
	switch ((VerticalAlign)Enum.Parse(typeof(VerticalAlign), valign))
	{
		case VerticalAlign.NotSet:
		case VerticalAlign.Middle:
			// bypass
			break;
		case VerticalAlign.Top:
			paste_y = 0;
			break;
		case VerticalAlign.Bottom:
			paste_y = height - new_height;
			break;
	}

	// Applico gli allineamenti orizzontali
	switch ((HorizontalAlign)Enum.Parse(typeof(HorizontalAlign), halign))
	{
		case HorizontalAlign.NotSet:
		case HorizontalAlign.Center:
			// bypass
			break;
		case HorizontalAlign.Left:
			paste_x = 0;
			break;
		case HorizontalAlign.Right:
			paste_x = width - new_width;
			break;
	}
	#endregion

	if (rounded)
	{
		Bitmap thumb = new Bitmap(width, height);

		Graphics gthumb = Graphics.FromImage(thumb);
		gthumb.CompositingQuality = CompositingQuality.HighQuality;
		gthumb.SmoothingMode = SmoothingMode.HighQuality;
		gthumb.InterpolationMode = InterpolationMode.HighQualityBicubic;
		gthumb.DrawImage(original_image, paste_x, paste_y, new_width, new_height);
		gthumb.Save();

		TextureBrush texture = new TextureBrush(thumb);

		Graphics graphic = Graphics.FromImage(output_image);
		graphic.CompositingQuality = CompositingQuality.HighQuality;
		graphic.SmoothingMode = SmoothingMode.HighQuality;
		graphic.InterpolationMode = InterpolationMode.HighQualityBicubic;
		graphic.FillRoundedRectangle(texture, 0, 0, width, height, radius);
		graphic.Save();
	}
	else
	{
		Graphics graphic = Graphics.FromImage(output_image);
		graphic.CompositingQuality = CompositingQuality.HighQuality;
		graphic.SmoothingMode = SmoothingMode.HighQuality;
		graphic.InterpolationMode = InterpolationMode.HighQualityBicubic;
		graphic.DrawImage(original_image, paste_x, paste_y, new_width, new_height);
		graphic.Save();
	}

	// Stream out dell'immagine
	StreamOut(output_image, context);
}
#endregion
...
</pre>
<h4>StreamOut</h4>
<p>Rimane quindi da definire il metodo StreamOut, che si occuperà di restituire al client l&#8217;immagine elaborata, concordamente al suo formato:</p>
<pre class="csharp:nogutter" name="code">
public void StreamOut(Bitmap bitmap, HttpContext context)
{
	switch (imgExtension.ToUpper())
	{
		case ".JPEG":
		case ".JPG":
			bitmap.Save(context.Response.OutputStream, ImageFormat.Jpeg);
			break;
		case ".GIF":
			bitmap.Save(context.Response.OutputStream, ImageFormat.Gif);
			break;
		case ".PNG":
			using (MemoryStream stream = new MemoryStream())
			{
				bitmap.Save(stream, ImageFormat.Png);
				stream.WriteTo(context.Response.OutputStream);
			}
			break;
	}
}
</pre>
<p>Poichè la png richiede uno stream bidirezionale è necessario eseguire il suo rendering in un MemoryStream, che verrà poi riversato nel <code>Response.OutputStream</code>.</p>
<p>A questo punto abbiamo un handler che può intercettare richieste di vario genere.<br />
Se ad esempio volessimo un&#8217;immagine ritagliata a 300 pixel per 300, con angoli stondati di 10 pixel di raggio avremmo la richiesta:</p>
<pre class="html:nogutter" name="code">
&lt;img alt="" src="imghandler.ashx?imagepath=image.jpg&#038;width=300&#038;height=300&#038;crop=true&#038;rounded=True&#038;radius=10" />
</pre>
<p>Se volessimo ottenere un&#8217;immagine scalata all&#8217;altezza di 200 pixel e ricalcolata proporzionalmente scriveremo:</p>
<pre class="html:nogutter" name="code">
&lt;img alt="" src="imghandler.ashx?imagepath=image.jpg&#038;height=200&#038;crop=false&#038;rounded=false" />
</pre>
<p>Ulteriori esempi sono presenti in un demo funzionante <a class="codiceLink" href='http://www.recoding.it/wp-content/uploads/2009/09/DemoImgHandler.zip'>scaricabile quì</a>.<br />
In un futuro articolo illustrerò come incapsulare quest&#8217;handler in un custom control, rendendo più semplice ed elegante il suo utilizzo.</p>
]]></content:encoded>
			<wfw:commentRss>http://www.recoding.it/?feed=rss2&amp;p=270</wfw:commentRss>
		<slash:comments>4</slash:comments>
		</item>
		<item>
		<title>magnifier &#8211; plugin per immagini navigabili con jQuery</title>
		<link>http://www.recoding.it/?p=255</link>
		<comments>http://www.recoding.it/?p=255#comments</comments>
		<pubDate>Thu, 16 Jul 2009 13:55:04 +0000</pubDate>
		<dc:creator>eros</dc:creator>
				<category><![CDATA[jQuery]]></category>
		<category><![CDATA[image]]></category>
		<category><![CDATA[magnifier]]></category>
		<category><![CDATA[navigable]]></category>
		<category><![CDATA[plugin]]></category>
		<category><![CDATA[zoom]]></category>

		<guid isPermaLink="false">http://www.recoding.it/?p=255</guid>
		<description><![CDATA[Pugin per costruire un navigatore di macroimmagini]]></description>
			<content:encoded><![CDATA[<div style='float:left; padding-right:12px;'>
<a class="codiceLink" target="_blank" href='http://plugins.jquery.com/project/magnifier'><img alt="" src="wp-content/themes/charcoal/images/box_js.gif"/>plugin page</a></div>
<div style='padding-right:12px;'>
<a class="codiceLink" target="_blank" href='http://www.recoding.it/wp-content/uploads/demos/magnifier-demo.htm'><img alt="" src="wp-content/uploads/2009/05/livedemo.gif"/>livedemo</a>
</div>
<p></p>
<div><img src="http://www.recoding.it/wp-content/uploads/2009/07/magnifier-logo.jpg" alt="magnifier- Logo" title="magnifier logo" width="200" height="200" class="size-full wp-image-141" style="margin: 10px 10px 0px 0px; float:left;" /><br />
Questo plugin permette di costruire un navigatore in stile photoshop su di una miniatura, caricando in maniera asincrona l&#8217;immagine originale in fullsize. E&#8217; ideale per immagini macro, panoramiche, o per mappe statiche.<br />
<br />
La versione attuale (0.5) si trova ancora in fase di beta, come al solito ogni segnalazione di errori o suggerimenti d&#8217;implementazione è ben accetta.
</div>
<h4>Come funziona</h4>
<p>Molto banalmente, basta inserire la miniatura nel proprio html, settarne uno pseudo attributo <code>fullsize</code> con il percorso dell&#8217;immagine originale, ed eseguire il metodo <code>.magnifier()</code> su di essa.<br />
<code>fullsize</code>, non è un attributo html standard, e probabilmente <a href="http://www.addfullsize.com/" target="_blank">non lo diverrà mai</a>, ma è semanticamente espressivo, e se non facciamo troppa attenzione alla validazione della nostra pagina web, tanto basta. </p>
<p>Per default il plugin aggiunge il contenitore dell&#8217;immagine originale direttamente nel parent del contenitore della miniatura, settandone le dimensioni a 4 volte la miniatura stessa.<br />
Ovviamente questo comportamento è configurabile tramite parametri, descritti estensivamente nella <a href="http://plugins.jquery.com/project/magnifier" target="_blank">pagina ufficiale</a> del plugin.</p>
<p>HTML:</p>
<pre class="xml:nogutter" name="code">
&lt;img src="miniature.jpg" fullsize="fullsize.jpg" alt="navigator" />
</pre>
<p>JS, parametri di default:</p>
<pre class="js:nogutter" name="code">
$("img[fullsize]").magnifier();
</pre>
<p>Posizioniamo invece la miniatura in alto a destra, e modifichiamo il messaggio di attesa:</p>
<pre class="js:nogutter" name="code">
$("img[fullsize]").magnifier({
    position: "top-right",
    loadingMessage: "Caricamento in corso..."
});
</pre>
<h4>Compatibilità</h4>
<p>E&#8217; stato testato correttamente con Firefox 3.0.x, Opera, Safari, Chrome, e IE6/IE7.</p>
]]></content:encoded>
			<wfw:commentRss>http://www.recoding.it/?feed=rss2&amp;p=255</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>showcase &#8211; slideshow plugin per jQuery</title>
		<link>http://www.recoding.it/?p=242</link>
		<comments>http://www.recoding.it/?p=242#comments</comments>
		<pubDate>Sat, 20 Jun 2009 15:27:47 +0000</pubDate>
		<dc:creator>eros</dc:creator>
				<category><![CDATA[jQuery]]></category>
		<category><![CDATA[image gallery]]></category>
		<category><![CDATA[plugin]]></category>
		<category><![CDATA[showcase]]></category>

		<guid isPermaLink="false">http://www.recoding.it/?p=242</guid>
		<description><![CDATA[Plugin di jQuery per creare uno showcase per immagini]]></description>
			<content:encoded><![CDATA[<div style='float:left; padding-right:12px;'>
<a class="codiceLink" target="_blank" href='http://plugins.jquery.com/project/showcase'><img alt="" src="wp-content/themes/charcoal/images/box_js.gif"/>plugin page</a></div>
<div style='padding-right:12px;'>
<a class="codiceLink" target="_blank" href='http://www.recoding.it/wp-content/uploads/demos/showcase-demo.htm'><img alt="" src="wp-content/uploads/2009/05/livedemo.gif"/>livedemo</a>
</div>
<p></p>
<div><img src="http://www.recoding.it/wp-content/uploads/2009/06/showcase-logo.jpg" alt="seek - Logo" title="seek_logo" width="200" height="200" class="size-full wp-image-141" style="margin: 10px 10px 0px 0px; float:left;" /><br />
Showcase è un plugin per jQuery che consente di costruire in maniera semplicissima un moderno slideshow per un set di immagini, consentendo un&#8217;ampia personalizzazione .<br />
<br />
<strong>Edit 02/02/2010:</strong> versione 2.0! Tutto nuovo, date un&#8217;occhiata alla documentazione.<br />
<strong>Edit 27/07/2009:</strong> rilasciata la versione 1.0, che implementa la creazione asincrona del set di immagini.<br />
<br />
La versione attuale (0.5) si trova ancora in fase di beta, ogni segnalazione di errori o suggerimenti d&#8217;implementazione è ben accetta.
</div>
<h4>Come funziona</h4>
<p><strong>Edit 27/07/2009:</strong> Vedere le note di rilascio della versione 1.0.</p>
<p>Allo stato attuale il plugin richiede la presenza di un <code>div</code> contenente una serie di tag anchor contenenti a loro volta le immagini che si intende visualizzare. L&#8217;attributo <code>alt</code> dell&#8217;immagine, poi, viene utilizzato per visualizzare una descrizione dell&#8217;immagine.</p>
<p>Ipotizzando la presenza di un div con id <code>showCase1</code>, il metodo per attivare lo showcase è molto semplicemente:</p>
<pre class="js:nogutter" name="code">
$("#showCase1").showcase();
</pre>
<p>Nel caso si voglia personalizzare l&#8217;aspetto dello showcase sono previsti numerosi parametri (per una descrizione completa consultare la <a href="http://plugins.jquery.com/project/showcase" target="_blank">pagina ufficiale</a> del plugin).</p>
<p>Se ad esempio volessimo utilizzare un effetto di slide verticale e numerare i pulsanti di navigazione:</p>
<pre class="js:nogutter" name="code">
$("#showCase1").showcase({
    animation: { type: "vertical-slider" },
    navigator: { showNumber: true }
});
</pre>
<p>Ancora, vogliamo che i pulsanti di navigazione compaiano nell&#8217;altro angolo dell&#8217;immagine, e che siano disposti verticalmente:</p>
<pre class="js:nogutter" name="code">
$("#showCase1").showcase({
    animation: { type: "vertical-slider" },
    navigator: { showNumber: true,
                     position: "top-left",
                     orientation: "vertical" }
});
</pre>
<p>Visualiziamo la miniatura delle immagini nei pulsanti di navigazione:</p>
<pre class="js:nogutter" name="code">
$("#showCase1").showcase({
    animation: { type: "vertical-slider" },
    navigator: { showNumber: true,
                     position: "top-left",
                     showMiniature: true,
                     orientation: "vertical",
                     item: { width: "64px", height: "48px" } }
});
</pre>
<h4>Problemi noti</h4>
<p>Il plugin si comporta correttamente con Firefox 3.0.x, Opera, Safari e Chrome, ma con IE6/IE7 si verificano (in maniera casuale, a quanto pare) dei problemi nel caricamento delle informazioni di dimesione delle immagini. Una possibile soluzione sarebbe quella di scolpire larghezza/altezza dello showcase in fase d&#8217;istanziazione, prevedendo un paio di parametri in più. Ma ciò non mi convince granchè.</p>
<p>Edit 26/05/2009:<br />
Nella versione 0.5 il problema della lettura delle dimensioni dell&#8217;immagine sembra definitivamente risolto.</p>
]]></content:encoded>
			<wfw:commentRss>http://www.recoding.it/?feed=rss2&amp;p=242</wfw:commentRss>
		<slash:comments>45</slash:comments>
		</item>
		<item>
		<title>seek &#8211; plugin di evidenziazione elementi</title>
		<link>http://www.recoding.it/?p=210</link>
		<comments>http://www.recoding.it/?p=210#comments</comments>
		<pubDate>Thu, 04 Jun 2009 20:40:07 +0000</pubDate>
		<dc:creator>eros</dc:creator>
				<category><![CDATA[jQuery]]></category>
		<category><![CDATA[plugin]]></category>

		<guid isPermaLink="false">http://www.recoding.it/?p=210</guid>
		<description><![CDATA[Plugin di ricerca ed evidenziazione per jQuery, made @recoding.it]]></description>
			<content:encoded><![CDATA[<div style='float:left; padding-right:12px;'>
<a class="codiceLink" target="_blank" href='http://plugins.jquery.com/project/seek'><img alt="" src="wp-content/themes/charcoal/images/box_js.gif"/>plugin page</a></div>
<div style='padding-right:12px;'>
<a class="codiceLink" target="_blank" href='http://www.recoding.it/wp-content/uploads/demos/seek-demo.htm'><img alt="" src="wp-content/uploads/2009/05/livedemo.gif"/>livedemo</a>
</div>
<p></p>
<div><img src="http://www.recoding.it/wp-content/uploads/2009/06/seek.jpg" alt="seek - Logo" title="seek_logo" width="200" height="200" class="size-full wp-image-141" style="margin: 10px 10px 0px 0px; float:left;" /><br />
Si tratta di un plugin dalla dubbia utilità, ma altamente spettacolare, che consente di evidenziare tramite un&#8217;animazione uno o più elementi della pagina.<br />
<br />
Potrebbe essere impiegato nell&#8217;evidenziazione degli elementi di una form validata, o chissà :)</p>
<div style='clear:both;'></div>
</div>
<h4>Come funziona:</h4>
<p>Il trucco consiste nel costruire dinamicamente un div che inizialmente ha la dimensione di un contenitore (di default il <code>window</code>) e che viene trasformato fino a boxare l&#8217;elemento indicato nel selettore; infine viene rimosso dal DOM.</p>
<p>Il metodo viene applicato banalmente:</p>
<pre class="js:nogutter" name="code">
$("#myDOMElement").seek();
</pre>
<p>E ci sono diversi aspetti customizzabili, attraverso i seguenti parametri:</p>
<ul>
<li><strong>css</strong> (object): espone alcune proprietà CSS
<ul>
<li><strong>backgroundColor</strong>, <strong>borderColor</strong>, <strong>borderStyle</strong>, <strong>borderWidth</strong>, <strong>position</strong>, <strong>top</strong>, <strong>bottom</strong>, <strong>right</strong>, <strong>left</strong></li>
</ul>
</li>
<li><strong>startFrom</strong> (jQuery object, default $(window)): indica l&#8217;elemento dal quale si esegue il morphing del div.</li>
<li><strong>animation</strong> (object): ciò che riguarda la trasformazione.
<ul>
<li><strong>easeFunction</strong> (string, default &#8220;swing&#8221;): il tipo di funzione easing applicata alla trasformazione, per una raccolta fantastica di funzioni fare<br />
riferimento a http://gsgd.co.uk/sandbox/jquery/easing/</li>
<li><strong>speed</strong> (int, default 500): il tempo, espresso in millisecondi, di durata della singola animazione.</li>
</ul>
</li>
<li><strong>multiple</strong> (object): Se il selettore di partenza corrisponde a più elementi del DOM entrano in gioco le seguenti proprietà.
<ul>
<li><strong>simultaneous</strong> (bool, default true): indica se l&#8217;animazione deve scattare simultaneamente per tutti gli elementi.</li>
<li><strong>interval</strong> (int, default 800): il tempo, espresso in millisecondi, di intervallo tra l&#8217;animazione di un elemento e l&#8217;altro.</li>
</ul>
</li>
</ul>
<h4>Compatibilità</h4>
<p>jQuery.seek è stato testato con successo su IE6, IE7, Firefox 3.0.x, Safari 3&#038;4, Opera 9.x e Google Chrome.</p>
<h4>Contest</h4>
<p>Il plugin può anche essere affascinante, ma lascia perplessi per quel che riguarda l&#8217;effettivo impiego.<br />
Lanciamo quindi un piccolo contest: chi linkerà una pagina in cui si fa un uso creativo e funzionale del plugin vincerà&#8230; niente di che: una nota al merito nella pagina dei plugin di jQuery.</p>
<p>Happy seeking.</p>
]]></content:encoded>
			<wfw:commentRss>http://www.recoding.it/?feed=rss2&amp;p=210</wfw:commentRss>
		<slash:comments>5</slash:comments>
		</item>
		<item>
		<title>jqPrint &#8211; plugin di stampa per jQuery</title>
		<link>http://www.recoding.it/?p=138</link>
		<comments>http://www.recoding.it/?p=138#comments</comments>
		<pubDate>Mon, 11 May 2009 19:03:56 +0000</pubDate>
		<dc:creator>eros</dc:creator>
				<category><![CDATA[jQuery]]></category>
		<category><![CDATA[plugin]]></category>
		<category><![CDATA[Print]]></category>

		<guid isPermaLink="false">http://www.recoding.it/?p=138</guid>
		<description><![CDATA[Plug-in di stampa per jQuery made @recoding.it]]></description>
			<content:encoded><![CDATA[<div style='float:left; padding-right:12px;'>
<a class="codiceLink" target="_blank" href='http://plugins.jquery.com/project/jqPrint'><img alt="" src="wp-content/themes/charcoal/images/box_js.gif"/>download plug-in</a></div>
<p><a class="codiceLink" target="_blank" href='http://www.recoding.it/wp-content/uploads/demos/jqprint-demo.htm'><img alt="" src="wp-content/uploads/2009/05/livedemo.gif"/>livedemo</a></p>
<div><img src="http://www.recoding.it/wp-content/uploads/2009/05/jqprint_logo.jpg" alt="jqPrint - Logo" title="jqprint_logo" width="215" height="154" class="size-full wp-image-141" style="margin: 10px 10px 0px 0px; float:left;" /><br />
jqPrint è un plug-in per jQuery che consente di stampare una pagina o una porzione di html partendo da un elemento o un selettore jQuery.<br />
<br />
Si basa principalmente sul famoso jPrintArea, di cui abbiamo corretto alcune imperfezioni e a cui abbiamo aggiunto alcune comode funzioni.</p>
<div style='clear:both;'></div>
</div>
<h4>Come funziona:</h4>
<p>Tutto ciò che bisogna fare è selezionare un elemento jQuery o un selettore e invocare il metodo <code>jqprint()</code>:</p>
<pre class="js:nogutter" name="code">
$("div.toPrint").jqprint();
// . . .
$divToPrint.jqprint();
</pre>
<p>Otterremo la finestra di dialogo del sistema per la stampa.</p>
<p>Di default il plug-in ricerca tra i CSS della pagina i file con l&#8217;attributo <code>media='print'</code> e li importa nel documento da stampare, ciò permette di separare nettamente la presentazione per la normale navigazione col browser e per la stampa.</p>
<p><b>Update 19/06/2009</b></p>
<p>Attualmente sono previsti i seguenti parametri:</p>
<ul>
<li><strong>debug</strong> (bool, default false): permette di visualizzare in fondo al body il contenuto dell&#8217;iframe utilizzato per la stampa.</li>
<li><strong>importCSS</strong> (bool, default true): indica al plugin se importare il CSS (con media=&#8217;print&#8217;, se presente) o se eseguire una stampa grezza del documento.</li>
<li><strong>printContainer</strong> (bool, default true): indica al plugin se stampare anche l&#8217;html<br />
dell&#8217;elemento usato come selettore, se settato a false può causare dei problemi nell&#8217;applicazione delle regole css.</li>
<li><strong>operaSupport</strong> (bool, default true): attiva un support per la stampa in Opera, effettuando la stampa da un tab temporaneo.</li>
</ul>
<h4>Compatibilità</h4>
<p>jqPrint è stato testato con successo con Firefox 3.0.x, IE6, IE7, Safari 3&#038;4, Google Chrome e, dalla versione 0.3, anche in Opera.<br />
Attualmente la stampa dei documenti fra i vari browser presenta alcune diversità. E&#8217; anche completamente dipendente dal CSS. Potrebbe essere interessante capire se siano applicabili delle funzioni di normalizzazione del layout&#8230; vedremo con le future release.</p>
<p>Per il momento buona stampa!</p>
]]></content:encoded>
			<wfw:commentRss>http://www.recoding.it/?feed=rss2&amp;p=138</wfw:commentRss>
		<slash:comments>20</slash:comments>
		</item>
		<item>
		<title>Costruire un semplice comando di ricerca per Ubiquity</title>
		<link>http://www.recoding.it/?p=116</link>
		<comments>http://www.recoding.it/?p=116#comments</comments>
		<pubDate>Tue, 28 Apr 2009 19:55:37 +0000</pubDate>
		<dc:creator>eros</dc:creator>
				<category><![CDATA[Browser]]></category>
		<category><![CDATA[jQuery]]></category>
		<category><![CDATA[Firefox]]></category>
		<category><![CDATA[Stackoverflow]]></category>
		<category><![CDATA[Ubiquity]]></category>

		<guid isPermaLink="false">http://www.recoding.it/?p=116</guid>
		<description><![CDATA[Piccolo esempio di creazione di un comando per Ubiquity di Firefox]]></description>
			<content:encoded><![CDATA[<p><a href="http://labs.mozilla.com/2008/08/introducing-ubiquity" target="_blank" style="float:left;"><img src="http://www.recoding.it/wp-content/uploads/2009/04/ubiquity.jpg" alt="ubiquity" title="ubiquity" width="169" height="219" class="aligncenter size-full wp-image-118" /></a>Per molti ancora è sconosciuto ma l&#8217;approccio di Ubiquity, l&#8217;estensione sperimentale di firefox, promette di cambiare la filosofia con cui utiliziamo il browser.</p>
<p>A detta dei suoi sviluppatori, infatti, ubiquity ha il compito di &#8220;connettere il web con il linguaggio&#8221; e ciò si traduce nella possibilità di utilizzare un linguaggio naturale per eseguire una serie di azioni sul web.<br />
Immaginiamo ad esempio di visitare una pagina web in cui viene citato il nome di un film, o di un luogo di cui non conosciamo niente. In condizioni normali dovremmo aprire un altro tab del browser, collegarci a google, yahoo, imdb o quant&#8217;altro, incollare il testo da ricercare ed effettuare la ricerca. Con ubiquity possiamo selezionare il testo, premere <code>CTRL+spazio</code> (o la combinazione di tasti configurata) e selezionare uno dei comandi suggeriti, che agiranno in questo caso direttamente sulla selezione, consentendoci velocemente di effettuare una traduzione, una ricerca, una conversione di formato ecc&#8230;</p>
<p>E&#8217;un esempio piuttosto banale, in realtà ubiquity è molto di più, ma questo non vuol essere un articolo introduttivo sull&#8217;utilizzo di ubiquity, è una semplice guida su come costruire un comando di ricerca.<br />
Per l&#8217;esattezza l&#8217;obiettivo è quello di costruire un comando di ricerca, con preview, per l&#8217;ottimo sito di q&#038;a <a href="http://www.stackoverflow.com">stackoverflow.com</a>.</p>
<p>Come prima cosa dobbiamo accedere alla sezione di configurazione di ubiquity, eseguendo il comando <code>help</code>.<br />
Da quì possiamo accedere alla pagina &#8220;Command editor&#8221;, che ci consentirà di digitare il codice dei nuovi comandi, che diventeranno immediatamente funzionanti:<br />
<img src="http://www.recoding.it/wp-content/uploads/2009/04/ubiquity_0.jpg" alt="ubiquity_0" title="ubiquity_0" width="550" height="327" class="aligncenter size-full wp-image-124" style="margin:0px; margin-top:10px;" /></p>
<p>Ovviamente avremo bisogno di dare uno sguardo alle command APIs e più precisamente alla libreria <b>CmdUtils</b>.<br />
Fortunatamente, come possiamo constatare, esiste un helper per la creazione di un comando di ricerca standard, il metodo <code>makeSearchCommand</code>, una specializzazione del più generico <code>CreateCommand</code>.</p>
<p>Come possiamo vedere <code>makeSearchCommand</code> accetta 3 parametri: <code>name</code>, <code>url</code>e <code>parser</code>.</p>
<p>Banalmente il <strong>name</strong> sarà ciò che si dovrà digitare per eseguire il comando una volta che questo sia stato completato; possiamo utilizzare &#8220;stackoverflow&#8221; per esteso, o il più rapido &#8220;so&#8221;, che non corrisponde a nessun comando di default.<br />
Per capire invece cosa passare ai parametri &#8220;name&#8221; e &#8220;parser&#8221; dobbiamo innanzitutto fare un po di reverse engineering su www.stackoverflow.com.</p>
<p>Proviamo come prima cosa ad eseguire una ricerca direttamente dal sito, ricercando i termini &#8220;asp.net&#8221;. Notiamo subito che il parametro di ricerca viene passato in querystring componendo il seguente url: &#8220;http://stackoverflow.com/search?q=asp.net&#8221;.</p>
<p>Possiamo quindi immaginare che il template dell&#8217;<strong>url</strong>, in conformità a quanto indicato nelle API, sarà una cosa del tipo: &#8220;http://stackoverflow.com/search?q={QUERY}&#8221;, dove il placeholder {QUERY} verrà di volta in volta sostituito dal parametro del nostro comando.</p>
<p>Il parametro <strong>parser</strong> è un po più complesso. E&#8217; costituito da alcune proprietà attraverso cui possiamo indicare, al parser che si occupa della preview del comando, come interpretare il risultato della ricerca. Vediamo in dettaglio quelle che ci interessano:</p>
<ul>
<li>container: indica il contenitore che incapsula i singoli risultati</li>
<li>title: il titolo del risultato</li>
<li>preview: cosa mostrare come anteprima</li>
<li>baseurl: nel caso in cui i link ai risultati siano espressi in indirizzi relativi, rappresenta la parte da rimontare a sinistra dell&#8217;indirizzo.</li>
</ul>
<p>Le proprietà container, title e preview verranno utilizzate dal parser come selettori per generare oggetti jQuery. Quello che ci serve di sapere quindi è la struttura della pagina dei risultati di stackoverflow, e per questo ci basta una sbirciatina all&#8217;html con firebug:<br />
<img src="http://www.recoding.it/wp-content/uploads/2009/04/ubiquity_11.jpg" alt="ubiquity_1" title="ubiquity_1" width="569" height="514" class="aligncenter size-full wp-image-127" style="margin:0px; margin-top:10px;" /><br />
Ora sappiamo che il selettore per il container sarà &#8220;<code>.question-summary</code>&#8220;, quello per il title &#8220;<code>h3 a</code>&#8221; e quello per la preview &#8220;<code>.excerpt</code>&#8220;.<br />
Dal codice si nota anche che l&#8217;href del link del titolo è un indirizzo relativo, avremo quindi bisogno impostare baseurl a &#8220;http://stackoverflow.com&#8221;.</p>
<p>Abbiamo tutto ciò che ci serve a questo punto per costruire il comando di ricerca, che risulterà vergognosamente semplice:</p>
<pre class="js:nogutter" name="code">
CmdUtils.makeSearchCommand({
  name: "stackoverflow",
  url: "http://stackoverflow.com/search?q={QUERY}",
  parser: {container: ".question-summary",
           title: "h3 a",
           preview: ".excerpt",
           baseurl: "http://stackoverflow.com" }
});
</pre>
<p>Nel momento stesso in cui scriviamo il comando nella finestra Command Editor, se è sintatticamente corretto, diventa immediatamente disponibile in ubiquity. Premiamo quindi <code>CTRL+spazio</code> e scriviamo &#8220;stackoverflow&#8221; seguito dai termini che ci interessa ricercare.<br />
Come previsto l&#8217;area di preview si popolerà con i primi 4 risultati disponibili, numerati per uno shortcut. A questo punto premendo invio eseguiremo la ricerca su un altro tab di firefox, mentre premendo CTRL+ALT+[numero] apriremo un tab direttamente al &#8220;<code>baseurl+link</code>&#8221; del risultato indicato dal numero del preview.</p>
<p>Ovviamente quest&#8217;esempio di comando risulta notevolmente semplificato dalla presenza del metodo <code>makeSearchCommand</code>, ma potrebbero presentarsi necessità più complesse di una semplice ricerca, basta dare un&#8217;occhiata ai comandi predefiniti per rendersi conto che abbiamo visto appena la punta dell&#8217;iceberg nell&#8217;analizzare il lato &#8220;developer&#8221; di questo interessantissimo componente.</p>
<p>Per ora è tutto. Liberi di commentare.</p>
]]></content:encoded>
			<wfw:commentRss>http://www.recoding.it/?feed=rss2&amp;p=116</wfw:commentRss>
		<slash:comments>3</slash:comments>
		</item>
		<item>
		<title>Cross binding client/server con XSLT</title>
		<link>http://www.recoding.it/?p=99</link>
		<comments>http://www.recoding.it/?p=99#comments</comments>
		<pubDate>Sat, 21 Mar 2009 16:56:44 +0000</pubDate>
		<dc:creator>eros</dc:creator>
				<category><![CDATA[Asp.Net]]></category>
		<category><![CDATA[jQuery]]></category>
		<category><![CDATA[Binding]]></category>
		<category><![CDATA[XML]]></category>
		<category><![CDATA[XSLT]]></category>

		<guid isPermaLink="false">http://www.recoding.it/?p=99</guid>
		<description><![CDATA[Presentazione di una soluzione client/server per effettuare bind di dati utilizzando XML e XSLT.]]></description>
			<content:encoded><![CDATA[<p><a class="codiceLink" href='http://www.recoding.it/wp-content/uploads/2009/03/xslt_cross_binding_demo.zip'><img src="wp-content/themes/charcoal/images/box_zip.gif" alt="" />download esempio</a></p>
<p>Non molto tempo fa mi sono trovato nella situazione di dover gestire il bind di una lista di elementi in maniera &#8220;mista&#8221;, ossia dovevo effettuare un primo bind lato server sulla base dei record di un database, e consentire poi all&#8217;utente finale di modificare lato client tale lista.</p>
<p>Il progetto in questione era in .NET, avrei potuto effettuare il bind lato server con un asp:Repeater o un qualsiasi altro controllo bindabile.<br />
Lato client avrei potuto utilizzare jQuery per gestire l&#8217;input dell&#8217;utente rigenerando l&#8217;html degli elementi aggiunti, con tutte le chiamate ajax del caso per il salvataggio dei dati sul DB.</p>
<p>Il problema più grosso di questo approccio è che il template utilizzato nel controllo .NET (tipicamente l&#8217;ItemTemplate del Repeater) non è esportabile lato client, ciò significa che se avessi dovuto modificare la struttura degli elementi della lista l&#8217;avrei dovuto fare su entrambi i fronti, client e server, mantenendoli allineati.<br />
Il bind lato client, poi, avrebbe dovuto essere effettuato allo stesso modo del server, quindi al disallineamento del template si sarebbe aggiunto un eventuale disallineamento delle logiche sui dati.</p>
<p>Non so se la soluzione da me trovata è già standardizzata né pretendo che sia la soluzione a tutti i problemi, analizzeremo in seguito i difetti di tale approccio, ma di sicuro ha una sua eleganza e portabilità.</p>
<p>Questa soluzione &#8220;cross&#8221; si basa fondamentalmente sull&#8217;utilizzo di XML per i dati e di fogli XSL per produrre frammenti di HTML partendo da tali dati, più alcuni accorgimenti per rendere il tutto il più automatico possibile.</p>
<p><strong>Lato server</strong><br />
Per il nostro esempio (<a href="http://www.recoding.it/wp-content/uploads/2009/03/xslt_cross_binding_demo.zip" target="_blank">scaricabile</a>) ci baseremo su una semplice lista anagrafica modellata dalle seguenti classi:</p>
<pre class="csharp:nogutter" name="code">
public class Anagrafiche
{
    public List&lt;Persona&gt; Persone { get; set; }
}

public class Persona
{
    public string Nome { get; set; }
    public string Cognome { get; set; }
    public string Comune { get; set; }

    public Persona() { }
}
</pre>
<p>Avremo quindi la necessità di presentare una lista di persone e di consentire all&#8217;utente di aggiungerne altre, evitando fastidiosi postback e l&#8217;utilizzo degli altrettanto fastidiosi UpdatePanel :)</p>
<p>La pagina .aspx contiene un controllo</p>
<pre class="xml:nogutter" name="code">
&lt;asp:Xml ID="xmlData" runat="server" />
</pre>
<p>che consente di renderizzare un documento XML (o un frammento XHTML, nel nostro caso) passatogli, effettuando eventualmente una trasformazione sui dati.<br />
Avremo poi 2 fogli .XSL per effettuare le trasformazioni, che funzioneranno da template. Il primo è il contenitore della lista, <code>persone.xsl</code>:</p>
<pre class="xml:nogutter" name="code">
&lt;?xml version="1.0" encoding="utf-8"?>
&lt;xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
&lt;xsl:include href="persona.xsl" />
	&lt;xsl:template match="/">
		&lt;div class="persone">
			&lt;span class="title">Lista utenti:&lt;/span>
			&lt;xsl:apply-templates select="//Persona" />
			&lt;div style="clear:both;">&lt;/div>
		&lt;/div>
	&lt;/xsl:template>
&lt;/xsl:stylesheet>
</pre>
<p>il secondo rappresenta invece l&#8217;elemento dell&#8217;anagrafica, <code>persona.xsl</code>:</p>
<pre class="xml:nogutter" name="code">
&lt;?xml version="1.0" encoding="utf-8"?>
&lt;xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
	&lt;xsl:template match="Persona">
		&lt;div class="persona">
			&lt;span>
				&lt;xsl:value-of select="Nome" />
			&lt;/span>
			&lt;xsl:text xml:space="preserve"> &lt;/xsl:text>
			&lt;span>
				&lt;xsl:value-of select="Cognome" />
			&lt;/span>
			&lt;span id="comune">
				Comune:
				&lt;xsl:value-of select="Comune" />
			&lt;/span>
		&lt;/div>
	&lt;/xsl:template>
&lt;/xsl:stylesheet>
</pre>
<p>Nell&#8217;esempio creo la lista delle anagrafiche a mano, ovviamente i dati possono avere qualsiasi provenienza</p>
<pre class="csharp:nogutter" name="code">
Anagrafiche anag = new Anagrafiche();

anag.Persone = new List&lt;Persona>();
anag.Persone.Add(new Persona() { Nome = "Mario", Cognome = "Rossi", Comune = "FantasiaPortamiVia" });
anag.Persone.Add(new Persona() { Nome = "Luigi", Cognome = "Verdi", Comune = "ComeSopra" });
</pre>
<p>Ora dobbiamo ottenere un documento XML dall&#8217;oggetto anag, fortunatamente ci viene in aiuto la serializzazione del .NET.<br />
Modifichiamo le classi <code>Anagrafiche</code> e <code>Persona</code>, aggiungendo i seguenti attributi:</p>
<pre class="csharp:nogutter" name="code">
using System.Xml;
using System.Xml.Serialization;
...

public class Anagrafiche
{
    [XmlArray("Persone"), XmlArrayItem(typeof(Persona), ElementName = "Persona")]
    public List&lt;Persona> Persone { get; set; }
}

[XmlRoot("Persona")]
public class Persona
{
	...
}
</pre>
<p>Ciò ci consentirà, tramite un XmlSerializer di ottenere un documento nel formato richiesto dai fogli XSL.</p>
<p>A questo punto possiamo già realizzare il bind lato server:</p>
<pre class="csharp:nogutter" name="code">
protected void Bind_Xml(Anagrafiche anag)
{
    // Serializzatore xml, sulla base della classe Anagrafiche
    XmlSerializer ser = new XmlSerializer(typeof(Anagrafiche));
    string sXml = "";

    // Creo un memorystream in cui riversare la stringa dell'xml, occhio allo using
    using (MemoryStream ms = new MemoryStream())
    {
        ser.Serialize(ms, anag);
        ms.Seek(0, SeekOrigin.Begin);

        StreamReader sr = new StreamReader(ms);

        sXml = sr.ReadToEnd();
    }

    if (sXml != "")
    {
        // Creo un XslTransform.
        // La classe è deprecata, in favore di XslCompiledTransform, ma per un piccolo esempio come questo andrà più che bene,
        // sembra anche più semplice da usare.

        XslTransform xsltRiepilogo = new XslTransform();
        xsltRiepilogo.Load(Server.MapPath("~/xslt/persone.xsl"));

        // Assegno il foglio xslt all'asp:Xml
        xmlData.Transform = xsltRiepilogo;

        // Assegno il documento xml al controllo, che in congiunzione con l'xslt produrrà l'html di cui ho bisogno.
        xmlData.DocumentContent = sXml;
        xmlData.DataBind();
    }
}
</pre>
<p>la parte a mio avviso più interessante è il processo automatico (con l&#8217;utilizzo dell&#8217;XmlSerializer) attraverso cui otteniamo il documento XML necessario, che per il nostro esempio sarà:</p>
<pre class="xml:nogutter" name="code">
&lt;?xml version="1.0"?>
&lt;Anagrafiche xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema">
  &lt;Persone>
    &lt;Persona>
      &lt;Nome>Mario&lt;/Nome>
      &lt;Cognome>Rossi&lt;/Cognome>
      &lt;Comune>FantasiaPortamiVia&lt;/Comune>
    &lt;/Persona>
    &lt;Persona>
      &lt;Nome>Luigi&lt;/Nome>
      &lt;Cognome>Verdi&lt;/Cognome>
      &lt;Comune>ComeSopra&lt;/Comune>
    &lt;/Persona>
  &lt;/Persone>
&lt;/Anagrafiche>
</pre>
<p>Da notare il fatto che il documento ha appunto il formato dichiarato nelle classi da cui deriva <code>[XmlArray("Persone") ...]</code> e <code>[XmlRoot("Persona")]</code> ci hanno consentito di definire i tag di output.<br />
Questo documento sarà quindi assegnato come stringa a sXml, a sua volta assegnato a <code>xmlData.DocumentContent</code>, come documento XML da elaborare.<br />
La riga</p>
<pre class="csharp:nogutter" name="code">
xmlData.Transform = xsltRiepilogo;
</pre>
<p>definisce invece con quale foglio si vogliono processare i dati.<br />
Basta un&#8217;infarinatura di XPath e XSLT per capire che l&#8217;output sarà un frammento HTML del tipo:</p>
<pre class="html:nogutter" name="code">
&lt;div class="persone">
    &lt;span class="title">Lista utenti:&lt;/span>
    &lt;div class="persona">
        &lt;span>Mario&lt;/span>
        &lt;span>Rossi&lt;/span>
        &lt;span id="comune"> Comune: FantasiaPortamiVia&lt;/span>
    &lt;/div>
    &lt;div class="persona">
        &lt;span>Luigi&lt;/span>
        &lt;span>Verdi&lt;/span>
        &lt;span id="comune"> Comune: ComeSopra&lt;/span>
    &lt;/div>
    &lt;div style="clear: both;" />
&lt;/div>
</pre>
<p>Et voilà, abbiamo la prima parte dell&#8217;opera.</p>
<p><strong>Lato client</strong><br />
Come dicevamo, vogliamo fare in modo che la lista possa crescere in modo asincrono tramite input inserito dall&#8217;utente.<br />
Per far ciò utilizzeremo jQuery, più l&#8217;ottimo plug-in Transform che trovate a quest&#8217;indirizzo <a href="http://plugins.jquery.com/project/Transform" target="_blank">http://plugins.jquery.com/project/Transform</a>, e che effettua lato client (e cross-browser, importante) le stesse operazioni della classe .NET XslTransform.</p>
<p>Come prima cosa dobbiamo configurare un web service che possa gestire le chiamate client, con un metodo che accetti in input nome, cognome e comune, e che ci restituisca quantomeno l&#8217;XML necessario ad effettuare la trasformazione con jQuery.</p>
<p>Ovviamente potremmo non passare dal server, tutto dipende da cosa nel dettaglio debba fare la nostra applicazione, ma c&#8217;è almeno un buon motivo per farlo: potremo utilizzare la serializzazione .NET su un elemento Persona, creato in base all&#8217;input dell&#8217;utente, ottenendo automaticamente l&#8217;XML che ci occorre per essere trasformato, questa volta non da persone.xsl, ma da persona.xsl.<br />
E questo spiega anche perchè abbiamo creato 2 fogli anzichè uno solo&#8230;</p>
<p>Nel mio caso, poi, il passaggio per il web service era obbligato dalla necessità di salvare il record nel database.</p>
<p>Quindi, una volta creato il Web Service assicuriamoci che abbia i seguenti using e attributi:</p>
<pre class="csharp:nogutter" name="code">
using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;
using System.Web.Services;
using System.Web.Script.Services;
using System.Xml.Serialization;
using System.Runtime.Serialization;
using System.Runtime.Serialization.Json;
using System.IO;

[ScriptService]
[WebService(Namespace = "http://tempuri.org/")]
[WebServiceBinding(ConformsTo = WsiProfiles.BasicProfile1_1)]

public class WebService : System.Web.Services.WebService {
	...
}
</pre>
<p>A questo punto creiamo una classe per le risposte al client:</p>
<pre class="csharp:nogutter" name="code">
[DataContract]
public class jsonResult
{
    [DataMember]
    public bool result { get; set; }

    [DataMember]
    public string xmlItem { get; set; }
}
</pre>
<p>Gli attributi <code>[DataContract]</code> e <code>[DataMember]</code> ci consentiranno di serializzare in json l&#8217;oggetto. Comodo no?<br />
Il metodo per l&#8217;aggiunta delle persone, che chiameremo <code>inserisci</code>, è il seguente:</p>
<pre class="csharp:nogutter" name="code">
[ScriptMethod(UseHttpGet = false)]
[WebMethod(EnableSession = true)]   // Ci serve per poter recuperare la session
public jsonResult inserisci(string nome, string cognome, string comune)
{
    bool bResult = true;
    string xml = "";

    try
    {
        XmlSerializer ser = new XmlSerializer(typeof(Persona));
        Persona nuovaPersona = (new Persona() { Nome = nome, Cognome = cognome, Comune = comune });

        using (MemoryStream ms = new MemoryStream())
        {
            ser.Serialize(ms, nuovaPersona);
            ms.Seek(0, SeekOrigin.Begin);

            StreamReader sr = new StreamReader(ms);

            xml = sr.ReadToEnd();
        }
    }
    catch
    {
        bResult = false;
    }

    jsonResult json = new jsonResult() { result = bResult, xmlItem = xml };

    return json;
}
</pre>
<p>L&#8217;attributo <code>[ScriptMethod(UseHttpGet = false)]</code> ci permette di effettuare la chiamata al metodo in post.</p>
<p>Nell&#8217;html abbiamo intanto creato 3 input text con i seguenti id: txtNome, txtCognome e txtComune; più un button btnAggiungi per eseguire l&#8217;inserimento.<br />
Il javascript è nell&#8217;esempio notevolmente semplificato, manca ogni tipo di validazione e gestione dell&#8217;errore, ma rende comunque l&#8217;idea:</p>
<pre class="js:nogutter" name="code">
$(function() {
    $("#btnAggiungi").click( function() {

        var v_nome = $("#txtNome").val();
        var v_cognome = $("#txtCognome").val();
        var v_comune = $("#txtComune").val();

        var jsonData = JSON.stringify({ nome: v_nome,
                                        cognome: v_cognome,
                                        comune: v_comune });

        $.ajax({
            type: "POST",
            url: "WebService.asmx/inserisci",
            data: jsonData,
            contentType: "application/json; charset=utf-8",
            success: function(resp) {
                var serverResp = JSON.parse(resp);
                var json = serverResp.d;

                if (json.result)
                {
                    $.transform({
                        xmlstr: json.xmlItem,
                        xsl: "xslt/persona.xsl",
                        async: false,
                        success: function(html, xsl, xml, obj) {
                            $(html).insertAfter(".persona:last");
                        }
                    });
                }
                else
                {
                    // Gestione dell'errore
                }
            },
            error: function() { /* Gestione dell'errore */ }
        });
    });
});
</pre>
<p>Come possiamo vedere viene gestito l&#8217;evento click di btnAggiungi, dove vengono raccolti i dati inseriti dall&#8217;utente, e tramite la libreria JSON2 (<a href="http://www.json.org/js.html" target="_blank">http://www.json.org/js.html</a>) viene generata una stringa sulla base dell&#8217;oggetto che racchiude i dati da postare al metodo inserisci del Web Sevice (da notare che gli attributi hanno lo stesso nome dei parametri del metodo).</p>
<p>Viene poi effettuata una chiamata ajax al web service, postando l&#8217;oggetto jsonData.<br />
La funzione di success riceve, nella variabile resp, il risultato della chiamata, ne effettua una conversione in oggetto json tramite <code>JSON.parse</code> (lato server l&#8217;avevamo serializzato) e verifica che json.d.result sia true (non si è verificato un errore lato server).<br />
A questo punto utiliziamo il plug-in Transform per elaborare <code>json.d.xmlItem</code> che come abbiamo visto contiene l&#8217;XML della nuova persona, selezionando come foglio persona.xsl.<br />
Nel success di <code>$.transform</code>, infine, effettuiamo l&#8217;append dell&#8217;html così ottenuto.</p>
<p>E questa è la fine del giro.</p>
<p>Abbiamo effettuato un bind che si potrebbe definire &#8220;cross&#8221;, svincolandoci dai template del .NET e creando una soluzione altamente portabile, ligia agli standard (XML, XPath e XSLT) e piuttosto flessibile.</p>
<p>Ovviamente ci sono anche i contro.<br />
- Innanzitutto questo esempio funziona bene con controlli html, ma se volessimo utilizzare controlli Asp.Net nel template XSLT le cose si complicherebbero notevolmente.<br />
- Eventuali paginazioni gestite lato server sono inutilizzabili, al crescere degli elementi dal client non ho controllo sul paginatore.<br />
- Infine non ho idea delle prestazioni generali della soluzione. Come ho già detto la classe XslTransform è stata deprecata in favore della XslCompiledTransform, che risulta più veloce, ma non ho fatto dei test per confrontarle con i template di un controllo bindabile&#8230;</p>
<p>Diciamo semplicemente che può essere un interessante esperimento.</p>
<p>Qualche commento, suggerimento o errori da segnalare?</p>
]]></content:encoded>
			<wfw:commentRss>http://www.recoding.it/?feed=rss2&amp;p=99</wfw:commentRss>
		<slash:comments>1</slash:comments>
		</item>
		<item>
		<title>Estendere i selettori di jQuery</title>
		<link>http://www.recoding.it/?p=12</link>
		<comments>http://www.recoding.it/?p=12#comments</comments>
		<pubDate>Sat, 28 Feb 2009 17:21:52 +0000</pubDate>
		<dc:creator>eros</dc:creator>
				<category><![CDATA[jQuery]]></category>

		<guid isPermaLink="false">http://www.recoding.it/?p=12</guid>
		<description><![CDATA[Come creare un plug-in per estendere le capacità dei selettori jQuery.]]></description>
			<content:encoded><![CDATA[<p><a class="codiceLink" href="http://www.recoding.it/wp-content/uploads/2009/02/estendere_selettori_jquery_demo.zip"><img src="wp-content/themes/charcoal/images/box_zip.gif" alt="" />download esempio</a><br />
<br />
jQuery, il potente framework javascript creato da John Resig, è già fornito nativamente di versatili <a href="http://docs.jquery.com/Selectors" target="_blank">selettori standard</a>, che coprono la quasi totalità delle necessità degli sviluppatori, garantendo la possibilità di selezionare gli elementi del DOM sulla base di una gerarchia, di un attributo o del contenuto.</p>
<p>Tuttavia possono crearsi situazioni in cui la selezione degli elementi risulta più complessa, creando la necessità di sviluppare specifiche funzioni che applichino una certa logica di selezione.</p>
<p>Fortunatamente esiste un semplice metodo per estendere le potenzialità del framework, creando dei selettori custom, personalizzabili, ed integrabili nelle precedenti espressioni di selezione.</p>
<p>Vediamo come.</p>
<p>Iniziamo con la creazione di un plug-in vuoto, in un file che chiameremo moreselectors.jquery.js, la struttura del plug-in sarà più o meno questa:</p>
<pre class="js:nogutter" name="code">(function($) {
    // estensione del namespace jQuery
    $.extend(
        // con questo estendiamo il parser dei selettori jQuery...
        $.expr[":"],
        {
            // aggiungendo una nuova regola e relativa funzione
            espressione: function(e) { ... }
        }
    );
})(jQuery);</pre>
<p>La funzione associata alla regola funzionerà da predicato, restituendo true o false determinerà l&#8217;inclusione o meno nell&#8217;array dei risultati dell&#8217;elemento corrente. Il parametro <code>e</code>, o <code>element</code>, indica l&#8217;elemento DOM in esame e sono possibili altri 2 parametri <code>i</code> che indica l&#8217;indice nell&#8217;array della selezione e <code>c</code> che rappresenta i componenti del selettore (come vedremo successivamente).<br />
Una volta che si sarà definita l&#8217;espressione e si è incluso il plug-in della pagina la sintassi per il suo utilizzo è quella che ben conosciamo:</p>
<pre class="js:nogutter" name="code">
$("div:espressione")
// es. filtrerà i div che rispettano il predicato della funzione
$("div:not(:espressione)")
// filtra gli elementi div che non rispettano il predicato
</pre>
<p>Utiliziamo come esempio questo blocco di html:</p>
<pre class="html:nogutter" name="code">
...
&lt;div&gt;
&lt;div class="divLevel1"&gt;
&lt;div class="divLevel2"&gt;a&lt;/div&gt;
&lt;/div&gt;
&lt;div class="divLevel1" style="border:dotted 1px #dedede;"&gt;
&lt;div class="divLevel2"&gt;b&lt;/div&gt;
&lt;/div&gt;
&lt;div class="divLevel1"&gt;(vuoto)&lt;/div&gt;
&lt;div class="divLevel1" style="background-color:Red;"&gt;
&lt;div class="divLevel2"&gt;d&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
...</pre>
<p>e vediamo come può essere l&#8217;espressione per la selezione dell&#8217;unico div.divLevel1 senza elementi child, selettore che chiameremo per l&#8217;appunto &#8220;no-child&#8221; (virgolette necessarie, in quanto c&#8217;è il carattere -):</p>
<pre class="js:nogutter" name="code">...
"no-child": function(e) {
                return $(e).find("*").length == 0;
            }
...
... utilizzo
$("div.divLevel1:no-child")</pre>
<p>Ora tentiamo di fare qualcosa di più complicato: selezioniamo il div con lo sfondo rosso:</p>
<pre class="js:nogutter" name="code">...
red: function(e) {
    var color = $(e).css("background-color").toLowerCase();
    if (color.indexOf("rgb") == -1)
    {
        return color == "#ff0000" || color == "red";
    }
    else
    {
        var rgb = $(e).css("background-color")
                      .replace("rgb(", "").replace(")", "");
        var componenti = rgb.split(", ");
        return (255 === parseInt(componenti[0], 10))
                &amp;&amp; (0 === parseInt(componenti[1], 10))
                &amp;&amp; (0 === parseInt(componenti[2], 10));
    }
}
...
... utilizzo
$("#div:red")</pre>
<p>In questo caso abbiamo dovuto fare una distinzione tra il modo in cui IE e firefox trattano l&#8217;informazione sul colore. In firefox la lettura (quantomeno con jQuery) di un color o di un background-color restituisce la rappresentazione rgb del colore stesso, mentre in IE si ottiene o il nome del colore o la sua rappresentazione esadecimale.</p>
<p>Infine vediamo come può essere creato un semplice selettore con parametri.<br />
Come abbiamo detto prima, il parametro <code>c</code> della funzione di espressione contiene i componenti del selettore, ed è in realtà un array restituito dal parser jQuery il cui contenuto, ipotizzando un selettore nella forma <code>:selettore(parametro)</code> è il seguente:<br />
<code>c[0] -&gt; ':selettore(parametro)'</code><br />
<code>c[1] -&gt; ':'</code><br />
<code>c[2] -&gt; 'selettore'</code><br />
<code>c[3] -&gt; 'parametro'</code></p>
<p>Detto ciò vediamo come può essere un selettore in grado di selezionare gli elementi con un border-style di un certo tipo:</p>
<pre class="js:nogutter" name="code">...
hasBorder: function(e, i, c) {
    // con un'espressione regolare controlliamo che il parametro
    // appartenga a un insieme di valori consentiti
    if (/none|hidden|dotted|dashed|solid|double|groove|ridge|inset|outset/
        .test(c[3]))
    {
        // poi controlliamo che i 4 border-style corrispondano al
        // parametro indicato
        return ((c[3] == $(e).css("border-right-style")) &amp;&amp;
                (c[3] == $(e).css("border-bottom-style")) &amp;&amp;
                (c[3] == $(e).css("border-left-style")) &amp;&amp;
                (c[3] == $(e).css("border-top-style")));
    }
    else
    {
        return false;
    }
}
...
... utilizzo
$(".divLevel1:hasBorder(dotted)")</pre>
<p>&#8230;e si potrebbe andare avanti per delle ore facendo esempi più o meno inutili.<br />
I selettori così creati ovviamente coesistono con i selettori standard e niente ci vieta di combinarli come ci pare.<br />
Commenti? Suggerimenti?</p>
]]></content:encoded>
			<wfw:commentRss>http://www.recoding.it/?feed=rss2&amp;p=12</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>Recoding.it</title>
		<link>http://www.recoding.it/?p=3</link>
		<comments>http://www.recoding.it/?p=3#comments</comments>
		<pubDate>Fri, 27 Feb 2009 00:32:50 +0000</pubDate>
		<dc:creator>admin</dc:creator>
				<category><![CDATA[Generica]]></category>

		<guid isPermaLink="false">http://www.recoding.it/?p=3</guid>
		<description><![CDATA[Nuovo blog rivolto agli sviluppatori web.]]></description>
			<content:encoded><![CDATA[<p>Recoding.it nasce come nuovo blog rivolto agli sviluppatori web.</p>
<p>Spazieremo dalle tecnologie di base (HTML/CSS) al DHTML (Javascript/jQuery) fino al server-side basato su piattaforma Asp.Net (C#).</p>
<p>Proporremo soluzioni presentate principalmente in forma di tutorial e snippet di codice, correlati da esempi e progetti scaricabili.</p>
<p>A presto per aggiornamenti.</p>
]]></content:encoded>
			<wfw:commentRss>http://www.recoding.it/?feed=rss2&amp;p=3</wfw:commentRss>
		<slash:comments>1</slash:comments>
		</item>
	</channel>
</rss>
