New Upstream Snapshot - libjs-jquery-tmpl

Ready changes

Summary

Merged new upstream version: 1.0.0~beta+git20181208.2.ef4ae98 (was: 1.0.0~beta+git20181208.1.ef4ae98).

Resulting package

Built on 2023-01-16T01:10 (took 3m32s)

The resulting binary packages can be installed (if you have the apt repository enabled) by running one of:

apt install -t fresh-snapshots libjs-jquery-tmpl

Diff

diff --git a/README.md b/README.md
index 7b2fa89..3bfb4c9 100644
--- a/README.md
+++ b/README.md
@@ -1,17 +1,245 @@
-# jQuery Templates plugin
-
-_Note: this plugin requires jquery version 1.4.2._
- 
-jQuery templates contain markup with binding expressions. Templates are applied to data objects or arrays, and rendered into the HTML DOM
-
-Documentation for the _jQuery Templates_ plugin can be found on the jQuery documentation site:
-<a href="http://api.jquery.com/category/plugins/templates/">http://api.jquery.com/category/plugins/templates</a>
-
-See also <a href="http://www.borismoore.com/2010/10/jquery-templates-is-now-official-jquery.html">http://www.borismoore.com/2010/10/jquery-templates-is-now-official-jquery.html</a> for more background.
-
-<p>The beta1 version of jQuery Templates is available on CDN at:
-<ul>
-<li><a href="http://ajax.microsoft.com/ajax/jquery.templates/beta1/jquery.tmpl.js">http://ajax.microsoft.com/ajax/jquery.templates/beta1/jquery.tmpl.js</a></li>
-<li><a href="http://ajax.microsoft.com/ajax/jquery.templates/beta1/jquery.tmpl.min.js">http://ajax.microsoft.com/ajax/jquery.templates/beta1/jquery.tmpl.min.js</a></li>
-</ul></p>
-
+# jQuery Templates plugin vBeta1.0.0
+
+_jQuery Templates is no longer in active development, and has been superseded by
+<a href="http://github.com/borismoore/jsrender">JsRender</a>_.
+
+JsRender functionality is a superset of the jQuery Templates feature set,
+and brings many improvements. The template tag syntax is similar. Rendering
+and compiling performance of JsRender is considerably better than jQuery templates performance.
+
+----
+
+_Note: This is the original official jQuery Templates plugin. The project was maintained by the
+jQuery team as an official jQuery plugin. Since the jQuery team has decided not to take this plugin
+past beta, it has been returned to the principal developer's GitHub account (Boris Moore).
+For more information on the history of jQuery Templates, and the roadmap going forward, see
+<a  href="http://www.borismoore.com/2011/10/jquery-templates-and-jsviews-roadmap.html">jQuery Templates and JsViews: The Roadmap</a>_
+
+----
+
+See vBeta1.0.0 tag for released beta version. Requires jQuery version 1.4.2.
+
+jQuery templates contain markup with binding expressions ('Template tags'). Templates are applied to data objects or arrays, and rendered into the HTML DOM.
+
+Note that documentation for the _jQuery Templates_ plugin is **no longer maintained on the jQuery documentation site**. 
+
+An archive copy of the original documentation (previously at api.jquery.com/category/plugins/templates/) can be found
+[here](http://web.archive.org/web/20120920065217/http://api.jquery.com/category/plugins/templates/).
+
+See also [http://www.borismoore.com/2010/10/jquery-templates-is-now-official-jquery.html](http://www.borismoore.com/2010/10/jquery-templates-is-now-official-jquery.html)
+for more background.
+
+Live versions of some of the demos from this repository can be found at
+[http://borismoore.github.io/jquery-tmpl/demos/step-by-step.html](http://borismoore.github.io/jquery-tmpl/demos/step-by-step.html).
+
+<p>The beta1 version of jQuery Templates is available on CDN at:
+<ul>
+<li><a href="http://ajax.microsoft.com/ajax/jquery.templates/beta1/jquery.tmpl.js">http://ajax.microsoft.com/ajax/jquery.templates/beta1/jquery.tmpl.js</a></li>
+<li><a href="http://ajax.microsoft.com/ajax/jquery.templates/beta1/jquery.tmpl.min.js">http://ajax.microsoft.com/ajax/jquery.templates/beta1/jquery.tmpl.min.js</a></li>
+</ul></p>
+
+---
+
+_The following is a restoration of jQuery's official documentation on tmpl() as it was on 12/28/2012. jQuery is Copyright 2012, John Resig_
+
+
+[Source](http://web.archive.org/web/20121014080309/http://api.jquery.com/jquery.tmpl/ "Permalink to jQuery.tmpl() � jQuery API")
+
+# jQuery.tmpl( template \[, data\] \[, options\] ) Returns: jQuery   
+
+## Description:
+**Render the specified HTML content as a template, using the specified data.**   version added: 1.4.3
+	jQuery.tmpl( template \[, data\] \[, options\] )
+	
+**template** The HTML markup or text to use as a template.
+
+**data** The data to render. This can be any JavaScript type, including Array or Object.
+
+**options** An optional map of user-defined key-value pairs. Extends the `tmplItem` data structure, available to the template during rendering. 
+
+This documentation topic concerns the *jQuery Templates* plugin (jquery-tmpl), which can be downloaded from: http://github.com/jquery/jquery-tmpl.  
+
+The `jQuery.tmpl()` method is designed for chaining with `.appendTo`, `.prependTo`, `.insertAfter` or `.insertBefore` as in the following example.
+
+### Example:
+```JavaScript
+    $.tmpl( "<li>${Name}</li>", { "Name" : "John Doe" }).appendTo( "#target" );
+```
+The `template` parameter can be any of the following: 
+
+*   A string containing markup.
+*   An HTML element (or jQuery object that wraps an element) whose content is to be used as the template.
+*   A string corresponding to the name of a named template (see jQuery.template() and .template()).
+*   A compiled-template function (see jQuery.template() and .template()).
+
+If `data` is an array, the template is rendered once for each data item in the array. If `data` is an object, or if the `data` parameter is missing or null, a single template item is rendered. 
+
+The return value is a jQuery collection of elements made up of the rendered template items (one for each data item in the array). If the template contains only one top-level element, then there will be one element for each data item in the array. 
+
+To insert the rendered template items into the HTML DOM, the returned jQuery collection should not be inserted directly into the DOM, but should be chained with `.appendTo`, `.prependTo`, `.insertAfter` or `.insertBefore`, as in following example: 
+```JavaScript
+    $.tmpl( myTemplate, myData ).appendTo( "#target" );
+```
+See also .tmpl().
+
+### Example 
+The following example shows how to use `jQuery.tmpl()` to render local data, using a template provided as a string: 
+```JavaScript
+      var movies = [
+		  { Name: "The Red Violin", ReleaseYear: "1998" },
+		  { Name: "Eyes Wide Shut", ReleaseYear: "1999" },
+		  { Name: "The Inheritance", ReleaseYear: "1976" }
+		];
+    
+      var markup = "<li><b>${Name}</b> (${ReleaseYear})</li>";
+
+	  // Compile the markup as a named template
+	  $.template( "movieTemplate", markup );
+
+	  // Render the template with the movies data and insert
+	  // the rendered HTML under the "movieList" element
+	  $.tmpl( "movieTemplate", movies )
+		  .appendTo( "#movieList" );
+```
+## Using Remote Data 
+Typically the data is not local and is instead obtained using an Ajax request to a remote service or page, as in the following example: 
+```JavaScript
+    var markup = "<li><b>${Name}</b> (${ReleaseYear})</li>";
+
+	// Compile the markup as a named template
+	$.template( "movieTemplate", markup );
+
+	$.ajax({
+	  dataType: "jsonp",
+	  url: moviesServiceUrl,
+	  jsonp: "$callback",
+	  success: showMovies
+	});
+
+	// Within the callback, use .tmpl() to render the data.
+	function showMovies( data ) {
+	  // Render the template with the "movies" data and insert
+	  // the rendered HTML under the 'movieList' element
+	  $.tmpl( "movieTemplate", data )
+		.appendTo( "#movieList" );
+	}
+```
+## The Markup for the Template  
+You can get the markup for the template from inline markup in the page, or from a string (possibly computed, or obtained remotely). For an example of how to use inline markup, see .tmpl().
+
+## Caching the Template 
+When a template is rendered, the markup is first converted into a compiled-template function. Every time `$.tmpl( markup , myData ).appendTo( "#target" )` is called, the template is recompiled. If the same template is to be used more than once for rendering data, you should ensure that the compiled template is cached. To cache the template when using markup that is obtained from a string (rather than from inline markup in the page), use `$.template( name, markup )` to create a named template for reuse. See jQuery.template().
+
+## Template Tags, Expressions, and Template Variables 
+Template tags such as the `${}` tag can used within jQuery templates in addition to text and HTML markup to enable a number of scenarios such as composition of templates, iteration over hierarchical data, parameterization of template rendering, etc. Template tags can render content based on the values of data item fields or template variables such as `$item` (corresponding to the template item), as well as expressions and function calls. See the documentation topics for each template tag: ${}, {{each}}, {{if}}, {{else}}, {{html}}, {{tmpl}} and {{wrap}}.
+
+## The `options` Parameter, and Template Items 
+Each template item (the result of rendering a data item with the template) is associated with a `tmplItem` data structure, which can be accessed using jQuery.tmplItem() and .tmplItem(), or the `$item` template variable. Any fields or anonomyous methods passed in with the `options` parameter of `jQuery.tmpl()` will extend the `tmplItem` data structure, and so be available to the template as in the following example: 
+```JavaScript
+    var markup = "<li>Some content: ${$item.myMethod()}.<br/>" 
+           + " More content: ${$item.myValue}.</li>";
+
+	// Compile the markup as a named template
+	$.template( "movieTemplate", markup );
+
+	// Render the template with the movies data
+	$.tmpl( "movieTemplate", movies,
+	  { 
+		  myValue: "somevalue", 
+		  myMethod: function() { 
+			  return "something";
+		  } 
+	  } 
+	).appendTo( "#movieList" );
+```
+## Examples:
+
+### Example: Render local data using jQuery.tmpl().
+```html
+	<!DOCTYPE html>
+	<html>
+	<head>
+	  <script src="http://code.jquery.com/jquery-latest.min.js"></script>
+	  <script src="http://ajax.microsoft.com/ajax/jquery.templates/beta1/jquery.tmpl.min.js"></script>
+	</head>
+	<body>
+	  
+	<ul id="movieList"></ul>
+
+	<script>
+	  var movies = [
+	  { Name: "The Red Violin", ReleaseYear: "1998" },
+	  { Name: "Eyes Wide Shut", ReleaseYear: "1999" },
+	  { Name: "The Inheritance", ReleaseYear: "1976" }
+	  ];
+
+	var markup = "<li><b>${Name}</b> (${ReleaseYear})</li>";
+
+	/* Compile the markup as a named template */
+	$.template( "movieTemplate", markup );
+
+	/* Render the template with the movies data and insert
+	   the rendered HTML under the "movieList" element */
+	$.tmpl( "movieTemplate", movies )
+	  .appendTo( "#movieList" );
+	</script>
+
+	</body>
+	</html>
+```
+### Example: Render data from a remote service, using jQuery.tmpl().
+```html
+	<!DOCTYPE html>
+	<html>
+	<head>
+	  <script src="http://code.jquery.com/jquery-latest.min.js"></script>
+	  <script src="http://ajax.microsoft.com/ajax/jquery.templates/beta1/jquery.tmpl.min.js"></script>
+	</head>
+	<body>
+	  
+	<button id="cartoonsBtn">Cartoons</button>
+	<button id="dramaBtn">Drama</button>
+
+	<ul id="movieList"></ul>
+
+	<script>
+	var markup = "<li><b>${Name}</b> (${ReleaseYear})</li>";
+
+	/* Compile the markup as a named template */
+	$.template( "movieTemplate", markup );
+
+	function getMovies( genre, skip, top ) {
+	  $.ajax({
+		dataType: "jsonp",
+		url: "http://odata.netflix.com/Catalog/Genres('" + genre
+		+ "')/Titles?$format=json&$skip="
+		+ skip + "&$top=" + top,
+		jsonp: "$callback",
+		success: function( data ) {
+		  /* Get the movies array from the data */
+		  var movies = data.d;
+
+		  /* Remove current set of movie template items */
+		  $( "#movieList" ).empty();
+
+		  /* Render the template items for each movie
+		  and insert the template items into the "movieList" */
+		  $.tmpl( "movieTemplate", movies )
+		  .appendTo( "#movieList" );
+		}
+	  });
+	}
+
+	$( "#cartoonsBtn" ).click( function() {
+	  getMovies( "Cartoons", 0, 6 );
+	});
+
+	$( "#dramaBtn" ).click( function() {
+	  getMovies( "Drama", 0, 6 );
+	});
+
+	</script>
+
+	</body>
+	</html>
+```
diff --git a/debian/changelog b/debian/changelog
index 811ae37..717154f 100644
--- a/debian/changelog
+++ b/debian/changelog
@@ -1,8 +1,10 @@
-libjs-jquery-tmpl (1.0.0~beta+dfsg-4) UNRELEASED; urgency=medium
+libjs-jquery-tmpl (1.0.0~beta+git20181208.2.ef4ae98-1) UNRELEASED; urgency=medium
 
   * Update standards version to 4.1.5, no changes needed.
+  * New upstream snapshot.
+  * New upstream snapshot.
 
- -- Debian Janitor <janitor@jelmer.uk>  Sun, 20 Feb 2022 03:39:10 -0000
+ -- Debian Janitor <janitor@jelmer.uk>  Mon, 16 Jan 2023 01:07:27 -0000
 
 libjs-jquery-tmpl (1.0.0~beta+dfsg-3) unstable; urgency=medium
 
diff --git a/demos/movies/PagesCore/movies.html b/demos/movies/PagesCore/movies.html
index 9421a0e..182e163 100644
--- a/demos/movies/PagesCore/movies.html
+++ b/demos/movies/PagesCore/movies.html
@@ -7,7 +7,7 @@ It uses the NetFlix OData JSONP services as a source of data.
 
 This version of the movies sample demo uses the
 	$( templateSelector ).tmpl( data ).appendTo( targetSelector )
-pattern, and does not use the tmplCmd plugin or the rendered event.
+pattern, and does not use the tmplPlus features (.tmplCmd, or the rendered event).
 -->
 <html xmlns="http://www.w3.org/1999/xhtml">
 <head>
@@ -16,10 +16,6 @@ pattern, and does not use the tmplCmd plugin or the rendered event.
 	<link href="../css/movies.css" rel="stylesheet" type="text/css" />
 </head>
 <body>
-<div id="about">
-	This is a demo of updates to the jQuery templating feature, proposed by Microsoft.<br/>
-	The source can be viewed here: <a href="http://github.com/nje/jquery-tmpl/">NJE Templating Branch on GitHub</a>
-</div>
 <div id="pageBody">
 <h1>Netflix: Book a Movie...</h1>
 
@@ -42,7 +38,7 @@ pattern, and does not use the tmplCmd plugin or the rendered event.
 	</table>
 	<br/>
 </div>
-<script src="http://code.jquery.com/jquery.js"></script>
+<script src="http://code.jquery.com/jquery.js" type="text/javascript"></script>
 <script src="../../../jquery.tmpl.js" type="text/javascript"></script>
 <script src="../components/jquery.pager.js" type="text/javascript"></script>
 <script src="../components/jquery-ui-1.8.1.custom.js" type="text/javascript"></script>
@@ -147,7 +143,7 @@ pattern, and does not use the tmplCmd plugin or the rendered event.
 			case "2":
 				compare = compareDate;
 				break;
-		} 
+		}
 
 		for ( var item in cart.bookings ) {
 			data.push( cart.bookings[item] );
@@ -157,7 +153,7 @@ pattern, and does not use the tmplCmd plugin or the rendered event.
 		for ( var i = 0, l = data.length; i < l; i++ ) {
 			$( bookingTmplItems[data[i].movie.Id].nodes ).appendTo( "#bookingsList" );
 		}
-	
+
 		function compareName( a, b ) {
 			return a == b ? 0 : (((a.movie.Name > b.movie.Name) !== reverse) ? 1 : -1);
 		}
@@ -196,7 +192,7 @@ pattern, and does not use the tmplCmd plugin or the rendered event.
 
 		$( "#movieTmpl" )
 			// Render movies using the movieTemplate
-			.tmpl( movies ) 
+			.tmpl( movies )
 
 			// Display rendered movies in the movieList container
 			.appendTo( "#movieList" )
@@ -232,7 +228,7 @@ pattern, and does not use the tmplCmd plugin or the rendered event.
 				return;
 			}
 			// Collapse previously selected booking, and switch to non-edit view
-			var oldSelected = selectedBooking; 
+			var oldSelected = selectedBooking;
 			$( "div", bookingTmplItems[oldSelected.movie.Id].nodes ).animate( { height: 0 }, 500, function() {
 				switchView( oldSelected );
 			});
@@ -249,7 +245,7 @@ pattern, and does not use the tmplCmd plugin or the rendered event.
 			var bookingNode = $( "#bookingEditTmpl" )
 
 				// Render the booking for the chosen movie using the bookingEditTemplate
-				.tmpl( booking, { animate: true } ) 
+				.tmpl( booking, { animate: true } )
 
 				// Append the rendered booking to the bookings list
 				.appendTo( "#bookingsList" )
@@ -262,7 +258,7 @@ pattern, and does not use the tmplCmd plugin or the rendered event.
 			bookingTmplItems[booking.movie.Id] = newItem;
 
 			// Attach handlers etc. on the rendered template.
-			bookingEditRendered( newItem ); 
+			bookingEditRendered( newItem );
 		}
 	}
 
@@ -297,7 +293,7 @@ pattern, and does not use the tmplCmd plugin or the rendered event.
 	}
 
 	function bookingRendered( item ) {
-		$( item.nodes ).click( function() { 
+		$( item.nodes ).click( function() {
 			selectBooking( item.data );
 		});
 		$( ".close", item.nodes ).click( removeBooking );
@@ -333,7 +329,7 @@ pattern, and does not use the tmplCmd plugin or the rendered event.
 		cartTmplItem.update();
 		$( bookingTmplItems[booking.movie.Id].nodes ).remove();
 		delete bookingTmplItems[booking.movie.Id];
-		return false; 
+		return false;
 	}
 
 	function removeBookings() {
@@ -351,10 +347,6 @@ pattern, and does not use the tmplCmd plugin or the rendered event.
 	function formatDate( date ) {
 		return date.toLocaleDateString();
 	}
-
-	function removeContext( item ) {
-		$( item.nodes ).remove();
-	}
 </script>
 
 </body>
diff --git a/demos/movies/PagesCore/moviesNoGlobals.html b/demos/movies/PagesCore/moviesNoGlobals.html
new file mode 100644
index 0000000..f470dc1
--- /dev/null
+++ b/demos/movies/PagesCore/moviesNoGlobals.html
@@ -0,0 +1,363 @@
+<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
+<!--
+This sample illustrates using templates for a more complete and
+realistic scenario.
+
+It uses the NetFlix OData JSONP services as a source of data.
+
+This version of the movies sample demo uses the
+	$( templateSelector ).tmpl( data ).appendTo( targetSelector )
+pattern, and does not use the tmplPlus features (.tmplCmd, or the rendered event).
+
+Notice that in the example there are no global variables:
+	Code is wrapped in  a function closure: (function($) {...})(jQuery);
+	The formatDate function within the closure is called from within the
+	template by passing it in with options:
+		$( "#bookingEditTmpl" ).tmpl( booking, { formatDate: formatDate } )
+	and accessing it on the template item, $item:
+		${$item.formatDate()}.
+-->
+<html xmlns="http://www.w3.org/1999/xhtml">
+<head>
+	<title>jQuery + OData + Netflix Catalog API</title>
+	<link href="../css/jquery-ui-1.8.1.custom.css" rel="stylesheet" type="text/css" />
+	<link href="../css/movies.css" rel="stylesheet" type="text/css" />
+</head>
+<body>
+<div id="pageBody">
+<h1>Netflix: Book a Movie...</h1>
+
+	<ul id="genres">
+		<li class="selected">Cartoons</li>
+		<li>Drama</li>
+		<li>Foreign</li>
+		<li>Action Classics</li>
+		<li>Horror</li>
+		<li>Sci-Fi Cult Classics</li>
+	</ul>
+
+	<div id="pager"><ul class="pages"><li class="pgEmpty">first</li><li class="pgEmpty">prev</li></ul></div>
+
+	<div id="movieList"></div>
+	<table id="bookingsList">
+		<tbody><tr class="cart"><td class="cart-false" colspan="4">
+			<span class="text">0 items in Cart...</span>
+		</td></tr></tbody>
+	</table>
+	<br/>
+</div>
+<script src="http://code.jquery.com/jquery.js" type="text/javascript"></script>
+<script src="../../../jquery.tmpl.js" type="text/javascript"></script>
+<script src="../components/jquery.pager.js" type="text/javascript"></script>
+<script src="../components/jquery-ui-1.8.1.custom.js" type="text/javascript"></script>
+
+<script id="movieTmpl" type="text/x-jquery-tmpl">
+	<div>
+		<div><img src="${BoxArt.LargeUrl}" /> </div>
+		<strong>${Name}</strong>
+		<p>{{html Synopsis}}</p>
+		<input type="button" title="Buy tickets for '${Name}'" value="Add to cart..." class="buyButton"/>
+		<br/>
+	</div>
+</script>
+
+<script id="cartTmpl" type="text/x-jquery-tmpl">
+	<td class="cart-${!!count}" colspan="4">
+		<span class="text">${count} items in Cart...</span>
+		{{if count}}
+			<span id="submit">Checkout</span>
+			<span id="cancel">Remove All</span>
+			{{if count>1}}
+				<span id="sort"><span id="sortBtn">Sort</span>:
+					<select>
+						<option value="0" {{if sortBy==="0"}}selected{{/if}}>Name A-Z</option>
+						<option value="1" {{if sortBy==="1"}}selected{{/if}}>Name Z-A</option>
+						<option value="2" {{if sortBy==="2"}}selected{{/if}}>Date</option>
+					</select>
+				</span>
+			{{/if}}
+			</select>
+		{{/if}}
+	</td>
+</script>
+
+<script id="bookingTitleTmpl" type="text/x-jquery-tmpl">
+	<tr class="bookingTitle${$item.mode}">
+		<td>${movie.Name}</td><td>${movieTheater}</td>
+		<td>${$item.formatDate()}</td>
+		<td>
+			${quantity}
+			<span class="ui-icon close"></span>
+		</td>
+	</tr>
+</script>
+
+<script id="bookingEditTmpl" type="text/x-jquery-tmpl">
+	{{tmpl($data, { mode: "Edit", formatDate: $item.formatDate }) "#bookingTitleTmpl"}}
+	<tr class="bookingEdit">
+		<td colspan="4">
+			<div class="fields">
+				<span>Movie Theater: </span><input class="theater" type="text" value="${movieTheater}" /><br/>
+				<span>Date: </span><input class="date" type="text" value="${$item.formatDate()}" /><br/>
+				<span>Quantity: </span><input class="quantity" type="text" value="${quantity}" />
+			</div>
+			<div><img src="${movie.BoxArt.LargeUrl}" /></div>
+		</td>
+	</tr>
+</script>
+
+<script type="text/javascript">
+(function($) {
+	var genre="Cartoons", pageIndex = 1, pageSize = 3, pageCount = 0,
+		cart = { bookings: {}, count: 0, sortBy:0 }, bookingTmplItems = {}, selectedBooking;
+
+	getMovies( pageIndex );
+
+	$( "#genres li" ).click( selectGenre );
+
+	$( ".cart" )
+		.delegate( "select", "change", sort )
+		.delegate( "#sortBtn", "click", sort )
+		.delegate( "#submit", "click", function() {
+			alert( cart.count + " bookings submitted for payment...");
+			removeBookings();
+		})
+		.delegate( "#cancel", "click", function() {
+			removeBookings();
+		})
+		.empty();
+
+	$( "#cartTmpl" )
+		.tmpl( cart )
+		.appendTo( ".cart", cart );
+
+	var cartTmplItem = $( ".cart td" ).tmplItem();
+
+	function selectGenre() {
+		$( "#genres li" ).removeClass( "selected" );
+		$( this ).addClass( "selected" );
+
+		pageIndex = 1;
+		genre = encodeURI( $(this).text() );
+		getMovies( pageIndex );
+	}
+
+	function sort() {
+		var compare = compareName, reverse = false, data = [];
+		cart.sortBy = $( "#sort select" ).val();
+		switch ( $( "#sort select" ).val() ) {
+			case "1":
+				reverse = true;
+				break;
+			case "2":
+				compare = compareDate;
+				break;
+		}
+
+		for ( var item in cart.bookings ) {
+			data.push( cart.bookings[item] );
+		}
+		data = data.sort( compare );
+
+		for ( var i = 0, l = data.length; i < l; i++ ) {
+			$( bookingTmplItems[data[i].movie.Id].nodes ).appendTo( "#bookingsList" );
+		}
+
+		function compareName( a, b ) {
+			return a == b ? 0 : (((a.movie.Name > b.movie.Name) !== reverse) ? 1 : -1);
+		}
+		function compareDate( a, b ) {
+			return a.date - b.date;
+		}
+	}
+
+	function getMovies( index ) {
+		var query = "http://odata.netflix.com/Catalog/Genres('" + genre + "')/Titles" +
+			"?$format=json" +
+			"&$inlinecount=allpages" +				// get total number of records
+			"&$skip=" + (index-1) * pageSize +		// skip to first record of page
+			"&$top=" + pageSize;					// page size
+
+		pageIndex = index;
+
+		$( "#movieList" )
+			.fadeOut( "medium", function () {
+				$.ajax({
+					dataType: "jsonp",
+					url: query,
+					jsonp: "$callback",
+					success: showMovies
+				});
+			});
+	}
+
+	function showMovies( data ) {
+		pageCount = Math.ceil( data.d.__count/pageSize ),
+			movies = data.d.results;
+
+		$( "#pager" ).pager({ pagenumber: pageIndex, pagecount: pageCount, buttonClickCallback: getMovies });
+
+		$( "#movieList" ).empty();
+
+		$( "#movieTmpl" )
+			// Render movies using the movieTemplate
+			.tmpl( movies )
+
+			// Display rendered movies in the movieList container
+			.appendTo( "#movieList" )
+
+			// Animate
+			.find( "div" ).fadeIn( 4000 ).end()
+
+			// Add click handler
+			.find( ".buyButton" ).click( function() {
+				buyTickets( $(this).tmplItem().data );
+			});
+
+		$( "#movieList" ).fadeIn( "medium" )
+	}
+
+	function buyTickets( movie ) {
+		// Add item to cart
+		var booking = cart.bookings[movie.Id];
+		if ( booking ) {
+			booking.quantity++;
+		} else {
+			cart.count++;
+			cartTmplItem.update();
+			booking = { movie: movie, date: new Date(), quantity: 1, movieTheater: "" };
+		}
+		selectBooking( booking );
+	}
+
+	function selectBooking( booking ) {
+		if ( selectedBooking ) {
+			if ( selectedBooking === booking ) {
+				updateBooking( bookingTmplItems[selectedBooking.movie.Id]);
+				return;
+			}
+			// Collapse previously selected booking, and switch to non-edit view
+			var oldSelected = selectedBooking;
+			$( "div", bookingTmplItems[oldSelected.movie.Id].nodes ).animate( { height: 0 }, 500, function() {
+				switchView( oldSelected );
+			});
+		}
+		selectedBooking = booking;
+		if ( !booking ) {
+			return;
+		}
+		if ( cart.bookings[booking.movie.Id] ) {
+			switchView( booking, true );
+		} else {
+			cart.bookings[booking.movie.Id] = booking;
+
+			var bookingNode = $( "#bookingEditTmpl" )
+
+				// Render the booking for the chosen movie using the bookingEditTemplate
+				.tmpl( booking, { animate: true, formatDate: formatDate } )
+
+				// Append the rendered booking to the bookings list
+				.appendTo( "#bookingsList" )
+
+				// Get the 2nd <tr> of the appended booking
+				.last()[0];
+
+			// Get the template item for the 2nd <tr>, which is the template item for the "bookingEditTmpl" template
+			var newItem = $.tmplItem( bookingNode );
+			bookingTmplItems[booking.movie.Id] = newItem;
+
+			// Attach handlers etc. on the rendered template.
+			bookingEditRendered( newItem );
+		}
+	}
+
+	function bookingEditRendered( item ) {
+		var data = item.data, nodes = item.nodes;
+
+		$( nodes[0] ).click( function() {
+			selectBooking();
+		});
+
+		$( ".close", nodes ).click( removeBooking );
+
+		$( ".date", nodes ).change( function() {
+			data.date = $(this).datepicker( "getDate" );
+			updateBooking( item );
+		})
+		.datepicker({ dateFormat: "DD, d MM, yy" });
+
+		$( ".quantity", nodes ).change( function() {
+			data.quantity = $(this).val();
+			updateBooking( item );
+		});
+
+		$( ".theater", nodes ).change( function() {
+			data.movieTheater = $(this).val();
+			updateBooking( item );
+		});
+
+		if ( item.animate ) {
+			$( "div", nodes ).css( "height", 0 ).animate( { height: 116 }, 500 );
+		}
+	}
+
+	function bookingRendered( item ) {
+		$( item.nodes ).click( function() {
+			selectBooking( item.data );
+		});
+		$( ".close", item.nodes ).click( removeBooking );
+	}
+
+	function switchView( booking, edit ) {
+		if ( !booking ) {
+			return;
+		}
+		var item = bookingTmplItems[booking.movie.Id],
+			tmpl = $( edit ? "#bookingEditTmpl" : "#bookingTitleTmpl" ).template();
+		if ( item.tmpl !== tmpl) {
+			item.tmpl = tmpl;
+			item.update();
+			(edit ? bookingEditRendered : bookingRendered)( item );
+		}
+	}
+
+	function updateBooking( item ) {
+		item.animate = false;
+		item.update();
+		(item.data === selectedBooking ? bookingEditRendered : bookingRendered)( item );
+		item.animate = true;
+	}
+
+	function removeBooking() {
+		var booking = $.tmplItem(this).data;
+		if ( booking === selectedBooking ) {
+			selectedBooking = null;
+		}
+		delete cart.bookings[booking.movie.Id];
+		cart.count--;
+		cartTmplItem.update();
+		$( bookingTmplItems[booking.movie.Id].nodes ).remove();
+		delete bookingTmplItems[booking.movie.Id];
+		return false;
+	}
+
+	function removeBookings() {
+		for ( var item in bookingTmplItems ) {
+			$( bookingTmplItems[item].nodes ).remove();
+			delete bookingTmplItems[item];
+		}
+		bookingTmplItems = {};
+		cart.count = 0;
+		cart.bookings = {};
+		selectedBooking = null;
+		cartTmplItem.update();
+	}
+
+	function formatDate() {
+		return this.data.date.toLocaleDateString();
+	}
+})(jQuery);
+</script>
+
+</body>
+</html>
diff --git a/demos/movies/PagesTmplPlus/movies1.html b/demos/movies/PagesTmplPlus/movies1.html
index 211feeb..2235e03 100644
--- a/demos/movies/PagesTmplPlus/movies1.html
+++ b/demos/movies/PagesTmplPlus/movies1.html
@@ -3,12 +3,9 @@
 This sample illustrates using templates for a more complete and
 realistic scenario.
 
-It uses the NetFlix OData JSONP services as a source of data.
-
-This version of the movies sample demo uses the
-	$( templateSelector ).tmpl( data ).appendTo( targetSelector )
-pattern, and also uses the tmplCmd plugin.
-It does not use the rendered event.
+It is similar to the movies/PagesCore/movies.html sample,
+except that it uses jquery.tmplPlus.js in order to take advantage
+of the .tmplCmd() features. It does not use the rendered event.
 -->
 <html xmlns="http://www.w3.org/1999/xhtml">
 <head>
@@ -17,10 +14,6 @@ It does not use the rendered event.
 	<link href="../css/movies.css" rel="stylesheet" type="text/css" />
 </head>
 <body>
-<div id="about">
-	This is a demo of updates to the jQuery templating feature, proposed by Microsoft.<br/>
-	The source can be viewed here: <a href="http://github.com/nje/jquery-tmpl/">NJE Templating Branch on GitHub</a>
-</div>
 <div id="pageBody">
 <h1>Netflix: Book a Movie...</h1>
 
@@ -43,7 +36,7 @@ It does not use the rendered event.
 	</table>
 	<br/>
 </div>
-<script src="http://code.jquery.com/jquery.js"></script>
+<script src="http://code.jquery.com/jquery.js" type="text/javascript"></script>
 <script src="../../../jquery.tmpl.js" type="text/javascript"></script>
 <script src="../../../jquery.tmplPlus.js" type="text/javascript"></script>
 <script src="../components/jquery.pager.js" type="text/javascript"></script>
@@ -149,7 +142,7 @@ It does not use the rendered event.
 			case "2":
 				compare = compareDate;
 				break;
-		} 
+		}
 
 		for ( var item in cart.bookings ) {
 			data.push( cart.bookings[item] );
@@ -165,7 +158,7 @@ It does not use the rendered event.
 			return a.date - b.date;
 		}
 	}
-		
+
 	function getMovies( index ) {
 		var query = "http://odata.netflix.com/Catalog/Genres('" + genre + "')/Titles" +
 			"?$format=json" +
@@ -189,7 +182,7 @@ It does not use the rendered event.
 	function showMovies( data ) {
 		pageCount = Math.ceil( data.d.__count/pageSize ),
 			movies = data.d.results;
-		
+
 		$( "#pager" ).pager({ pagenumber: pageIndex, pagecount: pageCount, buttonClickCallback: getMovies });
 
 		// show movies in template
@@ -197,7 +190,7 @@ It does not use the rendered event.
 
 		$( "#movieTmpl" )
 			// Render movies using the movieTemplate
-			.tmpl( movies ) 
+			.tmpl( movies )
 
 			// Display rendered movies in the movieList container
 			.appendTo( "#movieList" )
@@ -233,7 +226,7 @@ It does not use the rendered event.
 				return;
 			}
 			// Collapse previously selected booking, and switch to non-edit view
-			var oldSelected = selectedBooking; 
+			var oldSelected = selectedBooking;
 			$( "div", bookingItem( oldSelected ).nodes ).animate( { height: 0 }, 500, function() {
 				switchView( oldSelected );
 			});
@@ -246,11 +239,11 @@ It does not use the rendered event.
 			switchView( booking, true );
 		} else {
 			cart.bookings[booking.movie.Id] = booking;
-		
+
 			var bookingNode = $( "#bookingEditTmpl" )
-		
+
 				// Render the booking for the chosen movie using the bookingEditTemplate
-				.tmpl( booking, { animate: true } ) 
+				.tmpl( booking, { animate: true } )
 
 				// Append the rendered booking to the bookings list
 				.appendTo( "#bookingsList" )
@@ -296,7 +289,7 @@ It does not use the rendered event.
 			$( "div", nodes ).css( "height", 0 ).animate( { height: 116 }, 500 );
 		}
 	}
-	
+
 	function bookingRendered( item ) {
 		$( item.nodes ).click( function() {
 			selectBooking( item.data );
@@ -311,7 +304,7 @@ It does not use the rendered event.
 		var item = bookingItem( booking ),
 			tmpl = $( edit ? "#bookingEditTmpl" : "#bookingTitleTmpl" ).template();
 		if ( item.tmpl !== tmpl) {
-			item.tmpl = tmpl; 
+			item.tmpl = tmpl;
 			item.update();
 			(edit ? bookingEditRendered : bookingRendered)( item );
 		}
@@ -333,7 +326,7 @@ It does not use the rendered event.
 		cart.count--;
 		cartTmplItem.update();
 		$.tmplCmd( "remove", booking, bookingTmplItems );
-		return false; 
+		return false;
 	}
 
 	function removeBookings() {
diff --git a/demos/movies/PagesTmplPlus/movies2.html b/demos/movies/PagesTmplPlus/movies2.html
index ba948bd..bfdd621 100644
--- a/demos/movies/PagesTmplPlus/movies2.html
+++ b/demos/movies/PagesTmplPlus/movies2.html
@@ -3,11 +3,9 @@
 This sample illustrates using templates for a more complete and
 realistic scenario.
 
-It uses the NetFlix OData JSONP services as a source of data.
-
-This version of the movies sample demo uses the
-	$( templateSelector ).tmpl( data ).appendTo( targetSelector )
-pattern, and also uses the tmplCmd plugin and the rendered event.
+It is similar to the movies/PagesCore/movies.html sample,
+except that it uses jquery.tmplPlus.js in order to take advantage
+of the .tmplCmd() features and the rendered event.
 -->
 <html xmlns="http://www.w3.org/1999/xhtml">
 <head>
@@ -16,10 +14,6 @@ pattern, and also uses the tmplCmd plugin and the rendered event.
 	<link href="../css/movies.css" rel="stylesheet" type="text/css" />
 </head>
 <body>
-<div id="about">
-	This is a demo of updates to the jQuery templating feature, proposed by Microsoft.<br/>
-	The source can be viewed here: <a href="http://github.com/nje/jquery-tmpl/">NJE Templating Branch on GitHub</a>
-</div>
 <div id="pageBody">
 <h1>Netflix: Book a Movie...</h1>
 
@@ -42,7 +36,7 @@ pattern, and also uses the tmplCmd plugin and the rendered event.
 	</table>
 	<br/>
 </div>
-<script src="http://code.jquery.com/jquery.js"></script>
+<script src="http://code.jquery.com/jquery.js" type="text/javascript"></script>
 <script src="../../../jquery.tmpl.js" type="text/javascript"></script>
 <script src="../../../jquery.tmplPlus.js" type="text/javascript"></script>
 <script src="../components/jquery.pager.js" type="text/javascript"></script>
@@ -106,7 +100,7 @@ pattern, and also uses the tmplCmd plugin and the rendered event.
 <script type="text/javascript">
 	var genre="Cartoons", pageIndex = 1, pageSize = 3, pageCount = 0,
 		cart = { bookings: {}, count: 0, sortBy:0 }, bookingTmplItems = [], selectedBooking;
-		
+
 	getMovies( pageIndex );
 
 	$( "#genres li" ).click( selectGenre );
@@ -147,7 +141,7 @@ pattern, and also uses the tmplCmd plugin and the rendered event.
 			case "2":
 				compare = compareDate;
 				break;
-		} 
+		}
 
 		for ( var item in cart.bookings ) {
 			data.push( cart.bookings[item] );
@@ -155,7 +149,7 @@ pattern, and also uses the tmplCmd plugin and the rendered event.
 		data = data.sort( compare );
 
 		bookingTmplItems = $.tmplCmd( "replace", data, bookingTmplItems );
-	
+
 		function compareName( a, b ) {
 			return a == b ? 0 : (((a.movie.Name > b.movie.Name) !== reverse) ? 1 : -1);
 		}
@@ -163,7 +157,7 @@ pattern, and also uses the tmplCmd plugin and the rendered event.
 			return a.date - b.date;
 		}
 	}
-		
+
 	function getMovies( index ) {
 		var query = "http://odata.netflix.com/Catalog/Genres('" + genre + "')/Titles" +
 			"?$format=json" +
@@ -183,7 +177,7 @@ pattern, and also uses the tmplCmd plugin and the rendered event.
 				});
 			});
 	}
-	
+
 	function showMovies( data ) {
 		pageCount = Math.ceil( data.d.__count/pageSize ),
 			movies = data.d.results;
@@ -223,7 +217,7 @@ pattern, and also uses the tmplCmd plugin and the rendered event.
 				return;
 			}
 			// Collapse previously selected booking, and switch to non-edit view
-			var oldSelected = selectedBooking; 
+			var oldSelected = selectedBooking;
 			$( "div", bookingItem( oldSelected ).nodes ).animate( { height: 0 }, 500, function() {
 				switchView( oldSelected );
 			});
@@ -240,8 +234,8 @@ pattern, and also uses the tmplCmd plugin and the rendered event.
 			$( "#bookingEditTmpl" )
 
 				// Render the booking for the chosen movie using the bookingEditTemplate
-				.tmpl( booking, { 
-					animate: true, 
+				.tmpl( booking, {
+					animate: true,
 					rendered: onBookingEditRendered,
 					addedTmplItems: bookingTmplItems
 				})
@@ -286,7 +280,7 @@ pattern, and also uses the tmplCmd plugin and the rendered event.
 		});
 
 		$( ".close", nodes ).click( removeBooking );
-		
+
 		$( ".date", nodes ).change( function() {
 			data.date = $(this).datepicker( "getDate" );
 			updateBooking( item );
@@ -323,7 +317,7 @@ pattern, and also uses the tmplCmd plugin and the rendered event.
 		cart.count--;
 		cartTmplItem.update();
 		$.tmplCmd( "remove", booking, bookingTmplItems );
-		return false; 
+		return false;
 	}
 
 	function removeBookings() {
diff --git a/demos/movies/PagesTmplPlus/movies3.html b/demos/movies/PagesTmplPlus/movies3.html
index 889e539..0d71982 100644
--- a/demos/movies/PagesTmplPlus/movies3.html
+++ b/demos/movies/PagesTmplPlus/movies3.html
@@ -3,11 +3,11 @@
 This sample illustrates using templates for a more complete and
 realistic scenario.
 
-It uses the NetFlix OData JSONP services as a source of data.
-
-This version of the movies sample demo uses the
-	$( targetSelector ).append( templateSelector, data )
-pattern, and also uses the tmplCmd plugin and the rendered event.
+It is similar to the movies/PagesCore/movies.html sample,
+except that it uses jquery.tmplPlus.js in order to take advantage
+of the
+$( targetSelector ).append( templateSelector, data )
+pattern, as well as the .tmplCmd() features and the rendered event.
 -->
 <html xmlns="http://www.w3.org/1999/xhtml">
 <head>
@@ -16,10 +16,6 @@ pattern, and also uses the tmplCmd plugin and the rendered event.
 	<link href="../css/movies.css" rel="stylesheet" type="text/css" />
 </head>
 <body>
-<div id="about">
-	This is a demo of updates to the jQuery templating feature, proposed by Microsoft.<br/>
-	The source can be viewed here: <a href="http://github.com/nje/jquery-tmpl/">NJE Templating Branch on GitHub</a>
-</div>
 <div id="pageBody">
 <h1>Netflix: Book a Movie...</h1>
 
@@ -42,7 +38,7 @@ pattern, and also uses the tmplCmd plugin and the rendered event.
 	</table>
 	<br/>
 </div>
-<script src="http://code.jquery.com/jquery.js"></script>
+<script src="http://code.jquery.com/jquery.js" type="text/javascript"></script>
 <script src="../../../jquery.tmpl.js" type="text/javascript"></script>
 <script src="../../../jquery.tmplPlus.js" type="text/javascript"></script>
 <script src="../components/jquery.pager.js" type="text/javascript"></script>
@@ -61,7 +57,7 @@ pattern, and also uses the tmplCmd plugin and the rendered event.
 <script id="cartTmpl" type="text/x-jquery-tmpl">
 	<td class="cart-${!!count}" colspan="4">
 		<span class="text">${count} items in Cart...</span>
-		{{if count}} 
+		{{if count}}
 			<span id="submit">Checkout</span>
 			<span id="cancel">Remove All</span>
 			{{if count>1}}
@@ -106,7 +102,7 @@ pattern, and also uses the tmplCmd plugin and the rendered event.
 <script type="text/javascript">
 	var genre="Cartoons", pageIndex = 1, pageSize = 3, pageCount = 0,
 		cart = { bookings: {}, count: 0, sortBy:0 }, bookingTmplItems = [], selectedBooking;
-		
+
 	getMovies( pageIndex );
 
 	$( "#genres li" ).click( selectGenre );
@@ -145,7 +141,7 @@ pattern, and also uses the tmplCmd plugin and the rendered event.
 			case "2":
 				compare = compareDate;
 				break;
-		} 
+		}
 
 		for ( var item in cart.bookings ) {
 			data.push( cart.bookings[item] );
@@ -153,7 +149,7 @@ pattern, and also uses the tmplCmd plugin and the rendered event.
 		data = data.sort( compare );
 
 		bookingTmplItems = $.tmplCmd( "replace", data, bookingTmplItems );
-	
+
 		function compareName( a, b ) {
 			return a == b ? 0 : (((a.movie.Name > b.movie.Name) !== reverse) ? 1 : -1);
 		}
@@ -161,7 +157,7 @@ pattern, and also uses the tmplCmd plugin and the rendered event.
 			return a.date - b.date;
 		}
 	}
-		
+
 	function getMovies( index ) {
 		var query = "http://odata.netflix.com/Catalog/Genres('" + genre + "')/Titles" +
 			"?$format=json" +
@@ -187,7 +183,7 @@ pattern, and also uses the tmplCmd plugin and the rendered event.
 			movies = data.d.results;
 
 		$( "#pager" ).pager({ pagenumber: pageIndex, pagecount: pageCount, buttonClickCallback: getMovies });
-		
+
 		$( "#movieList" )
 			.empty()
 
@@ -217,7 +213,7 @@ pattern, and also uses the tmplCmd plugin and the rendered event.
 				return;
 			}
 			// Collapse previously selected booking, and switch to non-edit view
-			var oldSelected = selectedBooking; 
+			var oldSelected = selectedBooking;
 			$( "div", bookingItem( oldSelected ).nodes ).animate( { height: 0 }, 500, function() {
 				switchView( oldSelected );
 			});
@@ -233,7 +229,7 @@ pattern, and also uses the tmplCmd plugin and the rendered event.
 
 			// Render the booking for the chosen movie using the bookingEditTemplate, and append the rendered booking to the bookings list
 			$( "#bookingsList" ).append( "#bookingEditTmpl", booking, {
-				animate: true, 
+				animate: true,
 				rendered: onBookingEditRendered,
 				addedTmplItems: bookingTmplItems
 			});
@@ -269,7 +265,7 @@ pattern, and also uses the tmplCmd plugin and the rendered event.
 
 	function onBookingEditRendered( item ) {
 		var data = item.data, nodes = item.nodes;
-		
+
 		$( nodes[0] ).click( function() {
 			selectBooking();
 		});
@@ -312,7 +308,7 @@ pattern, and also uses the tmplCmd plugin and the rendered event.
 		cart.count--;
 		cartTmplItem.update();
 		$.tmplCmd( "remove", booking, bookingTmplItems );
-		return false; 
+		return false;
 	}
 
 	function removeBookings() {
diff --git a/demos/movies/components/jquery-ui-1.8.1.custom.js b/demos/movies/components/jquery-ui-1.8.1.custom.js
index 981369e..bcd3ad8 100644
--- a/demos/movies/components/jquery-ui-1.8.1.custom.js
+++ b/demos/movies/components/jquery-ui-1.8.1.custom.js
@@ -114,7 +114,7 @@ $.fn.extend({
 			})
 			: this._focus.apply(this, arguments);
 	},
-	
+
 	enableSelection: function() {
 		return this
 			.attr('unselectable', 'off')
@@ -146,7 +146,7 @@ $.fn.extend({
 		if (zIndex !== undefined) {
 			return this.css('zIndex', zIndex);
 		}
-		
+
 		if (this.length) {
 			var elem = $(this[0]), position, value;
 			while (elem.length && elem[0] !== document) {
@@ -322,7 +322,7 @@ $.extend(Datepicker.prototype, {
 		if (this.debug)
 			console.log.apply('', arguments);
 	},
-	
+
 	// TODO rename to "widget" when switching to widget factory
 	_widgetDatepicker: function() {
 		return this.dpDiv;
@@ -1703,7 +1703,7 @@ $.extend(Datepicker.prototype, {
 					drawMonth = 0;
 					drawYear++;
 				}
-				calender += '</tbody></table>' + (isMultiMonth ? '</div>' + 
+				calender += '</tbody></table>' + (isMultiMonth ? '</div>' +
 							((numMonths[0] > 0 && col == numMonths[1]-1) ? '<div class="ui-datepicker-row-break"></div>' : '') : '');
 				group += calender;
 			}
diff --git a/demos/movies/components/jquery-ui-1.8.1.custom.min.js b/demos/movies/components/jquery-ui-1.8.1.custom.min.js
new file mode 100644
index 0000000..ef9e731
--- /dev/null
+++ b/demos/movies/components/jquery-ui-1.8.1.custom.min.js
@@ -0,0 +1,96 @@
+/*!
+ * jQuery UI 1.8.1
+ *
+ * Copyright (c) 2010 AUTHORS.txt (http://jqueryui.com/about)
+ * Dual licensed under the MIT (MIT-LICENSE.txt)
+ * and GPL (GPL-LICENSE.txt) licenses.
+ *
+ * http://docs.jquery.com/UI
+ */
+jQuery.ui||function(c){c.ui={version:"1.8.1",plugin:{add:function(a,b,d){a=c.ui[a].prototype;for(var e in d){a.plugins[e]=a.plugins[e]||[];a.plugins[e].push([b,d[e]])}},call:function(a,b,d){if((b=a.plugins[b])&&a.element[0].parentNode)for(var e=0;e<b.length;e++)a.options[b[e][0]]&&b[e][1].apply(a.element,d)}},contains:function(a,b){return document.compareDocumentPosition?a.compareDocumentPosition(b)&16:a!==b&&a.contains(b)},hasScroll:function(a,b){if(c(a).css("overflow")=="hidden")return false;
+b=b&&b=="left"?"scrollLeft":"scrollTop";var d=false;if(a[b]>0)return true;a[b]=1;d=a[b]>0;a[b]=0;return d},isOverAxis:function(a,b,d){return a>b&&a<b+d},isOver:function(a,b,d,e,f,g){return c.ui.isOverAxis(a,d,f)&&c.ui.isOverAxis(b,e,g)},keyCode:{ALT:18,BACKSPACE:8,CAPS_LOCK:20,COMMA:188,CONTROL:17,DELETE:46,DOWN:40,END:35,ENTER:13,ESCAPE:27,HOME:36,INSERT:45,LEFT:37,NUMPAD_ADD:107,NUMPAD_DECIMAL:110,NUMPAD_DIVIDE:111,NUMPAD_ENTER:108,NUMPAD_MULTIPLY:106,NUMPAD_SUBTRACT:109,PAGE_DOWN:34,PAGE_UP:33,
+PERIOD:190,RIGHT:39,SHIFT:16,SPACE:32,TAB:9,UP:38}};c.fn.extend({_focus:c.fn.focus,focus:function(a,b){return typeof a==="number"?this.each(function(){var d=this;setTimeout(function(){c(d).focus();b&&b.call(d)},a)}):this._focus.apply(this,arguments)},enableSelection:function(){return this.attr("unselectable","off").css("MozUserSelect","")},disableSelection:function(){return this.attr("unselectable","on").css("MozUserSelect","none")},scrollParent:function(){var a;a=c.browser.msie&&/(static|relative)/.test(this.css("position"))||
+/absolute/.test(this.css("position"))?this.parents().filter(function(){return/(relative|absolute|fixed)/.test(c.curCSS(this,"position",1))&&/(auto|scroll)/.test(c.curCSS(this,"overflow",1)+c.curCSS(this,"overflow-y",1)+c.curCSS(this,"overflow-x",1))}).eq(0):this.parents().filter(function(){return/(auto|scroll)/.test(c.curCSS(this,"overflow",1)+c.curCSS(this,"overflow-y",1)+c.curCSS(this,"overflow-x",1))}).eq(0);return/fixed/.test(this.css("position"))||!a.length?c(document):a},zIndex:function(a){if(a!==
+undefined)return this.css("zIndex",a);if(this.length){a=c(this[0]);for(var b;a.length&&a[0]!==document;){b=a.css("position");if(b=="absolute"||b=="relative"||b=="fixed"){b=parseInt(a.css("zIndex"));if(!isNaN(b)&&b!=0)return b}a=a.parent()}}return 0}});c.extend(c.expr[":"],{data:function(a,b,d){return!!c.data(a,d[3])},focusable:function(a){var b=a.nodeName.toLowerCase(),d=c.attr(a,"tabindex");return(/input|select|textarea|button|object/.test(b)?!a.disabled:"a"==b||"area"==b?a.href||!isNaN(d):!isNaN(d))&&
+!c(a)["area"==b?"parents":"closest"](":hidden").length},tabbable:function(a){var b=c.attr(a,"tabindex");return(isNaN(b)||b>=0)&&c(a).is(":focusable")}})}(jQuery);
+;/*
+ * jQuery UI Datepicker 1.8.1
+ *
+ * Copyright (c) 2010 AUTHORS.txt (http://jqueryui.com/about)
+ * Dual licensed under the MIT (MIT-LICENSE.txt)
+ * and GPL (GPL-LICENSE.txt) licenses.
+ *
+ * http://docs.jquery.com/UI/Datepicker
+ *
+ * Depends:
+ *	jquery.ui.core.js
+ */
+(function(d){function J(){this.debug=false;this._curInst=null;this._keyEvent=false;this._disabledInputs=[];this._inDialog=this._datepickerShowing=false;this._mainDivId="ui-datepicker-div";this._inlineClass="ui-datepicker-inline";this._appendClass="ui-datepicker-append";this._triggerClass="ui-datepicker-trigger";this._dialogClass="ui-datepicker-dialog";this._disableClass="ui-datepicker-disabled";this._unselectableClass="ui-datepicker-unselectable";this._currentClass="ui-datepicker-current-day";this._dayOverClass=
+"ui-datepicker-days-cell-over";this.regional=[];this.regional[""]={closeText:"Done",prevText:"Prev",nextText:"Next",currentText:"Today",monthNames:["January","February","March","April","May","June","July","August","September","October","November","December"],monthNamesShort:["Jan","Feb","Mar","Apr","May","Jun","Jul","Aug","Sep","Oct","Nov","Dec"],dayNames:["Sunday","Monday","Tuesday","Wednesday","Thursday","Friday","Saturday"],dayNamesShort:["Sun","Mon","Tue","Wed","Thu","Fri","Sat"],dayNamesMin:["Su",
+"Mo","Tu","We","Th","Fr","Sa"],weekHeader:"Wk",dateFormat:"mm/dd/yy",firstDay:0,isRTL:false,showMonthAfterYear:false,yearSuffix:""};this._defaults={showOn:"focus",showAnim:"show",showOptions:{},defaultDate:null,appendText:"",buttonText:"...",buttonImage:"",buttonImageOnly:false,hideIfNoPrevNext:false,navigationAsDateFormat:false,gotoCurrent:false,changeMonth:false,changeYear:false,yearRange:"c-10:c+10",showOtherMonths:false,selectOtherMonths:false,showWeek:false,calculateWeek:this.iso8601Week,shortYearCutoff:"+10",
+minDate:null,maxDate:null,duration:"_default",beforeShowDay:null,beforeShow:null,onSelect:null,onChangeMonthYear:null,onClose:null,numberOfMonths:1,showCurrentAtPos:0,stepMonths:1,stepBigMonths:12,altField:"",altFormat:"",constrainInput:true,showButtonPanel:false,autoSize:false};d.extend(this._defaults,this.regional[""]);this.dpDiv=d('<div id="'+this._mainDivId+'" class="ui-datepicker ui-widget ui-widget-content ui-helper-clearfix ui-corner-all ui-helper-hidden-accessible"></div>')}function E(a,b){d.extend(a,
+b);for(var c in b)if(b[c]==null||b[c]==undefined)a[c]=b[c];return a}d.extend(d.ui,{datepicker:{version:"1.8.1"}});var y=(new Date).getTime();d.extend(J.prototype,{markerClassName:"hasDatepicker",log:function(){this.debug&&console.log.apply("",arguments)},_widgetDatepicker:function(){return this.dpDiv},setDefaults:function(a){E(this._defaults,a||{});return this},_attachDatepicker:function(a,b){var c=null;for(var e in this._defaults){var f=a.getAttribute("date:"+e);if(f){c=c||{};try{c[e]=eval(f)}catch(h){c[e]=
+f}}}e=a.nodeName.toLowerCase();f=e=="div"||e=="span";if(!a.id)a.id="dp"+ ++this.uuid;var i=this._newInst(d(a),f);i.settings=d.extend({},b||{},c||{});if(e=="input")this._connectDatepicker(a,i);else f&&this._inlineDatepicker(a,i)},_newInst:function(a,b){return{id:a[0].id.replace(/([^A-Za-z0-9_])/g,"\\\\$1"),input:a,selectedDay:0,selectedMonth:0,selectedYear:0,drawMonth:0,drawYear:0,inline:b,dpDiv:!b?this.dpDiv:d('<div class="'+this._inlineClass+' ui-datepicker ui-widget ui-widget-content ui-helper-clearfix ui-corner-all"></div>')}},
+_connectDatepicker:function(a,b){var c=d(a);b.append=d([]);b.trigger=d([]);if(!c.hasClass(this.markerClassName)){this._attachments(c,b);c.addClass(this.markerClassName).keydown(this._doKeyDown).keypress(this._doKeyPress).keyup(this._doKeyUp).bind("setData.datepicker",function(e,f,h){b.settings[f]=h}).bind("getData.datepicker",function(e,f){return this._get(b,f)});this._autoSize(b);d.data(a,"datepicker",b)}},_attachments:function(a,b){var c=this._get(b,"appendText"),e=this._get(b,"isRTL");b.append&&
+b.append.remove();if(c){b.append=d('<span class="'+this._appendClass+'">'+c+"</span>");a[e?"before":"after"](b.append)}a.unbind("focus",this._showDatepicker);b.trigger&&b.trigger.remove();c=this._get(b,"showOn");if(c=="focus"||c=="both")a.focus(this._showDatepicker);if(c=="button"||c=="both"){c=this._get(b,"buttonText");var f=this._get(b,"buttonImage");b.trigger=d(this._get(b,"buttonImageOnly")?d("<img/>").addClass(this._triggerClass).attr({src:f,alt:c,title:c}):d('<button type="button"></button>').addClass(this._triggerClass).html(f==
+""?c:d("<img/>").attr({src:f,alt:c,title:c})));a[e?"before":"after"](b.trigger);b.trigger.click(function(){d.datepicker._datepickerShowing&&d.datepicker._lastInput==a[0]?d.datepicker._hideDatepicker():d.datepicker._showDatepicker(a[0]);return false})}},_autoSize:function(a){if(this._get(a,"autoSize")&&!a.inline){var b=new Date(2009,11,20),c=this._get(a,"dateFormat");if(c.match(/[DM]/)){var e=function(f){for(var h=0,i=0,g=0;g<f.length;g++)if(f[g].length>h){h=f[g].length;i=g}return i};b.setMonth(e(this._get(a,
+c.match(/MM/)?"monthNames":"monthNamesShort")));b.setDate(e(this._get(a,c.match(/DD/)?"dayNames":"dayNamesShort"))+20-b.getDay())}a.input.attr("size",this._formatDate(a,b).length)}},_inlineDatepicker:function(a,b){var c=d(a);if(!c.hasClass(this.markerClassName)){c.addClass(this.markerClassName).append(b.dpDiv).bind("setData.datepicker",function(e,f,h){b.settings[f]=h}).bind("getData.datepicker",function(e,f){return this._get(b,f)});d.data(a,"datepicker",b);this._setDate(b,this._getDefaultDate(b),
+true);this._updateDatepicker(b);this._updateAlternate(b)}},_dialogDatepicker:function(a,b,c,e,f){a=this._dialogInst;if(!a){a="dp"+ ++this.uuid;this._dialogInput=d('<input type="text" id="'+a+'" style="position: absolute; top: -100px; width: 0px; z-index: -10;"/>');this._dialogInput.keydown(this._doKeyDown);d("body").append(this._dialogInput);a=this._dialogInst=this._newInst(this._dialogInput,false);a.settings={};d.data(this._dialogInput[0],"datepicker",a)}E(a.settings,e||{});b=b&&b.constructor==Date?
+this._formatDate(a,b):b;this._dialogInput.val(b);this._pos=f?f.length?f:[f.pageX,f.pageY]:null;if(!this._pos)this._pos=[document.documentElement.clientWidth/2-100+(document.documentElement.scrollLeft||document.body.scrollLeft),document.documentElement.clientHeight/2-150+(document.documentElement.scrollTop||document.body.scrollTop)];this._dialogInput.css("left",this._pos[0]+20+"px").css("top",this._pos[1]+"px");a.settings.onSelect=c;this._inDialog=true;this.dpDiv.addClass(this._dialogClass);this._showDatepicker(this._dialogInput[0]);
+d.blockUI&&d.blockUI(this.dpDiv);d.data(this._dialogInput[0],"datepicker",a);return this},_destroyDatepicker:function(a){var b=d(a),c=d.data(a,"datepicker");if(b.hasClass(this.markerClassName)){var e=a.nodeName.toLowerCase();d.removeData(a,"datepicker");if(e=="input"){c.append.remove();c.trigger.remove();b.removeClass(this.markerClassName).unbind("focus",this._showDatepicker).unbind("keydown",this._doKeyDown).unbind("keypress",this._doKeyPress).unbind("keyup",this._doKeyUp)}else if(e=="div"||e=="span")b.removeClass(this.markerClassName).empty()}},
+_enableDatepicker:function(a){var b=d(a),c=d.data(a,"datepicker");if(b.hasClass(this.markerClassName)){var e=a.nodeName.toLowerCase();if(e=="input"){a.disabled=false;c.trigger.filter("button").each(function(){this.disabled=false}).end().filter("img").css({opacity:"1.0",cursor:""})}else if(e=="div"||e=="span")b.children("."+this._inlineClass).children().removeClass("ui-state-disabled");this._disabledInputs=d.map(this._disabledInputs,function(f){return f==a?null:f})}},_disableDatepicker:function(a){var b=
+d(a),c=d.data(a,"datepicker");if(b.hasClass(this.markerClassName)){var e=a.nodeName.toLowerCase();if(e=="input"){a.disabled=true;c.trigger.filter("button").each(function(){this.disabled=true}).end().filter("img").css({opacity:"0.5",cursor:"default"})}else if(e=="div"||e=="span")b.children("."+this._inlineClass).children().addClass("ui-state-disabled");this._disabledInputs=d.map(this._disabledInputs,function(f){return f==a?null:f});this._disabledInputs[this._disabledInputs.length]=a}},_isDisabledDatepicker:function(a){if(!a)return false;
+for(var b=0;b<this._disabledInputs.length;b++)if(this._disabledInputs[b]==a)return true;return false},_getInst:function(a){try{return d.data(a,"datepicker")}catch(b){throw"Missing instance data for this datepicker";}},_optionDatepicker:function(a,b,c){var e=this._getInst(a);if(arguments.length==2&&typeof b=="string")return b=="defaults"?d.extend({},d.datepicker._defaults):e?b=="all"?d.extend({},e.settings):this._get(e,b):null;var f=b||{};if(typeof b=="string"){f={};f[b]=c}if(e){this._curInst==e&&
+this._hideDatepicker();var h=this._getDateDatepicker(a,true);E(e.settings,f);this._attachments(d(a),e);this._autoSize(e);this._setDateDatepicker(a,h);this._updateDatepicker(e)}},_changeDatepicker:function(a,b,c){this._optionDatepicker(a,b,c)},_refreshDatepicker:function(a){(a=this._getInst(a))&&this._updateDatepicker(a)},_setDateDatepicker:function(a,b){if(a=this._getInst(a)){this._setDate(a,b);this._updateDatepicker(a);this._updateAlternate(a)}},_getDateDatepicker:function(a,b){(a=this._getInst(a))&&
+!a.inline&&this._setDateFromField(a,b);return a?this._getDate(a):null},_doKeyDown:function(a){var b=d.datepicker._getInst(a.target),c=true,e=b.dpDiv.is(".ui-datepicker-rtl");b._keyEvent=true;if(d.datepicker._datepickerShowing)switch(a.keyCode){case 9:d.datepicker._hideDatepicker();c=false;break;case 13:c=d("td."+d.datepicker._dayOverClass,b.dpDiv).add(d("td."+d.datepicker._currentClass,b.dpDiv));c[0]?d.datepicker._selectDay(a.target,b.selectedMonth,b.selectedYear,c[0]):d.datepicker._hideDatepicker();
+return false;case 27:d.datepicker._hideDatepicker();break;case 33:d.datepicker._adjustDate(a.target,a.ctrlKey?-d.datepicker._get(b,"stepBigMonths"):-d.datepicker._get(b,"stepMonths"),"M");break;case 34:d.datepicker._adjustDate(a.target,a.ctrlKey?+d.datepicker._get(b,"stepBigMonths"):+d.datepicker._get(b,"stepMonths"),"M");break;case 35:if(a.ctrlKey||a.metaKey)d.datepicker._clearDate(a.target);c=a.ctrlKey||a.metaKey;break;case 36:if(a.ctrlKey||a.metaKey)d.datepicker._gotoToday(a.target);c=a.ctrlKey||
+a.metaKey;break;case 37:if(a.ctrlKey||a.metaKey)d.datepicker._adjustDate(a.target,e?+1:-1,"D");c=a.ctrlKey||a.metaKey;if(a.originalEvent.altKey)d.datepicker._adjustDate(a.target,a.ctrlKey?-d.datepicker._get(b,"stepBigMonths"):-d.datepicker._get(b,"stepMonths"),"M");break;case 38:if(a.ctrlKey||a.metaKey)d.datepicker._adjustDate(a.target,-7,"D");c=a.ctrlKey||a.metaKey;break;case 39:if(a.ctrlKey||a.metaKey)d.datepicker._adjustDate(a.target,e?-1:+1,"D");c=a.ctrlKey||a.metaKey;if(a.originalEvent.altKey)d.datepicker._adjustDate(a.target,
+a.ctrlKey?+d.datepicker._get(b,"stepBigMonths"):+d.datepicker._get(b,"stepMonths"),"M");break;case 40:if(a.ctrlKey||a.metaKey)d.datepicker._adjustDate(a.target,+7,"D");c=a.ctrlKey||a.metaKey;break;default:c=false}else if(a.keyCode==36&&a.ctrlKey)d.datepicker._showDatepicker(this);else c=false;if(c){a.preventDefault();a.stopPropagation()}},_doKeyPress:function(a){var b=d.datepicker._getInst(a.target);if(d.datepicker._get(b,"constrainInput")){b=d.datepicker._possibleChars(d.datepicker._get(b,"dateFormat"));
+var c=String.fromCharCode(a.charCode==undefined?a.keyCode:a.charCode);return a.ctrlKey||c<" "||!b||b.indexOf(c)>-1}},_doKeyUp:function(a){a=d.datepicker._getInst(a.target);if(a.input.val()!=a.lastVal)try{if(d.datepicker.parseDate(d.datepicker._get(a,"dateFormat"),a.input?a.input.val():null,d.datepicker._getFormatConfig(a))){d.datepicker._setDateFromField(a);d.datepicker._updateAlternate(a);d.datepicker._updateDatepicker(a)}}catch(b){d.datepicker.log(b)}return true},_showDatepicker:function(a){a=a.target||
+a;if(a.nodeName.toLowerCase()!="input")a=d("input",a.parentNode)[0];if(!(d.datepicker._isDisabledDatepicker(a)||d.datepicker._lastInput==a)){var b=d.datepicker._getInst(a);d.datepicker._curInst&&d.datepicker._curInst!=b&&d.datepicker._curInst.dpDiv.stop(true,true);var c=d.datepicker._get(b,"beforeShow");E(b.settings,c?c.apply(a,[a,b]):{});b.lastVal=null;d.datepicker._lastInput=a;d.datepicker._setDateFromField(b);if(d.datepicker._inDialog)a.value="";if(!d.datepicker._pos){d.datepicker._pos=d.datepicker._findPos(a);
+d.datepicker._pos[1]+=a.offsetHeight}var e=false;d(a).parents().each(function(){e|=d(this).css("position")=="fixed";return!e});if(e&&d.browser.opera){d.datepicker._pos[0]-=document.documentElement.scrollLeft;d.datepicker._pos[1]-=document.documentElement.scrollTop}c={left:d.datepicker._pos[0],top:d.datepicker._pos[1]};d.datepicker._pos=null;b.dpDiv.css({position:"absolute",display:"block",top:"-1000px"});d.datepicker._updateDatepicker(b);c=d.datepicker._checkOffset(b,c,e);b.dpDiv.css({position:d.datepicker._inDialog&&
+d.blockUI?"static":e?"fixed":"absolute",display:"none",left:c.left+"px",top:c.top+"px"});if(!b.inline){c=d.datepicker._get(b,"showAnim");var f=d.datepicker._get(b,"duration"),h=function(){d.datepicker._datepickerShowing=true;var i=d.datepicker._getBorders(b.dpDiv);b.dpDiv.find("iframe.ui-datepicker-cover").css({left:-i[0],top:-i[1],width:b.dpDiv.outerWidth(),height:b.dpDiv.outerHeight()})};b.dpDiv.zIndex(d(a).zIndex()+1);d.effects&&d.effects[c]?b.dpDiv.show(c,d.datepicker._get(b,"showOptions"),f,
+h):b.dpDiv[c||"show"](c?f:null,h);if(!c||!f)h();b.input.is(":visible")&&!b.input.is(":disabled")&&b.input.focus();d.datepicker._curInst=b}}},_updateDatepicker:function(a){var b=this,c=d.datepicker._getBorders(a.dpDiv);a.dpDiv.empty().append(this._generateHTML(a)).find("iframe.ui-datepicker-cover").css({left:-c[0],top:-c[1],width:a.dpDiv.outerWidth(),height:a.dpDiv.outerHeight()}).end().find("button, .ui-datepicker-prev, .ui-datepicker-next, .ui-datepicker-calendar td a").bind("mouseout",function(){d(this).removeClass("ui-state-hover");
+this.className.indexOf("ui-datepicker-prev")!=-1&&d(this).removeClass("ui-datepicker-prev-hover");this.className.indexOf("ui-datepicker-next")!=-1&&d(this).removeClass("ui-datepicker-next-hover")}).bind("mouseover",function(){if(!b._isDisabledDatepicker(a.inline?a.dpDiv.parent()[0]:a.input[0])){d(this).parents(".ui-datepicker-calendar").find("a").removeClass("ui-state-hover");d(this).addClass("ui-state-hover");this.className.indexOf("ui-datepicker-prev")!=-1&&d(this).addClass("ui-datepicker-prev-hover");
+this.className.indexOf("ui-datepicker-next")!=-1&&d(this).addClass("ui-datepicker-next-hover")}}).end().find("."+this._dayOverClass+" a").trigger("mouseover").end();c=this._getNumberOfMonths(a);var e=c[1];e>1?a.dpDiv.addClass("ui-datepicker-multi-"+e).css("width",17*e+"em"):a.dpDiv.removeClass("ui-datepicker-multi-2 ui-datepicker-multi-3 ui-datepicker-multi-4").width("");a.dpDiv[(c[0]!=1||c[1]!=1?"add":"remove")+"Class"]("ui-datepicker-multi");a.dpDiv[(this._get(a,"isRTL")?"add":"remove")+"Class"]("ui-datepicker-rtl");
+a==d.datepicker._curInst&&d.datepicker._datepickerShowing&&a.input&&a.input.is(":visible")&&!a.input.is(":disabled")&&a.input.focus()},_getBorders:function(a){var b=function(c){return{thin:1,medium:2,thick:3}[c]||c};return[parseFloat(b(a.css("border-left-width"))),parseFloat(b(a.css("border-top-width")))]},_checkOffset:function(a,b,c){var e=a.dpDiv.outerWidth(),f=a.dpDiv.outerHeight(),h=a.input?a.input.outerWidth():0,i=a.input?a.input.outerHeight():0,g=document.documentElement.clientWidth+d(document).scrollLeft(),
+k=document.documentElement.clientHeight+d(document).scrollTop();b.left-=this._get(a,"isRTL")?e-h:0;b.left-=c&&b.left==a.input.offset().left?d(document).scrollLeft():0;b.top-=c&&b.top==a.input.offset().top+i?d(document).scrollTop():0;b.left-=Math.min(b.left,b.left+e>g&&g>e?Math.abs(b.left+e-g):0);b.top-=Math.min(b.top,b.top+f>k&&k>f?Math.abs(f+i):0);return b},_findPos:function(a){for(var b=this._get(this._getInst(a),"isRTL");a&&(a.type=="hidden"||a.nodeType!=1);)a=a[b?"previousSibling":"nextSibling"];
+a=d(a).offset();return[a.left,a.top]},_hideDatepicker:function(a){var b=this._curInst;if(!(!b||a&&b!=d.data(a,"datepicker")))if(this._datepickerShowing){a=this._get(b,"showAnim");var c=this._get(b,"duration"),e=function(){d.datepicker._tidyDialog(b);this._curInst=null};d.effects&&d.effects[a]?b.dpDiv.hide(a,d.datepicker._get(b,"showOptions"),c,e):b.dpDiv[a=="slideDown"?"slideUp":a=="fadeIn"?"fadeOut":"hide"](a?c:null,e);a||e();if(a=this._get(b,"onClose"))a.apply(b.input?b.input[0]:null,[b.input?b.input.val():
+"",b]);this._datepickerShowing=false;this._lastInput=null;if(this._inDialog){this._dialogInput.css({position:"absolute",left:"0",top:"-100px"});if(d.blockUI){d.unblockUI();d("body").append(this.dpDiv)}}this._inDialog=false}},_tidyDialog:function(a){a.dpDiv.removeClass(this._dialogClass).unbind(".ui-datepicker-calendar")},_checkExternalClick:function(a){if(d.datepicker._curInst){a=d(a.target);a[0].id!=d.datepicker._mainDivId&&a.parents("#"+d.datepicker._mainDivId).length==0&&!a.hasClass(d.datepicker.markerClassName)&&
+!a.hasClass(d.datepicker._triggerClass)&&d.datepicker._datepickerShowing&&!(d.datepicker._inDialog&&d.blockUI)&&d.datepicker._hideDatepicker()}},_adjustDate:function(a,b,c){a=d(a);var e=this._getInst(a[0]);if(!this._isDisabledDatepicker(a[0])){this._adjustInstDate(e,b+(c=="M"?this._get(e,"showCurrentAtPos"):0),c);this._updateDatepicker(e)}},_gotoToday:function(a){a=d(a);var b=this._getInst(a[0]);if(this._get(b,"gotoCurrent")&&b.currentDay){b.selectedDay=b.currentDay;b.drawMonth=b.selectedMonth=b.currentMonth;
+b.drawYear=b.selectedYear=b.currentYear}else{var c=new Date;b.selectedDay=c.getDate();b.drawMonth=b.selectedMonth=c.getMonth();b.drawYear=b.selectedYear=c.getFullYear()}this._notifyChange(b);this._adjustDate(a)},_selectMonthYear:function(a,b,c){a=d(a);var e=this._getInst(a[0]);e._selectingMonthYear=false;e["selected"+(c=="M"?"Month":"Year")]=e["draw"+(c=="M"?"Month":"Year")]=parseInt(b.options[b.selectedIndex].value,10);this._notifyChange(e);this._adjustDate(a)},_clickMonthYear:function(a){a=this._getInst(d(a)[0]);
+a.input&&a._selectingMonthYear&&!d.browser.msie&&a.input.focus();a._selectingMonthYear=!a._selectingMonthYear},_selectDay:function(a,b,c,e){var f=d(a);if(!(d(e).hasClass(this._unselectableClass)||this._isDisabledDatepicker(f[0]))){f=this._getInst(f[0]);f.selectedDay=f.currentDay=d("a",e).html();f.selectedMonth=f.currentMonth=b;f.selectedYear=f.currentYear=c;this._selectDate(a,this._formatDate(f,f.currentDay,f.currentMonth,f.currentYear))}},_clearDate:function(a){a=d(a);this._getInst(a[0]);this._selectDate(a,
+"")},_selectDate:function(a,b){a=this._getInst(d(a)[0]);b=b!=null?b:this._formatDate(a);a.input&&a.input.val(b);this._updateAlternate(a);var c=this._get(a,"onSelect");if(c)c.apply(a.input?a.input[0]:null,[b,a]);else a.input&&a.input.trigger("change");if(a.inline)this._updateDatepicker(a);else{this._hideDatepicker();this._lastInput=a.input[0];typeof a.input[0]!="object"&&a.input.focus();this._lastInput=null}},_updateAlternate:function(a){var b=this._get(a,"altField");if(b){var c=this._get(a,"altFormat")||
+this._get(a,"dateFormat"),e=this._getDate(a),f=this.formatDate(c,e,this._getFormatConfig(a));d(b).each(function(){d(this).val(f)})}},noWeekends:function(a){a=a.getDay();return[a>0&&a<6,""]},iso8601Week:function(a){a=new Date(a.getTime());a.setDate(a.getDate()+4-(a.getDay()||7));var b=a.getTime();a.setMonth(0);a.setDate(1);return Math.floor(Math.round((b-a)/864E5)/7)+1},parseDate:function(a,b,c){if(a==null||b==null)throw"Invalid arguments";b=typeof b=="object"?b.toString():b+"";if(b=="")return null;
+for(var e=(c?c.shortYearCutoff:null)||this._defaults.shortYearCutoff,f=(c?c.dayNamesShort:null)||this._defaults.dayNamesShort,h=(c?c.dayNames:null)||this._defaults.dayNames,i=(c?c.monthNamesShort:null)||this._defaults.monthNamesShort,g=(c?c.monthNames:null)||this._defaults.monthNames,k=c=-1,l=-1,u=-1,j=false,o=function(p){(p=z+1<a.length&&a.charAt(z+1)==p)&&z++;return p},m=function(p){o(p);p=new RegExp("^\\d{1,"+(p=="@"?14:p=="!"?20:p=="y"?4:p=="o"?3:2)+"}");p=b.substring(s).match(p);if(!p)throw"Missing number at position "+
+s;s+=p[0].length;return parseInt(p[0],10)},n=function(p,w,G){p=o(p)?G:w;for(w=0;w<p.length;w++)if(b.substr(s,p[w].length)==p[w]){s+=p[w].length;return w+1}throw"Unknown name at position "+s;},r=function(){if(b.charAt(s)!=a.charAt(z))throw"Unexpected literal at position "+s;s++},s=0,z=0;z<a.length;z++)if(j)if(a.charAt(z)=="'"&&!o("'"))j=false;else r();else switch(a.charAt(z)){case "d":l=m("d");break;case "D":n("D",f,h);break;case "o":u=m("o");break;case "m":k=m("m");break;case "M":k=n("M",i,g);break;
+case "y":c=m("y");break;case "@":var v=new Date(m("@"));c=v.getFullYear();k=v.getMonth()+1;l=v.getDate();break;case "!":v=new Date((m("!")-this._ticksTo1970)/1E4);c=v.getFullYear();k=v.getMonth()+1;l=v.getDate();break;case "'":if(o("'"))r();else j=true;break;default:r()}if(c==-1)c=(new Date).getFullYear();else if(c<100)c+=(new Date).getFullYear()-(new Date).getFullYear()%100+(c<=e?0:-100);if(u>-1){k=1;l=u;do{e=this._getDaysInMonth(c,k-1);if(l<=e)break;k++;l-=e}while(1)}v=this._daylightSavingAdjust(new Date(c,
+k-1,l));if(v.getFullYear()!=c||v.getMonth()+1!=k||v.getDate()!=l)throw"Invalid date";return v},ATOM:"yy-mm-dd",COOKIE:"D, dd M yy",ISO_8601:"yy-mm-dd",RFC_822:"D, d M y",RFC_850:"DD, dd-M-y",RFC_1036:"D, d M y",RFC_1123:"D, d M yy",RFC_2822:"D, d M yy",RSS:"D, d M y",TICKS:"!",TIMESTAMP:"@",W3C:"yy-mm-dd",_ticksTo1970:(718685+Math.floor(492.5)-Math.floor(19.7)+Math.floor(4.925))*24*60*60*1E7,formatDate:function(a,b,c){if(!b)return"";var e=(c?c.dayNamesShort:null)||this._defaults.dayNamesShort,f=(c?
+c.dayNames:null)||this._defaults.dayNames,h=(c?c.monthNamesShort:null)||this._defaults.monthNamesShort;c=(c?c.monthNames:null)||this._defaults.monthNames;var i=function(o){(o=j+1<a.length&&a.charAt(j+1)==o)&&j++;return o},g=function(o,m,n){m=""+m;if(i(o))for(;m.length<n;)m="0"+m;return m},k=function(o,m,n,r){return i(o)?r[m]:n[m]},l="",u=false;if(b)for(var j=0;j<a.length;j++)if(u)if(a.charAt(j)=="'"&&!i("'"))u=false;else l+=a.charAt(j);else switch(a.charAt(j)){case "d":l+=g("d",b.getDate(),2);break;
+case "D":l+=k("D",b.getDay(),e,f);break;case "o":l+=g("o",(b.getTime()-(new Date(b.getFullYear(),0,0)).getTime())/864E5,3);break;case "m":l+=g("m",b.getMonth()+1,2);break;case "M":l+=k("M",b.getMonth(),h,c);break;case "y":l+=i("y")?b.getFullYear():(b.getYear()%100<10?"0":"")+b.getYear()%100;break;case "@":l+=b.getTime();break;case "!":l+=b.getTime()*1E4+this._ticksTo1970;break;case "'":if(i("'"))l+="'";else u=true;break;default:l+=a.charAt(j)}return l},_possibleChars:function(a){for(var b="",c=false,
+e=function(h){(h=f+1<a.length&&a.charAt(f+1)==h)&&f++;return h},f=0;f<a.length;f++)if(c)if(a.charAt(f)=="'"&&!e("'"))c=false;else b+=a.charAt(f);else switch(a.charAt(f)){case "d":case "m":case "y":case "@":b+="0123456789";break;case "D":case "M":return null;case "'":if(e("'"))b+="'";else c=true;break;default:b+=a.charAt(f)}return b},_get:function(a,b){return a.settings[b]!==undefined?a.settings[b]:this._defaults[b]},_setDateFromField:function(a,b){if(a.input.val()!=a.lastVal){var c=this._get(a,"dateFormat"),
+e=a.lastVal=a.input?a.input.val():null,f,h;f=h=this._getDefaultDate(a);var i=this._getFormatConfig(a);try{f=this.parseDate(c,e,i)||h}catch(g){this.log(g);e=b?"":e}a.selectedDay=f.getDate();a.drawMonth=a.selectedMonth=f.getMonth();a.drawYear=a.selectedYear=f.getFullYear();a.currentDay=e?f.getDate():0;a.currentMonth=e?f.getMonth():0;a.currentYear=e?f.getFullYear():0;this._adjustInstDate(a)}},_getDefaultDate:function(a){return this._restrictMinMax(a,this._determineDate(a,this._get(a,"defaultDate"),new Date))},
+_determineDate:function(a,b,c){var e=function(h){var i=new Date;i.setDate(i.getDate()+h);return i},f=function(h){try{return d.datepicker.parseDate(d.datepicker._get(a,"dateFormat"),h,d.datepicker._getFormatConfig(a))}catch(i){}var g=(h.toLowerCase().match(/^c/)?d.datepicker._getDate(a):null)||new Date,k=g.getFullYear(),l=g.getMonth();g=g.getDate();for(var u=/([+-]?[0-9]+)\s*(d|D|w|W|m|M|y|Y)?/g,j=u.exec(h);j;){switch(j[2]||"d"){case "d":case "D":g+=parseInt(j[1],10);break;case "w":case "W":g+=parseInt(j[1],
+10)*7;break;case "m":case "M":l+=parseInt(j[1],10);g=Math.min(g,d.datepicker._getDaysInMonth(k,l));break;case "y":case "Y":k+=parseInt(j[1],10);g=Math.min(g,d.datepicker._getDaysInMonth(k,l));break}j=u.exec(h)}return new Date(k,l,g)};if(b=(b=b==null?c:typeof b=="string"?f(b):typeof b=="number"?isNaN(b)?c:e(b):b)&&b.toString()=="Invalid Date"?c:b){b.setHours(0);b.setMinutes(0);b.setSeconds(0);b.setMilliseconds(0)}return this._daylightSavingAdjust(b)},_daylightSavingAdjust:function(a){if(!a)return null;
+a.setHours(a.getHours()>12?a.getHours()+2:0);return a},_setDate:function(a,b,c){var e=!b,f=a.selectedMonth,h=a.selectedYear;b=this._restrictMinMax(a,this._determineDate(a,b,new Date));a.selectedDay=a.currentDay=b.getDate();a.drawMonth=a.selectedMonth=a.currentMonth=b.getMonth();a.drawYear=a.selectedYear=a.currentYear=b.getFullYear();if((f!=a.selectedMonth||h!=a.selectedYear)&&!c)this._notifyChange(a);this._adjustInstDate(a);if(a.input)a.input.val(e?"":this._formatDate(a))},_getDate:function(a){return!a.currentYear||
+a.input&&a.input.val()==""?null:this._daylightSavingAdjust(new Date(a.currentYear,a.currentMonth,a.currentDay))},_generateHTML:function(a){var b=new Date;b=this._daylightSavingAdjust(new Date(b.getFullYear(),b.getMonth(),b.getDate()));var c=this._get(a,"isRTL"),e=this._get(a,"showButtonPanel"),f=this._get(a,"hideIfNoPrevNext"),h=this._get(a,"navigationAsDateFormat"),i=this._getNumberOfMonths(a),g=this._get(a,"showCurrentAtPos"),k=this._get(a,"stepMonths"),l=i[0]!=1||i[1]!=1,u=this._daylightSavingAdjust(!a.currentDay?
+new Date(9999,9,9):new Date(a.currentYear,a.currentMonth,a.currentDay)),j=this._getMinMaxDate(a,"min"),o=this._getMinMaxDate(a,"max");g=a.drawMonth-g;var m=a.drawYear;if(g<0){g+=12;m--}if(o){var n=this._daylightSavingAdjust(new Date(o.getFullYear(),o.getMonth()-i[0]*i[1]+1,o.getDate()));for(n=j&&n<j?j:n;this._daylightSavingAdjust(new Date(m,g,1))>n;){g--;if(g<0){g=11;m--}}}a.drawMonth=g;a.drawYear=m;n=this._get(a,"prevText");n=!h?n:this.formatDate(n,this._daylightSavingAdjust(new Date(m,g-k,1)),this._getFormatConfig(a));
+n=this._canAdjustMonth(a,-1,m,g)?'<a class="ui-datepicker-prev ui-corner-all" onclick="DP_jQuery_'+y+".datepicker._adjustDate('#"+a.id+"', -"+k+", 'M');\" title=\""+n+'"><span class="ui-icon ui-icon-circle-triangle-'+(c?"e":"w")+'">'+n+"</span></a>":f?"":'<a class="ui-datepicker-prev ui-corner-all ui-state-disabled" title="'+n+'"><span class="ui-icon ui-icon-circle-triangle-'+(c?"e":"w")+'">'+n+"</span></a>";var r=this._get(a,"nextText");r=!h?r:this.formatDate(r,this._daylightSavingAdjust(new Date(m,
+g+k,1)),this._getFormatConfig(a));f=this._canAdjustMonth(a,+1,m,g)?'<a class="ui-datepicker-next ui-corner-all" onclick="DP_jQuery_'+y+".datepicker._adjustDate('#"+a.id+"', +"+k+", 'M');\" title=\""+r+'"><span class="ui-icon ui-icon-circle-triangle-'+(c?"w":"e")+'">'+r+"</span></a>":f?"":'<a class="ui-datepicker-next ui-corner-all ui-state-disabled" title="'+r+'"><span class="ui-icon ui-icon-circle-triangle-'+(c?"w":"e")+'">'+r+"</span></a>";k=this._get(a,"currentText");r=this._get(a,"gotoCurrent")&&
+a.currentDay?u:b;k=!h?k:this.formatDate(k,r,this._getFormatConfig(a));h=!a.inline?'<button type="button" class="ui-datepicker-close ui-state-default ui-priority-primary ui-corner-all" onclick="DP_jQuery_'+y+'.datepicker._hideDatepicker();">'+this._get(a,"closeText")+"</button>":"";e=e?'<div class="ui-datepicker-buttonpane ui-widget-content">'+(c?h:"")+(this._isInRange(a,r)?'<button type="button" class="ui-datepicker-current ui-state-default ui-priority-secondary ui-corner-all" onclick="DP_jQuery_'+
+y+".datepicker._gotoToday('#"+a.id+"');\">"+k+"</button>":"")+(c?"":h)+"</div>":"";h=parseInt(this._get(a,"firstDay"),10);h=isNaN(h)?0:h;k=this._get(a,"showWeek");r=this._get(a,"dayNames");this._get(a,"dayNamesShort");var s=this._get(a,"dayNamesMin"),z=this._get(a,"monthNames"),v=this._get(a,"monthNamesShort"),p=this._get(a,"beforeShowDay"),w=this._get(a,"showOtherMonths"),G=this._get(a,"selectOtherMonths");this._get(a,"calculateWeek");for(var K=this._getDefaultDate(a),H="",C=0;C<i[0];C++){for(var L=
+"",D=0;D<i[1];D++){var M=this._daylightSavingAdjust(new Date(m,g,a.selectedDay)),t=" ui-corner-all",x="";if(l){x+='<div class="ui-datepicker-group';if(i[1]>1)switch(D){case 0:x+=" ui-datepicker-group-first";t=" ui-corner-"+(c?"right":"left");break;case i[1]-1:x+=" ui-datepicker-group-last";t=" ui-corner-"+(c?"left":"right");break;default:x+=" ui-datepicker-group-middle";t="";break}x+='">'}x+='<div class="ui-datepicker-header ui-widget-header ui-helper-clearfix'+t+'">'+(/all|left/.test(t)&&C==0?c?
+f:n:"")+(/all|right/.test(t)&&C==0?c?n:f:"")+this._generateMonthYearHeader(a,g,m,j,o,C>0||D>0,z,v)+'</div><table class="ui-datepicker-calendar"><thead><tr>';var A=k?'<th class="ui-datepicker-week-col">'+this._get(a,"weekHeader")+"</th>":"";for(t=0;t<7;t++){var q=(t+h)%7;A+="<th"+((t+h+6)%7>=5?' class="ui-datepicker-week-end"':"")+'><span title="'+r[q]+'">'+s[q]+"</span></th>"}x+=A+"</tr></thead><tbody>";A=this._getDaysInMonth(m,g);if(m==a.selectedYear&&g==a.selectedMonth)a.selectedDay=Math.min(a.selectedDay,
+A);t=(this._getFirstDayOfMonth(m,g)-h+7)%7;A=l?6:Math.ceil((t+A)/7);q=this._daylightSavingAdjust(new Date(m,g,1-t));for(var N=0;N<A;N++){x+="<tr>";var O=!k?"":'<td class="ui-datepicker-week-col">'+this._get(a,"calculateWeek")(q)+"</td>";for(t=0;t<7;t++){var F=p?p.apply(a.input?a.input[0]:null,[q]):[true,""],B=q.getMonth()!=g,I=B&&!G||!F[0]||j&&q<j||o&&q>o;O+='<td class="'+((t+h+6)%7>=5?" ui-datepicker-week-end":"")+(B?" ui-datepicker-other-month":"")+(q.getTime()==M.getTime()&&g==a.selectedMonth&&
+a._keyEvent||K.getTime()==q.getTime()&&K.getTime()==M.getTime()?" "+this._dayOverClass:"")+(I?" "+this._unselectableClass+" ui-state-disabled":"")+(B&&!w?"":" "+F[1]+(q.getTime()==u.getTime()?" "+this._currentClass:"")+(q.getTime()==b.getTime()?" ui-datepicker-today":""))+'"'+((!B||w)&&F[2]?' title="'+F[2]+'"':"")+(I?"":' onclick="DP_jQuery_'+y+".datepicker._selectDay('#"+a.id+"',"+q.getMonth()+","+q.getFullYear()+', this);return false;"')+">"+(B&&!w?"&#xa0;":I?'<span class="ui-state-default">'+q.getDate()+
+"</span>":'<a class="ui-state-default'+(q.getTime()==b.getTime()?" ui-state-highlight":"")+(q.getTime()==u.getTime()?" ui-state-active":"")+(B?" ui-priority-secondary":"")+'" href="#">'+q.getDate()+"</a>")+"</td>";q.setDate(q.getDate()+1);q=this._daylightSavingAdjust(q)}x+=O+"</tr>"}g++;if(g>11){g=0;m++}x+="</tbody></table>"+(l?"</div>"+(i[0]>0&&D==i[1]-1?'<div class="ui-datepicker-row-break"></div>':""):"");L+=x}H+=L}H+=e+(d.browser.msie&&parseInt(d.browser.version,10)<7&&!a.inline?'<iframe src="javascript:false;" class="ui-datepicker-cover" frameborder="0"></iframe>':
+"");a._keyEvent=false;return H},_generateMonthYearHeader:function(a,b,c,e,f,h,i,g){var k=this._get(a,"changeMonth"),l=this._get(a,"changeYear"),u=this._get(a,"showMonthAfterYear"),j='<div class="ui-datepicker-title">',o="";if(h||!k)o+='<span class="ui-datepicker-month">'+i[b]+"</span>";else{i=e&&e.getFullYear()==c;var m=f&&f.getFullYear()==c;o+='<select class="ui-datepicker-month" onchange="DP_jQuery_'+y+".datepicker._selectMonthYear('#"+a.id+"', this, 'M');\" onclick=\"DP_jQuery_"+y+".datepicker._clickMonthYear('#"+
+a.id+"');\">";for(var n=0;n<12;n++)if((!i||n>=e.getMonth())&&(!m||n<=f.getMonth()))o+='<option value="'+n+'"'+(n==b?' selected="selected"':"")+">"+g[n]+"</option>";o+="</select>"}u||(j+=o+(h||!(k&&l)?"&#xa0;":""));if(h||!l)j+='<span class="ui-datepicker-year">'+c+"</span>";else{g=this._get(a,"yearRange").split(":");var r=(new Date).getFullYear();i=function(s){s=s.match(/c[+-].*/)?c+parseInt(s.substring(1),10):s.match(/[+-].*/)?r+parseInt(s,10):parseInt(s,10);return isNaN(s)?r:s};b=i(g[0]);g=Math.max(b,
+i(g[1]||""));b=e?Math.max(b,e.getFullYear()):b;g=f?Math.min(g,f.getFullYear()):g;for(j+='<select class="ui-datepicker-year" onchange="DP_jQuery_'+y+".datepicker._selectMonthYear('#"+a.id+"', this, 'Y');\" onclick=\"DP_jQuery_"+y+".datepicker._clickMonthYear('#"+a.id+"');\">";b<=g;b++)j+='<option value="'+b+'"'+(b==c?' selected="selected"':"")+">"+b+"</option>";j+="</select>"}j+=this._get(a,"yearSuffix");if(u)j+=(h||!(k&&l)?"&#xa0;":"")+o;j+="</div>";return j},_adjustInstDate:function(a,b,c){var e=
+a.drawYear+(c=="Y"?b:0),f=a.drawMonth+(c=="M"?b:0);b=Math.min(a.selectedDay,this._getDaysInMonth(e,f))+(c=="D"?b:0);e=this._restrictMinMax(a,this._daylightSavingAdjust(new Date(e,f,b)));a.selectedDay=e.getDate();a.drawMonth=a.selectedMonth=e.getMonth();a.drawYear=a.selectedYear=e.getFullYear();if(c=="M"||c=="Y")this._notifyChange(a)},_restrictMinMax:function(a,b){var c=this._getMinMaxDate(a,"min");a=this._getMinMaxDate(a,"max");b=c&&b<c?c:b;return b=a&&b>a?a:b},_notifyChange:function(a){var b=this._get(a,
+"onChangeMonthYear");if(b)b.apply(a.input?a.input[0]:null,[a.selectedYear,a.selectedMonth+1,a])},_getNumberOfMonths:function(a){a=this._get(a,"numberOfMonths");return a==null?[1,1]:typeof a=="number"?[1,a]:a},_getMinMaxDate:function(a,b){return this._determineDate(a,this._get(a,b+"Date"),null)},_getDaysInMonth:function(a,b){return 32-(new Date(a,b,32)).getDate()},_getFirstDayOfMonth:function(a,b){return(new Date(a,b,1)).getDay()},_canAdjustMonth:function(a,b,c,e){var f=this._getNumberOfMonths(a);
+c=this._daylightSavingAdjust(new Date(c,e+(b<0?b:f[0]*f[1]),1));b<0&&c.setDate(this._getDaysInMonth(c.getFullYear(),c.getMonth()));return this._isInRange(a,c)},_isInRange:function(a,b){var c=this._getMinMaxDate(a,"min");a=this._getMinMaxDate(a,"max");return(!c||b.getTime()>=c.getTime())&&(!a||b.getTime()<=a.getTime())},_getFormatConfig:function(a){var b=this._get(a,"shortYearCutoff");b=typeof b!="string"?b:(new Date).getFullYear()%100+parseInt(b,10);return{shortYearCutoff:b,dayNamesShort:this._get(a,
+"dayNamesShort"),dayNames:this._get(a,"dayNames"),monthNamesShort:this._get(a,"monthNamesShort"),monthNames:this._get(a,"monthNames")}},_formatDate:function(a,b,c,e){if(!b){a.currentDay=a.selectedDay;a.currentMonth=a.selectedMonth;a.currentYear=a.selectedYear}b=b?typeof b=="object"?b:this._daylightSavingAdjust(new Date(e,c,b)):this._daylightSavingAdjust(new Date(a.currentYear,a.currentMonth,a.currentDay));return this.formatDate(this._get(a,"dateFormat"),b,this._getFormatConfig(a))}});d.fn.datepicker=
+function(a){if(!d.datepicker.initialized){d(document).mousedown(d.datepicker._checkExternalClick).find("body").append(d.datepicker.dpDiv);d.datepicker.initialized=true}var b=Array.prototype.slice.call(arguments,1);if(typeof a=="string"&&(a=="isDisabled"||a=="getDate"||a=="widget"))return d.datepicker["_"+a+"Datepicker"].apply(d.datepicker,[this[0]].concat(b));if(a=="option"&&arguments.length==2&&typeof arguments[1]=="string")return d.datepicker["_"+a+"Datepicker"].apply(d.datepicker,[this[0]].concat(b));
+return this.each(function(){typeof a=="string"?d.datepicker["_"+a+"Datepicker"].apply(d.datepicker,[this].concat(b)):d.datepicker._attachDatepicker(this,a)})};d.datepicker=new J;d.datepicker.initialized=false;d.datepicker.uuid=(new Date).getTime();d.datepicker.version="1.8.1";window["DP_jQuery_"+y]=d})(jQuery);
+;
\ No newline at end of file
diff --git a/demos/movies/components/jquery.pager.js b/demos/movies/components/jquery.pager.js
index 2d7bbbc..c98929f 100644
--- a/demos/movies/components/jquery.pager.js
+++ b/demos/movies/components/jquery.pager.js
@@ -9,7 +9,7 @@
 * Dual licensed under the MIT and GPL licenses:
 * http://www.opensource.org/licenses/mit-license.php
 * http://www.gnu.org/licenses/gpl.html
-* 
+*
 * Read the related blog post and contact the author at http://www.j-dee.com/2008/12/22/jquery-pager-plugin/
 *
 * This version is far from perfect and doesn't manage it's own state, therefore contributions are more than welcome!
@@ -17,10 +17,10 @@
 * Usage: .pager({ pagenumber: 1, pagecount: 15, buttonClickCallback: PagerClickTest });
 *
 * Where pagenumber is the visible page number
-*       pagecount is the total number of pages to display
-*       buttonClickCallback is the method to fire when a pager button is clicked.
+*	   pagecount is the total number of pages to display
+*	   buttonClickCallback is the method to fire when a pager button is clicked.
 *
-* buttonClickCallback signiture is PagerClickTest = function(pageclickednumber) 
+* buttonClickCallback signiture is PagerClickTest = function(pageclickednumber)
 * Where pageclickednumber is the number of the page clicked in the control.
 *
 * The included Pager.CSS file is a dependancy but can obviously tweaked to your wishes
@@ -28,101 +28,101 @@
 */
 (function($) {
 
-    $.fn.pager = function(options) {
-
-        var opts = $.extend({}, $.fn.pager.defaults, options);
-
-        return this.each(function() {
-
-        // empty out the destination element and then render out the pager with the supplied options
-            $(this).empty().append(renderpager(parseInt(options.pagenumber), parseInt(options.pagecount), options.buttonClickCallback));
-            
-            // specify correct cursor activity
-            $('.pages li').mouseover(function() { document.body.style.cursor = "pointer"; }).mouseout(function() { document.body.style.cursor = "auto"; });
-        });
-    };
-
-    // render and return the pager with the supplied options
-    function renderpager(pagenumber, pagecount, buttonClickCallback) {
-
-        // setup $pager to hold render
-        var $pager = $('<ul class="pages"></ul>');
-
-        // add in the previous and next buttons
-        $pager.append(renderButton('first', pagenumber, pagecount, buttonClickCallback)).append(renderButton('prev', pagenumber, pagecount, buttonClickCallback));
-
-        // pager currently only handles 10 viewable pages ( could be easily parameterized, maybe in next version ) so handle edge cases
-        var startPoint = 1;
-        var endPoint = 9;
-
-        if (pagenumber > 4) {
-            startPoint = pagenumber - 4;
-            endPoint = pagenumber + 4;
-        }
-
-        if (endPoint > pagecount) {
-            startPoint = pagecount - 8;
-            endPoint = pagecount;
-        }
-
-        if (startPoint < 1) {
-            startPoint = 1;
-        }
-
-        // loop thru visible pages and render buttons
-        for (var page = startPoint; page <= endPoint; page++) {
-
-            var currentButton = $('<li class="page-number">' + (page) + '</li>');
-
-            page == pagenumber ? currentButton.addClass('pgCurrent') : currentButton.click(function() { buttonClickCallback(this.firstChild.data); });
-            currentButton.appendTo($pager);
-        }
-
-        // render in the next and last buttons before returning the whole rendered control back.
-        $pager.append(renderButton('next', pagenumber, pagecount, buttonClickCallback)).append(renderButton('last', pagenumber, pagecount, buttonClickCallback));
-
-        return $pager;
-    }
-
-    // renders and returns a 'specialized' button, ie 'next', 'previous' etc. rather than a page number button
-    function renderButton(buttonLabel, pagenumber, pagecount, buttonClickCallback) {
-
-        var $Button = $('<li class="pgNext">' + buttonLabel + '</li>');
-
-        var destPage = 1;
-
-        // work out destination page for required button type
-        switch (buttonLabel) {
-            case "first":
-                destPage = 1;
-                break;
-            case "prev":
-                destPage = pagenumber - 1;
-                break;
-            case "next":
-                destPage = pagenumber + 1;
-                break;
-            case "last":
-                destPage = pagecount;
-                break;
-        }
-
-        // disable and 'grey' out buttons if not needed.
-        if (buttonLabel == "first" || buttonLabel == "prev") {
-            pagenumber <= 1 ? $Button.addClass('pgEmpty') : $Button.click(function() { buttonClickCallback(destPage); });
-        }
-        else {
-            pagenumber >= pagecount ? $Button.addClass('pgEmpty') : $Button.click(function() { buttonClickCallback(destPage); });
-        }
-
-        return $Button;
-    }
-
-    // pager defaults. hardly worth bothering with in this case but used as placeholder for expansion in the next version
-    $.fn.pager.defaults = {
-        pagenumber: 1,
-        pagecount: 1
-    };
+	$.fn.pager = function(options) {
+
+		var opts = $.extend({}, $.fn.pager.defaults, options);
+
+		return this.each(function() {
+
+		// empty out the destination element and then render out the pager with the supplied options
+			$(this).empty().append(renderpager(parseInt(options.pagenumber), parseInt(options.pagecount), options.buttonClickCallback));
+
+			// specify correct cursor activity
+			$('.pages li').mouseover(function() { document.body.style.cursor = "pointer"; }).mouseout(function() { document.body.style.cursor = "auto"; });
+		});
+	};
+
+	// render and return the pager with the supplied options
+	function renderpager(pagenumber, pagecount, buttonClickCallback) {
+
+		// setup $pager to hold render
+		var $pager = $('<ul class="pages"></ul>');
+
+		// add in the previous and next buttons
+		$pager.append(renderButton('first', pagenumber, pagecount, buttonClickCallback)).append(renderButton('prev', pagenumber, pagecount, buttonClickCallback));
+
+		// pager currently only handles 10 viewable pages ( could be easily parameterized, maybe in next version ) so handle edge cases
+		var startPoint = 1;
+		var endPoint = 9;
+
+		if (pagenumber > 4) {
+			startPoint = pagenumber - 4;
+			endPoint = pagenumber + 4;
+		}
+
+		if (endPoint > pagecount) {
+			startPoint = pagecount - 8;
+			endPoint = pagecount;
+		}
+
+		if (startPoint < 1) {
+			startPoint = 1;
+		}
+
+		// loop thru visible pages and render buttons
+		for (var page = startPoint; page <= endPoint; page++) {
+
+			var currentButton = $('<li class="page-number">' + (page) + '</li>');
+
+			page == pagenumber ? currentButton.addClass('pgCurrent') : currentButton.click(function() { buttonClickCallback(this.firstChild.data); });
+			currentButton.appendTo($pager);
+		}
+
+		// render in the next and last buttons before returning the whole rendered control back.
+		$pager.append(renderButton('next', pagenumber, pagecount, buttonClickCallback)).append(renderButton('last', pagenumber, pagecount, buttonClickCallback));
+
+		return $pager;
+	}
+
+	// renders and returns a 'specialized' button, ie 'next', 'previous' etc. rather than a page number button
+	function renderButton(buttonLabel, pagenumber, pagecount, buttonClickCallback) {
+
+		var $Button = $('<li class="pgNext">' + buttonLabel + '</li>');
+
+		var destPage = 1;
+
+		// work out destination page for required button type
+		switch (buttonLabel) {
+			case "first":
+				destPage = 1;
+				break;
+			case "prev":
+				destPage = pagenumber - 1;
+				break;
+			case "next":
+				destPage = pagenumber + 1;
+				break;
+			case "last":
+				destPage = pagecount;
+				break;
+		}
+
+		// disable and 'grey' out buttons if not needed.
+		if (buttonLabel == "first" || buttonLabel == "prev") {
+			pagenumber <= 1 ? $Button.addClass('pgEmpty') : $Button.click(function() { buttonClickCallback(destPage); });
+		}
+		else {
+			pagenumber >= pagecount ? $Button.addClass('pgEmpty') : $Button.click(function() { buttonClickCallback(destPage); });
+		}
+
+		return $Button;
+	}
+
+	// pager defaults. hardly worth bothering with in this case but used as placeholder for expansion in the next version
+	$.fn.pager.defaults = {
+		pagenumber: 1,
+		pagecount: 1
+	};
 
 })(jQuery);
 
diff --git a/demos/movies/css/jquery-ui-1.8.1.custom.css b/demos/movies/css/jquery-ui-1.8.1.custom.css
index 9871af7..199ad28 100644
--- a/demos/movies/css/jquery-ui-1.8.1.custom.css
+++ b/demos/movies/css/jquery-ui-1.8.1.custom.css
@@ -300,8 +300,8 @@
 .ui-button { display: inline-block; position: relative; padding: 0; margin-right: .1em; text-decoration: none !important; cursor: pointer; text-align: center; zoom: 1; overflow: visible; } /* the overflow property removes extra width in IE */
 .ui-button-icon-only { width: 2.2em; } /* to make room for the icon, a width needs to be set here */
 button.ui-button-icon-only { width: 2.4em; } /* button elements seem to need a little more width */
-.ui-button-icons-only { width: 3.4em; } 
-button.ui-button-icons-only { width: 3.7em; } 
+.ui-button-icons-only { width: 3.4em; }
+button.ui-button-icons-only { width: 3.7em; }
 
 /*button text element */
 .ui-button .ui-button-text { display: block; line-height: 1.4;  }
@@ -359,7 +359,7 @@ button.ui-button::-moz-focus-inner { border: 0; padding: 0; } /* reset extra pad
 .ui-datepicker .ui-datepicker-title { margin: 0 2.3em; line-height: 1.8em; text-align: center; }
 .ui-datepicker .ui-datepicker-title select { font-size:1em; margin:1px 0; }
 .ui-datepicker select.ui-datepicker-month-year {width: 100%;}
-.ui-datepicker select.ui-datepicker-month, 
+.ui-datepicker select.ui-datepicker-month,
 .ui-datepicker select.ui-datepicker-year { width: 49%;}
 .ui-datepicker table {width: 100%; font-size: .9em; border-collapse: collapse; margin:0 0 .4em; }
 .ui-datepicker th { padding: .7em .3em; text-align: center; font-weight: bold; border: 0;  }
diff --git a/demos/movies/css/movies.css b/demos/movies/css/movies.css
index 22e15d9..2514772 100644
--- a/demos/movies/css/movies.css
+++ b/demos/movies/css/movies.css
@@ -5,16 +5,6 @@
 	font-size: 12px;
 }
 
-#about 
-{
-	text-align:center;
-	margin: auto;
-	margin-bottom: 5px;
-	background-color: #F9F5FA;
-	padding: 3px;
-	width: 50%;
-}
-
 #pageBody
 {
 	display: block;
@@ -120,6 +110,7 @@
 	margin-bottom: 10px;
 	padding: 8px;
 	background-color: #fff;
+	height: 180px;
 }
 
 #movieList div div
@@ -128,6 +119,8 @@
 	float: left;
 	width: 120px;
 	height: 150px;
+	float: left;
+	clear: both;
 }
 
 #movieList div img
@@ -142,7 +135,10 @@
 
 .buyButton
 {
-	/* 	background: url(buyButton.png) no-repeat 0px -20px) */
+	float: left;
+	clear: both;
+	margin-left: 6px;
+	margin-top: 6px;
 }
 
 #bookingsList
@@ -202,10 +198,10 @@
 	cursor: pointer;
 }
 
-#submit 
+#submit
 {
 	float: left;
-} 
+}
 
 .bookingTitle
 {
diff --git a/demos/resources/demos.css b/demos/resources/demos.css
new file mode 100644
index 0000000..2492570
--- /dev/null
+++ b/demos/resources/demos.css
@@ -0,0 +1,7 @@
+body { padding: 10px; font-family: Verdana; font-size: small }
+h4 { font-size: inherit`; font-variant: small-caps; }
+.height { width: 100%; margin-bottom:10px; float: left; clear: both; }
+.bottom { height:400px; width: 100%; margin-bottom:10px; float: left; clear: both; }
+body > button { float: left; clear: right; margin: 3px }
+.subhead { margin: 15px 0 4px 0; font-weight:bolder; color:#116; font-family:Arial; font-size:10pt }
+a { color: #55b}
diff --git a/demos/resources/movielist.css b/demos/resources/movielist.css
new file mode 100644
index 0000000..916d85c
--- /dev/null
+++ b/demos/resources/movielist.css
@@ -0,0 +1,7 @@
+table { border-collapse: collapse; }
+table tr { color: blue; height: 25px; }
+.header { color: #009; border-bottom: solid #77c 2px; background-color: #E8E8F7; }
+.header th { padding:5px; border: 1px solid #77c; }
+#movieList tr td:first-child { width: 100px; }
+table { border: 2px solid blue; width: 480px; margin: 4px 0 4px 4px; padding: 2px; background-color: #f8f8f8; }
+table td { padding: 3px; margin: 3px; border: solid #77c 1px; }
diff --git a/demos/resources/syntaxhighlighter.css b/demos/resources/syntaxhighlighter.css
new file mode 100644
index 0000000..b53e3b3
--- /dev/null
+++ b/demos/resources/syntaxhighlighter.css
@@ -0,0 +1,513 @@
+/**
+ * SyntaxHighlighter
+ * http://alexgorbatchev.com/
+ *
+ * SyntaxHighlighter is donationware. If you are using it, please donate.
+ * http://alexgorbatchev.com/wiki/SyntaxHighlighter:Donate
+ *
+ * @version
+ * 2.0.296 (March 01 2009)
+ *
+ * @copyright
+ * Copyright (C) 2004-2009 Alex Gorbatchev.
+ *
+ * @license
+ * This file is part of SyntaxHighlighter.
+ *
+ * SyntaxHighlighter is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * SyntaxHighlighter is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with SyntaxHighlighter.  If not, see <http://www.gnu.org/licenses/>.
+ */
+.syntaxhighlighter,
+.syntaxhighlighter div,
+.syntaxhighlighter code,
+.syntaxhighlighter span
+{
+	margin: 0 !important;
+	padding: 0 !important;
+	border: 0 !important;
+	outline: 0 !important;
+	background: none !important;
+	text-align: left !important;
+	float: none !important;
+	vertical-align: baseline !important;
+	position: static !important;
+	left: auto !important;
+	top: auto !important;
+	right: auto !important;
+	bottom: auto !important;
+	height: auto !important;
+	width: auto !important;
+	line-height: 1.1em !important;
+	font-family: "Consolas", "Monaco", "Bitstream Vera Sans Mono", "Courier New", Courier, monospace !important;
+	font-weight: normal !important;
+	font-style: normal !important;
+	font-size: 1em !important;
+}
+
+.syntaxhighlighter
+{
+	width: 100% !important;
+	margin: 1em 0 1em 0 !important;
+	padding: 1px !important; /* adds a little border on top and bottom */
+	position: relative !important;
+}
+
+.syntaxhighlighter .bold {
+	font-weight: bold !important;
+}
+
+.syntaxhighlighter .italic {
+	font-style: italic !important;
+}
+
+.syntaxhighlighter .line .number
+{
+	float: left !important;
+	width: 3em !important;
+	padding-right: .3em !important;
+	text-align: right !important;
+	display: block !important;
+}
+
+/* Disable numbers when no gutter option is set */
+.syntaxhighlighter.nogutter .line .number
+{
+	display: none !important;
+}
+
+.syntaxhighlighter .line .content
+{
+	margin-left: 3.3em !important;
+	padding-left: .5em !important;
+	display: block !important;
+}
+
+.syntaxhighlighter .line .content .block
+{
+	display: block !important;
+	padding-left: 1.5em !important;
+	text-indent: -1.5em !important;
+}
+
+.syntaxhighlighter .line .content .spaces
+{
+	display: none !important;
+}
+
+/* Disable border and margin on the lines when no gutter option is set */
+.syntaxhighlighter.nogutter .line .content
+{
+	margin-left: 0 !important;
+	border-left: none !important;
+}
+
+.syntaxhighlighter .bar
+{
+}
+
+.syntaxhighlighter.collapsed .bar
+{
+
+}
+
+.syntaxhighlighter.nogutter .ruler
+{
+	margin-left: 0 !important;
+	padding-left: 0 !important;
+}
+
+.syntaxhighlighter .ruler
+{
+	padding: 0 0 .5em .5em !important;
+	margin-left: 3.3em !important;
+	overflow: hidden !important;
+}
+
+/* Adjust some properties when collapsed */
+
+.syntaxhighlighter.collapsed .lines,
+.syntaxhighlighter.collapsed .ruler
+{
+	display: none !important;
+}
+
+/* Styles for the toolbar */
+
+.syntaxhighlighter .toolbar
+{
+	position: absolute !important;
+	right: 0px !important;
+	top: 0px !important;
+	font-size: 1px !important;
+	padding: 8px 8px 8px 0 !important; /* in px because images don't scale with ems */
+}
+
+.syntaxhighlighter.collapsed .toolbar
+{
+	font-size: 80% !important;
+	padding: .2em 0 .5em .5em !important;
+	position: static !important;
+}
+
+.syntaxhighlighter .toolbar a.item,
+.syntaxhighlighter .toolbar .item
+{
+	display: block !important;
+	float: left !important;
+	margin-left: 8px !important;
+	background-repeat: no-repeat !important;
+	overflow: hidden !important;
+	text-indent: -5000px !important;
+}
+
+.syntaxhighlighter.collapsed .toolbar .item
+{
+	display: none !important;
+}
+
+.syntaxhighlighter.collapsed .toolbar .item.expandSource
+{
+	background-image: url(magnifier.png) !important;
+	display: inline !important;
+	text-indent: 0 !important;
+	width: auto !important;
+	float: none !important;
+	height: 16px !important;
+	padding-left: 20px !important;
+}
+
+.syntaxhighlighter .toolbar .item.viewSource
+{
+	background-image: url(page_white_code.png) !important;
+}
+
+.syntaxhighlighter .toolbar .item.printSource
+{
+	background-image: url(printer.png) !important;
+}
+
+.syntaxhighlighter .toolbar .item.copyToClipboard
+{
+	text-indent: 0 !important;
+	background: none !important;
+	overflow: visible !important;
+}
+
+.syntaxhighlighter .toolbar .item.about
+{
+	background-image: url(help.png) !important;
+}
+
+/**
+ * Print view.
+ * Colors are based on the default theme without background.
+ */
+
+.syntaxhighlighter.printing,
+.syntaxhighlighter.printing .line.alt1 .content,
+.syntaxhighlighter.printing .line.alt2 .content,
+.syntaxhighlighter.printing .line.highlighted .number,
+.syntaxhighlighter.printing .line.highlighted.alt1 .content,
+.syntaxhighlighter.printing .line.highlighted.alt2 .content,
+.syntaxhighlighter.printing .line .content .block
+{
+	background: none !important;
+}
+
+/* Gutter line numbers */
+.syntaxhighlighter.printing .line .number
+{
+	color: #bbb !important;
+}
+
+/* Add border to the lines */
+.syntaxhighlighter.printing .line .content
+{
+	color: #000 !important;
+}
+
+/* Toolbar when visible */
+.syntaxhighlighter.printing .toolbar,
+.syntaxhighlighter.printing .ruler
+{
+	display: none !important;
+}
+
+.syntaxhighlighter.printing a
+{
+	text-decoration: none !important;
+}
+
+.syntaxhighlighter.printing .plain,
+.syntaxhighlighter.printing .plain a
+{
+	color: #000 !important;
+}
+
+.syntaxhighlighter.printing .comments,
+.syntaxhighlighter.printing .comments a
+{
+	color: #008200 !important;
+}
+
+.syntaxhighlighter.printing .string,
+.syntaxhighlighter.printing .string a
+{
+	color: blue !important;
+}
+
+.syntaxhighlighter.printing .keyword
+{
+	color: #069 !important;
+	font-weight: bold !important;
+}
+
+.syntaxhighlighter.printing .preprocessor
+{
+	color: gray !important;
+}
+
+.syntaxhighlighter.printing .variable
+{
+	color: #a70 !important;
+}
+
+.syntaxhighlighter.printing .value
+{
+	color: #090 !important;
+}
+
+.syntaxhighlighter.printing .functions
+{
+	color: #ff1493 !important;
+}
+
+.syntaxhighlighter.printing .constants
+{
+	color: #0066CC !important;
+}
+
+.syntaxhighlighter.printing .script
+{
+	font-weight: bold !important;
+}
+
+.syntaxhighlighter.printing .color1,
+.syntaxhighlighter.printing .color1 a
+{
+	color: #808080 !important;
+}
+
+.syntaxhighlighter.printing .color2,
+.syntaxhighlighter.printing .color2 a
+{
+	color: #ff1493 !important;
+}
+
+.syntaxhighlighter.printing .color3,
+.syntaxhighlighter.printing .color3 a
+{
+	color: red !important;
+}
+
+/**
+ * SyntaxHighlighter
+ * http://alexgorbatchev.com/
+ *
+ * SyntaxHighlighter is donationware. If you are using it, please donate.
+ * http://alexgorbatchev.com/wiki/SyntaxHighlighter:Donate
+ *
+ * @version
+ * 2.0.296 (March 01 2009)
+ *
+ * @copyright
+ * Copyright (C) 2004-2009 Alex Gorbatchev.
+ *
+ * @license
+ * This file is part of SyntaxHighlighter.
+ *
+ * SyntaxHighlighter is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * SyntaxHighlighter is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with SyntaxHighlighter.  If not, see <http://www.gnu.org/licenses/>.
+ */
+/************************************
+ * Default Syntax Highlighter theme.
+ *
+ * Interface elements.
+ ************************************/
+
+.syntaxhighlighter
+{
+	background-color: #E7E5DC !important;
+}
+
+/* Highlighed line number */
+.syntaxhighlighter .line.highlighted .number
+{
+	background-color: #6CE26C !important;
+	color: black !important;
+}
+
+/* Highlighed line */
+.syntaxhighlighter .line.highlighted.alt1 .content,
+.syntaxhighlighter .line.highlighted.alt2 .content
+{
+	background-color: #6CE26C !important;
+}
+
+/* Gutter line numbers */
+.syntaxhighlighter .line .number
+{
+	color: #5C5C5C !important;
+}
+
+/* Add border to the lines */
+.syntaxhighlighter .line .content
+{
+	border-left: 3px solid #6CE26C !important;
+	color: #000 !important;
+}
+
+.syntaxhighlighter.printing .line .content
+{
+	border: 0 !important;
+}
+
+/* First line */
+.syntaxhighlighter .line.alt1 .content
+{
+	background-color: #fff !important;
+}
+
+/* Second line */
+.syntaxhighlighter .line.alt2 .content
+{
+	background-color: #F8F8F8 !important;
+}
+
+.syntaxhighlighter .line .content .block
+{
+	background: url(wrapping.png) 0 1.1em no-repeat !important;
+}
+
+.syntaxhighlighter .ruler
+{
+	color: silver !important;
+	background-color: #F8F8F8 !important;
+	border-left: 3px solid #6CE26C !important;
+}
+
+.syntaxhighlighter.nogutter .ruler
+{
+	border: 0 !important;
+}
+
+.syntaxhighlighter .toolbar
+{
+	background-color: #F8F8F8 !important;
+	border: #E7E5DC solid 1px !important;
+}
+
+.syntaxhighlighter .toolbar a
+{
+	color: #a0a0a0 !important;
+}
+
+.syntaxhighlighter .toolbar a:hover
+{
+	color: red !important;
+}
+
+/************************************
+ * Actual syntax highlighter colors.
+ ************************************/
+.syntaxhighlighter .plain,
+.syntaxhighlighter .plain a
+{
+	color: #000 !important;
+}
+
+.syntaxhighlighter .comments,
+.syntaxhighlighter .comments a
+{
+	color: #008200 !important;
+}
+
+.syntaxhighlighter .string,
+.syntaxhighlighter .string a
+{
+	color: blue !important;
+}
+
+.syntaxhighlighter .keyword
+{
+	color: #069 !important;
+	font-weight: bold !important;
+}
+
+.syntaxhighlighter .preprocessor
+{
+	color: gray !important;
+}
+
+.syntaxhighlighter .variable
+{
+	color: #a70 !important;
+}
+
+.syntaxhighlighter .value
+{
+	color: #090 !important;
+}
+
+.syntaxhighlighter .functions
+{
+	color: #ff1493 !important;
+}
+
+.syntaxhighlighter .constants
+{
+	color: #0066CC !important;
+}
+
+.syntaxhighlighter .script
+{
+	background-color: yellow !important;
+}
+
+.syntaxhighlighter .color1,
+.syntaxhighlighter .color1 a
+{
+	color: #808080 !important;
+}
+
+.syntaxhighlighter .color2,
+.syntaxhighlighter .color2 a
+{
+	color: #ff1493 !important;
+}
+
+.syntaxhighlighter .color3,
+.syntaxhighlighter .color3 a
+{
+	color: red !important;
+}
\ No newline at end of file
diff --git a/demos/resources/syntaxhighlighter.min.js b/demos/resources/syntaxhighlighter.min.js
new file mode 100644
index 0000000..2b37450
--- /dev/null
+++ b/demos/resources/syntaxhighlighter.min.js
@@ -0,0 +1,30 @@
+/**
+ * SyntaxHighlighter
+ * http://alexgorbatchev.com/
+ *
+ * SyntaxHighlighter is donationware. If you are using it, please donate.
+ * http://alexgorbatchev.com/wiki/SyntaxHighlighter:Donate
+ *
+ * @version
+ * 2.0.296 (March 01 2009)
+ *
+ * @copyright
+ * Copyright (C) 2004-2009 Alex Gorbatchev.
+ *
+ * @license
+ * This file is part of SyntaxHighlighter.
+ *
+ * SyntaxHighlighter is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * SyntaxHighlighter is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with SyntaxHighlighter.  If not, see <http://www.gnu.org/licenses/>.
+ */
+eval(function(e,d,a,c,b,f){b=function(a){return(a<d?"":b(parseInt(a/d)))+((a=a%d)>35?String.fromCharCode(a+29):a.toString(36))};if(!"".replace(/^/,String)){while(a--)f[b(a)]=c[a]||b(a);c=[function(a){return f[a]}];b=function(){return"\\w+"};a=1}while(a--)if(c[a])e=e.replace(new RegExp("\\b"+b(a)+"\\b","g"),c[a]);return e}('f(!1o.31){p 31=h(){p l={5S:{"1e-1c":"","73-2E":1,"2a-1P":u,"1I":u,"6T-6S":U,"1F-1P":4,"5F":O,"5x":U,"1q":U,"5v":O,"6i-6p":U,"6J":O},Q:{5D:u,5i:16,5k:16,7P:O,7I:"5L",1b:{4e:"91 1i",3Y:"92 1i",5l:"93 90 6K",6X:"8Z I 8W 1X 8X 6K 8Y",3u:"3u",6h:"?",1u:"31\\n\\n",5Z:"94\'t 95 7J C: ",7z:"9b 9c\'t 9a C 2j-2n 99: ",6B:"<!96 2j 97 \\"-//98//8b 8V 1.0 8U//8I\\" \\"2y://7A.7E.7o/8J/86/8b/86-8K.8H\\"><2j 8G=\\"2y://7A.7E.7o/8D/8E\\"><70><8F 2y-8L=\\"8M-8S\\" 5h=\\"2c/2j; 8T=8R-8\\" /><36>8Q 31</36></70><2R 1f=\\"2a-7T:8N,\'7h 8O 8P\',7h,7L;9d-53:#9e;53:#9D;2a-1P:9E;2c-8i:85;\\"><z 1f=\\"2c-8i:85;5n-4z:9F;\\"><z 1f=\\"2a-7T:9C,9B,9y,8C-7L;2a-1P:9z-9A;\\">31</z><z 1f=\\"2a-1P:.9G;5n-9H:9N;\\"><z>84 2.0.9O (9M 9L 6H)</z><z><a 2q=\\"2y://6a.4a\\" 9I=\\"4P\\" 1f=\\"53:#9J;2c-9K:9x;\\">2y://6a.4a</a></z></z><z>9w I 9k 9l.</z><z>9m 9j-6H 9i 9f.</z></z></2R></2j>"},7j:O},1p:{59:u,3q:u,3o:u,5j:{}},30:{},8a:{9g:/\\/\\*[\\s\\S]*?\\*\\//4M,9h:/\\/\\/.*$/4M,9n:/#.*$/4M,9o:/"(?:\\.|(\\\\\\")|[^\\""\\n])*"/g,9u:/\'(?:\\.|(\\\\\\\')|[^\\\'\'\\n])*\'/g,9v:/"(?:\\.|(\\\\\\")|[^\\""])*"/g,9t:/\'(?:\\.|(\\\\\\\')|[^\\\'\'])*\'/g,3e:/\\w+:\\/\\/[\\w-.\\/?%&=]*/g,9s:{E:/(&1C;|<)\\?=?/g,17:/\\?(&2m;|>)/g},9p:{E:/(&1C;|<)%=?/g,17:/%(&2m;|>)/g},9q:{E:/(&1C;|<)\\s*2n.*?(&2m;|>)/47,17:/(&1C;|<)\\/\\s*2n\\s*(&2m;|>)/47}},1q:{15:h(32){p 38=J.1t("37"),4L=l.1q.77;38.Y="1q";C(p 2N 1X 4L){p 6j=4L[2N],4G=R 6j(32),22=4G.15();32.5E[2N]=4G;f(22==u){1V}f(9r(22)=="9P"){22=l.1q.6C(22,32.1j,2N)}22.Y+="8w "+2N;38.1z(22)}q 38},6C:h(4v,6O,4w){p a=J.1t("a"),5e=a.1f,4F=l.Q,57=4F.5i,5c=4F.5k;a.2q="#"+4w;a.36=4v;a.5P=6O;a.6c=4w;a.1r=4v;f(5z(57)==O){5e.1N=57+"5H"}f(5z(5c)==O){5e.2u=5c+"5H"}a.8q=h(e){8j{l.1q.6f(c,e||1o.6g,c.5P,c.6c)}8m(e){l.B.1u(e.6F)}q O};q a},6f:h(7i,7a,5T,7O,72){p 4Y=l.1p.5j[5T],45;f(4Y==u||(45=4Y.5E[7O])==u){q u}q 45.2h(7i,7a,72)},77:{4e:h(4p){c.15=h(){f(4p.T("5v")!=U){q}q l.Q.1b.4e};c.2h=h(4q,8v,8B){p z=4p.z;4q.71.51(4q);z.Y=z.Y.D("5t","")}},3Y:h(8g){c.15=h(){q l.Q.1b.3Y};c.2h=h(8p,8o,8t){p 33=l.B.3d(8g.5r).D(/</g,"&1C;"),2b=l.B.4y("","4P",8r,8n,"8x=0, 8k=1, 8s=0, 6n=1");33=l.B.2W(33);2b.J.3t("<5L>"+33+"</5L>");2b.J.4N()}},5l:h(4S){p 3x,8y,5M=4S.1j;c.15=h(){p 2A=l.Q;f(2A.5D==u){q u}h 1L(5C){p 5s="";C(p 5A 1X 5C){5s+="<8z 1c=\'"+5A+"\' 1W=\'"+5C[5A]+"\'/>"}q 5s};h 2p(43){p 5p="";C(p 5o 1X 43){5p+=" "+5o+"=\'"+43[5o]+"\'"}q 5p};p 48={1N:2A.5i,2u:2A.5k,1j:5M+"ai",6z:"bu/x-74-79",36:l.Q.1b.5l},46={bl:"bm",bx:"bA",bz:"5P="+5M,bF:"O"},4t=2A.5D,35;f(/aQ/i.1R(61.6b)){35="<8h"+2p({aT:"bd:bf-b9-b5-b4-b7",b8:"2y://ba.be.4a/bb/74/bc/79/b3.b2#84=9,0,0,0"})+2p(48)+">"+1L(46)+1L({aU:4t})+"</8h>"}F{35="<aS"+2p(48)+2p(46)+2p({aR:4t})+"/>"}3x=J.1t("z");3x.1r=35;q 3x};c.2h=h(aV,aW,4R){p 68=4R.b1;6V(68){2J"7C":p 5d=l.B.2W(l.B.3d(4S.5r).D(/&1C;/g,"<").D(/&2m;/g,">").D(/&b0;/g,"&"));f(1o.6e){1o.6e.aZ("2c",5d)}F{q l.B.2W(5d)}2J"aX":l.B.1u(l.Q.1b.6X);2B;2J"aY":l.B.1u(4R.6F);2B}}},bh:h(4I){c.15=h(){q l.Q.1b.3u};c.2h=h(bg,by,bB){p 25=J.1t("bG"),1J=u;f(l.1p.3o!=u){J.2R.51(l.1p.3o)}l.1p.3o=25;25.1f.bC="bE:bD;1N:6m;2u:6m;E:-6l;4z:-6l;";J.2R.1z(25);1J=25.4J.J;6t(1J,1o.J);1J.3t("<z 1e=\\""+4I.z.Y.D("5t","")+" bn\\">"+4I.z.1r+"</z>");1J.4N();25.4J.4A();25.4J.3u();h 6t(6A,6s){p 2S=6s.7M("4K");C(p i=0;i<2S.v;i++){f(2S[i].6r.bk()=="6q"&&/bi\\.12$/.1R(2S[i].2q)){6A.3t("<4K 6z=\\"2c/12\\" 6r=\\"6q\\" 2q=\\""+2S[i].2q+"\\"></4K>")}}}}},bj:h(bo){c.15=h(){q l.Q.1b.6h};c.2h=h(bv,bt){p 2b=l.B.4y("","4P",bq,bs,"6n=0"),1J=2b.J;1J.3t(l.Q.1b.6B);1J.4N();2b.4A()}}}},B:{5m:h(6R){q 6R+3z.aN(3z.aa()*ab).2f()},5u:h(4E,4x){p 3l={},1S;C(1S 1X 4E){3l[1S]=4E[1S]}C(1S 1X 4x){3l[1S]=4x[1S]}q 3l},7w:h(4u){6V(4u){2J"U":q U;2J"O":q O}q 4u},4y:h(3e,6M,4D,4C,2I){p x=(6N.1N-4D)/2,y=(6N.2u-4C)/2;2I+=", E="+x+", 4z="+y+", 1N="+4D+", 2u="+4C;2I=2I.D(/^,/,"");p 4Q=1o.9Q(3e,6M,2I);4Q.4A();q 4Q},78:h(1A,29,23){f(1A.6D){1A["e"+29+23]=23;1A[29+23]=h(){1A["e"+29+23](1o.6g)};1A.6D("a6"+29,1A[29+23])}F{1A.a7(29,23,O)}},1u:h(A){1u(l.Q.1b.1u+A)},4c:h(5a,5Y){p 2k=l.1p.59,3j=u;f(2k==u){2k={};C(p 55 1X l.30){p 3g=l.30[55].ac;f(3g==u){1V}C(p i=0;i<3g.v;i++){2k[3g[i]]=55}}l.1p.59=2k}3j=l.30[2k[5a]];f(3j==u&&5Y!=O){l.B.1u(l.Q.1b.5Z+5a)}q 3j},42:h(A,60){p 2T=A.21("\\n");C(p i=0;i<2T.v;i++){2T[i]=60(2T[i])}q 2T.4h("\\n")},6x:h(){p z=J.1t("z"),3h=J.1t("z"),6d=10,i=1;28(i<=aO){f(i%6d===0){z.1r+=i;i+=(i+"").v}F{z.1r+="&ah;";i++}}3h.Y="5F 2E";3h.1z(z);q 3h},6U:h(A){q A.D(/^[ ]*[\\n]+|[\\n]*[ ]*$/g,"")},7d:h(A){p 3i,4V={},5b=R M("^\\\\[(?<54>(.*?))\\\\]$"),69=R M("(?<1c>[\\\\w-]+)"+"\\\\s*:\\\\s*"+"(?<1W>"+"[\\\\w-%#]+|"+"\\\\[.*?\\\\]|"+"\\".*?\\"|"+"\'.*?\'"+")\\\\s*;?","g");28((3i=69.N(A))!=u){p 2g=3i.1W.D(/^[\'"]|[\'"]$/g,"");f(2g!=u&&5b.1R(2g)){p m=5b.N(2g);2g=m.54.v>0?m.54.21(/\\s*,\\s*/):[]}4V[3i.1c]=2g}q 4V},80:h(A,12){f(A==u||A.v==0||A=="\\n"){q A}A=A.D(/</g,"&1C;");A=A.D(/ {2,}/g,h(m){p 4U="";C(p i=0;i<m.v-1;i++){4U+="&1O;"}q 4U+" "});f(12!=u){A=l.B.42(A,h(2i){f(2i.v==0){q""}p 3k="";2i=2i.D(/^(&1O;| )+/,h(s){3k=s;q""});f(2i.v==0){q 3k}q 3k+"<I 1e=\\""+12+"\\">"+2i+"</I>"})}q A},7f:h(66,67){p 2Z=66.2f();28(2Z.v<67){2Z="0"+2Z}q 2Z},5y:h(){p 3b=J.1t("z"),3f,3a=0,52=J.2R,1j=l.B.5m("5y"),2O="<z 1e=\\"",2Q="</z>",4W="</1T>";3b.1r=2O+"6L\\">"+2O+"26\\">"+2O+"2E\\">"+2O+"5h"+"\\"><1T 1e=\\"7V\\"><1T 1j=\\""+1j+"\\">&1O;"+4W+4W+2Q+2Q+2Q+2Q;52.1z(3b);3f=J.a5(1j);f(/a4/i.1R(61.6b)){p 5Q=1o.9V(3f,u);3a=7l(5Q.9W("1N"))}F{3a=3f.9U}52.51(3b);q 3a},6Q:h(7R,6Y){p 1F="";C(p i=0;i<6Y;i++){1F+=" "}q 7R.D(/\\t/g,1F)},6P:h(2P,41){p 9T=2P.21("\\n"),1F="\\t",5f="";C(p i=0;i<50;i++){5f+="                    "}h 7X(3c,18,7Z){q 3c.1M(0,18)+5f.1M(0,7Z)+3c.1M(18+1,3c.v)};2P=l.B.42(2P,h(20){f(20.1h(1F)==-1){q 20}p 18=0;28((18=20.1h(1F))!=-1){p 7U=41-18%41;20=7X(20,18,7U)}q 20});q 2P},3d:h(A){q(l.Q.7P==U)?A.D(/<br\\s*\\/?>|&1C;br\\s*\\/?&2m;/47,"\\n"):A},3P:h(A){q A.D(/\\s*$/g,"").D(/^\\s*/,"")},2W:h(A){p 1Q=l.B.3d(A).21("\\n"),9X=R 5B(),7S=/^\\s*/,24=9Y;C(p i=0;i<1Q.v&&24>0;i++){p 3V=1Q[i];f(l.B.3P(3V).v==0){1V}p 3S=7S.N(3V);f(3S==u){q A}24=3z.24(3S[0].v,24)}f(24>0){C(p i=0;i<1Q.v;i++){1Q[i]=1Q[i].1M(24)}}q 1Q.4h("\\n")},7B:h(2L,2K){f(2L.H<2K.H){q-1}F{f(2L.H>2K.H){q 1}F{f(2L.v<2K.v){q-1}F{f(2L.v>2K.v){q 1}}}}q 0},2D:h(8d,2G){h 8e(3W,81){q[R l.4i(3W[0],3W.H,81.12)]};p a2=0,3X=u,3y=[],8c=2G.4m?2G.4m:8e;28((3X=2G.3D.N(8d))!=u){3y=3y.2H(8c(3X,2G))}q 3y},6o:h(8f){q 8f.D(l.8a.3e,h(m){q"<a 2q=\\""+m+"\\">"+m+"</a>"})}},1I:h(7c,4l){h 88(4s){p 49=[];C(p i=0;i<4s.v;i++){49.K(4s[i])}q 49};p 3m=4l?[4l]:88(J.7M(l.Q.7I)),7g="1r",2t=u;f(3m.v===0){q}C(p i=0;i<3m.v;i++){p 2F=3m[i],2s=l.B.7d(2F.Y),2Y;2s=l.B.5u(7c,2s);2Y=2s["7J"];f(2Y==u){1V}f(2s["2j-2n"]=="U"){2t=R l.44(2Y)}F{p 4o=l.B.4c(2Y);f(4o){2t=R 4o()}F{1V}}2t.1I(2F[7g],2s);p 2l=2t.z;f(l.Q.7j){2l=J.1t("a0");2l.1W=2t.z.1r;2l.1f.1N="aj";2l.1f.2u="aE"}2F.71.aF(2l,2F)}},aD:h(76){l.B.78(1o,"aC",h(){l.1I(76)})}};l.4i=h(4j,75,12){c.1W=4j;c.H=75;c.v=4j.v;c.12=12};l.4i.Z.2f=h(){q c.1W};l.44=h(4g){p 1H=l.B.4c(4g),4f=R l.30.aA(),aB=u;f(1H==u){q}1H=R 1H();c.4X=4f;f(1H.3K==u){l.B.1u(l.Q.1b.7z+4g);q}4f.4B.K({3D:1H.3K.I,4m:7F});h 39(4d,7G){C(p j=0;j<4d.v;j++){4d[j].H+=7G}};h 7F(14,aH){p 7x=14.I,1E=[],4k=1H.4B,7p=14.H+14.E.v,2X=1H.3K,1n;C(p i=0;i<4k.v;i++){1n=l.B.2D(7x,4k[i]);39(1n,7p);1E=1E.2H(1n)}f(2X.E!=u&&14.E!=u){1n=l.B.2D(14.E,2X.E);39(1n,14.H);1E=1E.2H(1n)}f(2X.17!=u&&14.17!=u){1n=l.B.2D(14.17,2X.17);39(1n,14.H+14[0].aM(14.17));1E=1E.2H(1n)}q 1E}};l.44.Z.1I=h(7u,7t){c.4X.1I(7u,7t);c.z=c.4X.z};l.87=h(){};l.87.Z={T:h(7v,7n){p 5J=c.1L[7v];q l.B.7w(5J==u?7n:5J)},15:h(7y){q J.1t(7y)},7m:h(5G){C(p i=0;i<c.1Y.v;i++){p 2V=c.1Y[i];f(2V===u){1V}f((5G.H>2V.H)&&(5G.H<2V.H+2V.v)){q U}}q O},6v:h(3v,7D){p 2e=[];f(3v!=u){C(p i=0;i<3v.v;i++){2e=2e.2H(l.B.2D(7D,3v[i]))}}2e=2e.aL(l.B.7B);q 2e},6u:h(){C(p i=0;i<c.1Y.v;i++){f(c.7m(c.1Y[i])){c.1Y[i]=u}}},6k:h(2C){p 3w=2C.21(/\\n/g),3s=7l(c.T("73-2E")),7e=(3s+3w.v).2f().v,89=c.T("1I",[]);2C="";C(p i=0;i<3w.v;i++){p 1s=3w[i],2w=/^(&1O;|\\s)+/.N(1s),5w="2E aK"+(i%2==0?1:2),82=l.B.7f(3s+i,7e),83=89.1h((3s+i).2f())!=-1,1D=u;f(2w!=u){1D=2w[0].2f();1s=1s.1M(1D.v);1D=1D.D(/&1O;/g," ");2w=l.1p.3q*1D.v}F{2w=0}1s=l.B.3P(1s);f(1s.v==0){1s="&1O;"}f(83){5w+=" aI"}2C+="<z 1e=\\""+5w+"\\">"+"<I 1e=\\"aJ\\">"+82+".</I>"+"<1T 1e=\\"5h\\">"+(1D!=u?"<I 1e=\\"az\\">"+1D.D(/\\s/g,"&1O;")+"</I>":"")+"<1T 1e=\\"7V\\" 1f=\\"5n-E: "+2w+"5H !aq;\\">"+1s+"</1T>"+"</1T>"+"</z>"}q 2C},6w:h(5N,5K){p 18=0,3n="",3r=l.B.80;C(p i=0;i<5K.v;i++){p 1y=5K[i];f(1y===u||1y.v===0){1V}3n+=3r(5N.1M(18,1y.H-18),"63")+3r(1y.1W,1y.12);18=1y.H+1y.v}3n+=3r(5N.1M(18),"63");q 3n},1I:h(1g,5R){p am=l.Q,3p=l.1p,z,34;c.1L={};c.z=u;c.26=u;c.I=u;c.2d=u;c.5E={};c.1j=l.B.5m("ar");3p.5j[c.1j]=c;f(1g===u){1g=""}f(3p.3q===u){3p.3q=l.B.5y()}c.1L=l.B.5u(l.5S,5R||{});f(c.T("6J")==U){c.1L.1q=c.1L.5x=O}c.z=z=c.15("37");c.26=c.15("37");c.26.Y="26";z.Y="6L";z.1j=c.1j;f(c.T("5v")){z.Y+=" 5t"}f(c.T("5x")==O){z.Y+=" ax"}z.Y+=" "+c.T("1e-1c");z.1f.av=c.T("2a-1P","");c.5r=1g;c.I=l.B.6U(1g).D(/\\r/g," ");34=c.T("1F-1P");c.I=c.T("6T-6S")==U?l.B.6P(c.I,34):l.B.6Q(c.I,34);c.I=l.B.2W(c.I);f(c.T("1q")){c.2d=c.15("37");c.2d.Y="2d";c.2d.1z(l.1q.15(c));z.1z(c.2d)}f(c.T("5F")){z.1z(l.B.6x())}z.1z(c.26);c.1Y=c.6v(c.4B,c.I);c.6u();1g=c.6w(c.I,c.1Y);1g=c.6k(l.B.3P(1g));f(c.T("6i-6p")){1g=l.B.6o(1g)}c.26.1r=1g},au:h(A){A=A.D(/^\\s+|\\s+$/g,"").D(/\\s+/g,"\\\\b|\\\\b");q"\\\\b"+A+"\\\\b"},at:h(2z){c.3K={E:{3D:2z.E,12:"2n"},17:{3D:2z.17,12:"2n"},I:R M("(?<E>"+2z.E.1i+")"+"(?<I>.*?)"+"(?<17>"+2z.17.1i+")","aw")}}};q l}()}f(!5B.1h){5B.Z.1h=h(5V,3J){3J=3z.as(3J||0,0);C(p i=3J;i<c.v;i++){f(c[i]==5V){q i}}q-1}}f(!1o.M){(h(){p 2v={N:V.Z.N,64:5q.Z.64,D:5q.Z.D,21:5q.Z.21},1G={11:/(?:[^\\\\([#\\s.]+|\\\\(?!k<[\\w$]+>|[65]{[^}]+})[\\S\\s]?|\\((?=\\?(?!#|<[\\w$]+>)))+|(\\()(?:\\?(?:(#)[^)]*\\)|<([$\\w]+)>))?|\\\\(?:k<([\\w$]+)>|[65]{([^}]+)})|(\\[\\^?)|([\\S\\s])/g,al:/(?:[^$]+|\\$(?![1-9$&`\']|{[$\\w]+}))+|\\$(?:([1-9]\\d*|[$&`\'])|{([$\\w]+)})/g,3F:/^(?:\\s+|#.*)+/,3R:/^(?:[?*+]|{\\d+(?:,\\d*)?})/,7H:/&&\\[\\^?/g,7q:/]/g},7N=h(5I,7Y,7K){C(p i=7K||0;i<5I.v;i++){f(5I[i]===7Y){q i}}q-1},7k=/()??/.N("")[1]!==3A,3L={};M=h(1d,1U){f(1d 4T V){f(1U!==3A){3G 62("56\'t 58 an 6G ao 5X V ap ay")}q 1d.3I()}p 1U=1U||"",7s=1U.1h("s")>-1,7r=1U.1h("x")>-1,4b=O,3C=[],13=[],11=1G.11,G,3N,3O,3E,3B;11.L=0;28(G=2v.N.2x(11,1d)){f(G[2]){f(!1G.3R.1R(1d.19(11.L))){13.K("(?:)")}}F{f(G[1]){3C.K(G[3]||u);f(G[3]){4b=U}13.K("(")}F{f(G[4]){3E=7N(3C,G[4]);13.K(3E>-1?"\\\\"+(3E+1)+(5z(1d.5O(11.L))?"":"(?:)"):G[0])}F{f(G[5]){13.K(3L.6Z?3L.6Z.7C(G[5],G[0].5O(1)==="P"):G[0])}F{f(G[6]){f(1d.5O(11.L)==="]"){13.K(G[6]==="["?"(?!)":"[\\\\S\\\\s]");11.L++}F{3N=M.6E("&&"+1d.19(G.H),1G.7H,1G.7q,"",{5W:"\\\\"})[0];13.K(G[6]+3N+"]");11.L+=3N.v+1}}F{f(G[7]){f(7s&&G[7]==="."){13.K("[\\\\S\\\\s]")}F{f(7r&&1G.3F.1R(G[7])){3O=2v.N.2x(1G.3F,1d.19(11.L-1))[0].v;f(!1G.3R.1R(1d.19(11.L-1+3O))){13.K("(?:)")}11.L+=3O-1}F{13.K(G[7])}}}F{13.K(G[0])}}}}}}}3B=V(13.4h(""),2v.D.2x(1U,/[aG]+/g,""));3B.1v={1i:1d,2o:4b?3C:u};q 3B};M.ak=h(1c,o){3L[1c]=o};V.Z.N=h(A){p 1a=2v.N.2x(c,A),1c,i,5g;f(1a){f(7k&&1a.v>1){5g=R V("^"+c.1i+"$(?!\\\\s)",c.3U());2v.D.2x(1a[0],5g,h(){C(i=1;i<7b.v-2;i++){f(7b[i]===3A){1a[i]=3A}}})}f(c.1v&&c.1v.2o){C(i=1;i<1a.v;i++){1c=c.1v.2o[i-1];f(1c){1a[1c]=1a[i]}}}f(c.3H&&c.L>(1a.H+1a[0].v)){c.L--}}q 1a}})()}V.Z.3U=h(){q(c.3H?"g":"")+(c.9Z?"i":"")+(c.6W?"m":"")+(c.3F?"x":"")+(c.a1?"y":"")};V.Z.3I=h(7Q){p 3T=R M(c.1i,(7Q||"")+c.3U());f(c.1v){3T.1v={1i:c.1v.1i,2o:c.1v.2o?c.1v.2o.19(0):u}}q 3T};V.Z.2x=h(a3,A){q c.N(A)};V.Z.9S=h(9R,7W){q c.N(7W[0])};M.3Z=h(4r,4Z){p 40="/"+4r+"/"+(4Z||"");q M.3Z[40]||(M.3Z[40]=R M(4r,4Z))};M.3M=h(A){q A.D(/[-[\\]{}()*+?.\\\\^$|,#\\s]/g,"\\\\$&")};M.6E=h(A,E,W,1k,2M){p 2M=2M||{},2U=2M.5W,X=2M.af,1k=1k||"",4O=1k.1h("g")>-1,6y=1k.1h("i")>-1,5U=1k.1h("m")>-1,4H=1k.1h("y")>-1,1k=1k.D(/y/g,""),E=E 4T V?(E.3H?E:E.3I("g")):R M(E,"g"+1k),W=W 4T V?(W.3H?W:W.3I("g")):R M(W,"g"+1k),1K=[],2r=0,1m=0,1l=0,1x=0,27,1Z,1w,1B,3Q,4n;f(2U){f(2U.v>1){3G ae("56\'t 58 ag ad 5X 3M 6I")}f(5U){3G 62("56\'t 58 3M 6I 6G a8 a9 6W aP")}3Q=M.3M(2U);4n=R V("^(?:"+3Q+"[\\\\S\\\\s]|(?:(?!"+E.1i+"|"+W.1i+")[^"+3Q+"])+)+",6y?"i":"")}28(U){E.L=W.L=1l+(2U?(4n.N(A.19(1l))||[""])[0].v:0);1w=E.N(A);1B=W.N(A);f(1w&&1B){f(1w.H<=1B.H){1B=u}F{1w=u}}f(1w||1B){1m=(1w||1B).H;1l=(1w?E:W).L}F{f(!2r){2B}}f(4H&&!2r&&1m>1x){2B}f(1w){f(!2r++){27=1m;1Z=1l}}F{f(1B&&2r){f(!--2r){f(X){f(X[0]&&27>1x){1K.K([X[0],A.19(1x,27),1x,27])}f(X[1]){1K.K([X[1],A.19(27,1Z),27,1Z])}f(X[2]){1K.K([X[2],A.19(1Z,1m),1Z,1m])}f(X[3]){1K.K([X[3],A.19(1m,1l),1m,1l])}}F{1K.K(A.19(1Z,1m))}1x=1l;f(!4O){2B}}}F{E.L=W.L=0;3G b6("bw bp 8l 8u 8A")}}f(1m===1l){1l++}}f(4O&&!4H&&X&&X[0]&&A.v>1x){1K.K([X[0],A.19(1x),1x,A.v])}E.L=W.L=0;q 1K};',62,725,"||||||||||||this|||if||function||||sh||||var|return||||null|length||||div|str|utils|for|replace|left|else|_109|index|code|document|push|lastIndex|XRegExp|exec|false||config|new||getParam|true|RegExp|_121|vN|className|prototype||part|css|_107|_c3|create||right|pos|slice|_111|strings|name|_101|class|style|_ed|indexOf|source|id|_122|_12d|_12c|_ca|window|vars|toolbar|innerHTML|_e0|createElement|alert|_x|_131|_12e|_ec|appendChild|obj|_132|lt|_e5|_c6|tab|lib|_bd|highlight|doc|_12a|params|substr|width|nbsp|size|_97|test|_4b|span|_102|continue|value|in|matches|_130|_91|split|_8|_57|min|_3c|lines|_12f|while|_56|font|wnd|text|bar|_d7|toString|_6e|execute|_75|html|_5b|_b7|gt|script|captureNames|attributes|href|_12b|_b4|_b1|height|_f8|_e1|call|http|_f4|_28|break|_da|getMatches|line|_b3|_a1|concat|_51|case|m2|m1|_123|_5|_80|_88|_81|body|_40|_62|_124|_d4|unindent|_c9|_b5|_7a|brushes|SyntaxHighlighter|_2|_22|_f2|_32|title|DIV|_3|offsetMatches|_7d|_7b|_8e|fixForBlogger|url|_7c|_5e|_65|_6a|_5c|_76|_4a|_af|_e9|printFrame|_f0|spaceWidth|_ea|_dc|write|print|_d5|_db|_25|_a6|Math|undefined|_10d|_106|regex|_10c|extended|throw|global|addFlags|_f6|htmlScript|_100|escape|cc|len|trim|_133|quantifier|_9d|_116|getNativeFlags|_9c|_a2|_a5|viewSource|cache|key|_89|eachLine|_2c|HtmlScript|_18|_30|gi|_2f|_ad|com|_105|findBrush|_c0|expandSource|_be|_bc|join|Match|_b9|_c7|_ab|func|esc|_b6|_19|_1a|_11b|_ac|swf|_4c|_9|_b|_49|popup|top|focus|regexList|_50|_4f|_48|_e|_7|_129|_38|contentWindow|link|_4|gm|close|_126|_blank|win|_35|_24|instanceof|_73|_6b|_82|xmlBrush|_17|_11c||removeChild|_7e|color|values|_5d|can|_f|supply|discoveredBrushes|_59|_6c|_10|_37|_d|_8c|r2|content|toolbarItemWidth|highlighters|toolbarItemHeight|copyToClipboard|guid|margin|_2e|_2d|String|originalCode|_2a|collapsed|merge|collapse|_e2|gutter|measureSpace|isNaN|_2b|Array|_29|clipboardSwf|toolbarCommands|ruler|_d2|px|_fb|_d0|_e7|pre|_27|_e6|charAt|highlighterId|_83|_ee|defaults|_14|_128|_f5|escapeChar|one|_5a|noBrush|_61|navigator|TypeError|plain|match|pP|_78|_79|_36|_6d|alexgorbatchev|userAgent|commandName|_66|clipboardData|executeCommand|event|help|auto|_6|createDisplayLines|500px|0px|scrollbars|processUrls|links|stylesheet|rel|_3f|copyStyles|removeNestedMatches|findMatches|processMatches|createRuler|_127|type|_3e|aboutDialog|createButton|attachEvent|matchRecursive|message|when|2009|character|light|clipboard|syntaxhighlighter|_4e|screen|_a|processSmartTabs|processTabs|_47|tabs|smart|trimFirstAndLastLines|switch|multiline|copyToClipboardConfirmation|_85|unicode|head|parentNode|_16|first|shockwave|_ba|_b8|items|addEvent|flash|_13|arguments|_aa|parseParams|_dd|padNumber|_b0|Times|_12|debug|_ff|parseInt|isMatchNested|_cf|org|_c8|classRight|_104|_103|_cd|_cc|_ce|toBoolean|_c5|_d1|brushNotHtmlScript|www|matchesSortCallback|get|_d6|w3|process|_c1|classLeft|tagName|brush|_fd|serif|getElementsByTagName|_fa|_15|bloggerMode|_115|_84|_99|family|_93|block|args|insertSpaces|_fc|_90|decorate|_a3|_e3|_e4|version|center|xhtml1|Highlighter|toArray|_de|regexLib|DTD|_a7|_a0|defaultAdd|_a8|_1e|object|align|try|resizable|contains|catch|400|_20|_1f|onclick|750|menubar|_21|unbalanced|_1b|item|location|_26|param|delimiters|_1c|sans|1999|xhtml|meta|xmlns|dtd|EN|TR|transitional|equiv|Content|Georgia|New|Roman|About|utf|Type|charset|Transitional|XHTML|is|your|now|The|to|expand|view|copy|Can|find|DOCTYPE|PUBLIC|W3C|option|configured|Brush|wasn|background|fff|Gorbatchev|multiLineCComments|singleLineCComments|Alex|2004|syntax|highlighter|Copyright|singleLinePerlComments|doubleQuotedString|aspScriptTags|scriptScriptTags|typeof|phpScriptTags|multiLineSingleQuotedString|singleQuotedString|multiLineDoubleQuotedString|JavaScript|none|Helvetica|xx|large|Arial|Geneva|000|1em|3em|75em|bottom|target|0099FF|decoration|01|March|4em|296|string|open|_119|apply|_8a|offsetWidth|getComputedStyle|getPropertyValue|_98|1000|ignoreCase|textarea|sticky|_a4|_117|opera|getElementById|on|addEventListener|using|the|random|1000000|aliases|than|SyntaxError|valueNames|more|middot|_clipboard|70em|addPlugin|replaceVar|_ef|flags|constructing|from|important|highlighter_|max|forHtmlScript|getKeywords|fontSize|sgi|nogutter|another|spaces|Xml|_bf|load|all|30em|replaceChild|sx|_c4|highlighted|number|alt|sort|lastIndexOf|round|150|flag|msie|src|embed|classid|movie|_33|_34|ok|error|setData|amp|command|cab|swflash|96b8|11cf|Error|444553540000|codebase|ae6d|download|pub|cabs|clsid|macromedia|d27cdb6e|_39|printSource|shCore|about|toLowerCase|allowScriptAccess|always|printing|_42|data|500||250|_44|application|_43|subject|wmode|_3a|flashVars|transparent|_3b|cssText|absolute|position|menu|IFRAME".split("|"),0,{}));SyntaxHighlighter.brushes.JScript=function(){var a="abstract boolean break byte case catch char class const continue debugger default delete do double else enum export extends false final finally float for function goto if implements import in instanceof int interface long native new null package private protected public return short static super switch synchronized this throw throws transient true try typeof var void volatile while with";this.regexList=[{regex:SyntaxHighlighter.regexLib.singleLineCComments,css:"comments"},{regex:SyntaxHighlighter.regexLib.multiLineCComments,css:"comments"},{regex:SyntaxHighlighter.regexLib.doubleQuotedString,css:"string"},{regex:SyntaxHighlighter.regexLib.singleQuotedString,css:"string"},{regex:/\s*#.*/gm,css:"preprocessor"},{regex:new RegExp(this.getKeywords(a),"gm"),css:"keyword"}];this.forHtmlScript(SyntaxHighlighter.regexLib.scriptScriptTags)};SyntaxHighlighter.brushes.JScript.prototype=new SyntaxHighlighter.Highlighter;SyntaxHighlighter.brushes.JScript.aliases=["js","jscript","javascript"];SyntaxHighlighter.brushes.Xml=function(){function a(b){var e=SyntaxHighlighter.Match,f=b[0],d=(new XRegExp("(&lt;|<)[\\s\\/\\?]*(?<name>[:\\w-\\.]+)","xg")).exec(f),c=[];if(b.attributes!=null){var a,g=new XRegExp("(?<name> [\\w:\\-\\.]+)\\s*=\\s*(?<value> \".*?\"|'.*?'|\\w+)","xg");while((a=g.exec(f))!=null){c.push(new e(a.name,b.index+a.index,"color1"));c.push(new e(a.value,b.index+a.index+a[0].indexOf(a.value),"string"))}}d!=null&&c.push(new e(d.name,b.index+d[0].indexOf(d.name),"keyword"));return c}this.regexList=[{regex:new XRegExp("(\\&lt;|<)\\!\\[[\\w\\s]*?\\[(.|\\s)*?\\]\\](\\&gt;|>)","gm"),css:"color2"},{regex:new XRegExp("(\\&lt;|<)!--\\s*.*?\\s*--(\\&gt;|>)","gm"),css:"comments"},{regex:new XRegExp("(&lt;|<)[\\s\\/\\?]*(\\w+)(?<attributes>.*?)[\\s\\/\\?]*(&gt;|>)","sg"),func:a}]};SyntaxHighlighter.brushes.Xml.prototype=new SyntaxHighlighter.Highlighter;SyntaxHighlighter.brushes.Xml.aliases=["xml","xhtml","xslt","html","xhtml"];SyntaxHighlighter.config.bloggerMode=true;SyntaxHighlighter.config.clipboardSwf="http://alexgorbatchev.com/pub/sh/2.0.296/scripts/clipboard.swf";SyntaxHighlighter.defaults.toolbar=false;SyntaxHighlighter.defaults.smartTabs=true;SyntaxHighlighter.defaults.gutter=false;SyntaxHighlighter.all()
\ No newline at end of file
diff --git a/demos/samplesCore/Interactive/resources/tabs.css b/demos/samplesCore/Interactive/resources/tabs.css
index fb43ba9..7a84704 100644
--- a/demos/samplesCore/Interactive/resources/tabs.css
+++ b/demos/samplesCore/Interactive/resources/tabs.css
@@ -1,4 +1,4 @@
-body 
+body
 {
 	font-family: Arial;
 }
@@ -16,7 +16,7 @@
 	height: 200px;
 }
 
-.tabsView .body div 
+.tabsView .body div
 {
 	padding: 15px;
 	height: 60px;
diff --git a/demos/samplesCore/Interactive/resources/treeView.css b/demos/samplesCore/Interactive/resources/treeView.css
index 7d329b6..2ffabc6 100644
--- a/demos/samplesCore/Interactive/resources/treeView.css
+++ b/demos/samplesCore/Interactive/resources/treeView.css
@@ -22,13 +22,13 @@
 	margin-right:7px;
 }
 
-img.folder 
+img.folder
 {
 	width:15px;
 	height:14px;
 }
 
-img.expand 
+img.expand
 {
 	width:8px;
 	width:9px;
diff --git a/demos/samplesCore/Interactive/tabsTmpl.html b/demos/samplesCore/Interactive/tabsTmpl.html
index d433a3e..5a0337f 100644
--- a/demos/samplesCore/Interactive/tabsTmpl.html
+++ b/demos/samplesCore/Interactive/tabsTmpl.html
@@ -42,13 +42,13 @@ A tabs control against data, using {{tmpl}}
 			items: [
 				{ name: "Inline", title: "Template inline in a script block",
 					description:"Rendering a <span class='special'>template</span> declared in script block" },
-				{ name: "String", title: "Template as string", 
+				{ name: "String", title: "Template as string",
 					description:"Rendering a <span class='special'>template</span> passed as a string" },
-				{ name: "Remote", title: "Render remote data", 
+				{ name: "Remote", title: "Render remote data",
 					description:"Rendering remote data using <span class='special'>templates</span>" },
-				{ name: "TreeView", title: "Building a Tree View", 
+				{ name: "TreeView", title: "Building a Tree View",
 					description:"A tree view using recursive nested <span class='special'>templates</span>" },
-				{ name: "Tabs", title: "A Tabs control", 
+				{ name: "Tabs", title: "A Tabs control",
 					description:"Create a tabs control using <span class='special'>templates</span>" }
 			]
 		},
diff --git a/demos/samplesCore/Interactive/tabsWrapNested.html b/demos/samplesCore/Interactive/tabsWrapNested.html
index 453f60e..3553db6 100644
--- a/demos/samplesCore/Interactive/tabsWrapNested.html
+++ b/demos/samplesCore/Interactive/tabsWrapNested.html
@@ -13,7 +13,7 @@ Nested tabs controls against HTML markup, using {{wrap}}
 
 <div id="tabsView">..loading</div>
 
-<script src="http://code.jquery.com/jquery.js"></script>
+<script src="http://code.jquery.com/jquery.js" type="text/javascript"></script>
 <script src="../../../jquery.tmpl.js" type="text/javascript"></script>
 
 <script id="tabsTmpl" type="text/x-jquery-tmpl">
diff --git a/demos/samplesCore/Interactive/treeView.html b/demos/samplesCore/Interactive/treeView.html
index ea2f474..e34caa6 100644
--- a/demos/samplesCore/Interactive/treeView.html
+++ b/demos/samplesCore/Interactive/treeView.html
@@ -35,7 +35,7 @@ A tree view control using recursive templates
 </script>
 
 <script type="text/javascript">
-	
+
 	// ******************** Data for folders hierarchy, and samples ********************
 
 	var folders = {
@@ -56,17 +56,17 @@ A tree view control using recursive templates
 	};
 
 	var samples = [
-		{ name: "Template in script block", 
-			folders: [ ".tmpl" ], 
+		{ name: "Template in script block",
+			folders: [ ".tmpl" ],
 			description: "Rendering a template declared in script block" },
-		{ name: "Template as string", 
-			folders: [ ".tmpl" ], 
+		{ name: "Template as string",
+			folders: [ ".tmpl" ],
 			description:"Rendering a template passed as a string" },
-		{ name: "Render remote data", 
-			folders: [ "API" ], 
+		{ name: "Render remote data",
+			folders: [ "API" ],
 			description: "Rendering remote data using templates" },
-		{ name: "Tree View", 
-			folders: [ "tmpl", ".tmpl" ], 
+		{ name: "Tree View",
+			folders: [ "tmpl", ".tmpl" ],
 			description: "A tree view using recursive nested templates" }
 	];
 
@@ -74,7 +74,7 @@ A tree view control using recursive templates
 
 	$( "#samplesList" ).empty();
 	$( "#folderTmpl" ).tmpl( folders ).appendTo( "#samplesList" );
-	
+
 	// ******************** Events ********************
 
 	$( "#samplesList" )
@@ -95,16 +95,16 @@ A tree view control using recursive templates
 		});
 
 	// ******************** Helper functions ********************
-	
+
 	function getSamples( folderTmplItem ) {
 		return $.map( samples, function( sample ) {
 			return $.inArray( folderTmplItem.data.name, sample.folders ) > -1 ? sample : null;
 		});
-	} 
+	}
 
 	function getFolders( folderTmplItem ) {
 		return folderTmplItem.data.folders || [];
-	} 
+	}
 
 	function expanderImage() {
 		if ( hasContent( this ) ) {
diff --git a/demos/samplesCore/basic.html b/demos/samplesCore/basic.html
index 5226caa..9fe2502 100644
--- a/demos/samplesCore/basic.html
+++ b/demos/samplesCore/basic.html
@@ -1,15 +1,20 @@
+<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
 <!--
 This sample illustrates some basic templating features and scenarios.
 -->
+<html xmlns="http://www.w3.org/1999/xhtml">
+<head>
 <style type="text/css">
 	.clickable 	{
 		cursor:pointer;
 		color: Blue;
 	}
 </style>
-<script src="http://code.jquery.com/jquery.js"></script>
+</head>
+<body>
+<script src="http://code.jquery.com/jquery.js" type="text/javascript"></script>
 <script src="../../jquery.tmpl.js" type="text/javascript"></script>
-<script>
+<script type="text/javascript">
 
 var dataObject = {
 	firstName: "John",
@@ -21,9 +26,9 @@ var dataObject = {
 	]
 };
 
-var arrayOfDataObjects = [ 
+var arrayOfDataObjects = [
 	dataObject
-	, 
+	,
 	{
 		firstName: "Dave",
 		lastName: "Reed",
@@ -69,7 +74,7 @@ jQuery(function(){
 	// Store a string as a compiled template for later use
 	$.template( "myTmpl", '<li>My template: <a href="${url}">${getName()}</a> {{if $item.showCities}}(${cityJoin()}){{/if}}</li>' );
 
-	// Render stored template and insert after target. 
+	// Render stored template and insert after target.
 	// Target wrapped set has more than one element, so rendered template cloned into two places in DOM
 	$.tmpl( "myTmpl", dataObject )
 		.insertAfter( ".multiple" );
@@ -99,7 +104,7 @@ jQuery(function(){
 		.find( "span" )
 		.addClass( "clickable" )
 		.click( function() {
-			var item = $.tmplItem(this); 
+			var item = $.tmplItem(this);
 			alert( item.data.firstName + " at " + item.data.url );
 		});
 
@@ -130,4 +135,7 @@ jQuery(function(){
 
 <ul><li class="multiple">first</li><li class="multiple">last</li></ul>
 
-<div class="target">Target</div>
+<div class="target"></div>
+
+</body>
+</html>
\ No newline at end of file
diff --git a/demos/samplesCore/composition.html b/demos/samplesCore/composition.html
index 5e29750..6ac88ef 100644
--- a/demos/samplesCore/composition.html
+++ b/demos/samplesCore/composition.html
@@ -1,6 +1,9 @@
+<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
 <!--
 This sample illustrates template composition, including passing parameters to a {{tmpl}} tag.
 -->
+<html xmlns="http://www.w3.org/1999/xhtml">
+<head>
 <style type="text/css">
 	.peopleTable td { border:2px solid #555; text-align:center; }
 	.person{ background-color:#AFA; }
@@ -9,10 +12,12 @@ This sample illustrates template composition, including passing parameters to a
 	.separator { background-color:#C77; height:6px;}
 	.peopleTable { border-collapse:collapse; border:2px solid #555; }
 </style>
+</head>
+<body>
 
-<script src="http://code.jquery.com/jquery.js"></script>
+<script src="http://code.jquery.com/jquery.js" type="text/javascript"></script>
 <script src="../../jquery.tmpl.js" type="text/javascript"></script>
-<script>
+<script type="text/javascript">
 var people = [
 	{
 		firstName: "John",
@@ -43,7 +48,7 @@ var people = [
 function getName() {
 	return this.data.firstName + " " + this.data.lastName;
 }
-		 
+
 function alternate( item, array ) {
 	return ($.inArray( item, array ) % 2) ? "personAlt" : "person";
 }
@@ -59,7 +64,7 @@ function getTemplate( key ) {
 $(function(){
 	// Create named template, to be used in composition below
 	$.template( "citySeparator", '<tr class="citySeparator"><td colspan="2"></td></tr>' );
-	
+
 	$( "#tmplPeople" )
 		.tmpl( people )
 		.appendTo( ".peopleTable" );
@@ -89,3 +94,5 @@ $(function(){
 
 <table class="peopleTable"><tbody></tbody></table>
 
+</body>
+</html>
\ No newline at end of file
diff --git a/demos/samplesCore/conditional.html b/demos/samplesCore/conditional.html
index 924c13f..ad93a93 100644
--- a/demos/samplesCore/conditional.html
+++ b/demos/samplesCore/conditional.html
@@ -1,11 +1,13 @@
-<!--
+<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
+<!--
 This sample illustrates use of conditional template tags {{if}} and {{else}}.
 The comment tag {{! }} is also shown.
 -->
-
-<script src="http://code.jquery.com/jquery.js"></script>
+<html xmlns="http://www.w3.org/1999/xhtml">
+<body>
+<script src="http://code.jquery.com/jquery.js" type="text/javascript"></script>
 <script src="../../jquery.tmpl.js" type="text/javascript"></script>
-<script>
+<script type="text/javascript">
 $(function(){
 	$( "#movieTemplate" )
 	.tmpl( movies )
@@ -20,12 +22,12 @@ var movies = [
 ];
 </script>
 
-<script id="movieTemplate" type="text/x-jquery-tmpl"> 
+<script id="movieTemplate" type="text/x-jquery-tmpl">
 <li>
 	Title: ${Name}.
 	{{if Languages}}
 		(Alternative languages: ${Languages}).
-	{{else Subtitles}} 
+	{{else Subtitles}}
 		(Original language only. Subtitles in ${Subtitles}).
 	{{else}}
 		(Original version only, without subtitles).
@@ -34,3 +36,6 @@ var movies = [
 </script>
 
 <ul id="movieList"></ul>
+
+</body>
+</html>
diff --git a/demos/samplesCore/each.html b/demos/samplesCore/each.html
index c535fe2..441b54c 100644
--- a/demos/samplesCore/each.html
+++ b/demos/samplesCore/each.html
@@ -1,7 +1,10 @@
+<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
 <!--
-This sample illustrates using the {{each}} template tag, 
+This sample illustrates using the {{each}} template tag,
 for iterative rendering of nested markup within a template.
 -->
+<html xmlns="http://www.w3.org/1999/xhtml">
+<head>
 <style type="text/css">
 	.peopleTable td { border:2px solid #555; text-align:center; }
 	.person{ background-color:#AFA; }
@@ -10,10 +13,11 @@ for iterative rendering of nested markup within a template.
 	.cityseparator { background-color:#CCC; height:4px;}
 	.peopleTable { border-collapse:collapse; border:2px solid #555; }
 </style>
-
-<script src="http://code.jquery.com/jquery.js"></script>
+</head>
+<body>
+<script src="http://code.jquery.com/jquery.js" type="text/javascript"></script>
 <script src="../../jquery.tmpl.js" type="text/javascript"></script>
-<script>
+<script type="text/javascript">
 $(function(){
 	$("#tmplPeople")
 		.tmpl( people )
@@ -77,3 +81,5 @@ function alternate( item, array ) {
 	<tr class="separator"><td colspan="2"></td></tr>
 </tbody></table>
 
+</body>
+</html>
\ No newline at end of file
diff --git a/demos/samplesCore/parameters.html b/demos/samplesCore/parameters.html
index 44bafe5..8ad8fcb 100644
--- a/demos/samplesCore/parameters.html
+++ b/demos/samplesCore/parameters.html
@@ -1,6 +1,9 @@
+<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
 <!--
 This sample illustrates using the {{if}}, {{each}} and {{tmpl}} template tags, with parameters.
 -->
+<html xmlns="http://www.w3.org/1999/xhtml">
+<head>
 <style type="text/css">
 	.peopleTable td { border:2px solid #555; text-align:center; }
 	.person{ background-color:#AFA; }
@@ -9,13 +12,14 @@ This sample illustrates using the {{if}}, {{each}} and {{tmpl}} template tags, w
 	.cityseparator { background-color:#CCC; height:4px;}
 	.peopleTable { border-collapse:collapse; border:2px solid #555; }
 </style>
-
-<script src="http://code.jquery.com/jquery.js"></script>
+</head>
+<body>
+<script src="http://code.jquery.com/jquery.js" type="text/javascript"></script>
 <script src="../../jquery.tmpl.js" type="text/javascript"></script>
-<script>
+<script type="text/javascript">
 $(function(){
 	$("#tmplPeople")
-		.tmpl( people, {allCities: allCities} )
+		.tmpl( people )
 		.appendTo(".peopleTable");
 });
 
@@ -96,3 +100,5 @@ var startIndex = 1, endIndex = 3;
 	<tr class="separator"><td colspan="3"></td></tr>
 </tbody></table>
 
+</body>
+</html>
\ No newline at end of file
diff --git a/demos/samplesTmplPlus/basic.html b/demos/samplesTmplPlus/basic.html
index aac1b28..c9ff6ba 100644
--- a/demos/samplesTmplPlus/basic.html
+++ b/demos/samplesTmplPlus/basic.html
@@ -1,10 +1,18 @@
+<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
 <!--
-This sample illustrates some basic templating features and scenarios.
+This sample is equivalent to the samplesCore/basic.html sample,
+except that it uses jquery.tmplPlus.js in order to take advantage
+of the alternative API:
+    $( targetContainer ).append( template, data, options );
+rather than
+    $( template ).tmpl( data, options ).appendTo( targetContainer );
 -->
-<script src="http://code.jquery.com/jquery.js"></script>
+<html xmlns="http://www.w3.org/1999/xhtml">
+<body>
+<script src="http://code.jquery.com/jquery.js" type="text/javascript"></script>
 <script src="../../jquery.tmpl.js" type="text/javascript" ></script>
 <script src="../../jquery.tmplPlus.js" type="text/javascript"></script>
-<script>
+<script type="text/javascript">
 
 var dataObject = {
 	firstName: "John",
@@ -16,9 +24,9 @@ var dataObject = {
 	]
 };
 
-var arrayOfDataObjects = [ 
+var arrayOfDataObjects = [
 	dataObject
-	, 
+	,
 	{
 		firstName: "Dave",
 		lastName: "Reed",
@@ -56,14 +64,14 @@ function index( array ) {
 jQuery(function(){
 	// A template string
 	var tmpl = '<li><a href="${url}">${getName()}</a> {{if $item.showCities}}(${cityJoin()}){{/if}}</li>';
-		
+
 	// Renders one LI, filled with data, then appends it into the UL
 	$("ul").append( tmpl, dataObject, null );
 
 	// Store a string as a compiled template for later use
 	$.template( "myTmpl", '<li>My template: <a href="${url}">${getName()}</a> {{if $item.showCities}}(${cityJoin()}){{/if}}</li>' );
-	
-	// Render stored template and insert after target. 
+
+	// Render stored template and insert after target.
 	// Target wrapped set has more than one element, so rendered template cloned into two places in DOM
 	$(".multiple").after( "myTmpl", dataObject );
 
@@ -93,3 +101,5 @@ jQuery(function(){
 
 <div id="target">Target</div>
 
+</body>
+</html>
\ No newline at end of file
diff --git a/demos/samplesTmplPlus/composition.html b/demos/samplesTmplPlus/composition.html
index e316d63..34a1a3a 100644
--- a/demos/samplesTmplPlus/composition.html
+++ b/demos/samplesTmplPlus/composition.html
@@ -1,6 +1,14 @@
+<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
 <!--
-This sample illustrates template composition, including passing parameters to a {{tmpl}} tag.
+This sample is equivalent to the samplesCore/composition.html sample,
+except that it uses jquery.tmplPlus.js in order to take advantage
+of the alternative API:
+    $( targetContainer ).append( template, data, options );
+rather than
+    $( template ).tmpl( data, options ).appendTo( targetContainer );
 -->
+<html xmlns="http://www.w3.org/1999/xhtml">
+<head>
 <style type="text/css">
 	.peopleTable td { border:2px solid #555; text-align:center; }
 	.person{ background-color:#AFA; }
@@ -9,11 +17,13 @@ This sample illustrates template composition, including passing parameters to a
 	.separator { background-color:#C77; height:6px;}
 	.peopleTable { border-collapse:collapse; border:2px solid #555; }
 </style>
+</head>
+<body>
 
-<script src="http://code.jquery.com/jquery.js"></script>
+<script src="http://code.jquery.com/jquery.js" type="text/javascript"></script>
 <script src="../../jquery.tmpl.js" type="text/javascript"></script>
 <script src="../../jquery.tmplPlus.js" type="text/javascript"></script>
-<script>
+<script type="text/javascript">
 var people = [
 	{
 		firstName: "John",
@@ -44,7 +54,7 @@ var people = [
 function getName() {
 	return this.data.firstName + " " + this.data.lastName;
 }
-		 
+
 function alternate( item, array ) {
 	return ($.inArray( item, array ) % 2) ? "personAlt" : "person";
 }
@@ -89,3 +99,5 @@ $(function(){
 
 <table class="peopleTable"><tbody></tbody></table>
 
+</body>
+</html>
\ No newline at end of file
diff --git a/demos/samplesTmplPlus/each.html b/demos/samplesTmplPlus/each.html
deleted file mode 100644
index 6eedfd4..0000000
--- a/demos/samplesTmplPlus/each.html
+++ /dev/null
@@ -1,80 +0,0 @@
-<!--
-This sample illustrates using the {{each}} template tag, 
-for iterative rendering of nested markup within a template.
--->
-<style type="text/css">
-	.peopleTable td { border:2px solid #555; text-align:center; }
-	.person{ background-color:#AFA; }
-	.personAlt{ background-color:#9ED; }
-	.separator { background-color:#C77; height:6px;}
-	.cityseparator { background-color:#CCC; height:4px;}
-	.peopleTable { border-collapse:collapse; border:2px solid #555; }
-</style>
-
-<script src="http://code.jquery.com/jquery.js"></script>
-<script src="../../jquery.tmpl.js" type="text/javascript"></script>
-<script src="../../jquery.tmplPlus.js" type="text/javascript"></script>
-<script>
-$(function(){
-	$("#tmplPeople")
-		.tmpl( people )
-		.appendTo(".peopleTable");
-});
-
-var people = [
-	{
-		firstName: "John",
-		lastName: "Resig",
-		url: "http://ejohn.org/",
-		cities: [
-			{ name: "Boston", state: "MA" },
-			{ name: "San Francisco", state: "CA" }
-		]
-	},
-	{
-		firstName: "Dave",
-		lastName: "Reed",
-		url: "http://dave.org/",
-		cities: [
-			{ name: "Seattle", state: "WA" },
-			{ name: "Los Angeles", state: "CA" },
-			{ name: "New York", state: "NY" }
-		]
-	},
-	{
-		firstName: "Boris",
-		lastName: "Moore",
-		url: "http://boris.org/",
-		cities: [
-			{ name: "Redmond", state: "WA" }
-		]
-	}
-];
-
-function index( item, array ) {
-	return $.inArray( item, array ) + 1;
-}
-
-function getName() {
-	return this.data.firstName + " " + this.data.lastName;
-}
-
-function alternate( item, array ) {
-	return ($.inArray( item, array ) % 2) ? "personAlt" : "person";
-}
-</script>
-
-<script id="tmplPeople" type="text/x-jquery-tmpl">
-	<tr class="${alternate($data, people)}"><td colspan="2"><a href="${url}">${getName()}</a></td></tr>
-	{{each cities}}
-		<tr class="cityseparator"><td colspan="2"></td></tr>
-		<tr class="${alternate($data, people)}"><td colspan="2"><b><i>City ${index($value, cities)}:</i></b></td></tr>
-		<tr class="${alternate($data, people)}"><td><b>${name}</b></td><td>${state}</td></tr>
-	{{/each}}
-	<tr class="separator"><td colspan="2"></td></tr>
-</script>
-
-<table class="peopleTable"><tbody>
-	<tr class="separator"><td colspan="2"></td></tr>
-</tbody></table>
-
diff --git a/demos/samplesTmplPlus/parameters.html b/demos/samplesTmplPlus/parameters.html
index 8380561..13fa912 100644
--- a/demos/samplesTmplPlus/parameters.html
+++ b/demos/samplesTmplPlus/parameters.html
@@ -1,6 +1,14 @@
+<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
 <!--
-This sample illustrates using the {{if}}, {{each}} and {{tmpl}} template tags, with parameters.
+This sample is equivalent to the samplesCore/parameters.html sample,
+except that it uses jquery.tmplPlus.js in order to take advantage
+of the alternative API:
+    $( targetContainer ).append( template, data, options );
+rather than
+    $( template ).tmpl( data, options ).appendTo( targetContainer );
 -->
+<html xmlns="http://www.w3.org/1999/xhtml">
+<head>
 <style type="text/css">
 	.peopleTable td { border:2px solid #555; text-align:center; }
 	.person{ background-color:#AFA; }
@@ -9,11 +17,13 @@ This sample illustrates using the {{if}}, {{each}} and {{tmpl}} template tags, w
 	.cityseparator { background-color:#CCC; height:4px;}
 	.peopleTable { border-collapse:collapse; border:2px solid #555; }
 </style>
+</head>
+<body>
 
-<script src="http://code.jquery.com/jquery.js"></script>
+<script src="http://code.jquery.com/jquery.js" type="text/javascript"></script>
 <script src="../../jquery.tmpl.js" type="text/javascript"></script>
 <script src="../../jquery.tmplPlus.js" type="text/javascript"></script>
-<script>
+<script type="text/javascript">
 $(function(){
 	$(".peopleTable")
 		.append("#tmplPeople", people, {allCities: allCities} );
@@ -96,3 +106,5 @@ var startIndex = 1, endIndex = 3;
 	<tr class="separator"><td colspan="3"></td></tr>
 </tbody></table>
 
+</body>
+</html>
\ No newline at end of file
diff --git a/demos/step-by-step.html b/demos/step-by-step.html
new file mode 100644
index 0000000..3fc5b58
--- /dev/null
+++ b/demos/step-by-step.html
@@ -0,0 +1,33 @@
+<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
+<html>
+<head>
+<link href="resources/demos.css" rel="stylesheet" type="text/css" />
+</head>
+<body>
+<h2>jQuery Templates: Step by Step Examples</h2>
+<h3>App Example</h3>
+	<a href="movies/PagesCore/movies.html">Movies</a><br />
+
+<h3>Read-only rendering</h3>
+	<div class="subhead">API: $( "#template" ).tmpl( data ).appendTo( "#list");</div>
+	<a href="step-by-step/0_tmpl-read-only/0_local-data.html">Local data</a><br />
+	<a href="step-by-step/0_tmpl-read-only/1_remote-data.html">Remote data</a><br />
+
+<div class="subhead">Tags - Content</div>
+	<a href="step-by-step/0_tmpl-read-only/2_html-tag.html">${} and {{html}}</a><br />
+	<a href="step-by-step/0_tmpl-read-only/3_if-else-tag.html">{{if}} and {{else}}</a><br />
+	<a href="step-by-step/0_tmpl-read-only/4_each-tag.html">{{each}}</a><br />
+	<a href="step-by-step/0_tmpl-read-only/5_javascript.html">javascript demo</a><br />
+
+<div class="subhead">Tags - Composition</div>
+
+	<a href="step-by-step/0_tmpl-read-only/6_hierarchical-data.html">Hierarchical data - {{tmpl}}</a><br />
+
+<h3>Interactivity</h3>
+<div class="subhead">Navigating through data</div>
+	<a href="step-by-step/1_tmpl-interactive/0_accordion-switching-template.html">Switch templates: Accordion</a><br />
+	<a href="step-by-step/1_tmpl-interactive/1_tree-view-using-tmpl-tag.html">Recursive Tree View - {{tmpl}}</a><br />
+	<a href="step-by-step/1_tmpl-interactive/2_tabs-using-wrap-tag.html">Tabs View - {{wrap}}</a><br />
+
+</body>
+</html>
diff --git a/demos/step-by-step/0_tmpl-read-only/0_local-data-source.html b/demos/step-by-step/0_tmpl-read-only/0_local-data-source.html
new file mode 100644
index 0000000..a0ecb25
--- /dev/null
+++ b/demos/step-by-step/0_tmpl-read-only/0_local-data-source.html
@@ -0,0 +1,63 @@
+<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
+<html>
+<head>
+	<script src="http://code.jquery.com/jquery.js" type="text/javascript"></script>
+	<script src="../../../jquery.tmpl.js" type="text/javascript"></script>
+	<link href="../../resources/demos.css" rel="stylesheet" type="text/css" />
+
+	<link href="../../resources/syntaxhighlighter.css" rel="stylesheet" type="text/css" />
+	<script src="../../resources/syntaxhighlighter.min.js" type="text/javascript"></script>
+</head>
+<body>
+<a href="../../step-by-step.html">Home</a><br />
+<a href="0_local-data.html">Run</a>
+
+<h3>Demo: Render template against local data</h3>
+
+<!--=================== Demo Section ===================-->
+
+<script id="movieTemplate" type="text/x-jquery-tmpl"><li>
+	<b>${Name}</b> (${ReleaseYear})
+</li></script>
+
+<ul id="movieList"></ul>
+
+<script type="text/javascript">
+
+	var movies = [
+		{ Name: "The Red Violin", ReleaseYear: "1998" },
+		{ Name: "Eyes Wide Shut", ReleaseYear: "1999" },
+		{ Name: "The Inheritance", ReleaseYear: "1976" }
+	];
+
+	// Render the template with the movies data and insert
+	// the rendered HTML under the "movieList" element
+	$("#movieTemplate").tmpl(movies)
+	.appendTo("#movieList");
+
+</script>
+
+<!--================ End of Demo Section ================-->
+
+<h4>Data:</h4>
+<pre class="brush: js;">var movies = [
+	{ Name: "The Red Violin", ReleaseYear: "1998" },
+	{ Name: "Eyes Wide Shut", ReleaseYear: "1999" },
+	{ Name: "The Inheritance", ReleaseYear: "1976" }
+];</pre>
+
+<h4>HTML:</h4>
+<pre class="brush: xml;">&lt;script id="movieTemplate" type="text/x-jquery-tmpl">
+	&lt;li>
+		&lt;b>${Name}&lt;/b> (${ReleaseYear})
+	&lt;/li>
+&lt;/script>
+
+&lt;ul id="movieList">&lt;/ul></pre>
+
+<h4>Script:</h4>
+<pre class="brush: js;">$( "#movieTemplate" ).tmpl( movies )
+	.appendTo( "#movieList" );</pre>
+
+</body>
+</html>
diff --git a/demos/step-by-step/0_tmpl-read-only/0_local-data.html b/demos/step-by-step/0_tmpl-read-only/0_local-data.html
new file mode 100644
index 0000000..91bec2c
--- /dev/null
+++ b/demos/step-by-step/0_tmpl-read-only/0_local-data.html
@@ -0,0 +1,40 @@
+<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
+<html>
+<head>
+	<script src="http://code.jquery.com/jquery.js" type="text/javascript"></script>
+	<script src="../../../jquery.tmpl.js" type="text/javascript"></script>
+	<link href="../../resources/demos.css" rel="stylesheet" type="text/css" />
+</head>
+<body>
+<a href="../../step-by-step.html">Home</a><br />
+<a href="0_local-data-source.html">Source</a>
+
+<h3>Demo: Render template against local data</h3>
+
+<!--=================== Demo Section ===================-->
+
+<script id="movieTemplate" type="text/x-jquery-tmpl">
+	<li>
+		<b>${Name}</b> (${ReleaseYear})
+	</li>
+</script>
+
+<ul id="movieList"></ul>
+
+<script type="text/javascript">
+
+	var movies = [
+		{ Name: "The Red Violin", ReleaseYear: "1998" },
+		{ Name: "Eyes Wide Shut", ReleaseYear: "1999" },
+		{ Name: "The Inheritance", ReleaseYear: "1976" }
+	];
+
+	$( "#movieTemplate" ).tmpl( movies )
+		.appendTo( "#movieList" );
+
+</script>
+
+<!--================ End of Demo Section ================-->
+
+</body>
+</html>
diff --git a/demos/step-by-step/0_tmpl-read-only/1_remote-data-source.html b/demos/step-by-step/0_tmpl-read-only/1_remote-data-source.html
new file mode 100644
index 0000000..4222f05
--- /dev/null
+++ b/demos/step-by-step/0_tmpl-read-only/1_remote-data-source.html
@@ -0,0 +1,101 @@
+<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
+<html>
+<head>
+	<script src="http://code.jquery.com/jquery.js" type="text/javascript"></script>
+	<script src="../../../jquery.tmpl.js" type="text/javascript"></script>
+	<link href="../../resources/demos.css" rel="stylesheet" type="text/css" />
+
+	<link href="../../resources/syntaxhighlighter.css" rel="stylesheet" type="text/css" />
+	<script src="../../resources/syntaxhighlighter.min.js" type="text/javascript"></script>
+</head>
+<body>
+<a href="../../step-by-step.html">Home</a><br />
+<a href="1_remote-data.html">Run</a>
+
+<h3>Demo: Render template against remote data</h3>
+
+<!--=================== Demo Section ===================-->
+
+<script id="movieTemplate" type="text/x-jquery-tmpl"><li>
+	<b>${Name}</b> (${ReleaseYear})
+</li></script>
+
+<button id="cartoonsBtn">Cartoons</button>
+<button id="dramaBtn">Drama</button><br /><br />
+
+<div class="height">
+<div class="plusViewer edit">
+	<ul id="movieList"></ul>
+</div>
+</div>
+
+<script type="text/javascript">
+
+function getMovies( genre, skip, top ) {
+	$.ajax({
+
+		dataType: "jsonp",
+
+		url: "http://odata.netflix.com/Catalog/Genres('" + genre
+			+ "')/Titles?$format=json&$skip="
+			+ skip + "&$top=" + top,
+
+		jsonp: "$callback",
+
+		success: function( data ) {
+			var movies = data.d;				 // Get movies array from the data
+
+			$( "#movieList" ).empty();			 // Remove current set of movie template items
+
+			$( "#movieTemplate" ).tmpl( movies ) // Render template with movies data
+				.appendTo( "#movieList" );		 // and insert rendered HTML under "movieList" element
+		}
+	});
+}
+
+$( "#cartoonsBtn" ).click( function() {
+	getMovies( "Cartoons", 0, 6 );
+});
+
+$( "#dramaBtn" ).click( function() {
+	getMovies( "Drama", 0, 6 );
+});
+
+</script>
+
+<!--================ End of Demo Section ================-->
+
+<h4>HTML:</h4>
+
+<pre class="brush: xml;">&lt;script id="movieTemplate" type="text/x-jquery-tmpl">
+	&lt;li>
+		&lt;b>${Name}&lt;/b> (${ReleaseYear})
+	&lt;/li>
+&lt;/script>
+
+&lt;ul id="movieList">&lt;/ul></pre>
+
+<h4>Script:</h4>
+<pre class="brush: js;">$.ajax({
+
+	dataType: "jsonp",
+
+	url: "http://odata.netflix.com/Catalog/Genres('" + genre
+		+ "')/Titles?$format=json&$skip="
+		+ skip + "&$top=" + top,
+
+	jsonp: "$callback",
+
+	success: function( data ) {
+			var movies = data.d;				 // Get the movies array from the data
+
+			$( "#movieList" ).empty();			 // Remove current set of movie template items
+
+			$( "#movieTemplate" ).tmpl( movies ) // Render the template with the movies data
+				.appendTo( "#movieList" );		 // and insert the rendered HTML under the "movieList" element
+	}
+});
+</pre>
+</body>
+</html>
+
diff --git a/demos/step-by-step/0_tmpl-read-only/1_remote-data.html b/demos/step-by-step/0_tmpl-read-only/1_remote-data.html
new file mode 100644
index 0000000..4d36f93
--- /dev/null
+++ b/demos/step-by-step/0_tmpl-read-only/1_remote-data.html
@@ -0,0 +1,64 @@
+<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
+<html>
+<head>
+	<script src="http://code.jquery.com/jquery.js" type="text/javascript"></script>
+	<script src="../../../jquery.tmpl.js" type="text/javascript"></script>
+	<link href="../../resources/demos.css" rel="stylesheet" type="text/css" />
+</head>
+<body>
+<a href="../../step-by-step.html">Home</a><br />
+<a href="1_remote-data-source.html">Source</a>
+
+<h3>Demo: Render template against remote data</h3>
+
+<!--=================== Demo Section ===================-->
+
+<script id="movieTemplate" type="text/x-jquery-tmpl"><li>
+	<b>${Name}</b> (${ReleaseYear})
+</li></script>
+
+<button id="cartoonsBtn">Cartoons</button>
+<button id="dramaBtn">Drama</button>
+
+<div class="height">
+	<ul id="movieList"></ul>
+</div>
+
+<script type="text/javascript">
+
+function getMovies( genre, skip, top ) {
+	$.ajax({
+
+		dataType: "jsonp",
+
+		url: "http://odata.netflix.com/Catalog/Genres('" + genre
+			+ "')/Titles?$format=json&$skip="
+			+ skip + "&$top=" + top,
+
+		jsonp: "$callback",
+
+		success: function( data ) {
+			var movies = data.d;				 // Get the movies array from the data
+
+			$( "#movieList" ).empty();			 // Remove current set of movie template items
+
+			$( "#movieTemplate" ).tmpl( movies ) // Render the template with the movies data
+				.appendTo( "#movieList" );		 // and insert the rendered HTML under the "movieList" element
+		}
+	});
+}
+
+$( "#cartoonsBtn" ).click( function() {
+	getMovies( "Cartoons", 0, 6 );
+});
+
+$( "#dramaBtn" ).click( function() {
+	getMovies( "Drama", 0, 6 );
+});
+
+</script>
+
+<!--================ End of Demo Section ================-->
+</body>
+</html>
+
diff --git a/demos/step-by-step/0_tmpl-read-only/2_html-tag-source.html b/demos/step-by-step/0_tmpl-read-only/2_html-tag-source.html
new file mode 100644
index 0000000..338273a
--- /dev/null
+++ b/demos/step-by-step/0_tmpl-read-only/2_html-tag-source.html
@@ -0,0 +1,81 @@
+<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
+<html>
+<head>
+	<script src="http://code.jquery.com/jquery.js" type="text/javascript"></script>
+	<script src="../../../jquery.tmpl.js" type="text/javascript"></script>
+	<link href="../../resources/demos.css" rel="stylesheet" type="text/css" />
+
+	<link href="../../resources/syntaxhighlighter.css" rel="stylesheet" type="text/css" />
+	<script src="../../resources/syntaxhighlighter.min.js" type="text/javascript"></script>
+	<link href="../../resources/movielist.css" rel="stylesheet" type="text/css" />
+
+	<style type="text/css">
+		.role { font-weight: bold; font-style: italic; background-color: Yellow; }
+		.synopsis { background-color: white; padding: 15px; }
+		.red { font-weight: bold; font-style: italic; color: red;  }
+	</style>
+</head>
+<body>
+<a href="../../step-by-step.html">Home</a><br />
+<a href="2_html-tag.html">Run</a>
+
+<h3>Demo: Using ${} or {{html}} to render data values or expressions</h3>
+
+<ul>
+<li>${} is equivalent to {{= }}. It HTML encodes. (Better security, but slight perf cost)</li>
+<li>{{html}} does not encode. Used to render values that include html markup</li>
+</ul><br />
+
+<!--=================== Demo Section ===================-->
+
+<script id="movieTemplate" type="text/x-jquery-tmpl">
+	<tr>
+		<td>${Title}</td>
+		<td class="synopsis">{{html Synopsis}}</td>
+	</tr>
+</script>
+
+<table><tbody class="header"><tr><th>Title</th><th>Synopsis</th></tr></tbody>
+	<tbody id="movieList"></tbody>
+</table>
+
+<script type="text/javascript">
+
+	var movies = [
+		{
+			Title: "Meet Joe Black",
+			Synopsis: "The <span class='role'>grim reaper</span> (<a href='http://www.netflix.com/RoleDisplay/Brad_Pitt/73919'>Brad Pitt</a>) visits <span class='role'>Bill Parrish</span> (<a href='http://www.netflix.com/RoleDisplay/Anthony_Hopkins/43014'>Anthony Hopkins</a>)..."
+		},
+		{
+			Title: "Eyes Wide Shut",
+			Synopsis: "Director <span class='red'>Stanley Kubrick's</span> final film: <br/><br/><img src='http://cdn-4.nflximg.com/US/boxshots/large/5670434.jpg'/>"
+		}
+	];
+
+	$( "#movieTemplate" ).tmpl( movies )
+		.appendTo( "#movieList" );
+
+</script>
+
+<!--================ End of Demo Section ================-->
+
+<h4>Data:</h4>
+
+<pre class="brush: js;">var movies = [
+	{
+		Title: "Eyes Wide Shut",
+		Synopsis: "Director &lt;span class='red'>Stanley Kubrick's&lt;/span> final film:&lt;img src='...'/>"
+	}
+];</pre>
+
+<h4>HTML:</h4>
+
+<pre class="brush: xml;">&lt;script id="movieTemplate" type="text/x-jquery-tmpl">
+	&lt;tr>
+		&lt;td>${Title}&lt;/td>
+		&lt;td class="synopsis">{{html Synopsis}}&lt;/td>
+	&lt;/tr>
+&lt;/script></pre>
+
+</body>
+</html>
diff --git a/demos/step-by-step/0_tmpl-read-only/2_html-tag.html b/demos/step-by-step/0_tmpl-read-only/2_html-tag.html
new file mode 100644
index 0000000..8a0885e
--- /dev/null
+++ b/demos/step-by-step/0_tmpl-read-only/2_html-tag.html
@@ -0,0 +1,60 @@
+<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
+<html xmlns="http://www.w3.org/1999/xhtml">
+<head>
+	<script src="http://code.jquery.com/jquery.js" type="text/javascript"></script>
+	<script src="../../../jquery.tmpl.js" type="text/javascript"></script>
+	<link href="../../resources/demos.css" rel="stylesheet" type="text/css" />
+
+	<link href="../../resources/movielist.css" rel="stylesheet" type="text/css" />
+	<style type="text/css">
+		.role { font-weight: bold; font-style: italic; background-color: Yellow; }
+		.synopsis { background-color: white; padding: 15px; }
+		.director { font-weight: bold; font-style: italic; color: red;  }
+	</style>
+</head>
+<body>
+<a href="../../step-by-step.html">Home</a><br />
+<a href="2_html-tag-source.html">Source</a>
+
+<h3>Demo: Using ${} or {{html}} to render data values or expressions</h3>
+
+<ul>
+<li>${} is equivalent to {{= }}. It HTML encodes. (Better security, but slight perf cost)</li>
+<li>{{html}} does not encode. Used to render values that include html markup</li>
+</ul><br />
+
+<!--=================== Demo Section ===================-->
+
+<script id="movieTemplate" type="text/x-jquery-tmpl">
+	<tr>
+		<td>${Title}</td>
+		<td class="synopsis">{{html Synopsis}}</td>
+	</tr>
+</script>
+
+<table><tbody class="header"><tr><th>Title</th><th>Synopsis</th></tr></tbody>
+	<tbody id="movieList"></tbody>
+</table>
+
+<script type="text/javascript">
+
+	var movies = [
+		{
+			Title: "Meet Joe Black",
+			Synopsis: "The <span class='role'>grim reaper</span> (<a href='http://www.netflix.com/RoleDisplay/Brad_Pitt/73919'>Brad Pitt</a>) visits <span class='role'>Bill Parrish</span> (<a href='http://www.netflix.com/RoleDisplay/Anthony_Hopkins/43014'>Anthony Hopkins</a>)..."
+		},
+		{
+			Title: "Eyes Wide Shut",
+			Synopsis: "Director <span class='director'>Stanley Kubrick's</span> final film: <br/><br/><img src='http://cdn-4.nflximg.com/US/boxshots/large/5670434.jpg'/>"
+		}
+	];
+
+	$( "#movieTemplate" ).tmpl( movies )
+		.appendTo( "#movieList" );
+
+</script>
+
+<!--================ End of Demo Section ================-->
+
+</body>
+</html>
diff --git a/demos/step-by-step/0_tmpl-read-only/3_if-else-tag-source.html b/demos/step-by-step/0_tmpl-read-only/3_if-else-tag-source.html
new file mode 100644
index 0000000..a5d1d1b
--- /dev/null
+++ b/demos/step-by-step/0_tmpl-read-only/3_if-else-tag-source.html
@@ -0,0 +1,102 @@
+<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
+<html>
+<head>
+	<script src="http://code.jquery.com/jquery.js" type="text/javascript"></script>
+	<script src="../../../jquery.tmpl.js" type="text/javascript"></script>
+	<link href="../../resources/demos.css" rel="stylesheet" type="text/css" />
+
+	<link href="../../resources/movielist.css" rel="stylesheet" type="text/css" />
+	<link href="../../resources/syntaxhighlighter.css" rel="stylesheet" type="text/css" />
+	<script src="../../resources/syntaxhighlighter.min.js" type="text/javascript"></script>
+</head>
+<body>
+<a href="../../step-by-step.html">Home</a><br />
+<a href="3_if-else-tag.html">Run</a>
+
+<h3>Demo: Using {{if}} and {{else}} to render conditional sections.</h3>
+
+<!--=================== Demo Section ===================-->
+
+<script id="movieTemplate" type="text/x-jquery-tmpl">
+	<tr>
+		<td>${Title}</td>
+		<td>
+			{{if Languages}}
+				Alternative languages: <em>${Languages}</em>.
+			{{else Subtitles}}
+				Original language only... <br/>Subtitles in <em>${Subtitles}</em>.
+			{{else}}
+				Original version only, without subtitles.
+			{{/if}}
+		</td>
+	</tr>
+</script>
+
+<table><tbody class="header"><tr><th>Synopsis</th><th>Title</th></tr></tbody>
+	<tbody id="movieList"></tbody>
+</table>
+
+<script type="text/javascript">
+
+	var movies = [
+		{
+			Title: "Meet Joe Black",
+			Languages: "English and French",
+			Subtitles: "English"
+		},
+		{
+			Title: "Eyes Wide Shut",
+			Subtitles: "French and Spanish"
+		},
+		{
+			Title: "The Mighty"
+		},
+		{
+			Title: "City Hunter",
+			Languages: "Mandarin and Chinese"
+		}
+	];
+
+	$( "#movieTemplate" ).tmpl( movies )
+		.appendTo( "#movieList" );
+
+</script>
+
+<!--================ End of Demo Section ================-->
+
+<h4>Data:</h4>
+
+<pre class="brush: js;">var movies = [
+	{
+		Title: "Meet Joe Black",
+		Languages: "English and French",
+		Subtitles: "English"
+	},
+	{
+		Title: "Eyes Wide Shut",
+		Subtitles: "French and Spanish"
+	},
+	{
+		Title: "The Mighty"
+	}
+];</pre>
+
+<h4>HTML:</h4>
+
+<pre class="brush: xml;">&lt;script id="movieTemplate" type="text/x-jquery-tmpl">
+	&lt;tr>
+		&lt;td>${Title}&lt;/td>
+		&lt;td>
+			{{if Languages}}
+				Alternative languages: &lt;em>${Languages}&lt;/em>.
+			{{else Subtitles}}
+				Original language only... &lt;br/>Subtitles in &lt;em>${Subtitles}&lt;/em>.
+			{{else}}
+				Original version only, without subtitles.
+			{{/if}}
+		&lt;/td>
+	&lt;/tr>
+&lt;/script></pre>
+
+</body>
+</html>
diff --git a/demos/step-by-step/0_tmpl-read-only/3_if-else-tag.html b/demos/step-by-step/0_tmpl-read-only/3_if-else-tag.html
new file mode 100644
index 0000000..1495071
--- /dev/null
+++ b/demos/step-by-step/0_tmpl-read-only/3_if-else-tag.html
@@ -0,0 +1,66 @@
+<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
+<html xmlns="http://www.w3.org/1999/xhtml">
+<head>
+	<script src="http://code.jquery.com/jquery.js" type="text/javascript"></script>
+	<script src="../../../jquery.tmpl.js" type="text/javascript"></script>
+	<link href="../../resources/demos.css" rel="stylesheet" type="text/css" />
+
+	<link href="../../resources/movielist.css" rel="stylesheet" type="text/css" />
+</head>
+<body>
+<a href="../../step-by-step.html">Home</a><br />
+<a href="3_if-else-tag-source.html">Source</a>
+
+<h3>Demo: Using {{if}} and {{else}} to render conditional sections.</h3>
+
+<!--=================== Demo Section ===================-->
+
+<script id="movieTemplate" type="text/x-jquery-tmpl">
+	<tr>
+		<td>${Title}</td>
+		<td>
+			{{if Languages}}
+				Alternative languages: <em>${Languages}</em>.
+			{{else Subtitles}}
+				Original language only... <br/>Subtitles in <em>${Subtitles}</em>.
+			{{else}}
+				Original version only, without subtitles.
+			{{/if}}
+		</td>
+	</tr>
+</script>
+
+<table><tbody class="header"><tr><th>Synopsis</th><th>Title</th></tr></tbody>
+	<tbody id="movieList"></tbody>
+</table>
+
+<script type="text/javascript">
+
+	var movies = [
+		{
+			Title: "Meet Joe Black",
+			Languages: "English and French",
+			Subtitles: "English"
+		},
+		{
+			Title: "Eyes Wide Shut",
+			Subtitles: "French and Spanish"
+		},
+		{
+			Title: "The Mighty"
+		},
+		{
+			Title: "City Hunter",
+			Languages: "Mandarin and Chinese"
+		}
+	];
+
+	$( "#movieTemplate" ).tmpl( movies )
+		.appendTo( "#movieList" );
+
+</script>
+
+<!--================ End of Demo Section ================-->
+
+</body>
+</html>
diff --git a/demos/step-by-step/0_tmpl-read-only/4_each-tag-source.html b/demos/step-by-step/0_tmpl-read-only/4_each-tag-source.html
new file mode 100644
index 0000000..e5b663a
--- /dev/null
+++ b/demos/step-by-step/0_tmpl-read-only/4_each-tag-source.html
@@ -0,0 +1,104 @@
+<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
+<html>
+<head>
+	<script src="http://code.jquery.com/jquery.js" type="text/javascript"></script>
+	<script src="../../../jquery.tmpl.js" type="text/javascript"></script>
+	<link href="../../resources/demos.css" rel="stylesheet" type="text/css" />
+
+	<link href="../../resources/movielist.css" rel="stylesheet" type="text/css" />
+	<link href="../../resources/syntaxhighlighter.css" rel="stylesheet" type="text/css" />
+	<script src="../../resources/syntaxhighlighter.min.js" type="text/javascript"></script>
+</head>
+<body>
+<a href="../../step-by-step.html">Home</a><br />
+<a href="4_each-tag.html">Run</a>
+
+<h3>Demo: Using {{each}} to render repeating sections.</h3>
+
+<!--=================== Demo Section ===================-->
+
+<script id="movieTemplate" type="text/x-jquery-tmpl">
+	<tr>
+		<td>${Title}</td>
+		<td>Languages:
+			<em>
+				{{each Languages}}
+					${$value.Name}
+				{{/each}}
+			</em>
+		</td>
+	</tr>
+</script>
+
+<table><tbody class="header"><tr><th>Synopsis</th><th>Title</th></tr></tbody>
+	<tbody id="movieList"></tbody>
+</table>
+
+<script type="text/javascript">
+
+	function namePlusSeparator( name, index, length ){
+		var ret = name;
+		if ( index < length - 1 ) {
+			if ( index === length - 2 ) {
+				ret += " and";
+			} else {
+				ret += ",";
+			}
+		}
+		return ret;
+	}
+
+	var movies = [
+		{
+			Title: "Meet Joe Black",
+			Languages: [
+				{ Name: "English" },
+				{ Name: "French" }
+			]
+		},
+		{
+			Title: "Eyes Wide Shut",
+			Languages: [
+				{ Name: "French" },
+				{ Name: "German" },
+				{ Name: "Spanish" }
+			]
+		}
+	];
+
+	$( "#movieTemplate" ).tmpl( movies )
+		.appendTo( "#movieList" );
+
+</script>
+
+<!--================ End of Demo Section ================-->
+
+<h4>Data:</h4>
+
+<pre class="brush: js;">var movies = [
+	{
+		Title: "Meet Joe Black",
+		Languages: [
+			{ Name: "English" },
+			{ Name: "French" }
+		]
+	}
+];</pre>
+
+<h4>HTML:</h4>
+
+<pre class="brush: xml;">&lt;script id="movieTemplate" type="text/x-jquery-tmpl">
+	&lt;tr>
+		&lt;td>${Title}&lt;/td>
+		&lt;td>Languages:
+			&lt;em>
+				{{each Languages}}
+					${$value.Name}
+				{{/each}}
+			&lt;/em>
+		&lt;/td>
+	&lt;/tr>
+&lt;/script></pre>
+
+</body>
+</html>
diff --git a/demos/step-by-step/0_tmpl-read-only/4_each-tag.html b/demos/step-by-step/0_tmpl-read-only/4_each-tag.html
new file mode 100644
index 0000000..16d9849
--- /dev/null
+++ b/demos/step-by-step/0_tmpl-read-only/4_each-tag.html
@@ -0,0 +1,75 @@
+<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
+<html xmlns="http://www.w3.org/1999/xhtml">
+<head>
+	<script src="http://code.jquery.com/jquery.js" type="text/javascript"></script>
+	<script src="../../../jquery.tmpl.js" type="text/javascript"></script>
+	<link href="../../resources/demos.css" rel="stylesheet" type="text/css" />
+
+	<link href="../../resources/movielist.css" rel="stylesheet" type="text/css" />
+</head>
+<body>
+<a href="../../step-by-step.html">Home</a><br />
+<a href="4_each-tag-source.html">Source</a>
+
+<h3>Demo: Using {{each}} to render repeating sections.</h3>
+
+<!--=================== Demo Section ===================-->
+
+<script id="movieTemplate" type="text/x-jquery-tmpl">
+	<tr>
+		<td>${Title}</td>
+		<td>Languages:
+			<em>
+				{{each Languages}}
+					${$value.Name}
+				{{/each}}
+			</em>
+		</td>
+	</tr>
+</script>
+
+<table><tbody class="header"><tr><th>Synopsis</th><th>Title</th></tr></tbody>
+	<tbody id="movieList"></tbody>
+</table>
+
+<script type="text/javascript">
+
+	function namePlusSeparator( name, index, length ){
+		var ret = name;
+		if ( index < length - 1 ) {
+			if ( index === length - 2 ) {
+				ret += " and";
+			} else {
+				ret += ",";
+			}
+		}
+		return ret;
+	}
+
+	var movies = [
+		{
+			Title: "Meet Joe Black",
+			Languages: [
+				{ Name: "English" },
+				{ Name: "French" }
+			]
+		},
+		{
+			Title: "Eyes Wide Shut",
+			Languages: [
+				{ Name: "French" },
+				{ Name: "German" },
+				{ Name: "Spanish" }
+			]
+		}
+	];
+
+	$( "#movieTemplate" ).tmpl( movies )
+		.appendTo( "#movieList" );
+
+</script>
+
+<!--================ End of Demo Section ================-->
+
+</body>
+</html>
diff --git a/demos/step-by-step/0_tmpl-read-only/5_javascript-source.html b/demos/step-by-step/0_tmpl-read-only/5_javascript-source.html
new file mode 100644
index 0000000..1f3e86f
--- /dev/null
+++ b/demos/step-by-step/0_tmpl-read-only/5_javascript-source.html
@@ -0,0 +1,123 @@
+<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
+<html>
+<head>
+	<script src="http://code.jquery.com/jquery.js" type="text/javascript"></script>
+	<script src="../../../jquery.tmpl.js" type="text/javascript"></script>
+	<link href="../../resources/demos.css" rel="stylesheet" type="text/css" />
+
+	<link href="../../resources/movielist.css" rel="stylesheet" type="text/css" />
+	<link href="../../resources/syntaxhighlighter.css" rel="stylesheet" type="text/css" />
+	<script src="../../resources/syntaxhighlighter.min.js" type="text/javascript"></script>
+</head>
+<body>
+<a href="../../step-by-step.html">Home</a><br />
+<a href="5_javascript.html">Run</a>
+
+<h3>Demo: Using JavaScript expressions and functions calls within templates.</h3>
+
+<!--=================== Demo Section ===================-->
+
+<script id="movieTemplate" type="text/x-jquery-tmpl">
+	<tr>
+		<td>${Title}</td>
+		<td><b>Languages:</b>
+			<em>
+				{{each Languages}}
+					${$value.Name}{{if $index < Languages.length - 2}}, {{else $index === Languages.length - 2}} and {{/if}}
+				{{/each}}
+			</em><br/><br/>
+			<b>Subtitles:</b>
+			<em>
+				{{each( i, lang ) Subtitles}}
+					${namePlusSeparator( lang.Name, i, Subtitles.length )}
+				{{/each}}
+			</em>
+		</td>
+	</tr>
+</script>
+
+<table><tbody class="header"><tr><th>Synopsis</th><th>Title</th></tr></tbody>
+	<tbody id="movieList"></tbody>
+</table>
+
+<script type="text/javascript">
+
+	function namePlusSeparator( name, index, length ){
+		var ret = name.toUpperCase();
+		if ( index < length - 1 ) {
+			if ( index === length - 2 ) {
+				ret += " and";
+			} else {
+				ret += ",";
+			}
+		}
+		return ret;
+	}
+
+	var movies = [
+		{
+			Title: "Meet Joe Black",
+			Languages: [
+				{ Name: "English" },
+				{ Name: "French" }
+			],
+			Subtitles: [
+				{ Name: "English" },
+				{ Name: "French" },
+				{ Name: "Chinese" }
+			]
+		},
+		{
+			Title: "Eyes Wide Shut",
+			Languages: [
+				{ Name: "French" },
+				{ Name: "German" },
+				{ Name: "Spanish" }
+			],
+			Subtitles: [
+				{ Name: "English" }
+			]
+		}
+	];
+
+	$( "#movieTemplate" ).tmpl( movies )
+		.appendTo( "#movieList" );
+
+</script>
+
+<!--================ End of Demo Section ================-->
+
+<h4>HTML:</h4>
+
+<pre class="brush: xml;">&lt;script id="movieTemplate" type="text/x-jquery-tmpl">
+	&lt;td>${Title}&lt;/td>
+	&lt;td>&lt;b>Languages:&lt;/b>
+		&lt;em>
+			{{each Languages}}
+				${$value.Name}{{if $index &lt; Languages.length - 2}}, {{else $index === Languages.length - 2}} and {{/if}}
+			{{/each}}
+		&lt;/em>&lt;br/>&lt;br/>
+		&lt;b>Subtitles:&lt;/b>
+		&lt;em>
+			{{each( i, lang ) Subtitles}}
+				${namePlusSeparator( lang.Name, i, Subtitles.length )}
+			{{/each}}
+		&lt;/em>
+	&lt;/td>
+&lt;/script></pre>
+
+<h4>Script:</h4>
+<pre class="brush: js;">function namePlusSeparator( name, index, length ){
+	var ret = name.toUpperCase();
+	if ( index < length - 1 ) {
+		if ( index === length - 2 ) {
+			ret += " and";
+		} else {
+			ret += ",";
+		}
+	}
+	return ret;
+}</pre>
+
+</body>
+</html>
diff --git a/demos/step-by-step/0_tmpl-read-only/5_javascript.html b/demos/step-by-step/0_tmpl-read-only/5_javascript.html
new file mode 100644
index 0000000..c89e497
--- /dev/null
+++ b/demos/step-by-step/0_tmpl-read-only/5_javascript.html
@@ -0,0 +1,89 @@
+<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
+<html xmlns="http://www.w3.org/1999/xhtml">
+<head>
+	<script src="http://code.jquery.com/jquery.js" type="text/javascript"></script>
+	<script src="../../../jquery.tmpl.js" type="text/javascript"></script>
+	<link href="../../resources/demos.css" rel="stylesheet" type="text/css" />
+
+	<link href="../../resources/movielist.css" rel="stylesheet" type="text/css" />
+</head>
+<body>
+<a href="../../step-by-step.html">Home</a><br />
+<a href="5_javascript-source.html">Source</a>
+
+<h3>Demo: Using JavaScript expressions and functions calls within templates.</h3>
+
+<!--=================== Demo Section ===================-->
+
+<script id="movieTemplate" type="text/x-jquery-tmpl">
+	<tr>
+		<td>${Title}</td>
+		<td><b>Languages:</b>
+			<em>
+				{{each Languages}}
+					${$value.Name}{{if $index < Languages.length - 2}}, {{else $index === Languages.length - 2}} and {{/if}}
+				{{/each}}
+			</em><br/><br/>
+			<b>Subtitles:</b>
+			<em>
+				{{each( i, lang ) Subtitles}}
+					${namePlusSeparator( lang.Name, i, Subtitles.length )}
+				{{/each}}
+			</em>
+		</td>
+	</tr>
+</script>
+
+<table><tbody class="header"><tr><th>Synopsis</th><th>Title</th></tr></tbody>
+	<tbody id="movieList"></tbody>
+</table>
+
+<script type="text/javascript">
+
+	function namePlusSeparator( name, index, length ){
+		var ret = name.toUpperCase();
+		if ( index < length - 1 ) {
+			if ( index === length - 2 ) {
+				ret += " and";
+			} else {
+				ret += ",";
+			}
+		}
+		return ret;
+	}
+
+	var movies = [
+		{
+			Title: "Meet Joe Black",
+			Languages: [
+				{ Name: "English" },
+				{ Name: "French" }
+			],
+			Subtitles: [
+				{ Name: "English" },
+				{ Name: "French" },
+				{ Name: "Chinese" }
+			]
+		},
+		{
+			Title: "Eyes Wide Shut",
+			Languages: [
+				{ Name: "French" },
+				{ Name: "German" },
+				{ Name: "Spanish" }
+			],
+			Subtitles: [
+				{ Name: "English" }
+			]
+		}
+	];
+
+	$( "#movieTemplate" ).tmpl( movies )
+		.appendTo( "#movieList" );
+
+</script>
+
+<!--================ End of Demo Section ================-->
+
+</body>
+</html>
diff --git a/demos/step-by-step/0_tmpl-read-only/6_hierarchical-data-source.html b/demos/step-by-step/0_tmpl-read-only/6_hierarchical-data-source.html
new file mode 100644
index 0000000..e0df4d4
--- /dev/null
+++ b/demos/step-by-step/0_tmpl-read-only/6_hierarchical-data-source.html
@@ -0,0 +1,96 @@
+<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
+<html>
+<head>
+	<script src="http://code.jquery.com/jquery.js" type="text/javascript"></script>
+	<script src="../../../jquery.tmpl.js" type="text/javascript"></script>
+	<link href="../../resources/demos.css" rel="stylesheet" type="text/css" />
+
+	<link href="../../resources/movielist.css" rel="stylesheet" type="text/css" />
+	<link href="../../resources/syntaxhighlighter.css" rel="stylesheet" type="text/css" />
+	<script src="../../resources/syntaxhighlighter.min.js" type="text/javascript"></script>
+</head>
+<body>
+<a href="../../step-by-step.html">Home</a><br />
+<a href="6_hierarchical-data.html">Run</a>
+
+<h3>Demo: Using {{tmpl}} to render hierarchical data.</h3>
+
+<!--=================== Demo Section ===================-->
+
+<script id="movieTemplate" type="text/x-jquery-tmpl">
+	<tr>
+		<td>${Title}</td>
+		<td>
+			{{tmpl(Languages) "#languageTemplate"}}
+		</td>
+	</tr>
+</script>
+
+<script id="languageTemplate" type="text/x-jquery-tmpl">
+	<div>${Name}</div>
+</script>
+
+<div class="height">
+<table><tbody class="header"><tr><th>Title</th><th>Languages</th></tr></tbody>
+	<tbody id="movieList"></tbody>
+</table>
+</div>
+
+<script type="text/javascript">
+
+	var movies = [
+		{
+			Title: "Meet Joe Black",
+			Languages: [
+				{ Name: "English" },
+				{ Name: "French" }
+			]
+		},
+		{
+			Title: "Eyes Wide Shut",
+			Languages: [
+				{ Name: "French" },
+				{ Name: "German" },
+				{ Name: "Spanish" }
+			]
+		}
+	];
+
+	$( "#movieTemplate" ).tmpl( movies )
+		.appendTo( "#movieList" );
+
+</script>
+
+<!--================ End of Demo Section ================-->
+
+<h4>Data:</h4>
+<pre class="brush: js;">var movies = [
+	{ Name: "Meet Joe Black",
+		Languages:[
+			{ Name: "English" },
+			{ Name: "French" }
+		]
+	},
+	{ Name: "Eyes Wide Shut",
+		Languages:[
+			{ Name: "French" },
+			{ Name: "German" } ,
+			{ Name: "Spanish" }
+		]
+	}
+];</pre>
+
+<h4>HTML:</h4>
+<pre class="brush: xml;">&lt;script id="movieTemplate" type="text/x-jquery-tmpl">
+	&lt;tr>
+		&lt;td>${Name}&lt;/td>
+		&lt;td>{{tmpl(Languages) "#languageTemplate"}}&lt;/td>
+	&lt;/tr>
+&lt;/script>
+
+&lt;script id="languageTemplate" type="text/x-jquery-tmpl">
+	&lt;div>${Name}&lt;/div>
+&lt;/script></pre>
+
+</body>
+</html>
diff --git a/demos/step-by-step/0_tmpl-read-only/6_hierarchical-data.html b/demos/step-by-step/0_tmpl-read-only/6_hierarchical-data.html
new file mode 100644
index 0000000..2bce1e5
--- /dev/null
+++ b/demos/step-by-step/0_tmpl-read-only/6_hierarchical-data.html
@@ -0,0 +1,67 @@
+<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
+<html xmlns="http://www.w3.org/1999/xhtml">
+<head>
+	<script src="http://code.jquery.com/jquery.js" type="text/javascript"></script>
+	<script src="../../../jquery.tmpl.js" type="text/javascript"></script>
+	<link href="../../resources/demos.css" rel="stylesheet" type="text/css" />
+
+	<link href="../../resources/movielist.css" rel="stylesheet" type="text/css" />
+</head>
+<body>
+<a href="../../step-by-step.html">Home</a><br />
+<a href="6_hierarchical-data-source.html">Source</a>
+
+<h3>Demo: Using {{tmpl}} to render hierarchical data.</h3>
+
+<!--=================== Demo Section ===================-->
+
+<script id="movieTemplate" type="text/x-jquery-tmpl">
+	<tr>
+		<td>${Title}</td>
+		<td>
+			{{tmpl(Languages) "#languageTemplate"}}
+		</td>
+	</tr>
+</script>
+
+<script id="languageTemplate" type="text/x-jquery-tmpl">
+	<div>
+		<em>${Name}</em>
+	</div>
+</script>
+
+<table><tbody class="header"><tr><th>Title</th><th>Languages</th></tr></tbody>
+	<tbody id="movieList"></tbody>
+</table>
+
+<script type="text/javascript">
+
+	var movies = [
+		{
+			Title: "Meet Joe Black",
+			Languages: [
+				{ Name: "English" },
+				{ Name: "French" }
+			]
+		},
+		{
+			Title: "Eyes Wide Shut",
+			Languages: [
+				{ Name: "French" },
+				{ Name: "German" },
+				{ Name: "Spanish" }
+			]
+		}
+	];
+
+	$( "#movieTemplate" ).tmpl( movies )
+		.appendTo( "#movieList" );
+
+</script>
+
+<!--================ End of Demo Section ================-->
+
+</body>
+</html>
+
+
diff --git a/demos/step-by-step/1_tmpl-interactive/0_accordion-switching-template-source.html b/demos/step-by-step/1_tmpl-interactive/0_accordion-switching-template-source.html
new file mode 100644
index 0000000..c8b9f8b
--- /dev/null
+++ b/demos/step-by-step/1_tmpl-interactive/0_accordion-switching-template-source.html
@@ -0,0 +1,184 @@
+<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
+<html xmlns="http://www.w3.org/1999/xhtml">
+<head>
+	<script src="http://code.jquery.com/jquery.js" type="text/javascript"></script>
+	<script src="../../../jquery.tmpl.js" type="text/javascript"></script>
+	<link href="../../resources/demos.css" rel="stylesheet" type="text/css" />
+
+	<link href="../../resources/syntaxhighlighter.css" rel="stylesheet" type="text/css" />
+	<script src="../../resources/syntaxhighlighter.min.js" type="text/javascript"></script>
+	<style type="text/css">
+		table { cursor:pointer; border-collapse:collapse; border:2px solid blue; width:300px; margin:8px; }
+		table tr { border:1px solid blue; color:blue; background-color:#f8f8f8; } table td { padding:3px; } table tr:hover { color:red; }
+		.movieDetail { background-color:yellow; } .movieDetail.row1 { border-bottom:none; } .movieDetail.row2 { border-top:none; }
+	</style>
+</head>
+<body>
+<a href="../../step-by-step.html">Home</a><br />
+<a href="0_accordion-switching-template.html">Run</a>
+
+<h3>Accordion: Using dynamic switching of templates</h3>
+
+<!--=================== Demo Section ===================-->
+
+<script id="summaryTemplate" type="text/x-jquery-tmpl">
+	<tr class='movieSummary'><td>
+		${Title}
+	</td></tr>
+</script>
+
+<script id="detailTemplate" type="text/x-jquery-tmpl">
+	<tr class='movieDetail row1'><td>
+		${Title}
+	</td></tr>
+	<tr class='movieDetail row2'><td><b>Languages:</b>
+		{{tmpl(Languages) "#languageTemplate"}}
+	</td></tr>
+</script>
+
+<script id="languageTemplate" type="text/x-jquery-tmpl">
+	<div>
+		<em>${Name}</em>
+	</div>
+</script>
+
+Click for details:
+<div class="height">
+	<table><tbody id="movieList"></tbody></table>
+</div>
+
+<script type="text/javascript">
+
+	var selectedItem = null,
+
+		movies = [
+			{
+				Title: "The Red Violin",
+				Languages: [
+					{ Name: "English" },
+					{ Name: "French" }
+				]
+			},
+			{
+				Title: "Eyes Wide Shut",
+				Languages: [
+					{ Name: "French" },
+					{ Name: "German" },
+					{ Name: "Spanish" }
+				]
+			},
+			{
+				Title: "The Inheritance",
+				ReleaseYear: "1976",
+				Languages: [
+					{ Name: "English" },
+					{ Name: "Dutch" }
+				]
+			}
+		],
+
+		detailTemplate = $( "#detailTemplate" ).template(),
+
+		summaryTemplate = $( "#summaryTemplate" ).template();
+
+	function unselect( tmplItem ) {
+		/* Set the template of the selected item
+		   back to the summary template */
+		if ( selectedItem ) {
+			selectedItem.tmpl = summaryTemplate;
+			selectedItem.update();
+			selectedItem = null;
+		}
+	}
+
+	/* Render the summaryTemplate with the "movies" data */
+	$( "#summaryTemplate" )
+		.tmpl( movies )
+		.appendTo( "#movieList" );
+
+	/* Add onclick handlers for movie template items
+	   using the summary or details template */
+	$("#movieList")
+		.delegate( ".movieSummary", "click", function () {
+			/* Unselect the currently selected item */
+			unselect( selectedItem );
+
+			/* Get the template item data structure
+			   which this clicked element belongs to,
+			   and make it the selected item */
+			selectedItem = $.tmplItem(this);
+
+			/* Set the template on this item to the detail template */
+			selectedItem.tmpl = detailTemplate;
+			selectedItem.update();
+		})
+		.delegate( ".movieDetail", "click", function () {
+			/* Unselect the currently selected item */
+			unselect();
+		});
+
+</script>
+
+<!--================ End of Demo Section ================-->
+
+<h4>Data:</h4>
+<pre class="brush: js;">var movies = [
+	{
+		Title: "The Red Violin",
+		Languages: [
+			{ Name: "English" },
+			{ Name: "French" }
+		]
+	},
+	...
+];
+;</pre>
+
+<h4>HTML:</h4>
+<pre class="brush: xml;">&lt;script id="summaryTemplate" type="text/x-jquery-tmpl">
+	&lt;tr class='movieSummary'>&lt;td>
+		${Title}
+	&lt;/td>&lt;/tr>
+&lt;/script>
+
+&lt;script id="detailTemplate" type="text/x-jquery-tmpl">
+	&lt;tr class='movieDetail'>&lt;td>
+		${Title}
+	&lt;/td>&lt;/tr>
+	&lt;tr class='movieDetail'>&lt;td>&lt;b>Languages:&lt;/b>
+		{{tmpl(Languages) "#languageTemplate"}}
+	&lt;/td>&lt;/tr>
+&lt;/script>
+</pre>
+
+<h4>Script:</h4>
+<pre class="brush: js;">var selectedItem = null;
+
+$("#movieList")
+	.delegate( ".movieSummary", "click", function () {
+ 		/* Unselect the currently selected item */
+		unselect( selectedItem );
+
+		/* Get template item clicked element belongs to, and make it the selected item */
+		selectedItem = $.tmplItem(this);
+
+		/* Swap template on this item to the detail template, and update item */
+		selectedItem.tmpl = detailTemplate;
+		selectedItem.update();
+	})
+	.delegate( ".movieDetail", "click", function () {
+ 		/* Unselect the currently selected item */
+		unselect();
+	});
+
+function unselect( tmplItem ) {
+	if ( selectedItem ) {
+		/* Swap template back to the summary template and update item */
+		selectedItem.tmpl = summaryTemplate;
+		selectedItem.update();
+		selectedItem = null;
+	}
+}</pre>
+
+</body>
+</html>
diff --git a/demos/step-by-step/1_tmpl-interactive/0_accordion-switching-template.html b/demos/step-by-step/1_tmpl-interactive/0_accordion-switching-template.html
new file mode 100644
index 0000000..83b8eb3
--- /dev/null
+++ b/demos/step-by-step/1_tmpl-interactive/0_accordion-switching-template.html
@@ -0,0 +1,123 @@
+<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
+<html xmlns="http://www.w3.org/1999/xhtml">
+<head>
+	<script src="http://code.jquery.com/jquery.js" type="text/javascript"></script>
+	<script src="../../../jquery.tmpl.js" type="text/javascript"></script>
+	<link href="../../resources/demos.css" rel="stylesheet" type="text/css" />
+
+	<style type="text/css">
+		table { cursor:pointer; border-collapse:collapse; border:2px solid blue; width:300px; margin:8px; }
+		table tr { border:1px solid blue; color:blue; background-color:#f8f8f8; } table td { padding:3px; } table tr:hover { color:red; }
+		.movieDetail { background-color:yellow; } .movieDetail.row1 { border-bottom:none; } .movieDetail.row2 { border-top:none; }
+	</style>
+</head>
+<body>
+<a href="../../step-by-step.html">Home</a><br />
+<a href="0_accordion-switching-template-source.html">Source</a>
+
+<h3>Accordion: Using dynamic switching of templates</h3>
+
+<!--=================== Demo Section ===================-->
+
+<script id="summaryTemplate" type="text/x-jquery-tmpl">
+	<tr class='movieSummary'><td>
+		${Title}
+	</td></tr>
+</script>
+
+<script id="detailTemplate" type="text/x-jquery-tmpl">
+	<tr class='movieDetail row1'><td>
+		${Title}
+	</td></tr>
+	<tr class='movieDetail row2'><td><b>Languages:</b>
+		{{tmpl(Languages) "#languageTemplate"}}
+	</td></tr>
+</script>
+
+<script id="languageTemplate" type="text/x-jquery-tmpl">
+	<div>
+		<em>${Name}</em>
+	</div>
+</script>
+
+Click for details:
+<div class="height">
+	<table><tbody id="movieList"></tbody></table>
+</div>
+
+<script type="text/javascript">
+
+	var selectedItem = null,
+
+		movies = [
+			{
+				Title: "The Red Violin",
+				Languages: [
+					{ Name: "English" },
+					{ Name: "French" }
+				]
+			},
+			{
+				Title: "Eyes Wide Shut",
+				Languages: [
+					{ Name: "French" },
+					{ Name: "German" },
+					{ Name: "Spanish" }
+				]
+			},
+			{
+				Title: "The Inheritance",
+				ReleaseYear: "1976",
+				Languages: [
+					{ Name: "English" },
+					{ Name: "Dutch" }
+				]
+			}
+		],
+
+		detailTemplate = $( "#detailTemplate" ).template(),
+
+		summaryTemplate = $( "#summaryTemplate" ).template();
+
+	function unselect( tmplItem ) {
+		/* Set the template of the selected item
+		   back to the summary template */
+		if ( selectedItem ) {
+			selectedItem.tmpl = summaryTemplate;
+			selectedItem.update();
+			selectedItem = null;
+		}
+	}
+
+	/* Render the summaryTemplate with the "movies" data */
+	$( "#summaryTemplate" )
+		.tmpl( movies )
+		.appendTo( "#movieList" );
+
+	/* Add onclick handlers for movie template items
+	   using the summary or details template */
+	$("#movieList")
+		.delegate( ".movieSummary", "click", function () {
+			/* Unselect the currently selected item */
+			unselect( selectedItem );
+
+			/* Get the template item data structure
+			   which this clicked element belongs to,
+			   and make it the selected item */
+			selectedItem = $.tmplItem(this);
+
+			/* Set the template on this item to the detail template */
+			selectedItem.tmpl = detailTemplate;
+			selectedItem.update();
+		})
+		.delegate( ".movieDetail", "click", function () {
+			/* Unselect the currently selected item */
+			unselect();
+		});
+
+</script>
+
+<!--================ End of Demo Section ================-->
+
+</body>
+</html>
diff --git a/demos/step-by-step/1_tmpl-interactive/1_tree-view-using-tmpl-tag-source.html b/demos/step-by-step/1_tmpl-interactive/1_tree-view-using-tmpl-tag-source.html
new file mode 100644
index 0000000..3c8bf73
--- /dev/null
+++ b/demos/step-by-step/1_tmpl-interactive/1_tree-view-using-tmpl-tag-source.html
@@ -0,0 +1,173 @@
+<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
+<html>
+<head>
+	<script src="http://code.jquery.com/jquery.js" type="text/javascript"></script>
+	<script src="../../../jquery.tmpl.js" type="text/javascript"></script>
+	<link href="../../resources/demos.css" rel="stylesheet" type="text/css" />
+
+	<link href="../../resources/syntaxhighlighter.css" rel="stylesheet" type="text/css" />
+	<script src="../../resources/syntaxhighlighter.min.js" type="text/javascript"></script>
+	<style type="text/css">
+		.treeView li li {margin-left:24px;}
+		.toggle {cursor:pointer;vertical-align:middle;margin-right:7px;display:inline-block;border:1px solid #555;text-align:center;height:12px;width:12px;line-height:11px;background-color:#f8f8f8;color:Blue;}
+		.treeView, .treeView ul {padding:0;margin:0;} .treeView li {margin-left:8px;list-style-type:none;padding:2px;}
+		.treeView li.folderItem {color:Blue;text-decoration:underline;font-style:italic;margin-bottom:4px;}
+	</style>
+</head>
+<body>
+<a href="../../step-by-step.html">Home</a><br />
+<a href="1_tree-view-using-tmpl-tag.html">Run</a>
+
+<h3>Tree view: using recursive nested {{tmpl}} tags.</h3>
+
+<!--=================== Demo Section ===================-->
+
+<ul id="movieList" class="treeView"></ul>
+
+<script id="folderTemplate" type="text/x-jquery-tmpl">
+	<li>
+		{{if hasContent($data)}}
+			<span class="toggle">${$data.expanded ? "-" : "+"}</span>
+		{{/if}}
+		<span>${name}</span>
+	</li>
+	{{if expanded}}
+		<li>
+			<ul>{{tmpl(getFolderItems(name)) "#itemTemplate"}}</ul>
+			<ul>{{tmpl($data.folders || []) "#folderTemplate"}}</ul>
+		</li>
+	{{/if}}
+</script>
+
+<script id="itemTemplate" type="text/x-jquery-tmpl">
+	<li class="folderItem">${name}</li>
+</script>
+
+<script type="text/javascript">
+
+ /* Hierarchy of named folders */
+var rootFolder = {
+	name: "Categories",
+	folders: [
+		{ name: "Drama", folders: [
+			{ name: "Courtroom" },
+			{ name: "Political" }
+		]},
+		{ name: "Classic", folders: [
+			{ name: "Musicals", folders: [
+				{ name: "Jazz"},
+				{ name: "R&B/Soul"}
+			]},
+		]}
+	]
+};
+
+/* Array for the folder items. Each item can show up in one or more folders */
+var movies = [
+	{ name: "12 Angry Men",
+		folders: [ "Courtroom" ],
+		description: "A jury of 12 men must decide the fate of an 18-year-old boy."
+	},
+	{ name: "Word of Honor",
+		folders: [ "Courtroom", "Classic" ],
+		description: "One man's word against the U.S. military."
+	}
+];
+
+/* Declare the functions for getting the items and subfolders, etc. */
+function getFolderItems( name ) {
+	return $.map( movies, function( movie ) {
+		return $.inArray( name, movie.folders ) > -1 ? movie : null;
+	});
+}
+
+function hasContent( folder ) {
+	return folder.expanded || folder.folders && folder.folders.length || getFolderItems(folder.name).length;
+}
+
+/* Render the folderTemplate with the "movies" data */
+$( "#folderTemplate" ).tmpl( rootFolder ).appendTo( "#movieList" );
+
+$( "#movieList" )
+	.delegate( ".toggle", "click", function() {
+		/* Toggle expanded property on data, then update rendering */
+		var tmplItem = $.tmplItem( this );
+		tmplItem.data.expanded = !tmplItem.data.expanded;
+		tmplItem.update();
+	})
+	.delegate( ".folderItem", "click", function() {
+		alert( $.tmplItem( this ).data.description );
+	});
+
+</script>
+
+<!--================ End of Demo Section ================-->
+
+<h4>Data:</h4>
+<pre class="brush: js;">var rootFolder = {
+	name: "Categories",
+	folders: [
+		{ name: "Drama", folders: [
+			{ name: "Courtroom" },
+			{ name: "Political" }
+		]},
+		{ name: "Classic", folders: [
+			{ name: "Musicals", folders: [
+				{ name: "Jazz"},
+				{ name: "R&B/Soul"}
+			]},
+		]}
+	]
+};
+
+/* Array for the folder items. Each item can show up in one or more folders */
+var movies = [
+	{ name: "12 Angry Men",
+		folders: [ "Courtroom" ],
+		description: "A jury of 12 men must decide the fate of an 18-year-old boy."
+	},
+	{ name: "Word of Honor",
+		folders: [ "Courtroom", "Classic" ],
+		description: "One man's word against the U.S. military."
+	}
+];</pre>
+
+<h4>HTML:</h4>
+<pre class="brush: xml;">&lt;script id="folderTemplate" type="text/x-jquery-tmpl">
+	&lt;li>
+		{{if hasContent($data)}}
+			&lt;span class="toggle">${$data.expanded ? "-" : "+"}&lt;/span>
+		{{/if}}
+		&lt;span>${name}&lt;/span>
+	&lt;/li>
+	{{if expanded}}
+		&lt;li>
+			&lt;ul>{{tmpl(getFolderItems(name)) "#itemTemplate"}}&lt;/ul>
+			&lt;ul>{{tmpl($data.folders || []) "#folderTemplate"}}&lt;/ul>
+		&lt;/li>
+	{{/if}}
+&lt;/script>
+
+&lt;script id="itemTemplate" type="text/x-jquery-tmpl">
+	&lt;li class="folderItem">${name}&lt;/li>
+&lt;/script>
+</pre>
+
+<h4>Script:</h4>
+<pre class="brush: js;">$( "#folderTmpl" ).tmpl( folders ).appendTo( "#movieList" );
+
+$( "#movieList" )
+	.delegate( ".toggle", "click", function() {
+		/* Toggle expanded property on data, then update rendering */
+		var tmplItem = $.tmplItem( this );
+		tmplItem.data.expanded = !tmplItem.data.expanded;
+		tmplItem.update();
+	})
+	.delegate( ".folderItem", "click", function() {
+		alert( $.tmplItem( this ).data.description );
+	});</pre>
+
+</body>
+</html>
+<script>
+
diff --git a/demos/step-by-step/1_tmpl-interactive/1_tree-view-using-tmpl-tag.html b/demos/step-by-step/1_tmpl-interactive/1_tree-view-using-tmpl-tag.html
new file mode 100644
index 0000000..003dba6
--- /dev/null
+++ b/demos/step-by-step/1_tmpl-interactive/1_tree-view-using-tmpl-tag.html
@@ -0,0 +1,107 @@
+<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
+<html>
+<head>
+	<script src="http://code.jquery.com/jquery.js" type="text/javascript"></script>
+	<script src="../../../jquery.tmpl.js" type="text/javascript"></script>
+	<link href="../../resources/demos.css" rel="stylesheet" type="text/css" />
+
+	<style type="text/css">
+		.treeView li li {margin-left:24px;}
+		.toggle {cursor:pointer;vertical-align:middle;margin-right:7px;display:inline-block;border:1px solid #555;text-align:center;height:12px;width:12px;line-height:11px;background-color:#f8f8f8;color:Blue;}
+		.treeView, .treeView ul {padding:0;margin:0;} .treeView li {margin-left:8px;list-style-type:none;padding:2px;}
+		.treeView li.folderItem {color:Blue;text-decoration:underline;font-style:italic;margin-bottom:4px;}
+	</style>
+</head>
+<body>
+<a href="../../step-by-step.html">Home</a><br />
+<a href="1_tree-view-using-tmpl-tag-source.html">Source</a>
+
+<h3>Tree view: using recursive nested {{tmpl}} tags.</h3>
+
+<!--=================== Demo Section ===================-->
+
+<ul id="movieList" class="treeView"></ul>
+
+<script id="folderTemplate" type="text/x-jquery-tmpl">
+	<li>
+		{{if hasContent($data)}}
+			<span class="toggle">${$data.expanded ? "-" : "+"}</span>
+		{{/if}}
+		<span>${name}</span>
+	</li>
+	{{if expanded}}
+		<li>
+			<ul>{{tmpl(getFolderItems(name)) "#itemTemplate"}}</ul>
+			<ul>{{tmpl($data.folders || []) "#folderTemplate"}}</ul>
+		</li>
+	{{/if}}
+</script>
+
+<script id="itemTemplate" type="text/x-jquery-tmpl">
+	<li class="folderItem">${name}</li>
+</script>
+
+<script type="text/javascript">
+
+ /* Hierarchy of named folders */
+var rootFolder = {
+	name: "Categories",
+	folders: [
+		{ name: "Drama", folders: [
+			{ name: "Courtroom" },
+			{ name: "Political" }
+		]},
+		{ name: "Classic", folders: [
+			{ name: "Musicals", folders: [
+				{ name: "Jazz"},
+				{ name: "R&B/Soul"}
+			]},
+		]}
+	]
+};
+
+/* Array for the folder items. Each item can show up in one or more folders */
+var movies = [
+	{ name: "12 Angry Men",
+		folders: [ "Courtroom" ],
+		description: "A jury of 12 men must decide the fate of an 18-year-old boy."
+	},
+	{ name: "Word of Honor",
+		folders: [ "Courtroom", "Classic" ],
+		description: "One man's word against the U.S. military."
+	}
+];
+
+/* Declare the functions for getting the items and subfolders, etc. */
+function getFolderItems( name ) {
+	return $.map( movies, function( movie ) {
+		return $.inArray( name, movie.folders ) > -1 ? movie : null;
+	});
+}
+
+function hasContent( folder ) {
+	return folder.expanded || folder.folders && folder.folders.length || getFolderItems(folder.name).length;
+}
+
+/* Render the folderTemplate with the "movies" data */
+$( "#folderTemplate" ).tmpl( rootFolder ).appendTo( "#movieList" );
+
+$( "#movieList" )
+	.delegate( ".toggle", "click", function() {
+		/* Toggle expanded property on data, then update rendering */
+		var tmplItem = $.tmplItem( this );
+		tmplItem.data.expanded = !tmplItem.data.expanded;
+		tmplItem.update();
+	})
+	.delegate( ".folderItem", "click", function() {
+		alert( $.tmplItem( this ).data.description );
+	});
+
+</script>
+
+<!--================ End of Demo Section ================-->
+
+</body>
+</html>
+<script>
+
diff --git a/demos/step-by-step/1_tmpl-interactive/2_tabs-using-wrap-tag-source.html b/demos/step-by-step/1_tmpl-interactive/2_tabs-using-wrap-tag-source.html
new file mode 100644
index 0000000..ea4f07d
--- /dev/null
+++ b/demos/step-by-step/1_tmpl-interactive/2_tabs-using-wrap-tag-source.html
@@ -0,0 +1,181 @@
+<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
+<html>
+<head>
+	<script src="http://code.jquery.com/jquery.js" type="text/javascript"></script>
+	<script src="../../../jquery.tmpl.js" type="text/javascript"></script>
+	<link href="../../resources/demos.css" rel="stylesheet" type="text/css" />
+
+	<link href="../../resources/syntaxhighlighter.css" rel="stylesheet" type="text/css" />
+	<script src="../../resources/syntaxhighlighter.min.js" type="text/javascript"></script>
+	<style type="text/css">
+		.body { height: 175px; background-color: #fff; }
+		.body div { height: 60px; padding: 12px; font-weight: bold; color: #222; }
+		.body div span { margin: 7px 0 10px 0; font-style:italic; font-weight: normal; padding: 3px;}
+		.body div img { float: left; margin-right: 30px; }
+		.body h3 { text-align: center; }
+		.tabsView td { border: solid 1px #0000A6; border-top: none; border-right: solid 2px #1E1ED2; }
+		.tabsView th { cursor: pointer; padding: 2px; font-weight: normal; font-style: italic; color: #888; border: solid 1px #bbb; border-right: none; background-color: #f8f8f8; border-bottom: solid 1px #1E1ED2; }
+		#tabsView > .tabsView { width: 465px; }
+		.tabsView { width: 450px; border-collapse: collapse; border: none; margin: 5px; }
+		.tabsView tr { border-right: solid 1px #bbb; }
+		th.header_true { font-weight: bold; border: solid 1px #0000A6; border-right: solid 2px #1E1ED2; border-bottom: solid 1px #eee; color: #0000A6; background-color: #fff; }
+	</style>
+</head>
+<body>
+<a href="../../step-by-step.html">Home</a><br />
+<a href="2_tabs-using-wrap-tag.html">Run</a>
+
+<h3>Tab View: Using {{wrap}} for template composition incorporating wrapped HTML</h3>
+
+<!--=================== Demo Section ===================-->
+
+<script id="movieTemplate" type="text/x-jquery-tmpl">
+	<h2>${Title}</h2>
+
+	{{wrap "#tabsTemplate"}}
+		<h3>Details</h3>
+		<div>
+
+			Title: <span>${Title}</span><br/>
+			<h4>Languages:</h4>
+			{{each Languages}}<span>${$value.Name}</span><br/>{{/each}}
+
+		</div>
+
+		<h3>Description</h3>
+		<div>
+
+			<img src='http://cdn-4.nflximg.com/US/boxshots/large/5670434.jpg'/><br/><br/>
+			Director Stanley Kubrick's<br/> final film.
+
+		</div>
+
+		<h3>Comments</h3>
+		<div>
+
+			<ul>
+				<li>Great film...</li>
+				<li>The best</li>
+				<li>So boring, I couldn't keep my eyes open</li>
+			</ul>
+
+		</div>
+	{{/wrap}}
+</script>
+
+<script id="tabsTemplate" type="text/x-jquery-tmpl">
+	<table class="tabsView"><tbody>
+		<tr>
+			{{each $item.html("h3", true)}}
+				<th class="header_${$index === selectedTab}">
+					${$value}
+				</th>
+			{{/each}}
+		</tr>
+		<tr><td colspan='${$item.html("h3").length}'>
+			<div class="body">
+				{{html $item.html("div")[selectedTab]}}
+			</div>
+		</td></tr>
+	</tbody></table>
+</script>
+
+<br />
+<div id="tabsView">..loading</div>
+
+<script type="text/javascript">
+
+	/* Track the selected tab index for inner and outer tab views */
+	var movie = {
+			Title: "Meet Joe Black",
+			Languages: [
+				{ Name: "English" },
+				{ Name: "French" }
+			],
+			selectedTab: 0
+		};
+
+
+	function refresh() {
+		$( "#tabsView" ).empty();
+
+		$( "#movieTemplate" ).tmpl( movie )
+			.appendTo( "#tabsView" );
+	}
+
+	/* Render tabs view */
+	refresh();
+
+	$( "#tabsView" )
+		.delegate( ".tabsView th", "click", function() {
+			/* Set the selected tab index to this tab */
+			$.tmplItem( this ).data.selectedTab = $(this).index();
+
+			/* update the rendering */
+			refresh();
+		});
+
+</script>
+
+<!--================ End of Demo Section ================-->
+
+<h4>HTML:</h4>
+<pre class="brush: xml;">&lt;script id="movieTemplate" type="text/x-jquery-tmpl">
+	&lt;h2>${Title}&lt;/h2>
+
+	{{wrap "#tabsTemplate"}}
+		&lt;h3>Details&lt;/h3>
+		&lt;div>
+			Title: &lt;input value="${Title}" >
+			Languages:
+			{{each Languages}}&lt;span>${$value.Name}&lt;/span>{{/each}}
+		&lt;/div>
+
+		&lt;h3>Description&lt;/h3>
+		&lt;div>
+			... content of tab 2
+		&lt;/div>
+
+		&lt;h3>Comments&lt;/h3>
+		&lt;div>
+			... content of tab 3
+		&lt;/div>
+	{{/wrap}}
+&lt;/script>
+
+&lt;script id="tabsTemplate" type="text/x-jquery-tmpl">
+	&lt;table>&lt;tbody>
+		&lt;tr>
+			{{each $item.html("h3", true)}}
+				&lt;th class="header_${$index === selectedTab}">
+					${$value}
+				&lt;/th>
+			{{/each}}
+		&lt;/tr>
+		&lt;tr>&lt;td colspan='${$item.html("h3").length}'>
+			&lt;div>
+				{{html $item.html("div")[selectedTab]}}
+			&lt;/div>
+		&lt;/td>&lt;/tr>
+	&lt;/tbody>&lt;/table>
+&lt;/script>
+</pre>
+
+<h4>Script:</h4>
+<pre class="brush: js;">	function refresh() {
+		$( "#tabsView" ).empty();
+
+		$( "#movieTemplate" ).tmpl( movie )
+			.appendTo( "#tabsView" );
+	}
+
+	$( "#tabsView" )
+		.delegate( ".tabsView th", "click", function() {
+			/* Set the selected tab index to this tab */
+			$.tmplItem( this ).data.selectedTab = $(this).index();
+
+			/* update the rendering */
+			refresh();
+		});</pre>
+</body>
+</html>
diff --git a/demos/step-by-step/1_tmpl-interactive/2_tabs-using-wrap-tag.html b/demos/step-by-step/1_tmpl-interactive/2_tabs-using-wrap-tag.html
new file mode 100644
index 0000000..44b436c
--- /dev/null
+++ b/demos/step-by-step/1_tmpl-interactive/2_tabs-using-wrap-tag.html
@@ -0,0 +1,120 @@
+<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
+<html>
+<head>
+	<script src="http://code.jquery.com/jquery.js" type="text/javascript"></script>
+	<script src="../../../jquery.tmpl.js" type="text/javascript"></script>
+	<link href="../../resources/demos.css" rel="stylesheet" type="text/css" />
+
+	<style type="text/css">
+		.body { height: 175px; background-color: #fff; }
+		.body div { height: 60px; padding: 12px; font-weight: bold; color: #222; }
+		.body div span { margin: 7px 0 10px 0; font-style:italic; font-weight: normal; padding: 3px;}
+		.body div img { float: left; margin-right: 30px; }
+		.body h3 { text-align: center; }
+		.tabsView td { border: solid 1px #0000A6; border-top: none; border-right: solid 2px #1E1ED2; }
+		.tabsView th { cursor: pointer; padding: 2px; font-weight: normal; font-style: italic; color: #888; border: solid 1px #bbb; border-right: none; background-color: #f8f8f8; border-bottom: solid 1px #1E1ED2; }
+		#tabsView > .tabsView { width: 465px; }
+		.tabsView { width: 450px; border-collapse: collapse; border: none; margin: 5px; }
+		.tabsView tr { border-right: solid 1px #bbb; }
+		th.header_true { font-weight: bold; border: solid 1px #0000A6; border-right: solid 2px #1E1ED2; border-bottom: solid 1px #eee; color: #0000A6; background-color: #fff; }
+	</style>
+</head>
+<body>
+<a href="../../step-by-step.html">Home</a><br />
+<a href="2_tabs-using-wrap-tag-source.html">Source</a>
+
+<h3>Tab View: Using {{wrap}} for template composition incorporating wrapped HTML</h3>
+
+<!--=================== Demo Section ===================-->
+
+<script id="movieTemplate" type="text/x-jquery-tmpl">
+	<h2>${Title}</h2>
+
+	{{wrap "#tabsTemplate"}}
+		<h3>Details</h3>
+		<div>
+
+			Title: <span>${Title}</span><br/>
+			<h4>Languages:</h4>
+			{{each Languages}}<span>${$value.Name}</span><br/>{{/each}}
+
+		</div>
+
+		<h3>Description</h3>
+		<div>
+
+			<img src='http://cdn-4.nflximg.com/US/boxshots/large/5670434.jpg'/><br/><br/>
+			Director Stanley Kubrick's<br/> final film.
+
+		</div>
+
+		<h3>Comments</h3>
+		<div>
+
+			<ul>
+				<li>Great film...</li>
+				<li>The best</li>
+				<li>So boring, I couldn't keep my eyes open</li>
+			</ul>
+
+		</div>
+	{{/wrap}}
+</script>
+
+<script id="tabsTemplate" type="text/x-jquery-tmpl">
+	<table class="tabsView"><tbody>
+		<tr>
+			{{each $item.html("h3", true)}}
+				<th class="header_${$index === selectedTab}">
+					${$value}
+				</th>
+			{{/each}}
+		</tr>
+		<tr><td colspan='${$item.html("h3").length}'>
+			<div class="body">
+				{{html $item.html("div")[selectedTab]}}
+			</div>
+		</td></tr>
+	</tbody></table>
+</script>
+
+<br />
+<div id="tabsView">..loading</div><br />
+
+<script type="text/javascript">
+
+	/* Track the selected tab index for inner and outer tab views */
+	var movie = {
+			Title: "Meet Joe Black",
+			Languages: [
+				{ Name: "English" },
+				{ Name: "French" }
+			],
+			selectedTab: 0
+		};
+
+
+	function refresh() {
+		$( "#tabsView" ).empty();
+		$( "#movieTemplate" ).tmpl( movie )
+			.appendTo( "#tabsView" );
+	}
+
+	/* Render tabs view */
+	refresh();
+
+	$( "#tabsView" )
+		.delegate( ".tabsView th", "click", function() {
+			/* Set the selected tab index to this tab */
+			$.tmplItem( this ).data.selectedTab = $(this).index();
+
+			/* update the rendering */
+			refresh();
+		});
+
+</script>
+
+<!--================ End of Demo Section ================-->
+
+</body>
+</html>
diff --git a/jquery.tmpl.js b/jquery.tmpl.js
index 9003a1b..7d130d2 100644
--- a/jquery.tmpl.js
+++ b/jquery.tmpl.js
@@ -1,12 +1,21 @@
 /*!
- * jQuery Templates Plugin
+ * jQuery Templates Plugin 1.0.0pre
  * http://github.com/jquery/jquery-tmpl
+ * Requires jQuery 1.4.2
  *
- * Copyright Software Freedom Conservancy, Inc.
+ * Copyright 2011, Software Freedom Conservancy, Inc.
  * Dual licensed under the MIT or GPL Version 2 licenses.
  * http://jquery.org/license
  */
-(function( jQuery, undefined ){
+(function( factory ) {
+	if (typeof define === 'function' && define.amd) {
+		// Loading from AMD script loader. Register as an anonymous module.
+		define( ['jquery'], factory );
+	} else {
+		// Browser using plain <script> tag
+		factory( jQuery );
+	}
+}(function( jQuery ){
 	var oldManip = jQuery.fn.domManip, tmplItmAtt = "_tmplitem", htmlExpr = /^[^<]*(<[\w\W]+>)[^>]*$|\{\{\! /,
 		newTmplItems = {}, wrappedItems = {}, appendToTmplItems, topTmplItem = { key: 0, data: {} }, itemKey = 0, cloneIndex = 0, stack = [];
 
@@ -15,7 +24,7 @@
 		// The content field is a hierarchical array of strings and nested items (to be
 		// removed and replaced by nodes field of dom elements, once inserted in DOM).
 		var newItem = {
-			data: data || (parentItem ? parentItem.data : {}),
+			data: data || (data === 0 || data === false) ? data : (parentItem ? parentItem.data : {}),
 			_wrap: parentItem ? parentItem._wrap : null,
 			tmpl: null,
 			parent: parentItem || null,
@@ -27,7 +36,7 @@
 			update: tiUpdate
 		};
 		if ( options ) {
-			jQuery.extend( newItem, options, { nodes: [], parent: parentItem } );
+			jQuery.extend( newItem, options, { nodes: [], parent: parentItem });
 		}
 		if ( fn ) {
 			// Build the hierarchical content to be used during insertion into DOM
@@ -60,7 +69,7 @@
 				for ( i = 0, l = insert.length; i < l; i++ ) {
 					cloneIndex = i;
 					elems = (i > 0 ? this.clone(true) : this).get();
-					jQuery.fn[ original ].apply( jQuery(insert[i]), elems );
+					jQuery( insert[i] )[ original ]( elems );
 					ret = ret.concat( elems );
 				}
 				cloneIndex = 0;
@@ -91,14 +100,9 @@
 		},
 
 		domManip: function( args, table, callback, options ) {
-			// This appears to be a bug in the appendTo, etc. implementation
-			// it should be doing .call() instead of .apply(). See #6227
-			if ( args[0] && args[0].nodeType ) {
-				var dmArgs = jQuery.makeArray( arguments ), argsLength = args.length, i = 0, tmplItem;
-				while ( i < argsLength && !(tmplItem = jQuery.data( args[i++], "tmplItem" ))) {}
-				if ( argsLength > 1 ) {
-					dmArgs[0] = [jQuery.makeArray( args )];
-				}
+			if ( args[0] && jQuery.isArray( args[0] )) {
+				var dmArgs = jQuery.makeArray( arguments ), elems = args[0], elemsLength = elems.length, i = 0, tmplItem;
+				while ( i < elemsLength && !(tmplItem = jQuery.data( elems[i++], "tmplItem" ))) {}
 				if ( tmplItem && cloneIndex ) {
 					dmArgs[2] = function( fragClone ) {
 						// Handler called by oldManip when rendered template has been inserted into DOM.
@@ -147,7 +151,7 @@
 			if ( options && options.wrapped ) {
 				updateWrapped( options, options.wrapped );
 			}
-			ret = jQuery.isArray( data ) ? 
+			ret = jQuery.isArray( data ) ?
 				jQuery.map( data, function( dataItem ) {
 					return dataItem ? newTmplItem( options, parentItem, tmpl, dataItem ) : null;
 				}) :
@@ -181,21 +185,25 @@
 				// Compile template and associate with name
 				if ( typeof tmpl === "string" ) {
 					// This is an HTML string being passed directly in.
-					tmpl = buildTmplFn( tmpl )
+					tmpl = buildTmplFn( tmpl );
 				} else if ( tmpl instanceof jQuery ) {
 					tmpl = tmpl[0] || {};
 				}
 				if ( tmpl.nodeType ) {
 					// If this is a template block, use cached copy, or generate tmpl function and cache.
 					tmpl = jQuery.data( tmpl, "tmpl" ) || jQuery.data( tmpl, "tmpl", buildTmplFn( tmpl.innerHTML ));
+					// Issue: In IE, if the container element is not a script block, the innerHTML will remove quotes from attribute values whenever the value does not include white space.
+					// This means that foo="${x}" will not work if the value of x includes white space: foo="${x}" -> foo=value of x.
+					// To correct this, include space in tag: foo="${ x }" -> foo="value of x"
 				}
 				return typeof name === "string" ? (jQuery.template[name] = tmpl) : tmpl;
 			}
 			// Return named compiled template
-			return name ? (typeof name !== "string" ? jQuery.template( null, name ): 
-				(jQuery.template[name] || 
-					// If not in map, treat as a selector. (If integrated with core, use quickExpr.exec) 
-					jQuery.template( null, htmlExpr.test( name ) ? name : jQuery( name )))) : null; 
+			return name ? (typeof name !== "string" ? jQuery.template( null, name ):
+				(jQuery.template[name] ||
+					// If not in map, and not containing at least on HTML tag, treat as a selector.
+					// (If integrated with core, use quickExpr.exec)
+					jQuery.template( null, htmlExpr.test( name ) ? name : jQuery( name )))) : null;
 		},
 
 		encode: function( text ) {
@@ -208,15 +216,15 @@
 		tag: {
 			"tmpl": {
 				_default: { $2: "null" },
-				open: "if($notnull_1){_=_.concat($item.nest($1,$2));}"
+				open: "if($notnull_1){__=__.concat($item.nest($1,$2));}"
 				// tmpl target parameter can be of type function, so use $1, not $1a (so not auto detection of functions)
-				// This means that {{tmpl foo}} treats foo as a template (which IS a function). 
+				// This means that {{tmpl foo}} treats foo as a template (which IS a function).
 				// Explicit parens can be used if foo is a function that returns a template: {{tmpl foo()}}.
 			},
 			"wrap": {
 				_default: { $2: "null" },
-				open: "$item.calls(_,$1,$2);_=[];",
-				close: "call=$item.calls();_=call._.concat($item.wrap(call,_));"
+				open: "$item.calls(__,$1,$2);__=[];",
+				close: "call=$item.calls();__=call._.concat($item.wrap(call,__));"
 			},
 			"each": {
 				_default: { $2: "$index, $value" },
@@ -232,13 +240,13 @@
 				open: "}else if(($notnull_1) && $1a){"
 			},
 			"html": {
-				// Unecoded expression evaluation. 
-				open: "if($notnull_1){_.push($1a);}"
+				// Unecoded expression evaluation.
+				open: "if($notnull_1){__.push($1a);}"
 			},
 			"=": {
 				// Encoded expression evaluation. Abbreviated form is ${}.
 				_default: { $1: "$data" },
-				open: "if($notnull_1){_.push($.encode($1a));}"
+				open: "if($notnull_1){__.push($.encode($1a));}"
 			},
 			"!": {
 				// Comment tag. Skipped by parser
@@ -271,16 +279,16 @@
 	//========================== Private helper functions, used by code above ==========================
 
 	function build( tmplItem, nested, content ) {
-		// Convert hierarchical content into flat string array 
+		// Convert hierarchical content into flat string array
 		// and finally return array of fragments ready for DOM insertion
 		var frag, ret = content ? jQuery.map( content, function( item ) {
-			return (typeof item === "string") ? 
+			return (typeof item === "string") ?
 				// Insert template item annotations, to be converted to jQuery.data( "tmplItem" ) when elems are inserted into DOM.
 				(tmplItem.key ? item.replace( /(<\w+)(?=[\s>])(?![^>]*_tmplitem)([^>]*)/g, "$1 " + tmplItmAtt + "=\"" + tmplItem.key + "\" $2" ) : item) :
 				// This is a child template item. Build nested template.
 				build( item, tmplItem, item._ctnt );
-		}) : 
-		// If content is not defined, insert tmplItem directly. Not a template item. May be a string, or a string array, e.g. from {{html $item.html()}}. 
+		}) :
+		// If content is not defined, insert tmplItem directly. Not a template item. May be a string, or a string array, e.g. from {{html $item.html()}}.
 		tmplItem;
 		if ( nested ) {
 			return ret;
@@ -315,10 +323,11 @@
 	// Generate a reusable function that will serve to render a template against data
 	function buildTmplFn( markup ) {
 		return new Function("jQuery","$item",
-			"var $=jQuery,call,_=[],$data=$item.data;" +
+			// Use the variable __ to hold a string array while building the compiled template. (See https://github.com/jquery/jquery-tmpl/issues#issue/10).
+			"var $=jQuery,call,__=[],$data=$item.data;" +
 
 			// Introduce the data as local variables using with(){}
-			"with($data){_.push('" +
+			"with($data){__.push('" +
 
 			// Convert the template into pure JavaScript
 			jQuery.trim(markup)
@@ -329,7 +338,7 @@
 				function( all, slash, type, fnargs, target, parens, args ) {
 					var tag = jQuery.tmpl.tag[ type ], def, expr, exprAutoFnDetect;
 					if ( !tag ) {
-						throw "Template command not found: " + type;
+						throw "Unknown template tag: " + type;
 					}
 					def = tag._default || [];
 					if ( parens && !/\w$/.test(target)) {
@@ -337,36 +346,30 @@
 						parens = "";
 					}
 					if ( target ) {
-						target = unescape( target ); 
+						target = unescape( target );
 						args = args ? ("," + unescape( args ) + ")") : (parens ? ")" : "");
 						// Support for target being things like a.toLowerCase();
 						// In that case don't call with template item as 'this' pointer. Just evaluate...
-						expr = parens ? (target.indexOf(".") > -1 ? target + parens : ("(" + target + ").call($item" + args)) : target;
+						expr = parens ? (target.indexOf(".") > -1 ? target + unescape( parens ) : ("(" + target + ").call($item" + args)) : target;
 						exprAutoFnDetect = parens ? expr : "(typeof(" + target + ")==='function'?(" + target + ").call($item):(" + target + "))";
 					} else {
 						exprAutoFnDetect = expr = def.$1 || "null";
 					}
 					fnargs = unescape( fnargs );
-					return "');" + 
+					return "');" +
 						tag[ slash ? "close" : "open" ]
 							.split( "$notnull_1" ).join( target ? "typeof(" + target + ")!=='undefined' && (" + target + ")!=null" : "true" )
 							.split( "$1a" ).join( exprAutoFnDetect )
 							.split( "$1" ).join( expr )
-							.split( "$2" ).join( fnargs ?
-								fnargs.replace( /\s*([^\(]+)\s*(\((.*?)\))?/g, function( all, name, parens, params ) {
-									params = params ? ("," + params + ")") : (parens ? ")" : "");
-									return params ? ("(" + name + ").call($item" + params) : all;
-								})
-								: (def.$2||"")
-							) +
-						"_.push('";
+							.split( "$2" ).join( fnargs || def.$2 || "" ) +
+						"__.push('";
 				}) +
-			"');}return _;"
+			"');}return __;"
 		);
 	}
 	function updateWrapped( options, wrapped ) {
-		// Build the wrapped content. 
-		options._wrap = build( options, true, 
+		// Build the wrapped content.
+		options._wrap = build( options, true,
 			// Suport imperative scenario in which options.wrapped can be set to a selector or an HTML string.
 			jQuery.isArray( wrapped ) ? wrapped : [htmlExpr.test( wrapped ) ? wrapped : jQuery( wrapped ).html()]
 		).join("");
@@ -407,7 +410,7 @@
 					if ( !(tmplItem = newTmplItems[key]) ) {
 						// The item is for wrapped content, and was copied from the temporary parent wrappedItem.
 						tmplItem = wrappedItems[key];
-						tmplItem = newTmplItem( tmplItem, newTmplItems[pntNode]||wrappedItems[pntNode], null, true );
+						tmplItem = newTmplItem( tmplItem, newTmplItems[pntNode]||wrappedItems[pntNode] );
 						tmplItem.key = ++itemKey;
 						newTmplItems[itemKey] = tmplItem;
 					}
@@ -426,9 +429,9 @@
 			}
 			if ( tmplItem ) {
 				pntItem = tmplItem;
-				// Find the template item of the parent element. 
+				// Find the template item of the parent element.
 				// (Using !=, not !==, since pntItem.key is number, and pntNode may be a string)
-				while ( pntItem && pntItem.key != pntNode ) { 
+				while ( pntItem && pntItem.key != pntNode ) {
 					// Add this element as a top-level node for this rendered template item, as well as for any
 					// ancestor items between this item and the item of its parent element
 					pntItem.nodes.push( el );
@@ -442,8 +445,8 @@
 			}
 			function cloneTmplItem( key ) {
 				key = key + keySuffix;
-				tmplItem = newClonedItems[key] = 
-					(newClonedItems[key] || newTmplItem( tmplItem, newTmplItems[tmplItem.parent.key + keySuffix] || tmplItem.parent, null, true ));
+				tmplItem = newClonedItems[key] =
+					(newClonedItems[key] || newTmplItem( tmplItem, newTmplItems[tmplItem.parent.key + keySuffix] || tmplItem.parent ));
 			}
 		}
 	}
@@ -466,7 +469,7 @@
 		// nested template, using {{wrap}} tag
 		var options = call.options || {};
 		options.wrapped = wrapped;
-		// Apply the template, which may incorporate wrapped content, 
+		// Apply the template, which may incorporate wrapped content,
 		return jQuery.tmpl( jQuery.template( call.tmpl ), call.data, options, call.item );
 	}
 
@@ -486,4 +489,4 @@
 		jQuery.tmpl( null, null, null, this).insertBefore( coll[0] );
 		jQuery( coll ).remove();
 	}
-})( jQuery );
+}));
diff --git a/jquery.tmpl.min.js b/jquery.tmpl.min.js
new file mode 100644
index 0000000..0a72833
--- /dev/null
+++ b/jquery.tmpl.min.js
@@ -0,0 +1,10 @@
+/*!
+ * jQuery Templates Plugin 1.0.0pre
+ * http://github.com/jquery/jquery-tmpl
+ * Requires jQuery 1.4.2
+ *
+ * Copyright 2011, Software Freedom Conservancy, Inc.
+ * Dual licensed under the MIT or GPL Version 2 licenses.
+ * http://jquery.org/license
+ */
+!function(t){"function"==typeof define&&define.amd?define(["jquery"],t):t(jQuery)}(function(t){function e(e,n,l,a){var r={data:a||0===a||a===!1?a:n?n.data:{},_wrap:n?n._wrap:null,tmpl:null,parent:n||null,nodes:[],calls:u,nest:c,wrap:f,html:m,update:s};return e&&t.extend(r,e,{nodes:[],parent:n}),l&&(r.tmpl=l,r._ctnt=r._ctnt||r.tmpl(t,r),r.key=++w,(T.length?g:y)[w]=r),r}function n(e,a,r){var i,p=r?t.map(r,function(t){return"string"==typeof t?e.key?t.replace(/(<\w+)(?=[\s>])(?![^>]*_tmplitem)([^>]*)/g,"$1 "+_+'="'+e.key+'" $2'):t:n(t,e,t._ctnt)}):e;return a?p:(p=p.join(""),p.replace(/^\s*([^<\s][^<]*)?(<[\w\W]+>)([^>]*[^>\s])?\s*$/,function(e,n,a,r){i=t(a).get(),o(i),n&&(i=l(n).concat(i)),r&&(i=i.concat(l(r)))}),i?i:l(p))}function l(e){var n=document.createElement("div");return n.innerHTML=e,t.makeArray(n.childNodes)}function a(e){return Function("jQuery","$item","var $=jQuery,call,__=[],$data=$item.data;with($data){__.push('"+t.trim(e).replace(/([\\'])/g,"\\$1").replace(/[\r\t\n]/g," ").replace(/\$\{([^\}]*)\}/g,"{{= $1}}").replace(/\{\{(\/?)(\w+|.)(?:\(((?:[^\}]|\}(?!\}))*?)?\))?(?:\s+(.*?)?)?(\(((?:[^\}]|\}(?!\}))*?)\))?\s*\}\}/g,function(e,n,l,a,r,p,o){var u,c,f,m=t.tmpl.tag[l];if(!m)throw"Unknown template tag: "+l;return u=m._default||[],p&&!/\w$/.test(r)&&(r+=p,p=""),r?(r=i(r),o=o?","+i(o)+")":p?")":"",c=p?r.indexOf(".")>-1?r+i(p):"("+r+").call($item"+o:r,f=p?c:"(typeof("+r+")==='function'?("+r+").call($item):("+r+"))"):f=c=u.$1||"null",a=i(a),"');"+m[n?"close":"open"].split("$notnull_1").join(r?"typeof("+r+")!=='undefined' && ("+r+")!=null":"true").split("$1a").join(f).split("$1").join(c).split("$2").join(a||u.$2||"")+"__.push('"})+"');}return __;")}function r(e,l){e._wrap=n(e,!0,t.isArray(l)?l:[h.test(l)?l:t(l).html()]).join("")}function i(t){return t?t.replace(/\\'/g,"'").replace(/\\\\/g,"\\"):null}function p(t){var e=document.createElement("div");return e.appendChild(t.cloneNode(!0)),e.innerHTML}function o(n){function l(n){function l(t){t+=u,i=c[t]=c[t]||e(i,y[i.parent.key+u]||i.parent)}var a,r,i,p,o=n;if(p=n.getAttribute(_)){for(;o.parentNode&&1===(o=o.parentNode).nodeType&&!(a=o.getAttribute(_)););a!==p&&(o=o.parentNode?11===o.nodeType?0:o.getAttribute(_)||0:0,(i=y[p])||(i=g[p],i=e(i,y[o]||g[o]),i.key=++w,y[w]=i),k&&l(p)),n.removeAttribute(_)}else k&&(i=t.data(n,"tmplItem"))&&(l(i.key),y[i.key]=i,o=t.data(n.parentNode,"tmplItem"),o=o?o.key:0);if(i){for(r=i;r&&r.key!=o;)r.nodes.push(n),r=r.parent;delete i._ctnt,delete i._wrap,t.data(n,"tmplItem",i)}}var a,r,i,p,o,u="_"+k,c={};for(i=0,p=n.length;p>i;i++)if(1===(a=n[i]).nodeType){for(r=a.getElementsByTagName("*"),o=r.length-1;o>=0;o--)l(r[o]);l(a)}}function u(t,e,n,l){return t?void T.push({_:t,tmpl:e,item:this,data:n,options:l}):T.pop()}function c(e,n,l){return t.tmpl(t.template(e),n,l,this)}function f(e,n){var l=e.options||{};return l.wrapped=n,t.tmpl(t.template(e.tmpl),e.data,l,e.item)}function m(e,n){var l=this._wrap;return t.map(t(t.isArray(l)?l.join(""):l).filter(e||"*"),function(t){return n?t.innerText||t.textContent:t.outerHTML||p(t)})}function s(){var e=this.nodes;t.tmpl(null,null,null,this).insertBefore(e[0]),t(e).remove()}var d,$=t.fn.domManip,_="_tmplitem",h=/^[^<]*(<[\w\W]+>)[^>]*$|\{\{\! /,y={},g={},v={key:0,data:{}},w=0,k=0,T=[];t.each({appendTo:"append",prependTo:"prepend",insertBefore:"before",insertAfter:"after",replaceAll:"replaceWith"},function(e,n){t.fn[e]=function(l){var a,r,i,p,o=[],u=t(l),c=1===this.length&&this[0].parentNode;if(d=y||{},c&&11===c.nodeType&&1===c.childNodes.length&&1===u.length)u[n](this[0]),o=this;else{for(r=0,i=u.length;i>r;r++)k=r,a=(r>0?this.clone(!0):this).get(),t(u[r])[n](a),o=o.concat(a);k=0,o=this.pushStack(o,e,u.selector)}return p=d,d=null,t.tmpl.complete(p),o}}),t.fn.extend({tmpl:function(e,n,l){return t.tmpl(this[0],e,n,l)},tmplItem:function(){return t.tmplItem(this[0])},template:function(e){return t.template(e,this[0])},domManip:function(e,n,l){if(e[0]&&t.isArray(e[0])){for(var a,r=t.makeArray(arguments),i=e[0],p=i.length,o=0;p>o&&!(a=t.data(i[o++],"tmplItem")););a&&k&&(r[2]=function(e){t.tmpl.afterManip(this,e,l)}),$.apply(this,r)}else $.apply(this,arguments);return k=0,d||t.tmpl.complete(y),this}}),t.extend({tmpl:function(l,a,i,p){var o,u=!p;if(u)p=v,l=t.template[l]||t.template(null,l),g={};else if(!l)return l=p.tmpl,y[p.key]=p,p.nodes=[],p.wrapped&&r(p,p.wrapped),t(n(p,null,p.tmpl(t,p)));return l?("function"==typeof a&&(a=a.call(p||{})),i&&i.wrapped&&r(i,i.wrapped),o=t.isArray(a)?t.map(a,function(t){return t?e(i,p,l,t):null}):[e(i,p,l,a)],u?t(n(p,null,o)):o):[]},tmplItem:function(e){var n;for(e instanceof t&&(e=e[0]);e&&1===e.nodeType&&!(n=t.data(e,"tmplItem"))&&(e=e.parentNode););return n||v},template:function(e,n){return n?("string"==typeof n?n=a(n):n instanceof t&&(n=n[0]||{}),n.nodeType&&(n=t.data(n,"tmpl")||t.data(n,"tmpl",a(n.innerHTML))),"string"==typeof e?t.template[e]=n:n):e?"string"!=typeof e?t.template(null,e):t.template[e]||t.template(null,h.test(e)?e:t(e)):null},encode:function(t){return(""+t).split("<").join("&lt;").split(">").join("&gt;").split('"').join("&#34;").split("'").join("&#39;")}}),t.extend(t.tmpl,{tag:{tmpl:{_default:{$2:"null"},open:"if($notnull_1){__=__.concat($item.nest($1,$2));}"},wrap:{_default:{$2:"null"},open:"$item.calls(__,$1,$2);__=[];",close:"call=$item.calls();__=call._.concat($item.wrap(call,__));"},each:{_default:{$2:"$index, $value"},open:"if($notnull_1){$.each($1a,function($2){with(this){",close:"}});}"},"if":{open:"if(($notnull_1) && $1a){",close:"}"},"else":{_default:{$1:"true"},open:"}else if(($notnull_1) && $1a){"},html:{open:"if($notnull_1){__.push($1a);}"},"=":{_default:{$1:"$data"},open:"if($notnull_1){__.push($.encode($1a));}"},"!":{open:""}},complete:function(){y={}},afterManip:function(e,n,l){var a=11===n.nodeType?t.makeArray(n.childNodes):1===n.nodeType?[n]:[];l.call(e,n),o(a),k++}})});
\ No newline at end of file
diff --git a/jquery.tmplPlus.js b/jquery.tmplPlus.js
index 7daae86..38e6257 100644
--- a/jquery.tmplPlus.js
+++ b/jquery.tmplPlus.js
@@ -1,13 +1,22 @@
 /*!
+ * tmplPlus.js: for jQuery Templates Plugin 1.0.0pre
  * Additional templating features or support for more advanced/less common scenarios.
- * Requires jquery.tmpl.js 
+ * Requires jquery.tmpl.js
  * http://github.com/jquery/jquery-tmpl
  *
- * Copyright Software Freedom Conservancy, Inc.
+ * Copyright 2011, Software Freedom Conservancy, Inc.
  * Dual licensed under the MIT or GPL Version 2 licenses.
  * http://jquery.org/license
  */
-(function (jQuery) {
+(function( factory ) {
+    if (typeof define === 'function' && define.amd) {
+        // Loading from AMD script loader. Register as an anonymous module.
+        define( ['jquery'], factory );
+    } else {
+        // Browser using plain <script> tag
+        factory( jQuery );
+    }
+}(function( jQuery ){
 	var oldComplete = jQuery.tmpl.complete, oldManip = jQuery.fn.domManip;
 
 	// Override jQuery.tmpl.complete in order to provide rendered event.
@@ -15,13 +24,13 @@
 		var tmplItem;
 		oldComplete( tmplItems);
 		for ( tmplItem in tmplItems ) {
-			tmplItem =  tmplItems[tmplItem]; 
+			tmplItem =  tmplItems[tmplItem];
 			if ( tmplItem.addedTmplItems && jQuery.inArray( tmplItem, tmplItem.addedTmplItems ) === -1  ) {
 				tmplItem.addedTmplItems.push( tmplItem );
 			}
 		}
 		for ( tmplItem in tmplItems ) {
-			tmplItem =  tmplItems[tmplItem]; 
+			tmplItem =  tmplItems[tmplItem];
 			// Raise rendered event
 			if ( tmplItem.rendered ) {
 				tmplItem.rendered( tmplItem );
@@ -31,11 +40,11 @@
 
 	jQuery.extend({
 		tmplCmd: function( command, data, tmplItems ) {
-			var retTmplItems = [], before; 
+			var retTmplItems = [], before;
 			function find( data, tmplItems ) {
 				var found = [], tmplItem, ti, tl = tmplItems.length, dataItem, di = 0, dl = data.length;
 				for ( ; di < dl; ) {
-					dataItem = data[di++]; 
+					dataItem = data[di++];
 					for ( ti = 0; ti < tl; ) {
 						tmplItem = tmplItems[ti++];
 						if ( tmplItem.data === dataItem ) {
@@ -53,7 +62,7 @@
 				case "replace":
 					data.reverse();
 			}
-			jQuery.each( tmplItems ? find( data, tmplItems ) : data, function( i, tmplItem ) { 
+			jQuery.each( tmplItems ? find( data, tmplItems ) : data, function( i, tmplItem ) {
 				coll = tmplItem.nodes;
 				switch ( command ) {
 					case "update":
@@ -66,8 +75,8 @@
 						}
 						break;
 					case "replace":
-						before = before ? 
-							jQuery( coll ).insertBefore( before )[0] : 
+						before = before ?
+							jQuery( coll ).insertBefore( before )[0] :
 							jQuery( coll ).appendTo( coll[0].parentNode )[0];
 						retTmplItems.unshift( tmplItem );
 				}
@@ -95,4 +104,4 @@
 			return oldManip.apply( this, arguments );
 		}
 	});
-})(jQuery);
+}));
diff --git a/jquery.tmplPlus.min.js b/jquery.tmplPlus.min.js
new file mode 100644
index 0000000..3b6bd44
--- /dev/null
+++ b/jquery.tmplPlus.min.js
@@ -0,0 +1,11 @@
+/*!
+ * tmplPlus.js: for jQuery Templates Plugin 1.0.0pre
+ * Additional templating features or support for more advanced/less common scenarios.
+ * Requires jquery.tmpl.js
+ * http://github.com/jquery/jquery-tmpl
+ *
+ * Copyright 2011, Software Freedom Conservancy, Inc.
+ * Dual licensed under the MIT or GPL Version 2 licenses.
+ * http://jquery.org/license
+ */
+!function(e){"function"==typeof define&&define.amd?define(["jquery"],e):e(jQuery)}(function(e){var t=e.tmpl.complete,n=e.fn.domManip;e.tmpl.complete=function(n){var r;t(n);for(r in n)r=n[r],r.addedTmplItems&&-1===e.inArray(r,r.addedTmplItems)&&r.addedTmplItems.push(r);for(r in n)r=n[r],r.rendered&&r.rendered(r)},e.extend({tmplCmd:function(t,n,r){function a(e,t){for(var n,r,a,l=[],p=t.length,o=0,i=e.length;i>o;)for(a=e[o++],r=0;p>r;)n=t[r++],n.data===a&&l.push(n);return l}var l,p=[];switch(n=e.isArray(n)?n:[n],t){case"find":return a(n,r);case"replace":n.reverse()}return e.each(r?a(n,r):n,function(n,a){switch(coll=a.nodes,t){case"update":a.update();break;case"remove":e(coll).remove(),r&&r.splice(e.inArray(a,r),1);break;case"replace":l=l?e(coll).insertBefore(l)[0]:e(coll).appendTo(coll[0].parentNode)[0],p.unshift(a)}}),p}}),e.fn.extend({domManip:function(t,r,a){var l,p=t[1],o=t[0];return t.length<2||"object"!=typeof p||p.nodeType||p instanceof e?n.apply(this,arguments):(l=e.makeArray(arguments),l[0]=[e.tmpl(e.template(o),p,t[2],t[3])],l[2]=function(t){e.tmpl.afterManip(this,t,a)},n.apply(this,l))}})});
\ No newline at end of file
diff --git a/tests/core.js b/tests/core.js
new file mode 100644
index 0000000..a35fb93
--- /dev/null
+++ b/tests/core.js
@@ -0,0 +1,395 @@
+var testData = {
+		one: "first",
+		two: "second",
+		v: "test",
+		arr: ["AA","BB","CC"],
+		dict: {"leovinus":"this","scraliontis":"that","brobostigon":"other"},
+		fun: function () {
+			return 'RETURNED';
+		},
+		html: '<a>'
+};
+
+var R = function ( tmpl, data ) {
+	try {
+		return jQuery.tmpl( tmpl, data ).text();
+	}
+	catch ( e ) {
+		if ( typeof e === 'string' ) {
+			return 'ERROR: ' + e;
+		}
+		return e;
+	}
+};
+
+function test_handler( test_name, res, exp ) {
+	var is_err = ($.isFunction(exp) && exp.prototype instanceof Error);
+	if ( is_err && res instanceof exp ) {
+		ok( res instanceof exp, test_name );
+	}
+	else {
+		same( res, exp, test_name );
+	}
+}
+
+// these are used throughout to test if tag blocks suppress them
+jQuery.tmpl.tag.syntax_error = { open: "throw SyntaxError('test syntax error');" };
+jQuery.tmpl.tag.reference_error = { open: "throw ReferenceError('test reference error');" };
+jQuery.tmpl.tag.type_error = { open: "throw TypeError('test type error');" };
+
+module("Basics");
+
+
+	test("Basic Function", function() {
+		test_handler( 'plain text passes through untouched', R('lorem ipsum', testData), 'lorem ipsum' );
+		// TODO fixme
+		//test_handler( 'whitespace is left untouched', R('\n\tlorem\n\n\tipsum \t', testData), '\n\tlorem\n\n\tipsum \t' );
+		test_handler( 'simple variable output', R("${ one }", testData), "first" );
+
+		// throw errors with incomplete syntax
+		test_handler( 'multi word variable tag', R("${ a b c }}"), SyntaxError );
+		test_handler( "_ (underscore) cannot by used by data", R('${ _ }', {'_':'foo'}), TypeError );
+		test_handler( "$ cannot be used by data", R('${ $ }', {'$':'foo'}), TypeError );
+
+	});
+
+	// test errors are passed back correctly
+	test("Error Passing", function(){
+		test_handler( 'syntax', R("{{syntax_error }}", testData), SyntaxError );
+		test_handler( 'reference', R("{{reference_error }}", testData), ReferenceError );
+		test_handler( 'type', R("{{type_error }}", testData), TypeError );
+	});
+
+	// newlines should work because: ${ foo + "\n" } and they have whitespace management benefits
+	test("Newlines / Escaping", function(){
+		test_handler( "newlines do not kill tags", R('${\n \none\n }', testData), 'first');
+		// TODO fixme
+		//test_handler( "newlines in strings don't kill tags", R('${ "on\ne" }', testData), 'on\ne' );
+		//test_handler( "returns do not kill tags", R('${\r \r\none\r\n }', testData), 'first');
+		//test_handler( "returns in strings don't kill tags", R('${ "on\re" }', testData), 'on\re' );
+		//test_handler( "slashes in strings don't kill tags", R('${ "on\\e" }', testData), 'on\\e');
+		//test_handler( "newlines don't kill parsing", R('a\nb\nc${ 8 }.'), 'a\nb\nc8.');
+	});
+
+	test("Empty Tag", function() {
+		// TODO fixme
+		//test_handler( 'default', R("{{}}", testData), '{{}}' );
+		//test_handler( 'with whitespace', R("{{ }}"), '{{ }}' );
+		//test_handler( 'with tabs whitespace', R("{{\t\t}}"), '{{\t\t}}' );
+	});
+
+	test("Incorrect Nesting", function() {
+		test_handler( 'default', R("{{if 1}}{{if 1}}{{/if}}", testData), SyntaxError );
+		test_handler( 'extra /if', R("{{if 1}}{{/if}}{{/if}}", testData), SyntaxError );
+		test_handler( 'but terminated', R("{{if 1}}{{each arr}}{{/if}}{{/each}}", testData), SyntaxError );
+	});
+
+	test("Ignore Malformed Tags", function() {
+		test_handler( 'a {{one } b', R("a {{one } b", testData), 'a {{one } b' );
+		test_handler( 'first} {{second }', R("${ one }} {{two }", testData), 'first} {{two }' );
+		test_handler( '{{one }', R('{{one }', testData), '{{one }' );
+	});
+
+	// reserved words
+	test("Reserved Words", function(){
+		// TODO fixme
+		//test_handler( "Disallow new operator", R('${ new Object() }',{}), SyntaxError );
+		//test_handler( "Disallow delete operator", R('${ delete a }',{a:1}), SyntaxError );
+		test_handler( "Disallow function operator", R('${ function(){} }',{}), SyntaxError );
+		test_handler( "Disallow return", R('${ return a }',{a:1}), SyntaxError );
+		test_handler( "Disallow for", R('${ for a }',{a:1}), SyntaxError );
+		test_handler( "Disallow do/while", R('${ do{ a }while(a) }',{a:1}), SyntaxError );
+		test_handler( "Disallow if", R('${ if a }',{a:1}), SyntaxError );
+		test_handler( "Disallow try/catch", R('${ try{b.s}catch(e){} }',{a:1}), SyntaxError );
+		test_handler( "Disallow return keyword", R('${ return a }',{a:1}), SyntaxError );
+		test_handler( "Disallow with keyword", R('${ with (s) }',{a:1}), SyntaxError );
+		test_handler( "Disallow throw keyword", R('${ throw "foo" }',{a:1}), SyntaxError );
+	});
+
+	// these tests are a bit awkward because caching is done in $.render, not $.tmpl
+	test("Caching via $.template() and .template()", function() {
+		$.template('nametmpl', '<span>name: ${ v }</span>' );
+		test_handler( "using a named template", $.tmpl('nametmpl', testData).text(), 'name: test' );
+
+		var $elm = $( "<span>name: ${ v }</span>" );
+		$elm.template('nametmpl2');
+		test_handler( "using a named template created from a node", $.tmpl('nametmpl2', testData).text(), 'name: test' );
+	});
+
+	test("Bracketed Accessors", function(){
+		test_handler( "foo[\"bar\"]", R('${ foo["bar"] }',{foo:{bar:'baz'}}), 'baz' );
+		test_handler( "foo['bar']", R("${ foo['bar'] }",{foo:{bar:'baz'}}), 'baz' );
+	});
+
+	test("Escaping", function(){
+		// TODO fixme
+		//test_handler( 'echoing escapes html', R("${ 'foo<div>bar</div>baz' }"), 'foo&lt;div&gt;bar&lt;/div&gt;baz' );
+		//test_handler( 'echoing escapes html (lookup)', R("${ r }", {r:'foo<div>bar</div>baz'}), 'foo&lt;div&gt;bar&lt;/div&gt;baz' );
+		//test_handler( 'echoing escapes ampersands 1', R("${ '&' }"), '&amp;' );
+		//test_handler( 'echoing escapes ampersands 2', R("${ '&amp;' }"), '&amp;amp;' );
+		//test_handler( 'echoing escapes & < >', R("${ '-<&>-<&>-' }"), '-&lt;&amp;&gt;-&lt;&amp;&gt;-' );
+	});
+
+	test("Comments", function() {
+		test_handler( "comments are removed", R('A{{! comments test }}B', testData), "AB" );
+		test_handler( "comments are removed (2)", R('{{! inky }}foo{{! blinky }}', testData), 'foo' );
+		// TODO fixme
+		return;
+		test_handler( "comments may include string of comments", R('A{{! comments "}}" test }}B', testData), "AB" );
+		test_handler( "comments cannot nest other comments", R('A{# C{# E #}D #}B', testData), "AD #}B" );
+		test_handler( "comments may include strings with escapes (double)", R('A{# comments "str\"ing" test #}B', testData), "AB" );
+		test_handler( "comments may include strings with escapes (single)", R("A{# comments 'str\'ing' test #}B", testData), "AB" );
+		test_handler( "comments may include tags", R("A{# {{= v }} #}B", testData), "AB" );
+		test_handler( "comments may span lines", R("A{# \ncomments test\n #}B", testData), "AB" );
+
+		test_handler( "comments may contain invalid content (invalid tag)", R('1{{! {{ INVALID_TAG }} }}2', testData), '12' );
+		test_handler( "comments may contain invalid content (stray end tag)", R('1{{! {{/if}} }}2', testData), '12' );
+		test_handler( "comments may contain invalid content (stray else)", R('1{{! {{else}} }}2', testData), '12' );
+		test_handler( "comments may contain invalid content (invalid javascript)", R('1{{! {{if ...}} }}2', testData), '12' );
+
+	});
+
+	test("Variables", function() {
+
+		test_handler( "variable replacement", R('${ one }', testData), "first" );
+		test_handler( "many variables work", R('${ one }/${ two }', testData), "first/second" );
+		test_handler( "alternative variable syntax", R('${ one }', testData), "first" );
+		test_handler( "many variables work with alt syntax", R('${ one }/${ two }', testData), "first/second" );
+
+		test_handler( "basic string output (double)", R('${ "string" }', testData), "string" );
+		test_handler( "basic string output (single)", R("${ 'string' }", testData), "string" );
+		test_handler( "string quote escapes (double)", R('${ "str\\"i\\"ng" }', testData), 'str"i"ng' );
+		test_handler( "string quote escapes (single)", R("${ 'str\\'i\\'ng' }", testData), "str'i'ng" );
+
+		// TODO fixme
+		//test_handler( "string output with tag", R('${ "\\{\\{ tag \\}\\}" }', testData), "{{ tag }}" );
+		//test_handler( "string output with end of tag", R('${ "\\}\\}" }', testData), "}}" );
+
+		test_handler( 'empty variable tag (with tabs whitespace)', R("{{=\t\t}}", "self"), 'self' );
+		test_handler( 'empty variable tag', R("{{= }}", "self"), 'self' );
+		test_handler( 'empty variable tag (with space)', R("{{=}}", "self"), 'self' );
+
+		test_handler( "variable lookup error suppression", R('${ is_undefined }', testData), '' );
+		// TODO fixme
+		//test_handler( "variable lookup error suppression (with member)", R('${ is_undefined.member }', testData), '' );
+
+		test_handler( "variable and text (1)", R('A${ one }', testData), 'Afirst' );
+		test_handler( "variable and text (2)", R('${ one }B', testData), 'firstB' );
+		test_handler( "variable and text (3)", R('A${ one }B', testData), 'AfirstB' );
+
+		test_handler( "lookups work for submembers", R('${ a.b.c }', {a:{b:{c:"abc"}}}), 'abc' );
+		test_handler( "error suppression works for submembers", R('${ a.b.c }', {a:{b:{c:"abc"}}}), 'abc' );
+
+		test_handler( "functions can be called with in tags", R('${ foo() }', { foo:function(s){ return "bar"; }}), 'bar' );
+		test_handler( "functions pass strings correctly", R('${ foo("bar") }', { foo:function(s){ return s; }}), 'bar' );
+		test_handler( "functions pass arguments correctly", R('${ foo(bar) }', { foo:function(s){ return s; }, 'bar':'baz'}), 'baz' );
+
+		var cls = {
+			toString:function () {return 'S';},
+			toValue:function () {return 'V';}
+		}
+		test_handler( 'variables use toString, not toValue', R("${ foo }",{foo:cls}), 'S' );
+
+		test_handler( 'comma passes variables correctly', R("${ dot,dot,comma,dash }",{dot:'.','comma':',','dash':'-'}), '-' );
+
+		// TODO fixme
+		// @borgar says: I don't like this: it should use foo() to be consistant with foo().bar()
+		test_handler( 'variable gets called if it is callable', R("${ fun }", testData), 'RETURNED' );
+		test_handler( 'last variable in sequence gets called if it is callable', R("${ obj.fun }",{obj: testData}), 'RETURNED' );
+
+		var cls = {
+			foo: function () {
+				return { bar: function () {return 'BAZ'; } };
+			}
+		}
+		test_handler( 'member functions in a sequence don\'t get called', R("${ foo.bar }", cls), '' );
+
+	});
+
+	test("Falsy Values", function(){
+		test_handler( "(0)", R('${ 0 }'), '0' );
+		test_handler( "(false)", R('${ false }'), 'false' );
+		test_handler( "(null)", R('${ zero }'), '' );	 // it's debatable what we want here
+		test_handler( "(undefined)", R('${ undefined }'), '' );
+		test_handler( "(\"\")", R('${ "" }'), '' );
+		test_handler( "('')", R("${ '' }"), '' );
+	});
+
+	test("Falsy Lookups", function(){
+		test_handler( "(false)", R('${ zero }', {zero: 0}), '0' );
+		test_handler( "(false)", R('${ zero }', {zero: false}), 'false' );
+		test_handler( "(null)", R('${ zero }', {zero: null}), '' ); // it's debatable what we want here
+		test_handler( "(undefined)", R('${ zero }', {zero: undefined}), '' );
+		test_handler( "('')", R('${ zero }', {zero: ""}), '' );
+	 });
+
+	test("Javascript Operations", function(){
+		test_handler( "string concatination", R('${ one + "foo" }', testData), "firstfoo" );
+		test_handler( "adding", R('${ 1 + 5 }', testData), "6" );
+		test_handler( "subtracting", R('${ 9 - 5 }', testData), "4" );
+		test_handler( "modulo", R('${ 5 % 2 }', testData), "1" );
+		test_handler( "unary minus", R('${ -n }',{n:10}), "-10" );
+		test_handler( "unary plus", R('${ +n }',{n:"10"}), "10" );
+
+		test_handler( "in operator", R('${ "bar" in foo }',{foo:{bar:'baz'}}), "true" );
+		test_handler( "instanceof operator", R('${ foo instanceof Date }',{foo:new Date()}), "true" );
+		test_handler( "typeof operator", R('${ typeof "str" }',{}), "string" );
+
+		test_handler( "bitwise AND", R('${ n & 1 }',{n:5}), "1" );
+		test_handler( "bitwise OR", R('${ n | 1 }',{n:4}), "5" );
+		test_handler( "bitwise XOR", R('${ n ^ 1 }',{n:5}), "4" );
+		test_handler( "bitwise NOT", R('${ ~n }',{n:5}), "-6" );
+		test_handler( "left shift", R('${ n << 1 }',{n:5}), "10" );
+		test_handler( "right shift", R('${ n >> 1 }',{n:5}), "2" );
+		test_handler( "zero-fill right shift", R('${ n >>> 1 }',{n:5}), "2" );
+
+		test_handler( "comparing ==", R('${ 1 == 5 }', testData), "false" );
+		test_handler( "comparing !=", R('${ 1 != 5 }', testData), "true" );
+		test_handler( "comparing ===", R('${ 5 === 5 }', testData), "true" );
+		test_handler( "comparing !==", R('${ 5 !== 5 }', testData), "false" );
+		test_handler( "comparing >=", R('${ 1 >= 5 }', testData), "false" );
+		test_handler( "comparing >", R('${ 1 > 5 }', testData), "false" );
+		test_handler( "comparing <=", R('${ 1 <= 5 }', testData), "true" );
+		test_handler( "comparing <", R('${ 1 < 5 }', testData), "true" );
+		test_handler( "Logical OR", R('${ zero || "FALSY" }',{ zero: 0 }), "FALSY" );
+		test_handler( "Logical AND", R('${ zero && "TRUEY" }',{ zero: 1 }), "TRUEY" );
+		test_handler( "Conditional Operator", R('${ zero ? "zero" : "other" }',{ zero: 1 }), "zero" );
+		test_handler( "Unary logical NOT", R('${ !zero }',{ zero: 1 }), "false" );
+
+		test_handler( "Single-Quoted Strings", R("${ 'test' }",{}), "test" );
+		test_handler( "Single-Quoted Comparison", R("${ 'test' == testvar }",{ testvar: 'test' }), "true" );
+	});
+
+	test("Disallowed / Illegal", function(){
+		// TODO fixme
+		/*
+		test_handler( "Disallow incremental assignment", R('${ a += 1 }', {a:1}), SyntaxError );
+		test_handler( "Disallow decremental assignment", R('${ a -= 1 }', {a:1}), SyntaxError );
+		test_handler( "Disallow multiply assignment", R('${ a *= 1 }', {a:1}), SyntaxError );
+		test_handler( "Disallow division assignment", R('${ a /= 1 }', {a:1}), SyntaxError );
+		test_handler( "Disallow left shift assignment", R('${ a <<= 1 }', {a:1}), SyntaxError );
+		test_handler( "Disallow right shift assignment", R('${ a >>= 1 }', {a:1}), SyntaxError );
+		test_handler( "Disallow zero-fill right shift assignment", R('${ a >>>= 1 }', {a:1}), SyntaxError );
+		test_handler( "Disallow bitwise AND assignment", R('${ a &= 1 }', {a:1}), SyntaxError );
+		test_handler( "Disallow bitwise OR assignment", R('${ a |= 1 }', {a:1}), SyntaxError );
+		test_handler( "Disallow bitwise XOR assignment", R('${ a ^= 1 }', {a:1}), SyntaxError );
+
+		test_handler( "Disallow literal object creation", R('${ { a:"a"} }', {a:1}), SyntaxError );
+		test_handler( "Disallow literal array creation", R('${ [1,2,3] }', {a:1}), SyntaxError );
+
+		test_handler( "Disallow decrement", R('${ --a }',{a:1}), SyntaxError );
+		test_handler( "Disallow assignments", R('${ (a = 2) }',{a:1}), SyntaxError );
+		*/
+	});
+
+module("Commands");
+
+	test("Create New Command", function(){
+		$.getText = function ( str ) { return str.toUpperCase(); };
+		$.tmpl.tag.trans = { open: "_.push($.getText($1));" };
+		test_handler( "creating new command works", R('{{trans "translate" }}'), 'TRANSLATE' );
+		$.tmpl.tag._ = $.tmpl.tag.trans;
+		test_handler( "_ can by assigned a command", R('{{_ "translate" }}', {}), 'TRANSLATE' );
+		delete $.getText;
+		delete $.tmpl.tag.trans;
+		delete $.tmpl.tag._;
+	});
+
+	test("Each {{ each }}", function() {
+
+		test_handler( "loop", R('{{each arr}}${ $index }:${ this }/{{/each}}', testData), '0:AA/1:BB/2:CC/' );
+		test_handler( "loop", R('{{each arr}}${ $index }:${ $value }/{{/each}}', testData), '0:AA/1:BB/2:CC/' );
+		test_handler( "loop", R('{{each(i, item) arr}}${ i }:${ item }/{{/each }}', testData), '0:AA/1:BB/2:CC/' );
+		test_handler( "loop", R('{{each arr}}${ $index }:${ this }/{{/each }}', testData), '0:AA/1:BB/2:CC/' );
+
+		// TODO fixme
+		//test_handler( "first", R('{{each dict}}${ $index }:{{if $first }}first{{else}}!first{{/if }}/{{/each }}', testData), 'leovinus:first/scraliontis:!first/brobostigon:!first/' );
+		//test_handler( "first", R('{{each dict}}{{if !$first }}, {{/if }}${ $index }:${ this }}{{/each }}', testData), 'leovinus:this, scraliontis:that, brobostigon:other' );
+
+		// TODO fixme
+		//test_handler("html content", R('{{each arr}}<a>${ $index }</a>{{/each}}', testData), '<a>0</a><a>1</a><a>2</a>' );
+		//test_handler("html content", R('{{each arr}}<a>${ this }</a>{{/each}}', testData), '<a>AA</a><a>BB</a><a>CC</a>' );
+		//test_handler("html content", R('{{each this}}<a>${ data }</a>{{/each}}', [{data: 0}, {data: 1}, {data: 2}]), '<a>0</a><a>1</a><a>2</a>' );
+		//test_handler("html content with newlines", R('{{each this}}\n<a>${ data }</a>\n{{/each}}', [{data: 0}, {data: 1}, {data: 2}]), '\n<a>0</a>\n\n<a>1</a>\n\n<a>2</a>\n' );
+
+		test_handler( 'errors are passed back correctly (syntax)', R("{{each arr}}${ $i }{{syntax_error}}{{/each}}", testData), SyntaxError );
+		test_handler( 'errors are passed back correctly (reference)', R("{{each arr}}${ $i }{{reference_error}}{{/each}}", testData), ReferenceError );
+		test_handler( 'errors are passed back correctly (type)', R("{{each arr}}${ $i }{{type_error}}{{/each}}", testData), TypeError );
+
+	});
+
+	test("{{if}} and {{else}}", function() {
+		test_handler( "if:true", R('{{if a}}TRUE{{else}}FALSE{{/if}}', { a:true }), 'TRUE' );
+		test_handler( "if:false", R('{{if a}}TRUE{{else}}FALSE{{/if}}', { a:false }), 'FALSE' );
+		test_handler( "if:null", R('{{if a}}TRUE{{else}}FALSE{{/if}}', { a:null }), 'FALSE' );
+		test_handler( "if:undefined", R('{{if a}}TRUE{{else}}FALSE{{/if}}', { a:undefined }), 'FALSE' );
+		test_handler( "if:[]", R('{{if a}}TRUE{{else}}FALSE{{/if}}', { a:{} }), 'TRUE' );
+		test_handler( "if:{}", R('{{if a}}TRUE{{else}}FALSE{{/if}}', { a:[] }), 'TRUE' );
+		test_handler( "if:''", R('{{if a}}TRUE{{else}}FALSE{{/if}}', { a:"" }), 'FALSE' );
+		test_handler( "if:A", R('{{if a}}TRUE{{else}}FALSE{{/if}}', { a:"A" }), 'TRUE' );
+		test_handler( "if:0", R('{{if a}}TRUE{{else}}FALSE{{/if}}', { a:0 }), 'FALSE' );
+		test_handler( "if:1", R('{{if a}}TRUE{{else}}FALSE{{/if}}', { a:1 }), 'TRUE' );
+
+		test_handler( "/if ignores following text", R('{{if a}}TRUE{{else}}FALSE{{/if a}}', { a:1 }), 'TRUE' );
+
+		test_handler( 'errors are passed back correctly (syntax)', R("{{if true}}{{syntax_error}}{{/if}}", testData), SyntaxError );
+		test_handler( 'errors are passed back correctly (reference)', R("{{if true}}{{reference_error}}{{/if}}", testData), ReferenceError );
+		test_handler( 'errors are passed back correctly (type)', R("{{if true}}{{type_error}}{{/if}}", testData), TypeError );
+
+	});
+
+	test("{{tmpl() template}}", function() {
+
+		jQuery.template('test', '${ "test text" }');
+		test_handler( "simple include", R('{{tmpl "test"}}'), 'test text' );
+
+		jQuery.template('test2', '{{each arr}}[${ $value }]-{{/each }}');
+		test_handler( "data access", R('{{tmpl "test2"}}', testData), '[AA]-[BB]-[CC]-' );
+
+		var nestedData = {foo: 'bar'};
+
+		jQuery.template('nested', '{{tmpl "nested0"}}');
+		jQuery.template('nested0', '${ foo }');
+
+		test_handler( "nested - 1 level", R('{{tmpl "nested"}}', nestedData), 'bar' );
+
+		jQuery.template('nested0', '{{tmpl "nested1"}}');
+		jQuery.template('nested1', '{{tmpl "nested2"}}');
+		jQuery.template('nested2', '${ foo }');
+
+		test_handler( "nested - 2 levels", R('{{tmpl "nested" }}', nestedData), 'bar' );
+
+		nestedData = {foo: {bar: {sweet: 1} } };
+		jQuery.template('nested2', '${ foo.bar.sweet }');
+
+		test_handler( "nested - 2 levels - complex data", R('{{tmpl "nested" }}', nestedData), '1' );
+
+		// TODO fixme
+		jQuery.template('test', '${ $index }');
+		//test_handler( "{{each}} index variable", R('{{each arr}}{{tmpl "test" }}{{/each}}', testData), '012' );
+
+		jQuery.template('test', '${ n }');
+		//test_handler( "{{each}} index variable", R('{{each(n, item) arr}}{{tmpl "test"}}{{/each}}', testData), '012' );
+
+		jQuery.template('test', '${ item }');
+		//test_handler( "{{each}} item variable", R('{{each(n, item) arr}}{{tmpl "test" }}{{/each}}', testData), 'AABBCC' );
+	});
+
+	test("Html Output Unecoded {{html }}", function(){
+		// TODO fixme
+		//test_handler("encoded", R('{{= html}}', testData), '&lt;a&gt;');
+		//test_handler("unencoded", R('{{html html}}', testData), '<a>');
+	});
+
+module("Script Tag Caching");
+
+	test("Template Reuse", function(){
+		var template = $('#reuse'),
+			data = {data: 'pass1'};
+
+		var pass1 = template.tmpl(data).html();
+		data = {data: 'pass2'};
+		var pass2 = template.tmpl(data).html();
+
+		ok( "simple reuse test", pass1 == 'pass1' && pass2 == 'pass2' );
+	});
diff --git a/tests/index.html b/tests/index.html
new file mode 100644
index 0000000..1f5f3ae
--- /dev/null
+++ b/tests/index.html
@@ -0,0 +1,22 @@
+<!DOCTYPE html>
+<html>
+	<head>
+		<meta charset="utf-8" />
+		<title>jquery-tmpl unit tests</title>
+		<link rel="stylesheet" href="qunit.css" type="text/css" />
+		<script type="text/javascript" src="http://ajax.googleapis.com/ajax/libs/jquery/1.4.4/jquery.min.js"></script>
+		<script type="text/javascript" src="qunit.js"></script>
+		<script type="text/javascript" src="../jquery.tmpl.js"></script>
+		<script type="text/javascript" src="core.js"></script>
+	</head>
+	<body>
+		<script type="text/x-jquery-tmpl" id="reuse">
+			<a>{{= data}}</a>
+		</script>
+		<h1 id="qunit-header">jquery-tmpl unit tests</h1>
+		<h2 id="qunit-banner"></h2>
+		<div id="qunit-testrunner-toolbar"></div>
+		<h2 id="qunit-userAgent"></h2>
+		<ol id="qunit-tests"></ol>
+	</body>
+</html>
diff --git a/tests/qunit.css b/tests/qunit.css
new file mode 100644
index 0000000..708fb8d
--- /dev/null
+++ b/tests/qunit.css
@@ -0,0 +1,196 @@
+/** Font Family and Sizes */
+
+#qunit-tests, #qunit-header, #qunit-banner, #qunit-testrunner-toolbar, #qunit-userAgent, #qunit-testresult {
+	font-family: "Helvetica Neue Light", "HelveticaNeue-Light", "Helvetica Neue", Calibri, Helvetica, Arial;
+}
+
+#qunit-testrunner-toolbar, #qunit-userAgent, #qunit-testresult, #qunit-tests li { font-size: small; }
+#qunit-tests { font-size: smaller; }
+
+
+/** Resets */
+
+#qunit-tests, #qunit-tests ol, #qunit-header, #qunit-banner, #qunit-userAgent, #qunit-testresult {
+	margin: 0;
+	padding: 0;
+}
+
+
+/** Header */
+
+#qunit-header {
+	padding: 0.5em 0 0.5em 1em;
+
+	color: #8699a4;
+	background-color: #0d3349;
+
+	font-size: 1.5em;
+	line-height: 1em;
+	font-weight: normal;
+
+	border-radius: 15px 15px 0 0;
+	-moz-border-radius: 15px 15px 0 0;
+	-webkit-border-top-right-radius: 15px;
+	-webkit-border-top-left-radius: 15px;
+}
+
+#qunit-header a {
+	text-decoration: none;
+	color: #c2ccd1;
+}
+
+#qunit-header a:hover,
+#qunit-header a:focus {
+	color: #fff;
+}
+
+#qunit-banner {
+	height: 5px;
+}
+
+#qunit-testrunner-toolbar {
+	padding: 0em 0 0.5em 2em;
+}
+
+#qunit-userAgent {
+	padding: 0.5em 0 0.5em 2.5em;
+	background-color: #2b81af;
+	color: #fff;
+	text-shadow: rgba(0, 0, 0, 0.5) 2px 2px 1px;
+}
+
+
+/** Tests: Pass/Fail */
+
+#qunit-tests {
+	list-style-position: inside;
+}
+
+#qunit-tests li {
+	padding: 0.4em 0.5em 0.4em 2.5em;
+	border-bottom: 1px solid #fff;
+	list-style-position: inside;
+}
+
+#qunit-tests li strong {
+	cursor: pointer;
+}
+
+#qunit-tests ol {
+	margin-top: 0.5em;
+	padding: 0.5em;
+
+	background-color: #fff;
+
+	border-radius: 15px;
+	-moz-border-radius: 15px;
+	-webkit-border-radius: 15px;
+
+	box-shadow: inset 0px 2px 13px #999;
+	-moz-box-shadow: inset 0px 2px 13px #999;
+	-webkit-box-shadow: inset 0px 2px 13px #999;
+}
+
+#qunit-tests table {
+	border-collapse: collapse;
+	margin-top: .2em;
+}
+
+#qunit-tests th {
+	text-align: right;
+	vertical-align: top;
+	padding: 0 .5em 0 0;
+}
+
+#qunit-tests td {
+	vertical-align: top;
+}
+
+#qunit-tests pre {
+	margin: 0;
+	white-space: pre-wrap;
+	word-wrap: break-word;
+}
+
+#qunit-tests del {
+	background-color: #e0f2be;
+	color: #374e0c;
+	text-decoration: none;
+}
+
+#qunit-tests ins {
+	background-color: #ffcaca;
+	color: #500;
+	text-decoration: none;
+}
+
+/*** Test Counts */
+
+#qunit-tests b.counts                       { color: black; }
+#qunit-tests b.passed                       { color: #5E740B; }
+#qunit-tests b.failed                       { color: #710909; }
+
+#qunit-tests li li {
+	margin: 0.5em;
+	padding: 0.4em 0.5em 0.4em 0.5em;
+	background-color: #fff;
+	border-bottom: none;
+	list-style-position: inside;
+}
+
+/*** Passing Styles */
+
+#qunit-tests li li.pass {
+	color: #5E740B;
+	background-color: #fff;
+	border-left: 26px solid #C6E746;
+}
+
+#qunit-tests .pass                          { color: #528CE0; background-color: #D2E0E6; }
+#qunit-tests .pass .test-name               { color: #366097; }
+
+#qunit-tests .pass .test-actual,
+#qunit-tests .pass .test-expected           { color: #999999; }
+
+#qunit-banner.qunit-pass                    { background-color: #C6E746; }
+
+/*** Failing Styles */
+
+#qunit-tests li li.fail {
+	color: #710909;
+	background-color: #fff;
+	border-left: 26px solid #EE5757;
+}
+
+#qunit-tests .fail                          { color: #000000; background-color: #EE5757; }
+#qunit-tests .fail .test-name,
+#qunit-tests .fail .module-name             { color: #000000; }
+
+#qunit-tests .fail .test-actual             { color: #EE5757; }
+#qunit-tests .fail .test-expected           { color: green;   }
+
+#qunit-banner.qunit-fail,
+#qunit-testrunner-toolbar                   { background-color: #EE5757; }
+
+
+/** Footer */
+
+#qunit-testresult {
+	padding: 0.5em 0.5em 0.5em 2.5em;
+
+	color: #2b81af;
+	background-color: #D2E0E6;
+
+	border-radius: 0 0 15px 15px;
+	-moz-border-radius: 0 0 15px 15px;
+	-webkit-border-bottom-right-radius: 15px;
+	-webkit-border-bottom-left-radius: 15px;
+}
+
+/** Fixture */
+
+#qunit-fixture {
+	position: absolute;
+	top: -10000px;
+	left: -10000px;
+}
diff --git a/tests/qunit.js b/tests/qunit.js
new file mode 100644
index 0000000..f4cbbbe
--- /dev/null
+++ b/tests/qunit.js
@@ -0,0 +1,1364 @@
+/*
+ * QUnit - A JavaScript Unit Testing Framework
+ *
+ * http://docs.jquery.com/QUnit
+ *
+ * Copyright (c) 2009 John Resig, Jörn Zaefferer
+ * Dual licensed under the MIT (MIT-LICENSE.txt)
+ * and GPL (GPL-LICENSE.txt) licenses.
+ */
+
+(function(window) {
+
+var defined = {
+	setTimeout: typeof window.setTimeout !== "undefined",
+	sessionStorage: (function() {
+		try {
+			return !!sessionStorage.getItem;
+		} catch(e){
+			return false;
+		}
+  })()
+}
+
+var testId = 0;
+
+var Test = function(name, testName, expected, testEnvironmentArg, async, callback) {
+	this.name = name;
+	this.testName = testName;
+	this.expected = expected;
+	this.testEnvironmentArg = testEnvironmentArg;
+	this.async = async;
+	this.callback = callback;
+	this.assertions = [];
+};
+Test.prototype = {
+	init: function() {
+		var tests = id("qunit-tests");
+		if (tests) {
+			var b = document.createElement("strong");
+				b.innerHTML = "Running " + this.name;
+			var li = document.createElement("li");
+				li.appendChild( b );
+				li.id = this.id = "test-output" + testId++;
+			tests.appendChild( li );
+		}
+	},
+	setup: function() {
+		if (this.module != config.previousModule) {
+			if ( this.previousModule ) {
+				QUnit.moduleDone( this.module, config.moduleStats.bad, config.moduleStats.all );
+			}
+			config.previousModule = this.module;
+			config.moduleStats = { all: 0, bad: 0 };
+			QUnit.moduleStart( this.module, this.moduleTestEnvironment );
+		}
+
+		config.current = this;
+		this.testEnvironment = extend({
+			setup: function() {},
+			teardown: function() {}
+		}, this.moduleTestEnvironment);
+		if (this.testEnvironmentArg) {
+			extend(this.testEnvironment, this.testEnvironmentArg);
+		}
+
+		QUnit.testStart( this.testName, this.testEnvironment );
+
+		// allow utility functions to access the current test environment
+		// TODO why??
+		QUnit.current_testEnvironment = this.testEnvironment;
+
+		try {
+			if ( !config.pollution ) {
+				saveGlobal();
+			}
+
+			this.testEnvironment.setup.call(this.testEnvironment);
+		} catch(e) {
+			// TODO use testName instead of name for no-markup message?
+			QUnit.ok( false, "Setup failed on " + this.name + ": " + e.message );
+		}
+	},
+	run: function() {
+		if ( this.async ) {
+			QUnit.stop();
+		}
+
+		try {
+			this.callback.call(this.testEnvironment);
+		} catch(e) {
+			// TODO use testName instead of name for no-markup message?
+			fail("Test " + this.name + " died, exception and test follows", e, this.callback);
+			QUnit.ok( false, "Died on test #" + (this.assertions.length + 1) + ": " + e.message + " - " + QUnit.jsDump.parse(e) );
+			// else next test will carry the responsibility
+			saveGlobal();
+
+			// Restart the tests if they're blocking
+			if ( config.blocking ) {
+				start();
+			}
+		}
+	},
+	teardown: function() {
+		try {
+			checkPollution();
+			this.testEnvironment.teardown.call(this.testEnvironment);
+		} catch(e) {
+			// TODO use testName instead of name for no-markup message?
+			QUnit.ok( false, "Teardown failed on " + this.name + ": " + e.message );
+		}
+	},
+	finish: function() {
+		if ( this.expected && this.expected != this.assertions.length ) {
+			QUnit.ok( false, "Expected " + this.expected + " assertions, but " + this.assertions.length + " were run" );
+		}
+
+		var good = 0, bad = 0,
+			tests = id("qunit-tests");
+
+		config.stats.all += this.assertions.length;
+		config.moduleStats.all += this.assertions.length;
+
+		if ( tests ) {
+			var ol  = document.createElement("ol");
+
+			for ( var i = 0; i < this.assertions.length; i++ ) {
+				var assertion = this.assertions[i];
+
+				var li = document.createElement("li");
+				li.className = assertion.result ? "pass" : "fail";
+				li.innerHTML = assertion.message || (assertion.result ? "okay" : "failed");
+				ol.appendChild( li );
+
+				if ( assertion.result ) {
+					good++;
+				} else {
+					bad++;
+					config.stats.bad++;
+					config.moduleStats.bad++;
+				}
+			}
+
+			// store result when possible
+			defined.sessionStorage && sessionStorage.setItem("qunit-" + this.testName, bad);
+
+			if (bad == 0) {
+				ol.style.display = "none";
+			}
+
+			var b = document.createElement("strong");
+			b.innerHTML = this.name + " <b class='counts'>(<b class='failed'>" + bad + "</b>, <b class='passed'>" + good + "</b>, " + this.assertions.length + ")</b>";
+
+			addEvent(b, "click", function() {
+				var next = b.nextSibling, display = next.style.display;
+				next.style.display = display === "none" ? "block" : "none";
+			});
+
+			addEvent(b, "dblclick", function(e) {
+				var target = e && e.target ? e.target : window.event.srcElement;
+				if ( target.nodeName.toLowerCase() == "span" || target.nodeName.toLowerCase() == "b" ) {
+					target = target.parentNode;
+				}
+				if ( window.location && target.nodeName.toLowerCase() === "strong" ) {
+					window.location.search = "?" + encodeURIComponent(getText([target]).replace(/\(.+\)$/, "").replace(/(^\s*|\s*$)/g, ""));
+				}
+			});
+
+			var li = id(this.id);
+			li.className = bad ? "fail" : "pass";
+			li.style.display = resultDisplayStyle(!bad);
+			li.removeChild( li.firstChild );
+			li.appendChild( b );
+			li.appendChild( ol );
+
+			if ( bad ) {
+				var toolbar = id("qunit-testrunner-toolbar");
+				if ( toolbar ) {
+					toolbar.style.display = "block";
+					id("qunit-filter-pass").disabled = null;
+				}
+			}
+
+		} else {
+			for ( var i = 0; i < this.assertions.length; i++ ) {
+				if ( !this.assertions[i].result ) {
+					bad++;
+					config.stats.bad++;
+					config.moduleStats.bad++;
+				}
+			}
+		}
+
+		try {
+			QUnit.reset();
+		} catch(e) {
+			// TODO use testName instead of name for no-markup message?
+			fail("reset() failed, following Test " + this.name + ", exception and reset fn follows", e, QUnit.reset);
+		}
+
+		QUnit.testDone( this.testName, bad, this.assertions.length );
+	},
+
+	queue: function() {
+		var test = this;
+		synchronize(function() {
+			test.init();
+		});
+		function run() {
+			// each of these can by async
+			synchronize(function() {
+				test.setup();
+			});
+			synchronize(function() {
+				test.run();
+			});
+			synchronize(function() {
+				test.teardown();
+			});
+			synchronize(function() {
+				test.finish();
+			});
+		}
+		// defer when previous test run passed, if storage is available
+		var bad = defined.sessionStorage && +sessionStorage.getItem("qunit-" + this.testName);
+		if (bad) {
+			run();
+		} else {
+			synchronize(run);
+		};
+	}
+
+}
+
+var QUnit = {
+
+	// call on start of module test to prepend name to all tests
+	module: function(name, testEnvironment) {
+		config.previousModule = config.currentModule;
+		config.currentModule = name;
+		config.currentModuleTestEnviroment = testEnvironment;
+	},
+
+	asyncTest: function(testName, expected, callback) {
+		if ( arguments.length === 2 ) {
+			callback = expected;
+			expected = 0;
+		}
+
+		QUnit.test(testName, expected, callback, true);
+	},
+
+	test: function(testName, expected, callback, async) {
+		var name = '<span class="test-name">' + testName + '</span>', testEnvironmentArg;
+
+		if ( arguments.length === 2 ) {
+			callback = expected;
+			expected = null;
+		}
+		// is 2nd argument a testEnvironment?
+		if ( expected && typeof expected === 'object') {
+			testEnvironmentArg =  expected;
+			expected = null;
+		}
+
+		if ( config.currentModule ) {
+			name = '<span class="module-name">' + config.currentModule + "</span>: " + name;
+		}
+
+		if ( !validTest(config.currentModule + ": " + testName) ) {
+			return;
+		}
+
+		var test = new Test(name, testName, expected, testEnvironmentArg, async, callback);
+		test.previousModule = config.previousModule;
+		test.module = config.currentModule;
+		test.moduleTestEnvironment = config.currentModuleTestEnviroment;
+		test.queue();
+	},
+
+	/**
+	 * Specify the number of expected assertions to gurantee that failed test (no assertions are run at all) don't slip through.
+	 */
+	expect: function(asserts) {
+		config.current.expected = asserts;
+	},
+
+	/**
+	 * Asserts true.
+	 * @example ok( "asdfasdf".length > 5, "There must be at least 5 chars" );
+	 */
+	ok: function(a, msg) {
+		a = !!a;
+		var details = {
+			result: a,
+			message: msg
+		};
+		msg = escapeHtml(msg);
+		QUnit.log(a, msg, details);
+		config.current.assertions.push({
+			result: a,
+			message: msg
+		});
+	},
+
+	/**
+	 * Checks that the first two arguments are equal, with an optional message.
+	 * Prints out both actual and expected values.
+	 *
+	 * Prefered to ok( actual == expected, message )
+	 *
+	 * @example equal( format("Received {0} bytes.", 2), "Received 2 bytes." );
+	 *
+	 * @param Object actual
+	 * @param Object expected
+	 * @param String message (optional)
+	 */
+	equal: function(actual, expected, message) {
+		QUnit.push(expected == actual, actual, expected, message);
+	},
+
+	notEqual: function(actual, expected, message) {
+		QUnit.push(expected != actual, actual, expected, message);
+	},
+
+	deepEqual: function(actual, expected, message) {
+		QUnit.push(QUnit.equiv(actual, expected), actual, expected, message);
+	},
+
+	notDeepEqual: function(actual, expected, message) {
+		QUnit.push(!QUnit.equiv(actual, expected), actual, expected, message);
+	},
+
+	strictEqual: function(actual, expected, message) {
+		QUnit.push(expected === actual, actual, expected, message);
+	},
+
+	notStrictEqual: function(actual, expected, message) {
+		QUnit.push(expected !== actual, actual, expected, message);
+	},
+
+	raises: function(block, expected, message) {
+		var actual, ok = false;
+
+		if (typeof expected === 'string') {
+			message = expected;
+			expected = null;
+		}
+
+		try {
+			block();
+		} catch (e) {
+			actual = e;
+		}
+
+		if (actual) {
+			// we don't want to validate thrown error
+			if (!expected) {
+				ok = true;
+			// expected is a regexp
+			} else if (QUnit.objectType(expected) === "regexp") {
+				ok = expected.test(actual);
+			// expected is a constructor
+			} else if (actual instanceof expected) {
+				ok = true;
+			// expected is a validation function which returns true is validation passed
+			} else if (expected.call({}, actual) === true) {
+				ok = true;
+			}
+		}
+
+		QUnit.ok(ok, message);
+	},
+
+	start: function() {
+		// A slight delay, to avoid any current callbacks
+		if ( defined.setTimeout ) {
+			window.setTimeout(function() {
+				if ( config.timeout ) {
+					clearTimeout(config.timeout);
+				}
+
+				config.blocking = false;
+				process();
+			}, 13);
+		} else {
+			config.blocking = false;
+			process();
+		}
+	},
+
+	stop: function(timeout) {
+		config.blocking = true;
+
+		if ( timeout && defined.setTimeout ) {
+			config.timeout = window.setTimeout(function() {
+				QUnit.ok( false, "Test timed out" );
+				QUnit.start();
+			}, timeout);
+		}
+	}
+
+};
+
+// Backwards compatibility, deprecated
+QUnit.equals = QUnit.equal;
+QUnit.same = QUnit.deepEqual;
+
+// Maintain internal state
+var config = {
+	// The queue of tests to run
+	queue: [],
+
+	// block until document ready
+	blocking: true
+};
+
+// Load paramaters
+(function() {
+	var location = window.location || { search: "", protocol: "file:" },
+		GETParams = location.search.slice(1).split('&');
+
+	for ( var i = 0; i < GETParams.length; i++ ) {
+		GETParams[i] = decodeURIComponent( GETParams[i] );
+		if ( GETParams[i] === "noglobals" ) {
+			GETParams.splice( i, 1 );
+			i--;
+			config.noglobals = true;
+		} else if ( GETParams[i].search('=') > -1 ) {
+			GETParams.splice( i, 1 );
+			i--;
+		}
+	}
+
+	// restrict modules/tests by get parameters
+	config.filters = GETParams;
+
+	// Figure out if we're running the tests from a server or not
+	QUnit.isLocal = !!(location.protocol === 'file:');
+})();
+
+// Expose the API as global variables, unless an 'exports'
+// object exists, in that case we assume we're in CommonJS
+if ( typeof exports === "undefined" || typeof require === "undefined" ) {
+	extend(window, QUnit);
+	window.QUnit = QUnit;
+} else {
+	extend(exports, QUnit);
+	exports.QUnit = QUnit;
+}
+
+// define these after exposing globals to keep them in these QUnit namespace only
+extend(QUnit, {
+	config: config,
+
+	// Initialize the configuration options
+	init: function() {
+		extend(config, {
+			stats: { all: 0, bad: 0 },
+			moduleStats: { all: 0, bad: 0 },
+			started: +new Date,
+			updateRate: 1000,
+			blocking: false,
+			autostart: true,
+			autorun: false,
+			filters: [],
+			queue: []
+		});
+
+		var tests = id("qunit-tests"),
+			banner = id("qunit-banner"),
+			result = id("qunit-testresult");
+
+		if ( tests ) {
+			tests.innerHTML = "";
+		}
+
+		if ( banner ) {
+			banner.className = "";
+		}
+
+		if ( result ) {
+			result.parentNode.removeChild( result );
+		}
+	},
+
+	/**
+	 * Resets the test setup. Useful for tests that modify the DOM.
+	 *
+	 * If jQuery is available, uses jQuery's html(), otherwise just innerHTML.
+	 */
+	reset: function() {
+		if ( window.jQuery ) {
+			jQuery( "#main, #qunit-fixture" ).html( config.fixture );
+		} else {
+			var main = id( 'main' ) || id( 'qunit-fixture' );
+			if ( main ) {
+				main.innerHTML = config.fixture;
+			}
+		}
+	},
+
+	/**
+	 * Trigger an event on an element.
+	 *
+	 * @example triggerEvent( document.body, "click" );
+	 *
+	 * @param DOMElement elem
+	 * @param String type
+	 */
+	triggerEvent: function( elem, type, event ) {
+		if ( document.createEvent ) {
+			event = document.createEvent("MouseEvents");
+			event.initMouseEvent(type, true, true, elem.ownerDocument.defaultView,
+				0, 0, 0, 0, 0, false, false, false, false, 0, null);
+			elem.dispatchEvent( event );
+
+		} else if ( elem.fireEvent ) {
+			elem.fireEvent("on"+type);
+		}
+	},
+
+	// Safe object type checking
+	is: function( type, obj ) {
+		return QUnit.objectType( obj ) == type;
+	},
+
+	objectType: function( obj ) {
+		if (typeof obj === "undefined") {
+				return "undefined";
+
+		// consider: typeof null === object
+		}
+		if (obj === null) {
+				return "null";
+		}
+
+		var type = Object.prototype.toString.call( obj )
+			.match(/^\[object\s(.*)\]$/)[1] || '';
+
+		switch (type) {
+				case 'Number':
+						if (isNaN(obj)) {
+								return "nan";
+						} else {
+								return "number";
+						}
+				case 'String':
+				case 'Boolean':
+				case 'Array':
+				case 'Date':
+				case 'RegExp':
+				case 'Function':
+						return type.toLowerCase();
+		}
+		if (typeof obj === "object") {
+				return "object";
+		}
+		return undefined;
+	},
+
+	push: function(result, actual, expected, message) {
+		var details = {
+			result: result,
+			message: message,
+			actual: actual,
+			expected: expected
+		};
+
+		message = escapeHtml(message) || (result ? "okay" : "failed");
+		message = '<span class="test-message">' + message + "</span>";
+		expected = escapeHtml(QUnit.jsDump.parse(expected));
+		actual = escapeHtml(QUnit.jsDump.parse(actual));
+		var output = message + '<table><tr class="test-expected"><th>Expected: </th><td><pre>' + expected + '</pre></td></tr>';
+		if (actual != expected) {
+			output += '<tr class="test-actual"><th>Result: </th><td><pre>' + actual + '</pre></td></tr>';
+			output += '<tr class="test-diff"><th>Diff: </th><td><pre>' + QUnit.diff(expected, actual) +'</pre></td></tr>';
+		}
+		if (!result) {
+			var source = sourceFromStacktrace();
+			if (source) {
+				details.source = source;
+				output += '<tr class="test-source"><th>Source: </th><td><pre>' + source +'</pre></td></tr>';
+			}
+		}
+		output += "</table>";
+
+		QUnit.log(result, message, details);
+
+		config.current.assertions.push({
+			result: !!result,
+			message: output
+		});
+	},
+
+	// Logging callbacks
+	begin: function() {},
+	done: function(failures, total) {},
+	log: function(result, message) {},
+	testStart: function(name, testEnvironment) {},
+	testDone: function(name, failures, total) {},
+	moduleStart: function(name, testEnvironment) {},
+	moduleDone: function(name, failures, total) {}
+});
+
+if ( typeof document === "undefined" || document.readyState === "complete" ) {
+	config.autorun = true;
+}
+
+addEvent(window, "load", function() {
+	QUnit.begin();
+
+	// Initialize the config, saving the execution queue
+	var oldconfig = extend({}, config);
+	QUnit.init();
+	extend(config, oldconfig);
+
+	config.blocking = false;
+
+	var userAgent = id("qunit-userAgent");
+	if ( userAgent ) {
+		userAgent.innerHTML = navigator.userAgent;
+	}
+	var banner = id("qunit-header");
+	if ( banner ) {
+		var paramsIndex = location.href.lastIndexOf(location.search);
+		if ( paramsIndex > -1 ) {
+			var mainPageLocation = location.href.slice(0, paramsIndex);
+			if ( mainPageLocation == location.href ) {
+				banner.innerHTML = '<a href=""> ' + banner.innerHTML + '</a> ';
+			} else {
+				var testName = decodeURIComponent(location.search.slice(1));
+				banner.innerHTML = '<a href="' + mainPageLocation + '">' + banner.innerHTML + '</a> &#8250; <a href="">' + testName + '</a>';
+			}
+		}
+	}
+
+	var toolbar = id("qunit-testrunner-toolbar");
+	if ( toolbar ) {
+		toolbar.style.display = "none";
+
+		var filter = document.createElement("input");
+		filter.type = "checkbox";
+		filter.id = "qunit-filter-pass";
+		filter.disabled = true;
+		addEvent( filter, "click", function() {
+			var li = document.getElementsByTagName("li");
+			for ( var i = 0; i < li.length; i++ ) {
+				if ( li[i].className.indexOf("pass") > -1 ) {
+					li[i].style.display = filter.checked ? "none" : "";
+				}
+			}
+		});
+		toolbar.appendChild( filter );
+
+		var label = document.createElement("label");
+		label.setAttribute("for", "qunit-filter-pass");
+		label.innerHTML = "Hide passed tests";
+		toolbar.appendChild( label );
+	}
+
+	var main = id('main') || id('qunit-fixture');
+	if ( main ) {
+		config.fixture = main.innerHTML;
+	}
+
+	if (config.autostart) {
+		QUnit.start();
+	}
+});
+
+function done() {
+	config.autorun = true;
+
+	// Log the last module results
+	if ( config.currentModule ) {
+		QUnit.moduleDone( config.currentModule, config.moduleStats.bad, config.moduleStats.all );
+	}
+
+	var banner = id("qunit-banner"),
+		tests = id("qunit-tests"),
+		html = ['Tests completed in ',
+		+new Date - config.started, ' milliseconds.<br/>',
+		'<span class="passed">', config.stats.all - config.stats.bad, '</span> tests of <span class="total">', config.stats.all, '</span> passed, <span class="failed">', config.stats.bad,'</span> failed.'].join('');
+
+	if ( banner ) {
+		banner.className = (config.stats.bad ? "qunit-fail" : "qunit-pass");
+	}
+
+	if ( tests ) {
+		var result = id("qunit-testresult");
+
+		if ( !result ) {
+			result = document.createElement("p");
+			result.id = "qunit-testresult";
+			result.className = "result";
+			tests.parentNode.insertBefore( result, tests.nextSibling );
+		}
+
+		result.innerHTML = html;
+	}
+
+	QUnit.done( config.stats.bad, config.stats.all );
+}
+
+function validTest( name ) {
+	var i = config.filters.length,
+		run = false;
+
+	if ( !i ) {
+		return true;
+	}
+
+	while ( i-- ) {
+		var filter = config.filters[i],
+			not = filter.charAt(0) == '!';
+
+		if ( not ) {
+			filter = filter.slice(1);
+		}
+
+		if ( name.indexOf(filter) !== -1 ) {
+			return !not;
+		}
+
+		if ( not ) {
+			run = true;
+		}
+	}
+
+	return run;
+}
+
+// so far supports only Firefox, Chrome and Opera (buggy)
+// could be extended in the future to use something like https://github.com/csnover/TraceKit
+function sourceFromStacktrace() {
+	try {
+		throw new Error();
+	} catch ( e ) {
+		if (e.stacktrace) {
+			// Opera
+			return e.stacktrace.split("\n")[6];
+		} else if (e.stack) {
+			// Firefox, Chrome
+			return e.stack.split("\n")[4];
+		}
+	}
+}
+
+function resultDisplayStyle(passed) {
+	return passed && id("qunit-filter-pass") && id("qunit-filter-pass").checked ? 'none' : '';
+}
+
+function escapeHtml(s) {
+	if (!s) {
+		return "";
+	}
+	s = s + "";
+	return s.replace(/[\&"<>\\]/g, function(s) {
+		switch(s) {
+			case "&": return "&amp;";
+			case "\\": return "\\\\";
+			case '"': return '\"';
+			case "<": return "&lt;";
+			case ">": return "&gt;";
+			default: return s;
+		}
+	});
+}
+
+function synchronize( callback ) {
+	config.queue.push( callback );
+
+	if ( config.autorun && !config.blocking ) {
+		process();
+	}
+}
+
+function process() {
+	var start = (new Date()).getTime();
+
+	while ( config.queue.length && !config.blocking ) {
+		if ( config.updateRate <= 0 || (((new Date()).getTime() - start) < config.updateRate) ) {
+			config.queue.shift()();
+		} else {
+			window.setTimeout( process, 13 );
+			break;
+		}
+	}
+  if (!config.blocking && !config.queue.length) {
+    done();
+  }
+}
+
+function saveGlobal() {
+	config.pollution = [];
+
+	if ( config.noglobals ) {
+		for ( var key in window ) {
+			config.pollution.push( key );
+		}
+	}
+}
+
+function checkPollution( name ) {
+	var old = config.pollution;
+	saveGlobal();
+
+	var newGlobals = diff( old, config.pollution );
+	if ( newGlobals.length > 0 ) {
+		ok( false, "Introduced global variable(s): " + newGlobals.join(", ") );
+		config.current.expected++;
+	}
+
+	var deletedGlobals = diff( config.pollution, old );
+	if ( deletedGlobals.length > 0 ) {
+		ok( false, "Deleted global variable(s): " + deletedGlobals.join(", ") );
+		config.current.expected++;
+	}
+}
+
+// returns a new Array with the elements that are in a but not in b
+function diff( a, b ) {
+	var result = a.slice();
+	for ( var i = 0; i < result.length; i++ ) {
+		for ( var j = 0; j < b.length; j++ ) {
+			if ( result[i] === b[j] ) {
+				result.splice(i, 1);
+				i--;
+				break;
+			}
+		}
+	}
+	return result;
+}
+
+function fail(message, exception, callback) {
+	if ( typeof console !== "undefined" && console.error && console.warn ) {
+		console.error(message);
+		console.error(exception);
+		console.warn(callback.toString());
+
+	} else if ( window.opera && opera.postError ) {
+		opera.postError(message, exception, callback.toString);
+	}
+}
+
+function extend(a, b) {
+	for ( var prop in b ) {
+		a[prop] = b[prop];
+	}
+
+	return a;
+}
+
+function addEvent(elem, type, fn) {
+	if ( elem.addEventListener ) {
+		elem.addEventListener( type, fn, false );
+	} else if ( elem.attachEvent ) {
+		elem.attachEvent( "on" + type, fn );
+	} else {
+		fn();
+	}
+}
+
+function id(name) {
+	return !!(typeof document !== "undefined" && document && document.getElementById) &&
+		document.getElementById( name );
+}
+
+// Test for equality any JavaScript type.
+// Discussions and reference: http://philrathe.com/articles/equiv
+// Test suites: http://philrathe.com/tests/equiv
+// Author: Philippe Rathé <prathe@gmail.com>
+QUnit.equiv = function () {
+
+    var innerEquiv; // the real equiv function
+    var callers = []; // stack to decide between skip/abort functions
+    var parents = []; // stack to avoiding loops from circular referencing
+
+    // Call the o related callback with the given arguments.
+    function bindCallbacks(o, callbacks, args) {
+        var prop = QUnit.objectType(o);
+        if (prop) {
+            if (QUnit.objectType(callbacks[prop]) === "function") {
+                return callbacks[prop].apply(callbacks, args);
+            } else {
+                return callbacks[prop]; // or undefined
+            }
+        }
+    }
+
+    var callbacks = function () {
+
+        // for string, boolean, number and null
+        function useStrictEquality(b, a) {
+            if (b instanceof a.constructor || a instanceof b.constructor) {
+                // to catch short annotaion VS 'new' annotation of a declaration
+                // e.g. var i = 1;
+                //      var j = new Number(1);
+                return a == b;
+            } else {
+                return a === b;
+            }
+        }
+
+        return {
+            "string": useStrictEquality,
+            "boolean": useStrictEquality,
+            "number": useStrictEquality,
+            "null": useStrictEquality,
+            "undefined": useStrictEquality,
+
+            "nan": function (b) {
+                return isNaN(b);
+            },
+
+            "date": function (b, a) {
+                return QUnit.objectType(b) === "date" && a.valueOf() === b.valueOf();
+            },
+
+            "regexp": function (b, a) {
+                return QUnit.objectType(b) === "regexp" &&
+                    a.source === b.source && // the regex itself
+                    a.global === b.global && // and its modifers (gmi) ...
+                    a.ignoreCase === b.ignoreCase &&
+                    a.multiline === b.multiline;
+            },
+
+            // - skip when the property is a method of an instance (OOP)
+            // - abort otherwise,
+            //   initial === would have catch identical references anyway
+            "function": function () {
+                var caller = callers[callers.length - 1];
+                return caller !== Object &&
+                        typeof caller !== "undefined";
+            },
+
+            "array": function (b, a) {
+                var i, j, loop;
+                var len;
+
+                // b could be an object literal here
+                if ( ! (QUnit.objectType(b) === "array")) {
+                    return false;
+                }
+
+                len = a.length;
+                if (len !== b.length) { // safe and faster
+                    return false;
+                }
+
+                //track reference to avoid circular references
+                parents.push(a);
+                for (i = 0; i < len; i++) {
+                    loop = false;
+                    for(j=0;j<parents.length;j++){
+                        if(parents[j] === a[i]){
+                            loop = true;//dont rewalk array
+                        }
+                    }
+                    if (!loop && ! innerEquiv(a[i], b[i])) {
+                        parents.pop();
+                        return false;
+                    }
+                }
+                parents.pop();
+                return true;
+            },
+
+            "object": function (b, a) {
+                var i, j, loop;
+                var eq = true; // unless we can proove it
+                var aProperties = [], bProperties = []; // collection of strings
+
+                // comparing constructors is more strict than using instanceof
+                if ( a.constructor !== b.constructor) {
+                    return false;
+                }
+
+                // stack constructor before traversing properties
+                callers.push(a.constructor);
+                //track reference to avoid circular references
+                parents.push(a);
+
+                for (i in a) { // be strict: don't ensures hasOwnProperty and go deep
+                    loop = false;
+                    for(j=0;j<parents.length;j++){
+                        if(parents[j] === a[i])
+                            loop = true; //don't go down the same path twice
+                    }
+                    aProperties.push(i); // collect a's properties
+
+                    if (!loop && ! innerEquiv(a[i], b[i])) {
+                        eq = false;
+                        break;
+                    }
+                }
+
+                callers.pop(); // unstack, we are done
+                parents.pop();
+
+                for (i in b) {
+                    bProperties.push(i); // collect b's properties
+                }
+
+                // Ensures identical properties name
+                return eq && innerEquiv(aProperties.sort(), bProperties.sort());
+            }
+        };
+    }();
+
+    innerEquiv = function () { // can take multiple arguments
+        var args = Array.prototype.slice.apply(arguments);
+        if (args.length < 2) {
+            return true; // end transition
+        }
+
+        return (function (a, b) {
+            if (a === b) {
+                return true; // catch the most you can
+            } else if (a === null || b === null || typeof a === "undefined" || typeof b === "undefined" || QUnit.objectType(a) !== QUnit.objectType(b)) {
+                return false; // don't lose time with error prone cases
+            } else {
+                return bindCallbacks(a, callbacks, [b, a]);
+            }
+
+        // apply transition with (1..n) arguments
+        })(args[0], args[1]) && arguments.callee.apply(this, args.splice(1, args.length -1));
+    };
+
+    return innerEquiv;
+
+}();
+
+/**
+ * jsDump
+ * Copyright (c) 2008 Ariel Flesler - aflesler(at)gmail(dot)com | http://flesler.blogspot.com
+ * Licensed under BSD (http://www.opensource.org/licenses/bsd-license.php)
+ * Date: 5/15/2008
+ * @projectDescription Advanced and extensible data dumping for Javascript.
+ * @version 1.0.0
+ * @author Ariel Flesler
+ * @link {http://flesler.blogspot.com/2008/05/jsdump-pretty-dump-of-any-javascript.html}
+ */
+QUnit.jsDump = (function() {
+	function quote( str ) {
+		return '"' + str.toString().replace(/"/g, '\\"') + '"';
+	};
+	function literal( o ) {
+		return o + '';
+	};
+	function join( pre, arr, post ) {
+		var s = jsDump.separator(),
+			base = jsDump.indent(),
+			inner = jsDump.indent(1);
+		if ( arr.join )
+			arr = arr.join( ',' + s + inner );
+		if ( !arr )
+			return pre + post;
+		return [ pre, inner + arr, base + post ].join(s);
+	};
+	function array( arr ) {
+		var i = arr.length,	ret = Array(i);
+		this.up();
+		while ( i-- )
+			ret[i] = this.parse( arr[i] );
+		this.down();
+		return join( '[', ret, ']' );
+	};
+
+	var reName = /^function (\w+)/;
+
+	var jsDump = {
+		parse:function( obj, type ) { //type is used mostly internally, you can fix a (custom)type in advance
+			var	parser = this.parsers[ type || this.typeOf(obj) ];
+			type = typeof parser;
+
+			return type == 'function' ? parser.call( this, obj ) :
+				   type == 'string' ? parser :
+				   this.parsers.error;
+		},
+		typeOf:function( obj ) {
+			var type;
+			if ( obj === null ) {
+				type = "null";
+			} else if (typeof obj === "undefined") {
+				type = "undefined";
+			} else if (QUnit.is("RegExp", obj)) {
+				type = "regexp";
+			} else if (QUnit.is("Date", obj)) {
+				type = "date";
+			} else if (QUnit.is("Function", obj)) {
+				type = "function";
+			} else if (typeof obj.setInterval !== undefined && typeof obj.document !== "undefined" && typeof obj.nodeType === "undefined") {
+				type = "window";
+			} else if (obj.nodeType === 9) {
+				type = "document";
+			} else if (obj.nodeType) {
+				type = "node";
+			} else if (typeof obj === "object" && typeof obj.length === "number" && obj.length >= 0) {
+				type = "array";
+			} else {
+				type = typeof obj;
+			}
+			return type;
+		},
+		separator:function() {
+			return this.multiline ?	this.HTML ? '<br />' : '\n' : this.HTML ? '&nbsp;' : ' ';
+		},
+		indent:function( extra ) {// extra can be a number, shortcut for increasing-calling-decreasing
+			if ( !this.multiline )
+				return '';
+			var chr = this.indentChar;
+			if ( this.HTML )
+				chr = chr.replace(/\t/g,'   ').replace(/ /g,'&nbsp;');
+			return Array( this._depth_ + (extra||0) ).join(chr);
+		},
+		up:function( a ) {
+			this._depth_ += a || 1;
+		},
+		down:function( a ) {
+			this._depth_ -= a || 1;
+		},
+		setParser:function( name, parser ) {
+			this.parsers[name] = parser;
+		},
+		// The next 3 are exposed so you can use them
+		quote:quote,
+		literal:literal,
+		join:join,
+		//
+		_depth_: 1,
+		// This is the list of parsers, to modify them, use jsDump.setParser
+		parsers:{
+			window: '[Window]',
+			document: '[Document]',
+			error:'[ERROR]', //when no parser is found, shouldn't happen
+			unknown: '[Unknown]',
+			'null':'null',
+			undefined:'undefined',
+			'function':function( fn ) {
+				var ret = 'function',
+					name = 'name' in fn ? fn.name : (reName.exec(fn)||[])[1];//functions never have name in IE
+				if ( name )
+					ret += ' ' + name;
+				ret += '(';
+
+				ret = [ ret, QUnit.jsDump.parse( fn, 'functionArgs' ), '){'].join('');
+				return join( ret, QUnit.jsDump.parse(fn,'functionCode'), '}' );
+			},
+			array: array,
+			nodelist: array,
+			arguments: array,
+			object:function( map ) {
+				var ret = [ ];
+				QUnit.jsDump.up();
+				for ( var key in map )
+					ret.push( QUnit.jsDump.parse(key,'key') + ': ' + QUnit.jsDump.parse(map[key]) );
+				QUnit.jsDump.down();
+				return join( '{', ret, '}' );
+			},
+			node:function( node ) {
+				var open = QUnit.jsDump.HTML ? '&lt;' : '<',
+					close = QUnit.jsDump.HTML ? '&gt;' : '>';
+
+				var tag = node.nodeName.toLowerCase(),
+					ret = open + tag;
+
+				for ( var a in QUnit.jsDump.DOMAttrs ) {
+					var val = node[QUnit.jsDump.DOMAttrs[a]];
+					if ( val )
+						ret += ' ' + a + '=' + QUnit.jsDump.parse( val, 'attribute' );
+				}
+				return ret + close + open + '/' + tag + close;
+			},
+			functionArgs:function( fn ) {//function calls it internally, it's the arguments part of the function
+				var l = fn.length;
+				if ( !l ) return '';
+
+				var args = Array(l);
+				while ( l-- )
+					args[l] = String.fromCharCode(97+l);//97 is 'a'
+				return ' ' + args.join(', ') + ' ';
+			},
+			key:quote, //object calls it internally, the key part of an item in a map
+			functionCode:'[code]', //function calls it internally, it's the content of the function
+			attribute:quote, //node calls it internally, it's an html attribute value
+			string:quote,
+			date:quote,
+			regexp:literal, //regex
+			number:literal,
+			'boolean':literal
+		},
+		DOMAttrs:{//attributes to dump from nodes, name=>realName
+			id:'id',
+			name:'name',
+			'class':'className'
+		},
+		HTML:false,//if true, entities are escaped ( <, >, \t, space and \n )
+		indentChar:'  ',//indentation unit
+		multiline:true //if true, items in a collection, are separated by a \n, else just a space.
+	};
+
+	return jsDump;
+})();
+
+// from Sizzle.js
+function getText( elems ) {
+	var ret = "", elem;
+
+	for ( var i = 0; elems[i]; i++ ) {
+		elem = elems[i];
+
+		// Get the text from text nodes and CDATA nodes
+		if ( elem.nodeType === 3 || elem.nodeType === 4 ) {
+			ret += elem.nodeValue;
+
+		// Traverse everything else, except comment nodes
+		} else if ( elem.nodeType !== 8 ) {
+			ret += getText( elem.childNodes );
+		}
+	}
+
+	return ret;
+};
+
+/*
+ * Javascript Diff Algorithm
+ *  By John Resig (http://ejohn.org/)
+ *  Modified by Chu Alan "sprite"
+ *
+ * Released under the MIT license.
+ *
+ * More Info:
+ *  http://ejohn.org/projects/javascript-diff-algorithm/
+ *
+ * Usage: QUnit.diff(expected, actual)
+ *
+ * QUnit.diff("the quick brown fox jumped over", "the quick fox jumps over") == "the  quick <del>brown </del> fox <del>jumped </del><ins>jumps </ins> over"
+ */
+QUnit.diff = (function() {
+	function diff(o, n){
+		var ns = new Object();
+		var os = new Object();
+
+		for (var i = 0; i < n.length; i++) {
+			if (ns[n[i]] == null)
+				ns[n[i]] = {
+					rows: new Array(),
+					o: null
+				};
+			ns[n[i]].rows.push(i);
+		}
+
+		for (var i = 0; i < o.length; i++) {
+			if (os[o[i]] == null)
+				os[o[i]] = {
+					rows: new Array(),
+					n: null
+				};
+			os[o[i]].rows.push(i);
+		}
+
+		for (var i in ns) {
+			if (ns[i].rows.length == 1 && typeof(os[i]) != "undefined" && os[i].rows.length == 1) {
+				n[ns[i].rows[0]] = {
+					text: n[ns[i].rows[0]],
+					row: os[i].rows[0]
+				};
+				o[os[i].rows[0]] = {
+					text: o[os[i].rows[0]],
+					row: ns[i].rows[0]
+				};
+			}
+		}
+
+		for (var i = 0; i < n.length - 1; i++) {
+			if (n[i].text != null && n[i + 1].text == null && n[i].row + 1 < o.length && o[n[i].row + 1].text == null &&
+			n[i + 1] == o[n[i].row + 1]) {
+				n[i + 1] = {
+					text: n[i + 1],
+					row: n[i].row + 1
+				};
+				o[n[i].row + 1] = {
+					text: o[n[i].row + 1],
+					row: i + 1
+				};
+			}
+		}
+
+		for (var i = n.length - 1; i > 0; i--) {
+			if (n[i].text != null && n[i - 1].text == null && n[i].row > 0 && o[n[i].row - 1].text == null &&
+			n[i - 1] == o[n[i].row - 1]) {
+				n[i - 1] = {
+					text: n[i - 1],
+					row: n[i].row - 1
+				};
+				o[n[i].row - 1] = {
+					text: o[n[i].row - 1],
+					row: i - 1
+				};
+			}
+		}
+
+		return {
+			o: o,
+			n: n
+		};
+	}
+
+	return function(o, n){
+		o = o.replace(/\s+$/, '');
+		n = n.replace(/\s+$/, '');
+		var out = diff(o == "" ? [] : o.split(/\s+/), n == "" ? [] : n.split(/\s+/));
+
+		var str = "";
+
+		var oSpace = o.match(/\s+/g);
+		if (oSpace == null) {
+			oSpace = [" "];
+		}
+		else {
+			oSpace.push(" ");
+		}
+		var nSpace = n.match(/\s+/g);
+		if (nSpace == null) {
+			nSpace = [" "];
+		}
+		else {
+			nSpace.push(" ");
+		}
+
+		if (out.n.length == 0) {
+			for (var i = 0; i < out.o.length; i++) {
+				str += '<del>' + out.o[i] + oSpace[i] + "</del>";
+			}
+		}
+		else {
+			if (out.n[0].text == null) {
+				for (n = 0; n < out.o.length && out.o[n].text == null; n++) {
+					str += '<del>' + out.o[n] + oSpace[n] + "</del>";
+				}
+			}
+
+			for (var i = 0; i < out.n.length; i++) {
+				if (out.n[i].text == null) {
+					str += '<ins>' + out.n[i] + nSpace[i] + "</ins>";
+				}
+				else {
+					var pre = "";
+
+					for (n = out.n[i].row + 1; n < out.o.length && out.o[n].text == null; n++) {
+						pre += '<del>' + out.o[n] + oSpace[n] + "</del>";
+					}
+					str += " " + out.n[i].text + nSpace[i] + pre;
+				}
+			}
+		}
+
+		return str;
+	};
+})();
+
+})(this);

Debdiff

[The following lists of changes regard files as different if they have different names, permissions or owners.]

Files in second set of .debs but not in first

-rw-r--r--  root/root   /usr/share/doc/libjs-jquery-tmpl/README.md.gz

Files in first set of .debs but not in second

-rw-r--r--  root/root   /usr/share/doc/libjs-jquery-tmpl/README.md

No differences were encountered in the control files

More details

Full run details