<?xml version="1.0" encoding="UTF-8"?>
<commit>
  <added type="array">
    <added>
      <filename>resources/script/spry/includes/SpryCSVDataSet.js</filename>
    </added>
    <added>
      <filename>resources/script/spry/includes/SpryDOMUtils.js</filename>
    </added>
    <added>
      <filename>resources/script/spry/includes/SpryDataSetShell.js</filename>
    </added>
    <added>
      <filename>resources/script/spry/includes/SpryImageLoader.js</filename>
    </added>
    <added>
      <filename>resources/script/spry/includes/SpryNotifier.js</filename>
    </added>
    <added>
      <filename>resources/script/spry/includes/SpryTSVDataSet.js</filename>
    </added>
    <added>
      <filename>resources/script/spry/widgets/autosuggest/SpryAutoSuggest.html</filename>
    </added>
    <added>
      <filename>resources/script/spry/widgets/checkboxvalidation/SpryValidationCheckbox.html</filename>
    </added>
    <added>
      <filename>resources/script/spry/widgets/confirmvalidation/SpryValidationConfirm.css</filename>
    </added>
    <added>
      <filename>resources/script/spry/widgets/confirmvalidation/SpryValidationConfirm.html</filename>
    </added>
    <added>
      <filename>resources/script/spry/widgets/confirmvalidation/SpryValidationConfirm.js</filename>
    </added>
    <added>
      <filename>resources/script/spry/widgets/htmlpanel/SpryHTMLPanel.css</filename>
    </added>
    <added>
      <filename>resources/script/spry/widgets/htmlpanel/SpryHTMLPanel.html</filename>
    </added>
    <added>
      <filename>resources/script/spry/widgets/htmlpanel/SpryHTMLPanel.js</filename>
    </added>
    <added>
      <filename>resources/script/spry/widgets/passwordvalidation/SpryValidationPassword.css</filename>
    </added>
    <added>
      <filename>resources/script/spry/widgets/passwordvalidation/SpryValidationPassword.html</filename>
    </added>
    <added>
      <filename>resources/script/spry/widgets/passwordvalidation/SpryValidationPassword.js</filename>
    </added>
    <added>
      <filename>resources/script/spry/widgets/radiovalidation/SpryValidationRadio.html</filename>
    </added>
    <added>
      <filename>resources/script/spry/widgets/rating/SpryRating.css</filename>
    </added>
    <added>
      <filename>resources/script/spry/widgets/rating/SpryRating.html</filename>
    </added>
    <added>
      <filename>resources/script/spry/widgets/rating/SpryRating.js</filename>
    </added>
    <added>
      <filename>resources/script/spry/widgets/rating/SpryStarEmpty.gif</filename>
    </added>
    <added>
      <filename>resources/script/spry/widgets/rating/SpryStarEmpty.png</filename>
    </added>
    <added>
      <filename>resources/script/spry/widgets/rating/SpryStarFull.gif</filename>
    </added>
    <added>
      <filename>resources/script/spry/widgets/rating/SpryStarFull.png</filename>
    </added>
    <added>
      <filename>resources/script/spry/widgets/rating/SpryStarFullRO.gif</filename>
    </added>
    <added>
      <filename>resources/script/spry/widgets/rating/SpryStarFullRO.png</filename>
    </added>
    <added>
      <filename>resources/script/spry/widgets/rating/SpryStarHalf.gif</filename>
    </added>
    <added>
      <filename>resources/script/spry/widgets/rating/SpryStarHalf.png</filename>
    </added>
    <added>
      <filename>resources/script/spry/widgets/rating/SpryStarHalfRO.gif</filename>
    </added>
    <added>
      <filename>resources/script/spry/widgets/rating/SpryStarHalfRO.png</filename>
    </added>
    <added>
      <filename>resources/script/spry/widgets/rating/SpryStarHover.gif</filename>
    </added>
    <added>
      <filename>resources/script/spry/widgets/rating/SpryStarHover.png</filename>
    </added>
    <added>
      <filename>resources/script/spry/widgets/selectvalidation/SpryValidationSelect.html</filename>
    </added>
    <added>
      <filename>resources/script/spry/widgets/textareavalidation/SpryValidationTextArea.html</filename>
    </added>
    <added>
      <filename>resources/script/spry/widgets/textfieldvalidation/SpryValidationTextField.html</filename>
    </added>
    <added>
      <filename>resources/script/spry/widgets/tooltip/SpryTooltip.css</filename>
    </added>
    <added>
      <filename>resources/script/spry/widgets/tooltip/SpryTooltip.html</filename>
    </added>
    <added>
      <filename>resources/script/spry/widgets/tooltip/SpryTooltip.js</filename>
    </added>
  </added>
  <modified type="array">
    <modified>
      <diff>@@ -138,12 +138,16 @@ else
         'selectvalidation/SpryValidationSelect.js',
         'textareavalidation/SpryValidationTextarea.js',
         'checkboxvalidation/SpryValidationCheckbox.js',
+        'passwordvalidation/SpryValidationPassword.js',
+        'confirmvalidation/SpryValidationConfirm.js',
     );
     $spry['css'] = array(
         'textfieldvalidation/SpryValidationTextField.css',
         'selectvalidation/SpryValidationSelect.css',
         'textareavalidation/SpryValidationTextarea.css',
         'checkboxvalidation/SpryValidationCheckbox.css',
+        'passwordvalidation/SpryValidationPassword.css',
+        'confirmvalidation/SpryValidationConfirm.css',
     );
     $renderer-&gt;assign('spry',$spry);
 </diff>
      <filename>index.php</filename>
    </modified>
    <modified>
      <diff>@@ -1,4 +1,4 @@
-// SpryData.js - version 0.35 - Spry Pre-Release 1.5
+// SpryData.js - version 0.45 - Spry Pre-Release 1.6.1
 //
 // Copyright (c) 2006. Adobe Systems Incorporated.
 // All rights reserved.
@@ -37,7 +37,7 @@ var Spry; if (!Spry) Spry = {};
 
 if (!Spry.Utils) Spry.Utils = {};
 
-Spry.Utils.msProgIDs = [&quot;MSXML2.XMLHTTP.6.0&quot;, &quot;MSXML2.XMLHTTP.5.0&quot;, &quot;MSXML2.XMLHTTP.4.0&quot;, &quot;MSXML2.XMLHTTP.3.0&quot;, &quot;MSXML2.XMLHTTP&quot;, &quot;Microsoft.XMLHTTP&quot;];
+Spry.Utils.msProgIDs = [&quot;MSXML2.XMLHTTP.6.0&quot;, &quot;MSXML2.XMLHTTP.3.0&quot;];
 
 Spry.Utils.createXMLHttpRequest = function()
 {
@@ -209,6 +209,20 @@ Spry.Utils.updateContent = function (ele, url, finishFunc, opts)
 	}, opts);
 };
 
+//////////////////////////////////////////////////////////////////////
+//
+// Functions from SpryDOMUtils.js
+//   - These have been left in for backwards compatibility, but they
+//     should only be defined if Spry.$$ (SpryDOMUtils.js) is not
+//     already included.
+//   - If SpryDOMUtils.js is included *after* SpryData.js, these
+//     functions will be replaced with the latest versions in
+//     SpryDOMUtils.js.
+//
+//////////////////////////////////////////////////////////////////////
+
+if (!Spry.$$)
+{
 Spry.Utils.addEventListener = function(element, eventType, handler, capture)
 {
 	try
@@ -245,6 +259,62 @@ Spry.Utils.addLoadListener = function(handler)
 		window.attachEvent('onload', handler);
 };
 
+Spry.Utils.addClassName = function(ele, className)
+{
+	ele = Spry.$(ele);
+	if (!ele || !className || (ele.className &amp;&amp; ele.className.search(new RegExp(&quot;\\b&quot; + className + &quot;\\b&quot;)) != -1))
+		return;
+	ele.className += (ele.className ? &quot; &quot; : &quot;&quot;) + className;
+};
+
+Spry.Utils.removeClassName = function(ele, className)
+{
+	ele = Spry.$(ele);
+	if (!ele || !className || (ele.className &amp;&amp; ele.className.search(new RegExp(&quot;\\b&quot; + className + &quot;\\b&quot;)) == -1))
+		return;
+	ele.className = ele.className.replace(new RegExp(&quot;\\s*\\b&quot; + className + &quot;\\b&quot;, &quot;g&quot;), &quot;&quot;);
+};
+
+Spry.Utils.getObjectByName = function(name)
+{
+	var result = null;
+	if (name)
+	{
+		var lu = window;
+		var objPath = name.split(&quot;.&quot;);
+		for (var i = 0; lu &amp;&amp; i &lt; objPath.length; i++)
+		{
+			result = lu[objPath[i]];
+			lu = result;
+		}
+	}
+	return result;
+};
+
+//////////////////////////////////////////////////////////////////////
+//
+// Define Prototype's $() convenience function, but make sure it is
+// namespaced under Spry so that we avoid collisions with other
+// toolkits.
+//
+//////////////////////////////////////////////////////////////////////
+
+Spry.$ = function(element)
+{
+	if (arguments.length &gt; 1)
+	{
+		for (var i = 0, elements = [], length = arguments.length; i &lt; length; i++)
+			elements.push(Spry.$(arguments[i]));
+		return elements;
+	}
+	if (typeof element == 'string')
+		element = document.getElementById(element);
+	return element;
+};
+} // if (!Spry.$$)
+
+//////////////////////////////////////////////////////////////////////
+
 Spry.Utils.eval = function(str)
 {
 	// Call this method from your JS function when
@@ -561,22 +631,7 @@ Spry.Utils.getNodesByFunc = function(root, func)
 	return resultArr;
 };
 
-Spry.Utils.addClassName = function(ele, className)
-{
-	ele = Spry.$(ele);
-	if (!ele || !className || (ele.className &amp;&amp; ele.className.search(new RegExp(&quot;\\b&quot; + className + &quot;\\b&quot;)) != -1))
-		return;
-	ele.className += (ele.className ? &quot; &quot; : &quot;&quot;) + className;
-};
-
-Spry.Utils.removeClassName = function(ele, className)
-{
-	ele = Spry.$(ele);
-	if (!ele || !className || (ele.className &amp;&amp; ele.className.search(new RegExp(&quot;\\b&quot; + className + &quot;\\b&quot;)) == -1))
-		return;
-	ele.className = ele.className.replace(new RegExp(&quot;\\s*\\b&quot; + className + &quot;\\b&quot;, &quot;g&quot;), &quot;&quot;);
-};
-
+// XXX: UNUSED FUNCTION
 Spry.Utils.getFirstChildWithNodeName = function(node, nodeName)
 {
 	var child = node.firstChild;
@@ -591,194 +646,6 @@ Spry.Utils.getFirstChildWithNodeName = function(node, nodeName)
 	return null;
 };
 
-Spry.Utils.nodeContainsElementNode = function(node)
-{
-	if (node)
-	{
-		node = node.firstChild;
-
-		while (node)
-		{
-			if (node.nodeType == 1 /* Node.ELEMENT_NODE */)
-				return true;
-
-			node = node.nextSibling;
-		}
-	}
-	return false;
-};
-
-Spry.Utils.getNodeText = function(node)
-{
-	var txt = &quot;&quot;;
-
-	if (!node)
-		return;
-
-	try
-	{
-		var child = node.firstChild;
-
-		while (child)
-		{
-			try
-			{
-				if (child.nodeType == 3 /* TEXT_NODE */)
-					txt += Spry.Utils.encodeEntities(child.data);
-				else if (child.nodeType == 4 /* CDATA_SECTION_NODE */)
-					txt += child.data;
-			} catch (e) { Spry.Debug.reportError(&quot;Spry.Utils.getNodeText() exception caught: &quot; + e + &quot;\n&quot;); }
-
-			child = child.nextSibling;
-		}
-	}
-	catch (e) { Spry.Debug.reportError(&quot;Spry.Utils.getNodeText() exception caught: &quot; + e + &quot;\n&quot;); }
-
-	return txt;
-};
-
-Spry.Utils.CreateObjectForNode = function(node)
-{
-	if (!node)
-		return null;
-
-	var obj = new Object();
-	var i = 0;
-	var attr = null;
-
-	try
-	{
-		for (i = 0; i &lt; node.attributes.length; i++)
-		{
-			attr = node.attributes[i];
-			if (attr &amp;&amp; attr.nodeType == 2 /* Node.ATTRIBUTE_NODE */)
-				obj[&quot;@&quot; + attr.name] = attr.value;
-		}
-	}
-	catch (e)
-	{
-		Spry.Debug.reportError(&quot;Spry.Utils.CreateObjectForNode() caught exception while accessing attributes: &quot; + e + &quot;\n&quot;);
-	}
-
-	var child = node.firstChild;
-
-	if (child &amp;&amp; !child.nextSibling &amp;&amp; child.nodeType != 1 /* Node.ELEMENT_NODE */)
-	{
-		// We have a single child and it's not an element. It must
-		// be the text value for this node. Add it to the record set and
-		// give it the column the same name as the node.
-
-		obj[node.nodeName] = Spry.Utils.getNodeText(node);
-	}
-
-	while (child)
-	{
-		// Add the text value for each child element. Note that
-		// We skip elements that have element children (sub-elements)
-		// because we don't handle multi-level data sets right now.
-
-		if (child.nodeType == 1 /* Node.ELEMENT_NODE */)
-		{
-			if (!Spry.Utils.nodeContainsElementNode(child))
-			{
-				obj[child.nodeName] = Spry.Utils.getNodeText(child);
-
-				// Now add properties for any attributes on the child. The property
-				// name will be of the form &quot;&lt;child.nodeName&gt;/@&lt;attr.name&gt;&quot;.
-				try
-				{
-					var namePrefix = child.nodeName + &quot;/@&quot;;
-
-					for (i = 0; i &lt; child.attributes.length; i++)
-					{
-						attr = child.attributes[i];
-						if (attr &amp;&amp; attr.nodeType == 2 /* Node.ATTRIBUTE_NODE */)
-							obj[namePrefix + attr.name] = attr.value;
-					}
-				}
-				catch (e)
-				{
-					Spry.Debug.reportError(&quot;Spry.Utils.CreateObjectForNode() caught exception while accessing attributes: &quot; + e + &quot;\n&quot;);
-				}
-			}
-		}
-
-		child = child.nextSibling;
-	}
-
-	return obj;
-};
-
-Spry.Utils.getRecordSetFromXMLDoc = function(xmlDoc, path, suppressColumns)
-{
-	if (!xmlDoc || !path)
-		return null;
-
-	var recordSet = new Object();
-	recordSet.xmlDoc = xmlDoc;
-	recordSet.xmlPath = path;
-	recordSet.dataHash = new Object;
-	recordSet.data = new Array;
-	recordSet.getData = function() { return this.data; };
-
-	// Use the XPath library to find the nodes that will
-	// make up our data set. The result should be an array
-	// of subtrees that we need to flatten.
-
-	var ctx = new ExprContext(xmlDoc);
-	var pathExpr = xpathParse(path);
-	var e = pathExpr.evaluate(ctx);
-
-	// XXX: Note that we should check the result type of the evaluation
-	// just in case it's a boolean, string, or number value instead of
-	// a node set.
-
-	var nodeArray = e.nodeSetValue();
-
-	var isDOMNodeArray = true;
-
-	if (nodeArray &amp;&amp; nodeArray.length &gt; 0)
-		isDOMNodeArray = nodeArray[0].nodeType != 2 /* Node.ATTRIBUTE_NODE */;
-
-	var nextID = 0;
-
-	// We now have the set of nodes that make up our data set
-	// so process each one.
-
-	for (var i = 0; i &lt; nodeArray.length; i++)
-	{
-		var rowObj = null;
-
-		if (suppressColumns)
-			rowObj = new Object;
-		else
-		{
-			if (isDOMNodeArray)
-				rowObj = Spry.Utils.CreateObjectForNode(nodeArray[i]);
-			else // Must be a Node.ATTRIBUTE_NODE array.
-			{
-				rowObj = new Object;
-				rowObj[&quot;@&quot; + nodeArray[i].name] = nodeArray[i].value;
-			}
-		}
-
-		if (rowObj)
-		{
-			// We want to make sure that every row has a unique ID and since we
-			// we don't know which column, if any, in this recordSet is a unique
-			// identifier, we generate a unique ID ourselves and store it under
-			// the ds_RowID column in the row object.
-
-			rowObj['ds_RowID'] = nextID++;
-			rowObj['ds_XMLNode'] = nodeArray[i];
-			recordSet.dataHash[rowObj['ds_RowID']] = rowObj;
-			recordSet.data.push(rowObj);
-		}
-	}
-
-	return recordSet;
-};
-
 Spry.Utils.setOptions = function(obj, optionsObj, ignoreUndefinedProps)
 {
 	if (!optionsObj)
@@ -915,27 +782,6 @@ Spry.Utils.SelectionManager.clearSelection = function(selectionGroupName)
 	groupObj.clearSelection();
 };
 
-//////////////////////////////////////////////////////////////////////
-//
-// Define Prototype's $() convenience function, but make sure it is
-// namespaced under Spry so that we avoid collisions with other
-// toolkits.
-//
-//////////////////////////////////////////////////////////////////////
-
-Spry.$ = function(element)
-{
-	if (arguments.length &gt; 1)
-	{
-		for (var i = 0, elements = [], length = arguments.length; i &lt; length; i++)
-			elements.push(Spry.$(arguments[i]));
-		return elements;
-	}
-	if (typeof element == 'string')
-		element = document.getElementById(element);
-	return element;
-};
-
 Spry.Utils.Notifier = function()
 {
 	this.observers = [];
@@ -1099,11 +945,11 @@ Spry.Debug.reportError = function(str)
 
 Spry.Data = {};
 Spry.Data.regionsArray = {};
+Spry.Data.initRegionsOnLoad = true;
 
 Spry.Data.initRegions = function(rootNode)
 {
-	if (!rootNode)
-		rootNode = document.body;
+	rootNode = rootNode ? Spry.$(rootNode) : document.body;
 
 	var lastRegionFound = null;
 
@@ -1162,6 +1008,7 @@ Spry.Data.initRegions = function(rootNode)
 	});
 
 	var name, dataSets, i;
+	var newRegions = [];
 
 	for (i = 0; i &lt; regions.length; i++)
 	{
@@ -1255,7 +1102,7 @@ Spry.Data.initRegions = function(rootNode)
 						if (childrenOnly)
 						{
 								var oComment = document.createComment(openTag);
-								var cComment = document.createComment(closeTag)
+								var cComment = document.createComment(closeTag);
 
 								if (!lastStartComment)
 									node.insertBefore(oComment, node.firstChild);
@@ -1334,9 +1181,11 @@ Spry.Data.initRegions = function(rootNode)
 		// Create a Spry.Data.Region object for this region.
 		var region = new Spry.Data.Region(rgn, name, isDetailRegion, dataStr, dataSets, regionStates, regionStateMap, hasBehaviorAttributes);
 		Spry.Data.regionsArray[region.name] = region;
+		newRegions.push(region);
 	}
 
-	Spry.Data.updateAllRegions();
+	for (var i = 0; i &lt; newRegions.length; i++)
+		newRegions[i].updateContent();
 };
 
 Spry.Data.initRegions.nextUniqueRegionID = 0;
@@ -1365,6 +1214,23 @@ Spry.Data.updateAllRegions = function()
 		Spry.Data.updateRegion(regionName);
 };
 
+Spry.Data.getDataSetByName = function(dataSetName)
+{
+	// Currently, there is no registry of mappings between
+	// data set names and data set objects. For now, the assumption
+	// is that the user has declared and created a data set in the
+	// global space.
+	//
+	// We check for the presence of a global variable with the
+	// specified name, and then make sure that its value is an
+	// object with at least 2 of the data set base functions defined.
+
+	var ds = window[dataSetName];
+	if (typeof ds != &quot;object&quot; || !ds.getData || !ds.filter)
+		return null;
+	return ds;
+};
+
 //////////////////////////////////////////////////////////////////////
 //
 // Spry.Data.DataSet
@@ -1426,6 +1292,56 @@ Spry.Data.DataSet.prototype.getDataWasLoaded = function()
 	return this.dataWasLoaded;
 };
 
+Spry.Data.DataSet.prototype.getValue = function(valueName, rowContext)
+{
+	var result = undefined;
+
+	// If a rowContext is not defined, we default to
+	// using the current row.
+
+	if (!rowContext)
+		rowContext = this.getCurrentRow();
+
+	switch(valueName)
+	{
+		case &quot;ds_RowNumber&quot;:
+			result = this.getRowNumber(rowContext);
+			break;
+		case &quot;ds_RowNumberPlus1&quot;:
+			result = this.getRowNumber(rowContext) + 1;
+			break;
+		case &quot;ds_RowCount&quot;:
+			result = this.getRowCount();
+			break;
+		case &quot;ds_UnfilteredRowCount&quot;:
+			result = this.getRowCount(true);
+			break;
+		case &quot;ds_CurrentRowNumber&quot;:
+			result = this.getCurrentRowNumber();
+			break;
+		case &quot;ds_CurrentRowID&quot;:
+			result = this.getCurrentRowID();
+			break;
+		case &quot;ds_EvenOddRow&quot;:
+			result = (this.getRowNumber(rowContext) % 2) ? Spry.Data.Region.evenRowClassName : Spry.Data.Region.oddRowClassName;
+			break;
+		case &quot;ds_SortOrder&quot;:
+			result = this.getSortOrder();
+			break;
+		case &quot;ds_SortColumn&quot;:
+			result = this.getSortColumn();
+			break;
+		default:
+			// We have an unknown value, check to see if the current
+			// row has column value that matches the valueName.
+			if (rowContext)
+				result = rowContext[valueName];
+			break;
+	}
+
+	return result;
+};
+
 Spry.Data.DataSet.prototype.setDataFromArray = function(arr, fireSyncLoad)
 {
 	this.notifyObservers(&quot;onPreLoad&quot;);
@@ -1477,7 +1393,10 @@ Spry.Data.DataSet.prototype.loadData = function(syncLoad)
 		self.dataWasLoaded = true;
 
 		self.applyColumnTypes();
+
+		self.disableNotifications();
 		self.filterAndSortData();
+		self.enableNotifications();
 
 		self.notifyObservers(&quot;onPostLoad&quot;);
 		self.notifyObservers(&quot;onDataChanged&quot;);
@@ -1507,7 +1426,7 @@ Spry.Data.DataSet.prototype.filterAndSortData = function()
 	// specified in sortOnLoad.
 
 	if (this.keepSorted &amp;&amp; this.getSortColumn())
-		this.sort(this.lastSortColumns, this.lastSortOrder)
+		this.sort(this.lastSortColumns, this.lastSortOrder);
 	else if (this.sortOnLoad)
 		this.sort(this.sortOnLoad, this.sortOrderOnLoad);
 
@@ -2160,8 +2079,7 @@ Spry.Data.HTTPSourceDataSet.prototype.recalculateDataSetDependencies = function(
 				var ds = null;
 				if (!this.dataSetsForDataRefStrings[dsName])
 				{
-					try { ds = eval(dsName); } catch (e) { ds = null; }
-
+					ds = Spry.Data.getDataSetByName(dsName);
 					if (dsName &amp;&amp; ds)
 					{
 						// The dataSetsForDataRefStrings array serves as both an
@@ -2321,7 +2239,10 @@ Spry.Data.HTTPSourceDataSet.prototype.setDataFromDoc = function(rawDataDoc)
 
 	this.loadDataIntoDataSet(rawDataDoc);
 	this.applyColumnTypes();
+
+	this.disableNotifications();
 	this.filterAndSortData();
+	this.enableNotifications();
 
 	this.notifyObservers(&quot;onPostLoad&quot;);
 	this.notifyObservers(&quot;onDataChanged&quot;);
@@ -2570,6 +2491,7 @@ Spry.Data.XMLDataSet = function(dataSetURL, dataSetPath, dataSetOptions)
 	this.xpath = dataSetPath;
 	this.doc = null;
 	this.subPaths = [];
+	this.entityEncodeStrings = true;
 
 	Spry.Data.HTTPSourceDataSet.call(this, dataSetURL, dataSetOptions);
 
@@ -2610,6 +2532,199 @@ Spry.Data.XMLDataSet.prototype.setXPath = function(path)
 	}
 };
 
+Spry.Data.XMLDataSet.nodeContainsElementNode = function(node)
+{
+	if (node)
+	{
+		node = node.firstChild;
+
+		while (node)
+		{
+			if (node.nodeType == 1 /* Node.ELEMENT_NODE */)
+				return true;
+
+			node = node.nextSibling;
+		}
+	}
+	return false;
+};
+
+Spry.Data.XMLDataSet.getNodeText = function(node, encodeText, encodeCData)
+{
+	var txt = &quot;&quot;;
+
+	if (!node)
+		return;
+
+	try
+	{
+		var child = node.firstChild;
+
+		while (child)
+		{
+			try
+			{
+				if (child.nodeType == 3 /* TEXT_NODE */)
+					txt += encodeText ? Spry.Utils.encodeEntities(child.data) : child.data;
+				else if (child.nodeType == 4 /* CDATA_SECTION_NODE */)
+					txt += encodeCData ? Spry.Utils.encodeEntities(child.data) : child.data;
+			} catch (e) { Spry.Debug.reportError(&quot;Spry.Data.XMLDataSet.getNodeText() exception caught: &quot; + e + &quot;\n&quot;); }
+
+			child = child.nextSibling;
+		}
+	}
+	catch (e) { Spry.Debug.reportError(&quot;Spry.Data.XMLDataSet.getNodeText() exception caught: &quot; + e + &quot;\n&quot;); }
+
+	return txt;
+};
+
+Spry.Data.XMLDataSet.createObjectForNode = function(node, encodeText, encodeCData)
+{
+	if (!node)
+		return null;
+
+	var obj = new Object();
+	var i = 0;
+	var attr = null;
+
+	try
+	{
+		for (i = 0; i &lt; node.attributes.length; i++)
+		{
+			attr = node.attributes[i];
+			if (attr &amp;&amp; attr.nodeType == 2 /* Node.ATTRIBUTE_NODE */)
+				obj[&quot;@&quot; + attr.name] = attr.value;
+		}
+	}
+	catch (e)
+	{
+		Spry.Debug.reportError(&quot;Spry.Data.XMLDataSet.createObjectForNode() caught exception while accessing attributes: &quot; + e + &quot;\n&quot;);
+	}
+
+	var child = node.firstChild;
+
+	if (child &amp;&amp; !child.nextSibling &amp;&amp; child.nodeType != 1 /* Node.ELEMENT_NODE */)
+	{
+		// We have a single child and it's not an element. It must
+		// be the text value for this node. Add it to the record set and
+		// give it the column the same name as the node.
+
+		obj[node.nodeName] = Spry.Data.XMLDataSet.getNodeText(node, encodeText, encodeCData);
+	}
+
+	while (child)
+	{
+		// Add the text value for each child element. Note that
+		// We skip elements that have element children (sub-elements)
+		// because we don't handle multi-level data sets right now.
+
+		if (child.nodeType == 1 /* Node.ELEMENT_NODE */)
+		{
+			if (!Spry.Data.XMLDataSet.nodeContainsElementNode(child))
+			{
+				obj[child.nodeName] = Spry.Data.XMLDataSet.getNodeText(child, encodeText, encodeCData);
+
+				// Now add properties for any attributes on the child. The property
+				// name will be of the form &quot;&lt;child.nodeName&gt;/@&lt;attr.name&gt;&quot;.
+				try
+				{
+					var namePrefix = child.nodeName + &quot;/@&quot;;
+
+					for (i = 0; i &lt; child.attributes.length; i++)
+					{
+						attr = child.attributes[i];
+						if (attr &amp;&amp; attr.nodeType == 2 /* Node.ATTRIBUTE_NODE */)
+							obj[namePrefix + attr.name] = attr.value;
+					}
+				}
+				catch (e)
+				{
+					Spry.Debug.reportError(&quot;Spry.Data.XMLDataSet.createObjectForNode() caught exception while accessing attributes: &quot; + e + &quot;\n&quot;);
+				}
+			}
+		}
+
+		child = child.nextSibling;
+	}
+
+	return obj;
+};
+
+Spry.Data.XMLDataSet.getRecordSetFromXMLDoc = function(xmlDoc, path, suppressColumns, entityEncodeStrings)
+{
+	if (!xmlDoc || !path)
+		return null;
+
+	var recordSet = new Object();
+	recordSet.xmlDoc = xmlDoc;
+	recordSet.xmlPath = path;
+	recordSet.dataHash = new Object;
+	recordSet.data = new Array;
+	recordSet.getData = function() { return this.data; };
+
+	// Use the XPath library to find the nodes that will
+	// make up our data set. The result should be an array
+	// of subtrees that we need to flatten.
+
+	var ctx = new ExprContext(xmlDoc);
+	var pathExpr = xpathParse(path);
+	var e = pathExpr.evaluate(ctx);
+
+	// XXX: Note that we should check the result type of the evaluation
+	// just in case it's a boolean, string, or number value instead of
+	// a node set.
+
+	var nodeArray = e.nodeSetValue();
+
+	var isDOMNodeArray = true;
+
+	if (nodeArray &amp;&amp; nodeArray.length &gt; 0)
+		isDOMNodeArray = nodeArray[0].nodeType != 2 /* Node.ATTRIBUTE_NODE */;
+
+	var nextID = 0;
+
+	var encodeText = true;
+	var encodeCData = false;
+	if (typeof entityEncodeStrings == &quot;boolean&quot;)
+		encodeText = encodeCData = entityEncodeStrings;
+
+	// We now have the set of nodes that make up our data set
+	// so process each one.
+
+	for (var i = 0; i &lt; nodeArray.length; i++)
+	{
+		var rowObj = null;
+
+		if (suppressColumns)
+			rowObj = new Object;
+		else
+		{
+			if (isDOMNodeArray)
+				rowObj = Spry.Data.XMLDataSet.createObjectForNode(nodeArray[i], encodeText, encodeCData);
+			else // Must be a Node.ATTRIBUTE_NODE array.
+			{
+				rowObj = new Object;
+				rowObj[&quot;@&quot; + nodeArray[i].name] = nodeArray[i].value;
+			}
+		}
+
+		if (rowObj)
+		{
+			// We want to make sure that every row has a unique ID and since we
+			// we don't know which column, if any, in this recordSet is a unique
+			// identifier, we generate a unique ID ourselves and store it under
+			// the ds_RowID column in the row object.
+
+			rowObj['ds_RowID'] = nextID++;
+			rowObj['ds_XMLNode'] = nodeArray[i];
+			recordSet.dataHash[rowObj['ds_RowID']] = rowObj;
+			recordSet.data.push(rowObj);
+		}
+	}
+
+	return recordSet;
+};
+
 Spry.Data.XMLDataSet.PathNode = function(path)
 {
 	this.path = path;
@@ -2797,7 +2912,7 @@ Spry.Data.XMLDataSet.prototype.flattenSubPaths = function(rs, subPaths)
 			// the XML node for the base row and flatten the data into
 			// a tabular recordset structure.
 
-			var newRS = Spry.Utils.getRecordSetFromXMLDoc(row.ds_XMLNode, xpathArray[j], (subPaths[j].xpath ? false : true));
+			var newRS = Spry.Data.XMLDataSet.getRecordSetFromXMLDoc(row.ds_XMLNode, xpathArray[j], (subPaths[j].xpath ? false : true), this.entityEncodeStrings);
 
 			// If this subPath has additional subPaths beneath it,
 			// flatten and join them with the recordset we just created.
@@ -2981,7 +3096,7 @@ Spry.Data.XMLDataSet.prototype.loadDataIntoDataSet = function(rawDataDoc)
 		suppressColumns = commonParent.xpath ? false : true;
 	}
 
-	rs = Spry.Utils.getRecordSetFromXMLDoc(rawDataDoc, mainXPath, suppressColumns);
+	rs = Spry.Data.XMLDataSet.getRecordSetFromXMLDoc(rawDataDoc, mainXPath, suppressColumns, this.entityEncodeStrings);
 
 	if (!rs)
 	{
@@ -3393,8 +3508,7 @@ Spry.Data.Region.setRowAttrClickHandler = function(node, dsName, rowAttr, funcNa
 {
 		if (dsName)
 		{
-			var ds = null;
-			try { ds = Spry.Utils.eval(dsName); } catch(e) { ds = null; };
+			var ds = Spry.Data.getDataSetByName(dsName);
 			if (ds)
 			{
 				rowIDAttr = node.attributes.getNamedItem(rowAttr);
@@ -3485,16 +3599,12 @@ Spry.Data.Region.behaviorAttrs[&quot;spry:sort&quot;] =
 			// Check the first string in the attribute to see if a data set was
 			// specified. If so, make sure we use it for the sort.
 
-			try
+			var specifiedDS = Spry.Data.getDataSetByName(colArray[0]);
+			if (specifiedDS)
 			{
-				var specifiedDS = eval(colArray[0]);
-				if (specifiedDS &amp;&amp; (typeof specifiedDS) == &quot;object&quot;)
-				{
-					ds = specifiedDS;
-					colArray.shift();
-				}
-
-			} catch(e) {}
+				ds = specifiedDS;
+				colArray.shift();
+			}
 
 			// Check to see if the last string in the attribute is the name of
 			// a sort order. If so, use that sort order during the sort.
@@ -3632,7 +3742,7 @@ Spry.Data.Region.processContentPI = function(inStr)
 	}
 
 	return outStr;
-}
+};
 
 Spry.Data.Region.prototype.tokenizeData = function(dataStr)
 {
@@ -3756,14 +3866,10 @@ Spry.Data.Region.prototype.tokenizeData = function(dataStr)
 
 						if (selectedDataSetName)
 						{
-							try
+							dataSet = Spry.Data.getDataSetByName(selectedDataSetName);
+							if (!dataSet)
 							{
-								dataSet = eval(selectedDataSetName);
-							}
-							catch (e)
-							{
-								Spry.Debug.reportError(&quot;Caught exception in tokenizeData() while trying to retrieve data set (&quot; + selectedDataSetName + &quot;): &quot; + e + &quot;\n&quot;);
-								dataSet = null;
+								Spry.Debug.reportError(&quot;Failed to retrieve data set (&quot; + selectedDataSetName + &quot;) for &quot; + piName + &quot;\n&quot;);
 								selectedDataSetName = &quot;&quot;;
 							}
 						}
@@ -3813,6 +3919,37 @@ Spry.Data.Region.prototype.tokenizeData = function(dataStr)
 	return rootToken;
 };
 
+Spry.Data.Region.prototype.callScriptFunction = function(funcName, processContext)
+{
+	var result = undefined;
+
+	funcName = funcName.replace(/^\s*\{?\s*function::\s*|\s*\}?\s*$/g, &quot;&quot;);
+	var func = Spry.Utils.getObjectByName(funcName);
+	if (func)
+		result = func(this.name, function() { return processContext.getValueFromDataSet.apply(processContext, arguments); });
+
+	return result;
+};
+
+Spry.Data.Region.prototype.evaluateExpression = function(exprStr, processContext)
+{
+	var result = undefined;
+
+	try
+	{
+		if (exprStr.search(/^\s*function::/) != -1)
+			result = this.callScriptFunction(exprStr, processContext);
+		else
+			result = Spry.Utils.eval(Spry.Data.Region.processDataRefString(processContext, exprStr, null, true));
+	}
+	catch(e)
+	{
+		Spry.Debug.trace(&quot;Caught exception in Spry.Data.Region.prototype.evaluateExpression() while evaluating: &quot; + Spry.Utils.encodeEntities(exprStr) + &quot;\n    Exception:&quot; + e + &quot;\n&quot;);
+	}
+
+	return result;
+};
+
 Spry.Data.Region.prototype.processTokenChildren = function(outputArr, token, processContext)
 {
 	var children = token.children;
@@ -3861,16 +3998,9 @@ Spry.Data.Region.prototype.processTokens = function(outputArr, token, processCon
 					{
 						dsContext.setRowIndex(i);
 						var testVal = true;
+
 						if (token.data.jsExpr)
-						{
-							var jsExpr = Spry.Data.Region.processDataRefString(processContext, token.data.jsExpr, null, true);
-							try { testVal = Spry.Utils.eval(jsExpr); }
-							catch(e)
-							{
-								Spry.Debug.trace(&quot;Caught exception in Spry.Data.Region.prototype.processTokens while evaluating: &quot; + jsExpr + &quot;\n    Exception:&quot; + e + &quot;\n&quot;);
-								testVal = true;
-							}
-						}
+							testVal = this.evaluateExpression(token.data.jsExpr, processContext);
 
 						if (testVal)
 							this.processTokenChildren(outputArr, token, processContext);
@@ -3883,16 +4013,7 @@ Spry.Data.Region.prototype.processTokens = function(outputArr, token, processCon
 				var testVal = true;
 
 				if (token.data.jsExpr)
-				{
-					var jsExpr = Spry.Data.Region.processDataRefString(processContext, token.data.jsExpr, null, true);
-
-					try { testVal = Spry.Utils.eval(jsExpr); }
-					catch(e)
-					{
-						Spry.Debug.trace(&quot;Caught exception in Spry.Data.Region.prototype.processTokens while evaluating: &quot; + jsExpr + &quot;\n    Exception:&quot; + e + &quot;\n&quot;);
-						testVal = true;
-					}
-				}
+					testVal = this.evaluateExpression(token.data.jsExpr, processContext);
 
 				if (testVal)
 					this.processTokenChildren(outputArr, token, processContext);
@@ -3918,13 +4039,7 @@ Spry.Data.Region.prototype.processTokens = function(outputArr, token, processCon
 						{
 							if (child.data.jsExpr)
 							{
-								var jsExpr = Spry.Data.Region.processDataRefString(processContext, child.data.jsExpr, null, true);
-								try { testVal = Spry.Utils.eval(jsExpr); }
-								catch(e)
-								{
-									Spry.Debug.trace(&quot;Caught exception in Spry.Data.Region.prototype.processTokens while evaluating: &quot; + jsExpr + &quot;\n    Exception:&quot; + e + &quot;\n&quot;);
-									testVal = false;
-								}
+								testVal = this.evaluateExpression(child.data.jsExpr, processContext);
 
 								if (testVal)
 								{
@@ -3962,52 +4077,36 @@ Spry.Data.Region.prototype.processTokens = function(outputArr, token, processCon
 		case Spry.Data.Region.Token.VALUE_TOKEN:
 
 			var dataSet = token.dataSet;
-			if (!dataSet &amp;&amp; this.dataSets &amp;&amp; this.dataSets.length &gt; 0 &amp;&amp; this.dataSets[0])
-			{
-				// No dataSet was specified by the token, so use whatever the first
-				// data set specified in the region.
+			var val = undefined;
 
-				dataSet = this.dataSets[0];
-			}
-			if (!dataSet)
+			if (dataSet &amp;&amp; dataSet == &quot;function&quot;)
 			{
-				Spry.Debug.reportError(&quot;processTokens(): Value reference has no data set specified: &quot; + token.regionStr + &quot;\n&quot;);
-				return &quot;&quot;;
-			}
+				// This value token doesn't contain a data set data reference, it
+				// contains a function call, so call it.
 
-			var dsContext = processContext.getDataSetContext(dataSet);
-			if (!dsContext)
-			{
-				Spry.Debug.reportError(&quot;processTokens: Failed to get a data set context!\n&quot;);
-				return &quot;&quot;;
+				val = this.callScriptFunction(token.data, processContext);
 			}
-
-			var ds = dsContext.getDataSet();
-
-			if (token.data == &quot;ds_RowNumber&quot;)
-				outputArr.push(dsContext.getRowIndex());
-			else if (token.data == &quot;ds_RowNumberPlus1&quot;)
-				outputArr.push(dsContext.getRowIndex() + 1);
-			else if (token.data == &quot;ds_RowCount&quot;)
-				outputArr.push(dsContext.getNumRows());
-			else if (token.data == &quot;ds_UnfilteredRowCount&quot;)
-				outputArr.push(dsContext.getNumRows(true));
-			else if (token.data == &quot;ds_CurrentRowNumber&quot;)
-				outputArr.push(ds.getRowNumber(ds.getCurrentRow()));
-			else if (token.data == &quot;ds_CurrentRowID&quot;)
-				outputArr.push(ds.getCurrentRowID());
-			else if (token.data == &quot;ds_EvenOddRow&quot;)
-				outputArr.push((dsContext.getRowIndex() % 2) ? Spry.Data.Region.evenRowClassName : Spry.Data.Region.oddRowClassName);
-			else if (token.data == &quot;ds_SortOrder&quot;)
-				outputArr.push(ds.getSortOrder());
-			else if (token.data == &quot;ds_SortColumn&quot;)
-				outputArr.push(ds.getSortColumn());
 			else
 			{
-				var curDataSetRow = dsContext.getCurrentRow();
-				if (curDataSetRow)
-					outputArr.push(curDataSetRow[token.data]);
+				if (!dataSet &amp;&amp; this.dataSets &amp;&amp; this.dataSets.length &gt; 0 &amp;&amp; this.dataSets[0])
+				{
+					// No dataSet was specified by the token, so use whatever the first
+					// data set specified in the region.
+	
+					dataSet = this.dataSets[0];
+				}
+				if (!dataSet)
+				{
+					Spry.Debug.reportError(&quot;processTokens(): Value reference has no data set specified: &quot; + token.regionStr + &quot;\n&quot;);
+					return &quot;&quot;;
+				}
+	
+				val = processContext.getValueFromDataSet(dataSet, token.data);
 			}
+
+			if (typeof val != &quot;undefined&quot;)
+				outputArr.push(val + &quot;&quot;);
+
 			break;
 		default:
 			Spry.Debug.reportError(&quot;processTokens(): Invalid token type: &quot; + token.regionStr + &quot;\n&quot;);
@@ -4206,69 +4305,22 @@ Spry.Data.Region.processDataRefString = function(processingContext, regionStr, d
 		var fieldName = reArray[0].replace(/^\{|.*::|\}/g, &quot;&quot;);
 		var row = null;
 
-		if (processingContext)
-		{
-			var dsContext = processingContext.getDataSetContext(dsName);
+		var val = &quot;&quot;;
 
-			if (fieldName == &quot;ds_RowNumber&quot;)
-			{
-				resultStr += dsContext.getRowIndex();
-				row = null;
-			}
-			else if (fieldName == &quot;ds_RowNumberPlus1&quot;)
-			{
-				resultStr += (dsContext.getRowIndex() + 1);
-				row = null;
-			}
-			else if (fieldName == &quot;ds_RowCount&quot;)
-			{
-				resultStr += dsContext.getNumRows();
-				row = null;
-			}
-			else if (fieldName == &quot;ds_UnfilteredRowCount&quot;)
-			{
-				resultStr += dsContext.getNumRows(true);
-				row = null;
-			}
-			else if (fieldName == &quot;ds_CurrentRowNumber&quot;)
-			{
-				var ds = dsContext.getDataSet();
-				resultStr += ds.getRowNumber(ds.getCurrentRow());
-				row = null;
-			}
-			else if (fieldName == &quot;ds_CurrentRowID&quot;)
-			{
-				var ds = dsContext.getDataSet();
-				resultStr += &quot;&quot; + ds.getCurrentRowID();
-				row = null;
-			}
-			else if (fieldName == &quot;ds_EvenOddRow&quot;)
-			{
-				resultStr += (dsContext.getRowIndex() % 2) ? Spry.Data.Region.evenRowClassName : Spry.Data.Region.oddRowClassName;
-				row = null;
-			}
-			else if (fieldName == &quot;ds_SortOrder&quot;)
-			{
-				resultStr += dsContext.getDataSet().getSortOrder();
-				row = null;
-			}
-			else if (fieldName == &quot;ds_SortColumn&quot;)
-			{
-				resultStr += dsContext.getDataSet().getSortColumn();
-				row = null;
-			}
-			else
-				row = processingContext.getCurrentRowForDataSet(dsName);
-		}
+		if (processingContext)
+			val = processingContext.getValueFromDataSet(dsName, fieldName);
 		else
 		{
 			var ds = dsName ? dataSetsToUse[dsName] : dataSetsToUse[0];
 			if (ds)
-				row = ds.getCurrentRow();
+				val = ds.getValue(fieldName);
 		}
 
-		if (row)
-			resultStr += isJSExpr ? Spry.Utils.escapeQuotesAndLineBreaks(&quot;&quot; + row[fieldName]) : row[fieldName];
+		if (typeof val != &quot;undefined&quot;)
+		{
+			val += &quot;&quot;; // Make sure val is converted to a string.
+			resultStr += isJSExpr ? Spry.Utils.escapeQuotesAndLineBreaks(val) : val;
+		}
 
 		if (startSearchIndex == re.lastIndex)
 		{
@@ -4309,7 +4361,7 @@ Spry.Data.Region.strToDataSetsArray = function(str, returnRegionNames)
 		if (arr[i] &amp;&amp; !Spry.Data.Region.PI.instructions[arr[i]])
 		{
 			try {
-				var dataSet = eval(arr[i]);
+				var dataSet = Spry.Data.getDataSetByName(arr[i]);
 
 				if (!foundHash[arr[i]])
 				{
@@ -4337,7 +4389,7 @@ Spry.Data.Region.DSContext = function(dataSet, processingContext)
 
 	// Private Methods:
 
-	function getInternalRowIndex() { return m_curRowIndexArray[m_curRowIndexArray.length - 1].rowIndex; }
+	var getInternalRowIndex = function() { return m_curRowIndexArray[m_curRowIndexArray.length - 1].rowIndex; };
 
 	// Public Methods:
 	this.resetAll = function() { m_curRowIndexArray = [ { rowIndex: m_dataSet.getCurrentRow() } ] };
@@ -4355,7 +4407,16 @@ Spry.Data.Region.DSContext = function(dataSet, processingContext)
 	this.setData = function(data)
 	{
 		this.getCurrentState().data = data;
-	}
+	};
+	this.getValue = function(valueName, rowContext)
+	{
+		var result = &quot;&quot;;
+		var curState = this.getCurrentState();
+		var ds = curState.nestedDS ? curState.nestedDS : this.getDataSet();
+		if (ds)
+			result = ds.getValue(valueName, rowContext);
+		return result;
+	};
 	this.getCurrentRow = function()
 	{
 		if (m_curRowIndexArray.length &lt; 2 || getInternalRowIndex() &lt; 0)
@@ -4398,9 +4459,17 @@ Spry.Data.Region.DSContext = function(dataSet, processingContext)
 			if (nestedDS)
 			{
 				var currentState = this.getCurrentState();
+				currentState.nestedDS = nestedDS;
 				currentState.data = nestedDS.getData();
 				currentState.rowIndex = nestedDS.getCurrentRowNumber();
 
+				// getCurrentRowNumber() will return a -1 if the nestedDS has
+				// no data in it. If the rowIndex is -1, we need to reset it back to
+				// zero so the dsContext doesn't attempt to use the *real* current
+				// row of the data set.
+
+				currentState.rowIndex = currentState.rowIndex &lt; 0 ? 0 : currentState.rowIndex;
+
 				var numChildren = m_children.length;
 				for (var i = 0; i &lt; numChildren; i++)
 					m_children[i].syncDataWithParentRow(this, currentState.rowIndex, currentState.data);
@@ -4413,6 +4482,7 @@ Spry.Data.Region.DSContext = function(dataSet, processingContext)
 		var newState = new Object;
 		newState.rowIndex = curState.rowIndex;
 		newState.data = curState.data;
+		newState.nestedDS = curState.nestedDS;
 
 		m_curRowIndexArray.push(newState);
 
@@ -4532,7 +4602,7 @@ Spry.Data.Region.ProcessingContext.prototype.getDataSetContext = function(dataSe
 
 	if (typeof dataSet == 'string')
 	{
-		try { dataSet = eval(dataSet); } catch (e) { dataSet = null; }
+		dataSet = Spry.Data.getDataSetByName(dataSet);
 		if (!dataSet)
 			return null;
 	}
@@ -4547,6 +4617,52 @@ Spry.Data.Region.ProcessingContext.prototype.getDataSetContext = function(dataSe
 	return null;
 };
 
+Spry.Data.Region.ProcessingContext.prototype.getValueFromDataSet = function()
+{
+	var dsName = &quot;&quot;;
+	var columnName = &quot;&quot;;
+
+	if (arguments.length &gt; 1)
+	{
+		// The caller is passing in the data set name and the
+		// name of the data reference separately.
+
+		dsName = arguments[0];
+		columnName = arguments[1];
+	}
+	else
+	{
+		// The caller is passing a single string which can be in one
+		// of the following forms:
+		//
+		//    &quot;columnName&quot;
+		//    &quot;dsName::columnName&quot;
+		//    &quot;{columnName}&quot;
+		//    &quot;{dsName::columnName}&quot;
+
+		var dataRef = arguments[0].replace(/\s*{\s*|\s*}\s*/g, &quot;&quot;);
+		if (dataRef.search(&quot;::&quot;) != -1)
+		{
+			dsName = dataRef.replace(/::.*/, &quot;&quot;);
+			columnName = dataRef.replace(/.*::/, &quot;&quot;);
+		}
+		else
+			columnName = dataRef;
+	}
+
+	var result = &quot;&quot;;
+	var dsContext = this.getDataSetContext(dsName);
+	if (dsContext)
+		result = dsContext.getValue(columnName, dsContext.getCurrentRow());
+	else
+		Spry.Debug.reportError(&quot;getValueFromDataSet: Failed to get &quot; + dsName + &quot; context for the &quot; + this.region.regionNode.id + &quot; region.\n&quot;);
+
+	return result;
+};
+
+// Define a short-hand name for developers.
+Spry.Data.Region.ProcessingContext.prototype.$v = Spry.Data.Region.ProcessingContext.prototype.getValueFromDataSet;
+
 Spry.Data.Region.ProcessingContext.prototype.getCurrentRowForDataSet = function(dataSet)
 {
 	var dsc = this.getDataSetContext(dataSet);
@@ -4592,4 +4708,4 @@ Spry.Data.Region.Token.PIData = function(piName, data, jsExpr, regionState)
 	this.regionState = regionState;
 };
 
-Spry.Utils.addLoadListener(function() { setTimeout(function() { Spry.Data.initRegions(); }, 0); });
+Spry.Utils.addLoadListener(function() { setTimeout(function() { if (Spry.Data.initRegionsOnLoad) Spry.Data.initRegions(); }, 0); });</diff>
      <filename>resources/script/spry/includes/SpryData.js</filename>
    </modified>
    <modified>
      <diff>@@ -1,4 +1,4 @@
-// SpryDataExtensions.js - version 0.2 - Spry Pre-Release 1.5
+// SpryDataExtensions.js - version 0.4 - Spry Pre-Release 1.6.1
 //
 // Copyright (c) 2007. Adobe Systems Incorporated.
 // All rights reserved.
@@ -33,9 +33,9 @@
 //
 //////////////////////////////////////////////////////////////////////
 
-Spry.Data.DataSet.multiFilterFunc = function(ds, row, rowNumber)
+Spry.Data.DataSet.multiFilterFuncs = {};
+Spry.Data.DataSet.multiFilterFuncs.and = function(ds, row, rowNumber, filters)
 {
-	var filters = ds.activeFilters;
 	if (filters)
 	{
 		var numFilters = filters.length;
@@ -49,6 +49,33 @@ Spry.Data.DataSet.multiFilterFunc = function(ds, row, rowNumber)
 	return row;
 };
 
+Spry.Data.DataSet.multiFilterFuncs.or = function(ds, row, rowNumber, filters)
+{
+	if (filters &amp;&amp; filters.length &gt; 0)
+	{
+		var numFilters = filters.length;
+		for (var i = 0; i &lt; numFilters; i++)
+		{
+			var savedRow = row;
+			row = filters[i](ds, row, rowNumber);
+			if (row)
+				return row;
+			row = savedRow;
+		}
+		return null;
+	}
+	return row;
+};
+
+Spry.Data.DataSet.prototype.getMultiFilterFunc = function()
+{
+	var func = Spry.Data.DataSet.multiFilterFuncs[this.getFilterMode()];
+	if (!func)
+		func = Spry.Data.DataSet.multiFilterFuncs[&quot;and&quot;];
+	var filters = this.activeFilters;
+	return function(ds, row, rowNumber) { return func(ds, row, rowNumber, filters); };
+};
+
 Spry.Data.DataSet.prototype.addFilter = function(filterFunc, doApplyFilters)
 {
 	if (!this.hasFilter(filterFunc))
@@ -96,12 +123,12 @@ Spry.Data.DataSet.prototype.getFilters = function(filterFunc)
 	if (!this.activeFilters)
 		this.activeFilters = [];
 	return this.activeFilters;
-}
+};
 
 Spry.Data.DataSet.prototype.applyFilters = function()
 {
 	if (this.activeFilters &amp;&amp; this.activeFilters.length &gt; 0)
-		this.filter(Spry.Data.DataSet.multiFilterFunc);
+		this.filter(this.getMultiFilterFunc());
 	else
 		this.filter(null);
 };
@@ -120,3 +147,17 @@ Spry.Data.DataSet.prototype.hasFilter = function(filterFunc)
 	}
 	return false;
 };
+
+Spry.Data.DataSet.prototype.getFilterMode = function()
+{
+	return this.filterMode ? this.filterMode : &quot;and&quot;;
+};
+
+Spry.Data.DataSet.prototype.setFilterMode = function(mode, doApplyFilters)
+{
+	var oldMode = this.getFilterMode();
+	this.filterMode = mode;
+	if (doApplyFilters)
+		this.applyFilters();
+	return oldMode;
+};</diff>
      <filename>resources/script/spry/includes/SpryDataExtensions.js</filename>
    </modified>
    <modified>
      <diff>@@ -1,4 +1,4 @@
-// SpryDebug.js - version: 0.7 - Spry Pre-Release 1.5
+// SpryDebug.js - version 0.9 - Spry Pre-Release 1.6.1
 //
 // Copyright (c) 2007. Adobe Systems Incorporated.
 // All rights reserved.
@@ -29,28 +29,28 @@
 
 var Spry; if (!Spry) Spry = {};
 
-Spry.BrowserSniff = function() {
+Spry.BrowserSniff = function()
+{
 	var b = navigator.appName.toString();
 	var up = navigator.platform.toString();
 	var ua = navigator.userAgent.toString();
 
-	this.mozilla = this.ie = this.opera = r = false;
+	this.mozilla = this.ie = this.opera = this.safari = false;
 	var re_opera = /Opera.([0-9\.]*)/i;
 	var re_msie = /MSIE.([0-9\.]*)/i;
 	var re_gecko = /gecko/i;
-	var re_safari = /safari\/([\d\.]*)/i;
-
-	if (ua.match(re_opera)) {
-		r = ua.match(re_opera);
+	var re_safari = /(applewebkit|safari)\/([\d\.]*)/i;
+	var r = false;
+	
+	if ( (r = ua.match(re_opera))) {
 		this.opera = true;
 		this.version = parseFloat(r[1]);
-	} else if (ua.match(re_msie)) {
-		r = ua.match(re_msie);
+	} else if ( (r = ua.match(re_msie))) {
 		this.ie = true;
 		this.version = parseFloat(r[1]);
-	} else if (ua.match(re_safari)) {
+	} else if ( (r = ua.match(re_safari))) {
 		this.safari = true;
-		this.version = 1.4;
+		this.version = parseFloat(r[2]);
 	} else if (ua.match(re_gecko)) {
 		var re_gecko_version = /rv:\s*([0-9\.]+)/i;
 		r = ua.match(re_gecko_version);
@@ -88,7 +88,7 @@ Spry.Debugger = function(){
 			rmvListener(self.headdiv, 'mouseup', this.mudwfunc, false);
 			rmvListener(document.body, 'mousemove', self.mmdwfunc, false);
 			for (var k in self){
-				var t = typeof self[k]
+				var t = typeof self[k];
 				if (t != &quot;function&quot;){
 					if (t == &quot;object&quot; &amp;&amp; self[k].innerHTML)
 						self[k].innerHTML = '';
@@ -96,12 +96,12 @@ Spry.Debugger = function(){
 					self[k] = null;
 				}
 			}
-	}
+	};
 
 	this.myerrorhandler = function( errType, errURL, errLineNum )
 	{
 		try{
-			self.out('&lt;div style=&quot;color:red&quot;&gt;' + self.explode(errType + &quot; \n &quot; + errURL + ' on line: ' + errLineNum, 0) + '&lt;/div&gt;');
+			self.out('&lt;div class=&quot;error&quot;&gt;' + self.explode(errType + &quot; \n &quot; + errURL + ' on line: ' + errLineNum, 0) + '&lt;br style=&quot;clear: both;&quot; /&gt;&lt;/div&gt;');
 		}catch(err){alert(err.message);}
 		return false;
 	};
@@ -122,8 +122,8 @@ Spry.Debugger = function(){
 		};
 	}
 
-	this.unloadfunc = function(e){self.onunloaddocument()}
-	this.loadfunc = function(e){self.init()}
+	this.unloadfunc = function(e){self.onunloaddocument()};
+	this.loadfunc = function(e){self.init()};
 	Spry.Debugger.Utils.addEvListener(window, 'beforeunload', this.unloadfunc, false);
 	Spry.Debugger.Utils.addEvListener(window, 'load', this.loadfunc, false);
 	window.onerror = this.myerrorhandler;
@@ -140,6 +140,7 @@ Spry.Debugger.prototype.init = function(){
 		var textdiv = document.createElement('div');
 		var consolediv = document.createElement('div');
 		var cleardiv = document.createElement('div');
+		var iframe = document.createElement('iframe');
 
 		var scripts = document.getElementsByTagName('script');
 		var link = '';
@@ -173,6 +174,11 @@ Spry.Debugger.prototype.init = function(){
 
 		debugwindow.id = 'debugdiv';
 
+		iframe.src = 'javascript:&quot;&quot;';
+		iframe.frameBorder = '0';
+		iframe.scrolling = 'no';
+		iframe.id = 'debugIframe';
+
 		//scroll in the visible area on refresh
 		setTimeout(function(){
 			try{
@@ -199,17 +205,18 @@ Spry.Debugger.prototype.init = function(){
 		headdiv.appendChild(maximdiv);
 		headdiv.appendChild(cleardiv);
 		headdiv.appendChild(invdiv);
+		debugwindow.parentNode.appendChild(iframe);
 		this.jstext = document.getElementById('debugtext');
 		this.jstext.setAttribute('AutoComplete', 'off');
 		var self = this;
 
-		this.dkfunc = function(e){return self.debuggerKey(e)}
-		this.cdwfunc = function(e){return self.closeDebugWindow(e)}
-		this.cldwfunc = function(e){return self.clearDebugWindow(e)}
-		this.mdwfunc = function(e){return self.maximDebugWindow(e)}
-		this.mddwfunc = function(e){return self.mousedownDebugWindow(e)}
-		this.mmdwfunc = function(e){return self.mousemoveDebugWindow(e)}
-		this.mudwfunc = function(e){return self.mouseupDebugWindow(e)}
+		this.dkfunc = function(e){return self.debuggerKey(e)};
+		this.cdwfunc = function(e){return self.closeDebugWindow(e)};
+		this.cldwfunc = function(e){return self.clearDebugWindow(e)};
+		this.mdwfunc = function(e){return self.maximDebugWindow(e)};
+		this.mddwfunc = function(e){return self.mousedownDebugWindow(e)};
+		this.mmdwfunc = function(e){return self.mousemoveDebugWindow(e)};
+		this.mudwfunc = function(e){return self.mouseupDebugWindow(e)};
 		this.imdfunc = function(e){return self.introspectPage(e)};
 		var addEv = Spry.Debugger.Utils.addEvListener;
 		addEv(self.jstext, 'keydown', this.dkfunc, false);
@@ -231,6 +238,11 @@ Spry.Debugger.prototype.init = function(){
 	}
 	// clear stack
 	this.out();
+	setTimeout(function(){if (Spry.is.ie &amp;&amp; Spry.is.version &lt; 7)
+	{
+			iframe.style.height = debugwindow.offsetHeight + 'px';
+			iframe.style.width = debugwindow.offsetWidth + 'px';
+	}},0);
 };
 Spry.Debugger.prototype.introspectPage = function(e){
 	if (this.introspRun &amp;&amp; this.introspRun == true){
@@ -465,7 +477,7 @@ Spry.Debugger.prototype.dumpObjectEl = function(e, k, depth){
 	var ctrl='';
 	try{
 		var tipe = typeof e[k];
-		if (tipe == 'unknown') return '&lt;div class=&quot;varlabel&quot;&gt;'+k+'&lt;/div&gt;&lt;div class=&quot;varvalue&quot;&gt; value unknown&lt;/div&gt;';
+		if (tipe == 'unknown') return '&lt;div class=&quot;varlabel&quot;&gt;'+k+'&lt;/div&gt;&lt;div class=&quot;varvalue&quot;&gt;value unknown&lt;/div&gt;';
 		if (tipe != 'function'){
 			if (typeof k == 'number' || k.toUpperCase() != k){
 				ctrl += '&lt;div class=&quot;varlabel';
@@ -521,7 +533,7 @@ Spry.Debugger.prototype.explode = function (e, depth){
 		ctrl += '&lt;div class=&quot;dumptable' + ((depth &gt; 0)?' hidedump' : '') + '&quot;&gt;';
 		switch (typeof e){
 			case 'object':
-				if ( typeof e.length == 'undefined' || (e.length &gt; 0 &amp;&amp; typeof e[0] == 'undefined')){
+				if ( typeof e.length == 'undefined' || (e.length &gt; 0 &amp;&amp; typeof e.push == 'undefined')){
 					for (var k in e){
 						if (k != 'domConfig' &amp;&amp; typeof e[k] != 'function'){
 							ctrl += this.dumpObjectEl(e, k, depth);
@@ -532,7 +544,7 @@ Spry.Debugger.prototype.explode = function (e, depth){
 									css = document.defaultView.getComputedStyle(e, null);
 								else if (e.currentStyle) 
 									css = e.currentStyle;
-								ctrl += '&lt;div style=&quot;color:#FF3333&quot; class=&quot;varlabel&quot;&gt;[Computed Style]:&lt;/div&gt;&lt;div class=&quot;varvalue&quot;&gt;[&lt;a href=&quot;#&quot; onclick=&quot;Spry.Debugger.Utils.makeVisible(this); return false;&quot;&gt;STYLE&lt;/a&gt;]';
+								ctrl += '&lt;div class=&quot;varlabel computed&quot;&gt;[Computed Style]:&lt;/div&gt;&lt;div class=&quot;varvalue&quot;&gt;[&lt;a href=&quot;#&quot; onclick=&quot;Spry.Debugger.Utils.makeVisible(this); return false;&quot;&gt;STYLE&lt;/a&gt;]';
 								ctrl += this.explode(css, 1);
 								ctrl += '&lt;/div&gt;';
 							}
@@ -550,7 +562,7 @@ Spry.Debugger.prototype.explode = function (e, depth){
 					if (e.length == 0){
 						ctrl += '&lt;div class=&quot;specialvaluedump&quot; colspan=&quot;2&quot;&gt;Empty Array&lt;/div&gt;';
 					}else{
-						ctrl += '&lt;div class=&quot;varlabel&quot;&gt;Length&lt;/div&gt;&lt;div&gt;'+e.length+'&lt;/div&gt;';
+						ctrl += '&lt;div class=&quot;varlabel&quot;&gt;Length&lt;/div&gt;&lt;div class=&quot;varvalue&quot;&gt;'+e.length+'&lt;/div&gt;';
 						for (var k = 0; k &lt; e.length; k++)
 							ctrl += this.dumpObjectEl(e, k, depth);
 					}
@@ -581,7 +593,7 @@ Spry.Debugger.prototype.explode = function (e, depth){
 			case 'boolean':
 				ctrl +=  '&lt;div class=&quot;varlabel&quot;&gt;Boolean:&lt;/div&gt;&lt;div class=&quot;varvalue&quot;&gt;' + e +'&lt;/div&gt;';
 		}
-		ctrl += '&lt;/div&gt;';
+		ctrl += '&lt;br class=&quot;clear&quot; /&gt;&lt;/div&gt;';
 		return ctrl;
 
 	}catch(e){this.out('Spry.Debugger.explode error: ' + e.message);}
@@ -593,38 +605,42 @@ Spry.Debugger.prototype.log = function (){
 	var self = this;
 	setTimeout(function(){
 		var ctrl = '';
-		var scrollSave = self.textdiv.scrollHeight;
 		if (t.length &gt; 0)
 				for (var j =0; j &lt; t.length; j++)
 					ctrl += self.explode(t[j], 0);
 
 		self.out(ctrl);
-		self.textdiv.scrollTop = scrollSave;
 	}, 10);
 };
 
-Spry.Debugger.prototype.out = function(str){
-		if (typeof buffer == 'undefined')
-			buffer = '';
+Spry.Debugger.prototype.out = function(str, notype){
+	if (typeof buffer == 'undefined')
+		buffer = '';
 
-		var t = this.textdiv;
-
-		if (t){
-			if (!t.innerHTML)
-				t.innerHTML = '';
+	var t = this.textdiv;
+	var self = this;
 
+	if (t){
+		if (!t.innerHTML)
+			t.innerHTML = '';
+		
+		setTimeout(function(){
 			if (buffer.length &gt; 0){
 				t.innerHTML += buffer;
 				buffer = '';
 			}
-			if (typeof str == 'string')
-				t.innerHTML += str + '&lt;hr style=&quot;clear:both&quot;&gt;';
-		}else{
-			var self = this;
-			setTimeout(function(){self.out();}, 400);
-			if (typeof str == 'string')
-				buffer += str+'&lt;hr style=&quot;clear:both&quot;&gt;';
-		}
+			if (typeof str == 'string'){
+				var scrollSave = self.textdiv.scrollHeight;
+				t.innerHTML += str + '&lt;br class=&quot;clear&quot; /&gt;';
+				self.textdiv.scrollTop = scrollSave;
+			}
+		}, 0);
+
+	}else{
+		setTimeout(function(){self.out();}, 400);
+		if (typeof str == 'string')
+			buffer += str+'&lt;br class=&quot;clear&quot; /&gt;';
+	}
 };
 
 Spry.Debugger.prototype.closeDebugWindow = function(e){
@@ -645,26 +661,32 @@ Spry.Debugger.prototype.clearDebugWindow = function(e){
 };
 Spry.Debugger.prototype.maximDebugWindow = function(){
 	var main = this.debugdiv;
-	if (this.textdiv){
+	if (this.textdiv)
 		this.textdiv.style.display = 'block';
-	}
-	if (main.style.width != '99%'){
-		this.width = main.style.width;
-		this.height = main.style.height;
+	
+	if (!main.className)
+		main.className = '';		
+
+	if (main.className.indexOf('maximized') == -1){
 		this.left = main.style.left;
-		this.top = main.style.top;
-		this.hheight = this.textdiv.style.height;
-		main.style.width = '99%';
-		main.style.height = '99%';
+		main.className += 'maximized';
 		main.style.left = '0';
-		main.style.top = '0';
-		main.style.height = '94%'
+		if (document.documentElement){
+			main.style.height = document.documentElement.clientHeight;
+			main.style.width = document.documentElement.clientWidth;
+		}
 	}else{
-		main.style.width = this.width;
-		main.style.height = this.height;
+		main.className = main.className.replace(/maximized/i, '');
 		main.style.left = this.left;
-		main.style.top = this.top;
-		this.textdiv.style.height = this.hheight;
+		main.style.height = '';
+		main.style.width = '';
+	}
+	var layer = document.getElementById('debugIframe');
+	if (layer){
+		layer.style.top = main.style.top;
+		layer.style.left = main.style.left;
+		layer.style.height = main.offsetHeight;
+		layer.style.width = main.offsetWidth;
 	}
 	return true;
 };
@@ -695,12 +717,19 @@ Spry.Debugger.prototype.mouseupDebugWindow = function (e){
 Spry.Debugger.prototype.mousemoveDebugWindow = function (e){
 	e = e||event;
 	if (this.startDrag){
-		this.debugdiv.style.opacity = 0.6;
-		this.debugdiv.style.filter = 'alpha(opacity=60)';
+		if (Spry.is.ie)
+			this.debugdiv.style.filter = 'alpha(opacity=60)';
+		else
+			this.debugdiv.style.opacity = 0.6;
 		var x = e.screenX - initialX;
 		var y = e.screenY - initialY;
 		this.debugdiv.style.left = (topX + x) + 'px';
 		this.debugdiv.style.top = (topY + y) + 'px';
+		var layer = document.getElementById('debugIframe');
+		if (layer){
+			layer.style.top = this.debugdiv.style.top;
+			layer.style.left = this.debugdiv.style.left;
+		}
 	}
 	return false;
 };
@@ -712,13 +741,41 @@ Spry.Debugger.prototype.mousemoveDebugWindow = function (e){
 /////////////////////////////////////////////////////////////////
 
 if (!Spry.Debugger.Utils) Spry.Debugger.Utils = {};
+Spry.Debugger.Utils.camelize = function(stringToCamelize)
+{
+	if (stringToCamelize.indexOf('-') == -1){
+		return stringToCamelize;	
+	}
+	var oStringList = stringToCamelize.split('-');
+	var isFirstEntry = true;
+	var camelizedString = '';
+
+	for(var i=0; i &lt; oStringList.length; i++)
+	{
+		if(oStringList[i].length&gt;0)
+		{
+			if(isFirstEntry)
+			{
+				camelizedString = oStringList[i];
+				isFirstEntry = false;
+			}
+			else
+			{
+				var s = oStringList[i];
+				camelizedString += s.charAt(0).toUpperCase() + s.substring(1);
+			}
+		}
+	}
+
+	return camelizedString;
+};
 Spry.Debugger.Utils.getStyleProp = function(element, prop)
 {
 	var value;
 	try
 	{
 		if (element.style)
-			value = element.style[Spry.Effect.Utils.camelize(prop)];
+			value = element.style[Spry.Debugger.Utils.camelize(prop)];
 
 		if (!value)
 		{
@@ -729,7 +786,7 @@ Spry.Debugger.Utils.getStyleProp = function(element, prop)
 			}
 			else if (element.currentStyle) 
 			{
-					value = element.currentStyle[Spry.Effect.Utils.camelize(prop)];
+					value = element.currentStyle[Spry.Debugger.Utils.camelize(prop)];
 			}
 		}
 	}
@@ -798,10 +855,8 @@ Spry.Debugger.Utils.getBorderBox = function (el, doc) {
 		ret.x -= blw;
 		ret.y -= btw;
 		// opera &amp; (safari absolute) incorrectly account for body offsetTop
-		var ua = navigator.userAgent.toLowerCase();
-		if (Spry.is.opera || Spry.is.safari &amp;&amp; Spry.Debugger.Utils.getIntProp(el, 'position') == 'absolute')
+		if (Spry.is.opera || Spry.is.safari &amp;&amp; Spry.Debugger.Utils.getStyleProp(el, 'position') == 'absolute')
 			ret.y -= doc.body.offsetTop;
-
 	}
 	if (el.parentNode)
 			parent = el.parentNode;
@@ -864,18 +919,29 @@ Spry.Debugger.Utils.stopEvent = function(e){
 };
 Spry.Debugger.Utils.logXHRequest = function(req, m, url){
 	if (req.readyState == 4){
-		var o = '&lt;div class=&quot;urldump&quot;&gt;' + m + ' ' + url + ' &lt;a href=&quot;#&quot; onclick=&quot;Spry.Debugger.Utils.makeVisible(this);return false;&quot;&gt;Content&lt;/a&gt;';
-		o += '&lt;div class=&quot;contenturldump hidedump&quot;&gt;&lt;pre&gt;' + req.responseText.replace(/&lt;/g, '&amp;lt;') + '&lt;/pre&gt;&lt;/div&gt;';
-		o +='| &lt;span&gt;&lt;a href=&quot;#&quot; onclick=&quot;Spry.Debugger.Utils.makeVisible(this); return false;&quot;&gt; Headers&lt;/a&gt;';
+		var uniqIdHeader = (new Date()).getTime() + '' + Math.random();
+		var uniqIdContent = (new Date()).getTime() + '' + Math.random();
+		var o = '&lt;div class=&quot;urldump&quot;&gt;' + m + ' ' + url + ' &lt;a href=&quot;#&quot; onclick=&quot;Spry.Debugger.Utils.makeVisible(\''+uniqIdHeader+'\', false); Spry.Debugger.Utils.makeVisible(\''+uniqIdContent+'\'); return false;&quot;&gt;Content&lt;/a&gt; | &lt;a href=&quot;#&quot; onclick=&quot;Spry.Debugger.Utils.makeVisible(\''+uniqIdContent+'\', false); Spry.Debugger.Utils.makeVisible(\''+uniqIdHeader+'\'); return false;&quot;&gt; Headers&lt;/a&gt;';
+		o += '&lt;div&gt;&lt;div class=&quot;contenturldump hidedump&quot; id=&quot;'+uniqIdContent+'&quot;&gt;&lt;pre&gt;' + req.responseText.replace(/&lt;/g, '&amp;lt;') + '&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;';
 		var k = req.getAllResponseHeaders();
-		o += '&lt;div class=&quot;contenturldump hidedump&quot;&gt;&lt;pre&gt;' + k + '&lt;/pre&gt;';
-		o += '&lt;/div&gt;&lt;/span&gt;&lt;/div&gt;';
+		o += '&lt;div&gt;&lt;div class=&quot;contenturldump hidedump&quot; id=&quot;' + uniqIdHeader + '&quot;&gt;&lt;pre&gt;' + k + '&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;';
 		debug.out(o);
 	}
 };
 
-Spry.Debugger.Utils.makeVisible = function(el){
-  if (el.parentNode){
+Spry.Debugger.Utils.makeVisible = function(el, visible){
+	el = Spry.Debugger.Utils.getElement(el);
+	if (typeof visible != 'undefined')
+	{
+		if (visible == 'block'){
+			el.style.display = 'block';
+		}else{
+			el.style.display = 'none';	
+		}
+		return;
+	}
+  if (el.parentNode)
+	{
 		var l = el.parentNode.getElementsByTagName('div');
 		var b = false;
 		for (var i = 0; i &lt; l.length; i++){
@@ -886,11 +952,21 @@ Spry.Debugger.Utils.makeVisible = function(el){
 		}
 		if (b){
 			var tmp = b.style.display || Spry.Debugger.Utils.getStyleProp(b, 'display');
-			b.style.display = (tmp &amp;&amp; tmp == 'block') ? 'none' : 'block';
+			if (tmp &amp;&amp; tmp == 'block'){
+				b.style.display =  'none';
+				//b.innerHTML = b.innerHTML.replace(/&amp;/g, '&amp;amp;').replace(/&lt;/g, '&amp;lt;').replace(/&gt;/g, '&amp;gt;');
+			}else{
+				//b.innerHTML = b.innerHTML.replace(/&amp;gt;/g, '&gt;').replace(/&amp;lt;/g, '&lt;').replace(/&amp;amp;/g, '&amp;');
+				b.style.display =  'block';
+			}
 		}
 	}
 };
-
+Spry.Debugger.Utils.getElement = function(el){
+	if (typeof el == 'string')
+		return document.getElementById(el);
+	return el;
+};
 if (typeof debug == 'undefined' || typeof debug.toString == 'undefined'){
 	var debug = new Spry.Debugger();
 	if (typeof console == 'undefined'){</diff>
      <filename>resources/script/spry/includes/SpryDebug.js</filename>
    </modified>
    <modified>
      <diff>@@ -1,4 +1,4 @@
-// Spry.Effect.js - version 0.35 - Spry Pre-Release 1.5
+// Spry.Effect.js - version 0.38 - Spry Pre-Release 1.6.1
 //
 // Copyright (c) 2006. Adobe Systems Incorporated.
 // All rights reserved.
@@ -114,9 +114,10 @@ Spry.Effect.Registry.prototype.addEffect = function(effect, element, options)
 	{
 		var len = this.effects.length;
 		this.effects[len] = {};
-		this.effects[len].effect = effect;
-		this.effects[len].element = element;
-		this.effects[len].options = options;
+		var eff = this.effects[len];
+		eff.effect = effect;
+		eff.element = Spry.Effect.getElement(element);
+		eff.options = options;
 	}
 };
 
@@ -171,12 +172,6 @@ Spry.Effect.Utils.Rectangle = function()
 	this.units = &quot;px&quot;;
 };
 
-Spry.Effect.Utils.PositionedRectangle = function()
-{
-	this.position = new Spry.Effect.Utils.Position;
-	this.rectangle = new Spry.Effect.Utils.Rectangle;
-};
-
 Spry.Effect.Utils.intToHex = function(integerNum) 
 {
 	var result = integerNum.toString(16);
@@ -192,15 +187,16 @@ Spry.Effect.Utils.hexToInt = function(hexStr)
 
 Spry.Effect.Utils.rgb = function(redInt, greenInt, blueInt)
 {
-	var redHex = Spry.Effect.Utils.intToHex(redInt);
-	var greenHex = Spry.Effect.Utils.intToHex(greenInt);
-	var blueHex = Spry.Effect.Utils.intToHex(blueInt);
+	var intToHex = Spry.Effect.Utils.intToHex;
+	var redHex = intToHex(redInt);
+	var greenHex = intToHex(greenInt);
+	var blueHex = intToHex(blueInt);
 	compositeColorHex = redHex.concat(greenHex, blueHex).toUpperCase();
 	compositeColorHex = '#' + compositeColorHex;
 	return compositeColorHex;
 };
 
-Spry.Effect.Utils.longVersion = function(color){
+Spry.Effect.Utils.longColorVersion = function(color){
 	if ( color.match(/^#[0-9a-f]{3}$/i) ){
 		var tmp = color.split('');
 		var color = '#';
@@ -299,7 +295,7 @@ Spry.Effect.Utils.fetchChildImages = function(startEltIn, targetImagesOut)
 
 	if(startEltIn.hasChildNodes())
 	{
-		var childImages = startEltIn.getElementsByTagName('img')
+		var childImages = startEltIn.getElementsByTagName('img');
 		var imageCnt = childImages.length;
 		for(var i=0; i&lt;imageCnt; i++)
 		{
@@ -328,10 +324,9 @@ Spry.Effect.Utils.optionsAreIdentical = function(optionsA, optionsB)
 
 		for (var prop in optionsA)
 		{
-			if (optionsA[prop] === undefined)
-				if(optionsB[prop] !== undefined)
-					return false;
-			else if((optionsB[prop] === undefined) || (optionsA[prop] != optionsB[prop]))
+			var typeA = typeof optionsA[prop];
+			var typeB = typeof optionsB[prop];
+			if ( typeA != typeB || (typeA != 'undefined' &amp;&amp; optionsA[prop] != optionsB[prop]))
 				return false;
 		}
 
@@ -844,7 +839,7 @@ Spry.Effect.Animator.prototype.start = function(withoutTimer)
 	this.startMilliseconds = currDate.getTime();
 
 	if (this.element.id)
-		this.element = document.getElementById(this.element.id)
+		this.element = document.getElementById(this.element.id);
 
 	if (this.cancelRemaining != 0 &amp;&amp; this.options.toggle)
 	{
@@ -968,7 +963,7 @@ Spry.Effect.Animator.prototype.doToggle = function()
 			this.direction = Spry.forwards;
 		}
 	}
-}
+};
 
 Spry.Effect.Animator.prototype.prepareStart = function()
 {
@@ -1084,12 +1079,16 @@ Spry.Effect.Size = function(element, fromRect, toRect, options)
 	element = this.element;
 
 	if (fromRect.units != toRect.units)
+	{
 		Spry.Effect.Utils.showError('Spry.Effect.Size: Conflicting units (' + fromRect.units + ', ' + toRect.units + ')');
+		return false;
+	}
 
 	this.units = fromRect.units;
 
 	var originalRect = Spry.Effect.getDimensionsRegardlessOfDisplayState(element);
 	this.originalWidth = originalRect.width;
+	this.originalHeight = originalRect.height;
 
 	this.startWidth = fromRect.width;
 	this.startHeight = fromRect.height;
@@ -1129,28 +1128,24 @@ Spry.Effect.Size = function(element, fromRect, toRect, options)
 	if (isPercent(this.startWidth))
 	{
 		var startWidthPercent = Spry.Effect.Utils.getPercentValue(this.startWidth);
-		//var originalRect = Spry.Effect.getDimensions(element);
 		this.startWidth = originalRect.width * (startWidthPercent / 100);
 	}
 
 	if (isPercent(this.startHeight))
 	{
 		var startHeightPercent = Spry.Effect.Utils.getPercentValue(this.startHeight);
-		//var originalRect = Spry.Effect.getDimensions(element);
 		this.startHeight = originalRect.height * (startHeightPercent / 100);
 	}
 
 	if (isPercent(this.stopWidth))
 	{
 		var stopWidthPercent = Spry.Effect.Utils.getPercentValue(this.stopWidth);
-		var originalRect = Spry.Effect.getDimensionsRegardlessOfDisplayState(element);
 		this.stopWidth = originalRect.width * (stopWidthPercent / 100);
 	}
 
 	if (isPercent(this.stopHeight))
 	{
 		var stopHeightPercent = Spry.Effect.Utils.getPercentValue(this.stopHeight);
-		var originalRect = Spry.Effect.getDimensionsRegardlessOfDisplayState(element);
 		this.stopHeight = originalRect.height * (stopHeightPercent / 100);
 	}
 
@@ -1168,7 +1163,6 @@ Spry.Effect.Size.prototype.animate = function()
 	var direction = 0;
 	var floor = Math.floor;
 	var elapsed = this.getElapsedMilliseconds();
-	var position = this.options.transition(elapsed , 0, 1, this.options.duration);
 
 	if (this.direction == Spry.forwards) {
 		width = floor(this.options.transition(elapsed, this.startWidth, this.stopWidth - this.startWidth, this.options.duration));
@@ -1179,7 +1173,9 @@ Spry.Effect.Size.prototype.animate = function()
 		height = floor(this.options.transition(elapsed, this.stopHeight, this.startHeight - this.stopHeight, this.options.duration));
 		direction = -1;
 	}
-	fontSize = this.fontFactor*width/this.originalWidth;
+
+	var propFactor = width/this.originalWidth;
+	fontSize = this.fontFactor * propFactor;
 
 	var elStyle = this.element.style;
 	if (width &lt; 0)
@@ -1191,16 +1187,16 @@ Spry.Effect.Size.prototype.animate = function()
 	elStyle.width = width + this.units;
 	elStyle.height = height + this.units;
 
-	if (this.options.useCSSBox == true)
+	if (typeof this.options.useCSSBox != 'undefined' &amp;&amp; this.options.useCSSBox == true)
 	{
-		var intProp = Spry.Effect.intPropStyle
+		var intProp = Spry.Effect.intPropStyle;
 		var origTop = intProp(this.element, 'top');
 		var origLeft = intProp(this.element, 'left');
 		var origMarginTop = intProp(this.element, 'margin-top');
 		var origMarginLeft = intProp(this.element, 'margin-left');
 
-		var widthFactor = width/this.startWidth;
-		var heightFactor = height/this.startHeight;
+		var widthFactor = propFactor;
+		var heightFactor = height / this.originalHeight;
 		var border_top = floor(this.startFromBorder_top * heightFactor);
 		var border_bottom = floor(this.startFromBorder_bottom * heightFactor);
 		var border_left = floor(this.startFromBorder_left * widthFactor);
@@ -1228,13 +1224,12 @@ Spry.Effect.Size.prototype.animate = function()
 		elStyle.marginRight = margin_right + this.units;
 
 		// compensate the margin shrinking
-		elStyle.left = (origLeft + origMarginLeft - margin_left) + this.units;
-		elStyle.top = (origTop + origMarginTop - margin_top) + this.units;
+		elStyle.left = floor(origLeft + origMarginLeft - margin_left) + this.units;
+		elStyle.top = floor(origTop + origMarginTop - margin_top) + this.units;
 	}
 
-	if (this.options.scaleContent == true)
+	if (this.options.scaleContent)
 	{
-		var propFactor = width/this.originalWidth;
 
 		for(var i=0; i &lt; this.childImages.length; i++)
 		{
@@ -1258,7 +1253,7 @@ Spry.Effect.Size.prototype.prepareStart = function()
 
 	if (this.dynamicFromRect == true)
 	{
-		var fromRect = Spry.Effect.getDimensions(element);
+		var fromRect = Spry.Effect.getDimensions(this.element);
 		this.startWidth = fromRect.width;
 		this.startHeight = fromRect.height;
 
@@ -1312,6 +1307,9 @@ Spry.Effect.Opacity.prototype.animate = function()
 	else if (this.direction == Spry.backwards) 
 		opacity = this.options.transition(elapsed, this.stopOpacity, this.startOpacity - this.stopOpacity, this.options.duration);
 
+	if (opacity &lt; 0)
+		opacity = 0;
+
 	if(/MSIE/.test(navigator.userAgent))
 	{
 		var tmpval = Spry.Effect.getStyleProp(this.element,'filter');
@@ -1337,7 +1335,7 @@ Spry.Effect.Opacity.prototype.prepareStart = function()
 
 	if (this.dynamicStartOpacity == true)
 	{
-		this.startOpacity = Spry.Effect.getOpacity(element);
+		this.startOpacity = Spry.Effect.getOpacity(this.element);
 		this.opacityRange = this.startOpacity - this.stopOpacity;
 	}
 };
@@ -1775,8 +1773,8 @@ Spry.Effect.Highlight = function (element, options)
 	if ( toColor.indexOf('rgb') != -1 )
 		var toColor = Spry.Effect.Utils.rgb(parseInt(toColor.substring(toColor.indexOf('(')+1, toColor.indexOf(',')),10), parseInt(toColor.substring(toColor.indexOf(',')+1, toColor.lastIndexOf(',')),10), parseInt(toColor.substring(toColor.lastIndexOf(',')+1, toColor.indexOf(')')),10));
 
-	var fromColor = Spry.Effect.Utils.longVersion(fromColor);
-	var toColor = Spry.Effect.Utils.longVersion(toColor);
+	var fromColor = Spry.Effect.Utils.longColorVersion(fromColor);
+	var toColor = Spry.Effect.Utils.longColorVersion(toColor);
 
 	this.restoreBackgroundImage = Spry.Effect.getStyleProp(element, 'background-image');
 
@@ -1979,6 +1977,7 @@ Spry.Effect.Grow = function (element, options)
 
 	var optionFrom = options ? options.from : dimRect.width;
 	var optionTo   = options ? options.to : 0;
+	var pixelValue = Spry.Effect.Utils.getPixelValue;
 
 	if (options)
 	{
@@ -1997,13 +1996,13 @@ Spry.Effect.Grow = function (element, options)
 			{
 				if(calcHeight)
 				{
-					fromRect.height = Spry.Effect.Utils.getPixelValue(options.from);
-					fromRect.width  = Spry.Effect.Utils.getPixelValue(options.from) / propFactor;
+					fromRect.height = pixelValue(options.from);
+					fromRect.width  = pixelValue(options.from) / propFactor;
 				}
 				else
 				{
-					fromRect.width = Spry.Effect.Utils.getPixelValue(options.from);
-					fromRect.height = propFactor * Spry.Effect.Utils.getPixelValue(options.from);
+					fromRect.width = pixelValue(options.from);
+					fromRect.height = propFactor * pixelValue(options.from);
 				}
 			}
 		}
@@ -2018,13 +2017,13 @@ Spry.Effect.Grow = function (element, options)
 			{
 				if(calcHeight)
 				{
-					toRect.height = Spry.Effect.Utils.getPixelValue(options.to);
-					toRect.width  = Spry.Effect.Utils.getPixelValue(options.to) / propFactor;
+					toRect.height = pixelValue(options.to);
+					toRect.width  = pixelValue(options.to) / propFactor;
 				}
 				else
 				{
-					toRect.width = Spry.Effect.Utils.getPixelValue(options.to);
-					toRect.height = propFactor * Spry.Effect.Utils.getPixelValue(options.to);
+					toRect.width = pixelValue(options.to);
+					toRect.height = propFactor * pixelValue(options.to);
 				}
 			}
 		}
@@ -2154,7 +2153,7 @@ Spry.Effect.Squish = function (element, options)
 	options.growCenter = false;
 	Spry.Effect.Grow.call(this, element, options);
 	this.name = 'Squish';
-}
+};
 Spry.Effect.Squish.prototype = new Spry.Effect.Grow();
 Spry.Effect.Squish.prototype.constructor = Spry.Effect.Squish;
 
@@ -2297,6 +2296,8 @@ Spry.Effect.DropOut = function (element, options)
 	var durationInMilliseconds = 1000;
 	var fps = 60;
 	var kindOfTransition = Spry.fifthTransition;
+	var direction = Spry.forwards;
+	var doToggle = false;
 	this.name = 'DropOut';
 
 	Spry.Effect.makePositioned(element);
@@ -2307,6 +2308,7 @@ Spry.Effect.DropOut = function (element, options)
 		if (options.toggle != null) doToggle = options.toggle;
 		if (options.fps != null) fps = options.fps;
 		if (options.transition != null) kindOfTransition = options.transition;
+		if (options.dropIn != null) direction = -1;
 	}
 
 	var startOffsetPosition = new Spry.Effect.Utils.Position();
@@ -2321,7 +2323,7 @@ Spry.Effect.DropOut = function (element, options)
 
 	var toPos = new Spry.Effect.Utils.Position;
 	toPos.x = startOffsetPosition.x + 0;
-	toPos.y = startOffsetPosition.y + 160;
+	toPos.y = startOffsetPosition.y + (direction * 160);
 
 	options = {from:fromPos, to:toPos, duration:durationInMilliseconds, toggle:doToggle, transition: kindOfTransition, fps: fps};
 	var moveEffect = new Spry.Effect.Move(element, options.from, options.to, options);
@@ -2335,7 +2337,7 @@ Spry.Effect.DropOut = function (element, options)
 
 	var self = this;
 	this.addObserver({
-		onPreEffect:function(){if (self.direction == Spry.backwards){self.element.style.display = 'block';}},
+		onPreEffect:function(){self.element.style.display = 'block';},
 		onPostEffect: function(){if (self.direction == Spry.forwards){self.element.style.display = 'none';}}
 	});
 </diff>
      <filename>resources/script/spry/includes/SpryEffects.js</filename>
    </modified>
    <modified>
      <diff>@@ -1,4 +1,4 @@
-// SpryHTMLDataSet.js - version 0.17 - Spry Pre-Release 1.5
+// SpryHTMLDataSet.js - version 0.20 - Spry Pre-Release 1.6.1
 //
 // Copyright (c) 2006. Adobe Systems Incorporated.
 // All rights reserved.
@@ -27,12 +27,6 @@
 // ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
 // POSSIBILITY OF SUCH DAMAGE.
 
-/*
- *  HTMLDataSet (0.16)
- *  
- */
-
-
 //////////////////////////////////////////////////////////////////////
 //
 // Spry.Data.HTMLDataSet
@@ -44,7 +38,7 @@ Spry.Data.HTMLDataSet = function(dataSetURL, sourceElementID, dataSetOptions)
 	this.sourceElementID = sourceElementID; // ID of the html element to be used as a data source
 	this.sourceElement = null;  			      // The actual html element to be used as a data source
 
-  this.sourceWasInitialized = false;
+	this.sourceWasInitialized = false;
 	this.usesExternalFile = (dataSetURL != null) ? true : false;
 	
 	this.firstRowAsHeaders = true;
@@ -54,7 +48,9 @@ Spry.Data.HTMLDataSet = function(dataSetURL, sourceElementID, dataSetOptions)
 	
 	this.rowSelector = null;
 	this.dataSelector = null;
-	
+
+	this.tableModeEnabled = true;
+
 	Spry.Data.HTTPSourceDataSet.call(this, dataSetURL, dataSetOptions);
 };
 
@@ -253,7 +249,9 @@ Spry.Data.HTMLDataSet.prototype.loadData = function()
 			}
 			self.dataWasLoaded = true;
 			
+			self.disableNotifications();
 			self.filterAndSortData();
+			self.enableNotifications();
 			
 			self.notifyObservers(&quot;onPostLoad&quot;);
 			self.notifyObservers(&quot;onDataChanged&quot;);	
@@ -331,20 +329,15 @@ Spry.Data.HTMLDataSet.prototype.getDataFromSourceElement = function()
     return null;
 
 	var extractedData;
-	var usesTable = false;
-	switch (this.sourceElement.nodeName.toLowerCase())
-	{
-		case &quot;table&quot;:
-   		usesTable = true;
-			extractedData = this.getDataFromHTMLTable();
-			break;
-		default:
-			extractedData = this.getDataFromNestedStructure();
-	}
+	var usesTable = (this.tableModeEnabled &amp;&amp; this.sourceElement.nodeName.toLowerCase() == &quot;table&quot;);
+	if (usesTable)
+		extractedData = this.getDataFromHTMLTable();
+	else
+		extractedData = this.getDataFromNestedStructure();
+
 	if (!extractedData)
      return null;
-	
-	
+
 	// Flip Columns / Rows
 	if (this.useColumnsAsRows) 
 	{
@@ -453,7 +446,7 @@ Spry.Data.HTMLDataSet.prototype.getDataFromHTMLTable = function()
      
      var dataRow;
      if (extractedData[rowIdx]) dataRow = extractedData[rowIdx];
-     else dataRow = new Array
+     else dataRow = new Array;
      
      var offset = 0;
      var cells = row.cells;
@@ -491,13 +484,14 @@ Spry.Data.HTMLDataSet.prototype.getDataFromHTMLTable = function()
          nextRowIndex = rowIdx + rowOffIdx;
          var nextDataRow;
          if (extractedData[nextRowIndex]) nextDataRow = extractedData[nextRowIndex];
-         else nextDataRow = new Array
+         else nextDataRow = new Array;
          
+         var rowSpanCellOffset = startOffset;
          for (var offIdx = 0; offIdx &lt; colspan; offIdx++)
          {
-           nextCellIndex = cellIdx + startOffset;
+           nextCellIndex = cellIdx + rowSpanCellOffset;
            nextDataRow[nextCellIndex] = cellValue;
-           startOffset ++;
+           rowSpanCellOffset ++;
          }
          extractedData[nextRowIndex] = nextDataRow;
        }</diff>
      <filename>resources/script/spry/includes/SpryHTMLDataSet.js</filename>
    </modified>
    <modified>
      <diff>@@ -1,4 +1,4 @@
-// SpryJSONDataSet.js - version 0.3 - Spry Pre-Release 1.5
+// SpryJSONDataSet.js - version 0.6 - Spry Pre-Release 1.6.1
 //
 // Copyright (c) 2007. Adobe Systems Incorporated.
 // All rights reserved.
@@ -303,6 +303,8 @@ Spry.Data.JSONDataSet.flattenDataIntoRecordSet = function(jsonObj, path, pathIsO
 		// objects, so run through all of the arrays and build up row objects and add them
 		// to our record set.
 
+		var rowID = 0;
+
 		for (var i = 0; i &lt; numMatches; i++)
 		{
 			var obj = matches[i];
@@ -328,8 +330,8 @@ Spry.Data.JSONDataSet.flattenDataIntoRecordSet = function(jsonObj, path, pathIsO
 					var colName = colNames[k];
 					row[colName] = obj[colName][j];
 				}
-				row.ds_RowID = i;
-				rs.dataHash[i] = row;
+				row.ds_RowID = rowID++;
+				rs.dataHash[row.ds_RowID] = row;
 				rs.data.push(row);
 			}
 		}
@@ -608,6 +610,30 @@ Spry.Data.JSONDataSet.prototype.parseJSON = function(str, filter)
 	throw new Error(&quot;Failed to parse JSON string.&quot;);
 };
 
+Spry.Data.JSONDataSet.prototype.syncColumnTypesToData = function()
+{
+	// Run through every column in the first row and set the column type
+	// to match the type of the value currently in the column, but only
+	// if the column type is not already set.
+	//
+	// For the sake of performance, there are a couple of big assumptions
+	// being made here. Specifically, we are assuming that *every* row in the
+	// data set has the same set of column names defined, and that the value
+	// for a specific column has the same type as a value in the same column
+	// in any other row.
+
+	var row = this.getData()[0];
+	for (var colName in row)
+	{
+		if (!this.columnTypes[colName])
+		{
+			var type = typeof row[colName];
+			if (type == &quot;number&quot;)
+				this.setColumnType(colName, type);
+		}
+	}
+};
+
 // Translate the raw JSON string (rawDataDoc) into an object, find the
 // data within the object we are interested in, and flatten it into
 // a set of rows for our data set.
@@ -637,4 +663,6 @@ Spry.Data.JSONDataSet.prototype.loadDataIntoDataSet = function(rawDataDoc)
 	this.data = rs.data;
 	this.dataHash = rs.dataHash;
 	this.dataWasLoaded = (this.doc != null);
+
+	this.syncColumnTypesToData();
 };</diff>
      <filename>resources/script/spry/includes/SpryJSONDataSet.js</filename>
    </modified>
    <modified>
      <diff>@@ -1,4 +1,4 @@
-// SpryNestedJSONDataSet.js - version 0.1 - Spry Pre-Release 1.5
+// SpryNestedJSONDataSet.js - version 0.5 - Spry Pre-Release 1.6.1
 //
 // Copyright (c) 2007. Adobe Systems Incorporated.
 // All rights reserved.
@@ -147,13 +147,54 @@ Spry.Data.NestedJSONDataSet.prototype.onPreParentContextChange = function(notifi
 	this.ignoreOnDataChanged = true;
 };
 
+Spry.Data.NestedJSONDataSet.prototype.filterAndSortData = function()
+{
+	// This method is almost identical to the one from the
+	// DataSet base class, except that it does not set the
+	// current row id.
+
+	// If there is a data filter installed, run it.
+
+	if (this.filterDataFunc)
+		this.filterData(this.filterDataFunc, true);
+
+	// If the distinct flag was set, run through all the records in the recordset
+	// and toss out any that are duplicates.
+
+	if (this.distinctOnLoad)
+		this.distinct(this.distinctFieldsOnLoad);
+
+	// If sortOnLoad was set, sort the data based on the columns
+	// specified in sortOnLoad.
+
+	if (this.keepSorted &amp;&amp; this.getSortColumn())
+		this.sort(this.lastSortColumns, this.lastSortOrder);
+	else if (this.sortOnLoad)
+		this.sort(this.sortOnLoad, this.sortOrderOnLoad);
+
+	// If there is a view filter installed, run it.
+
+	if (this.filterFunc)
+		this.filter(this.filterFunc, true);
+};
+
 Spry.Data.NestedJSONDataSet.prototype.loadData = function()
 {
 	var parentDS = this.parentDataSet;
 
-	if (!parentDS || parentDS.getLoadDataRequestIsPending() || !parentDS.getDataWasLoaded() || !this.jpath)
+	if (!parentDS || parentDS.getLoadDataRequestIsPending() || !this.jpath)
 		return;
 
+	if (!parentDS.getDataWasLoaded())
+	{
+		// Someone is asking us to load our data, but our parent
+		// hasn't even initiated a load yet. Tell our parent to
+		// load its data, so we can get our data!
+
+		parentDS.loadData();
+		return;
+	}
+
 	this.notifyObservers(&quot;onPreLoad&quot;);
 
 	this.nestedDataSets = [];
@@ -194,6 +235,12 @@ Spry.Data.NestedJSONDataSet.prototype.loadData = function()
 
 					var ds = new Spry.Data.DataSet(this.options);
 
+					// Make sure the internal nested data set has the same set
+					// of columnTypes as the nested data set itself.
+
+					for (var cname in this.columnTypes)
+						ds.setColumnType(cname, this.columnTypes[cname]);
+
 					// Flatten any data that matches our XPath and stuff it into
 					// our new nested data set.
 
@@ -222,7 +269,7 @@ Spry.Data.NestedJSONDataSet.prototype.loadData = function()
 					// Add an observer on the nested data set so that whenever it dispatches
 					// a notifications, we forward it on as if we generated the notification.
 		
-					ds.addObserver(function(notificationType, notifier, data){ self.notifyObservers(notificationType, data); });
+					ds.addObserver(function(notificationType, notifier, data){ if (notifier == self.currentDS) setTimeout(function() { self.notifyObservers(notificationType, data); }, 0); });
 				}
 			}
 		}
@@ -234,7 +281,11 @@ Spry.Data.NestedJSONDataSet.prototype.loadData = function()
 	this.pendingRequest.timer = setTimeout(function() {
 		self.pendingRequest = null;
 		self.dataWasLoaded = true;
-	
+
+		self.disableNotifications();
+		self.filterAndSortData();
+		self.enableNotifications();
+
 		self.notifyObservers(&quot;onPostLoad&quot;);
 		self.notifyObservers(&quot;onDataChanged&quot;);
 	}, 0);
@@ -319,6 +370,15 @@ Spry.Data.NestedJSONDataSet.prototype.setColumnType = function(columnNames, colu
 {
 	if (columnNames)
 	{
+		// Make sure the nested JSON data set tracks the column types
+		// that the user sets so that if our data changes, we can re-apply
+		// the column types.
+
+		Spry.Data.DataSet.prototype.setColumnType.call(this, columnNames, columnType);
+
+		// Now apply the column types to any internal nested data sets
+		// that exist.
+
 		var dsArr = this.nestedDataSets;
 		var dsArrLen = dsArr.length;
 		for (var i = 0; i &lt; dsArrLen; i++)
@@ -344,47 +404,57 @@ Spry.Data.NestedJSONDataSet.prototype.distinct = function(columnNames)
 	}
 };
 
-Spry.Data.NestedJSONDataSet.prototype.getSortColumn = function() {
-	if (this.currentDS)
-		return this.currentDS.getSortColumn();
-	return &quot;&quot;;
-};
-
-Spry.Data.NestedJSONDataSet.prototype.getSortOrder = function() {
-	if (this.currentDS)
-		return this.currentDS.getSortOrder();
-	return &quot;&quot;;
-};
-
 Spry.Data.NestedJSONDataSet.prototype.sort = function(columnNames, sortOrder)
 {
 	if (columnNames)
 	{
+		// Forward the sort request to all internal data sets.
+
 		var dsArr = this.nestedDataSets;
 		var dsArrLen = dsArr.length;
 		for (var i = 0; i &lt; dsArrLen; i++)
 			dsArr[i].dataSet.sort(columnNames, sortOrder);
+
+		// Make sure we store a local copy of the last sort order
+		// column so we can restore it if new data is loaded.
+
+		if (dsArrLen &gt; 0)
+		{
+			var ds = dsArr[0].dataSet;
+			this.lastSortColumns = ds.lastSortColumns.slice(0); // Copy the array.
+			this.lastSortOrder = ds.lastSortOrder;
+		}
 	}
 };
 
 Spry.Data.NestedJSONDataSet.prototype.filterData = function(filterFunc, filterOnly)
 {
-	if (columnNames)
-	{
-		var dsArr = this.nestedDataSets;
-		var dsArrLen = dsArr.length;
-		for (var i = 0; i &lt; dsArrLen; i++)
-			dsArr[i].dataSet.filterData(filterFunc, filterOnly);
-	}
+	// Store a copy of the filterFunc so we can apply it
+	// if the data set loads new data.
+
+	this.filterDataFunc = filterFunc;
+
+	// Now set the filterFunc on all of the internal
+	// data sets.
+
+	var dsArr = this.nestedDataSets;
+	var dsArrLen = dsArr.length;
+	for (var i = 0; i &lt; dsArrLen; i++)
+		dsArr[i].dataSet.filterData(filterFunc, filterOnly);
 };
 
 Spry.Data.NestedJSONDataSet.prototype.filter = function(filterFunc, filterOnly)
 {
-	if (columnNames)
-	{
-		var dsArr = this.nestedDataSets;
-		var dsArrLen = dsArr.length;
-		for (var i = 0; i &lt; dsArrLen; i++)
-			dsArr[i].dataSet.filter(filterFunc, filterOnly);
-	}
+	// Store a copy of the filterFunc so we can apply it
+	// if the data set loads new data.
+
+	this.filterFunc = filterFunc;
+
+	// Now set the filterFunc on all of the internal
+	// data sets.
+
+	var dsArr = this.nestedDataSets;
+	var dsArrLen = dsArr.length;
+	for (var i = 0; i &lt; dsArrLen; i++)
+		dsArr[i].dataSet.filter(filterFunc, filterOnly);
 };</diff>
      <filename>resources/script/spry/includes/SpryNestedJSONDataSet.js</filename>
    </modified>
    <modified>
      <diff>@@ -1,4 +1,4 @@
-// SpryNestedXMLDataSet.js - version 0.1 - Spry Pre-Release 1.5
+// SpryNestedXMLDataSet.js - version 0.7 - Spry Pre-Release 1.6.1
 //
 // Copyright (c) 2007. Adobe Systems Incorporated.
 // All rights reserved.
@@ -37,6 +37,7 @@ Spry.Data.NestedXMLDataSet = function(parentDataSet, xpath, options)
 	this.currentDSAncestor = null;
 	this.options = options;
 	this.ignoreOnDataChanged = false;
+	this.entityEncodeStrings = parentDataSet ? parentDataSet.entityEncodeStrings : true;
 
 	Spry.Data.DataSet.call(this, options);
 
@@ -147,12 +148,53 @@ Spry.Data.NestedXMLDataSet.prototype.onPreParentContextChange = function(notifie
 	this.ignoreOnDataChanged = true;
 };
 
+Spry.Data.NestedXMLDataSet.prototype.filterAndSortData = function()
+{
+	// This method is almost identical to the one from the
+	// DataSet base class, except that it does not set the
+	// current row id.
+
+	// If there is a data filter installed, run it.
+
+	if (this.filterDataFunc)
+		this.filterData(this.filterDataFunc, true);
+
+	// If the distinct flag was set, run through all the records in the recordset
+	// and toss out any that are duplicates.
+
+	if (this.distinctOnLoad)
+		this.distinct(this.distinctFieldsOnLoad);
+
+	// If sortOnLoad was set, sort the data based on the columns
+	// specified in sortOnLoad.
+
+	if (this.keepSorted &amp;&amp; this.getSortColumn())
+		this.sort(this.lastSortColumns, this.lastSortOrder);
+	else if (this.sortOnLoad)
+		this.sort(this.sortOnLoad, this.sortOrderOnLoad);
+
+	// If there is a view filter installed, run it.
+
+	if (this.filterFunc)
+		this.filter(this.filterFunc, true);
+};
+
 Spry.Data.NestedXMLDataSet.prototype.loadData = function()
 {
 	var parentDS = this.parentDataSet;
 
-	if (!parentDS || parentDS.getLoadDataRequestIsPending() || !parentDS.getDataWasLoaded() || !this.xpath)
+	if (!parentDS || parentDS.getLoadDataRequestIsPending() || !this.xpath)
+		return;
+
+	if (!parentDS.getDataWasLoaded())
+	{
+		// Someone is asking us to load our data, but our parent
+		// hasn't even initiated a load yet. Tell our parent to
+		// load its data, so we can get our data!
+
+		parentDS.loadData();
 		return;
+	}
 
 	this.notifyObservers(&quot;onPreLoad&quot;);
 
@@ -194,10 +236,16 @@ Spry.Data.NestedXMLDataSet.prototype.loadData = function()
 
 					var ds = new Spry.Data.DataSet(this.options);
 
+					// Make sure the internal nested data set has the same set
+					// of columnTypes as the nested data set itself.
+
+					for (var cname in this.columnTypes)
+						ds.setColumnType(cname, this.columnTypes[cname]);
+
 					// Flatten any data that matches our XPath and stuff it into
 					// our new nested data set.
 
-					var dataArr = Spry.Utils.getRecordSetFromXMLDoc(row.ds_XMLNode, this.xpath);
+					var dataArr = Spry.Data.XMLDataSet.getRecordSetFromXMLDoc(row.ds_XMLNode, this.xpath, false, this.entityEncodeStrings);
 					ds.setDataFromArray(dataArr.data, true);
 
 					// Create an object that stores the relationship between our
@@ -222,7 +270,7 @@ Spry.Data.NestedXMLDataSet.prototype.loadData = function()
 					// Add an observer on the nested data set so that whenever it dispatches
 					// a notifications, we forward it on as if we generated the notification.
 		
-					ds.addObserver(function(notificationType, notifier, data){ self.notifyObservers(notificationType, data); });
+					ds.addObserver(function(notificationType, notifier, data){ if (notifier == self.currentDS) setTimeout(function() { self.notifyObservers(notificationType, data); }, 0); });
 				}
 			}
 		}
@@ -234,7 +282,11 @@ Spry.Data.NestedXMLDataSet.prototype.loadData = function()
 	this.pendingRequest.timer = setTimeout(function() {
 		self.pendingRequest = null;
 		self.dataWasLoaded = true;
-	
+
+		self.disableNotifications();
+		self.filterAndSortData();
+		self.enableNotifications();
+
 		self.notifyObservers(&quot;onPostLoad&quot;);
 		self.notifyObservers(&quot;onDataChanged&quot;);
 	}, 0);
@@ -319,6 +371,15 @@ Spry.Data.NestedXMLDataSet.prototype.setColumnType = function(columnNames, colum
 {
 	if (columnNames)
 	{
+		// Make sure the nested xml data set tracks the column types
+		// that the user sets so that if our data changes, we can re-apply
+		// the column types.
+
+		Spry.Data.DataSet.prototype.setColumnType.call(this, columnNames, columnType);
+
+		// Now apply the column types to any internal nested data sets
+		// that exist.
+
 		var dsArr = this.nestedDataSets;
 		var dsArrLen = dsArr.length;
 		for (var i = 0; i &lt; dsArrLen; i++)
@@ -344,47 +405,66 @@ Spry.Data.NestedXMLDataSet.prototype.distinct = function(columnNames)
 	}
 };
 
-Spry.Data.NestedXMLDataSet.prototype.getSortColumn = function() {
-	if (this.currentDS)
-		return this.currentDS.getSortColumn();
-	return &quot;&quot;;
-};
-
-Spry.Data.NestedXMLDataSet.prototype.getSortOrder = function() {
-	if (this.currentDS)
-		return this.currentDS.getSortOrder();
-	return &quot;&quot;;
-};
-
 Spry.Data.NestedXMLDataSet.prototype.sort = function(columnNames, sortOrder)
 {
 	if (columnNames)
 	{
+		// Forward the sort request to all internal data sets.
+
 		var dsArr = this.nestedDataSets;
 		var dsArrLen = dsArr.length;
 		for (var i = 0; i &lt; dsArrLen; i++)
 			dsArr[i].dataSet.sort(columnNames, sortOrder);
+
+		// Make sure we store a local copy of the last sort order
+		// column so we can restore it if new data is loaded.
+
+		if (dsArrLen &gt; 0)
+		{
+			var ds = dsArr[0].dataSet;
+			this.lastSortColumns = ds.lastSortColumns.slice(0); // Copy the array.
+			this.lastSortOrder = ds.lastSortOrder;
+		}
 	}
 };
 
 Spry.Data.NestedXMLDataSet.prototype.filterData = function(filterFunc, filterOnly)
 {
-	if (columnNames)
-	{
-		var dsArr = this.nestedDataSets;
-		var dsArrLen = dsArr.length;
-		for (var i = 0; i &lt; dsArrLen; i++)
-			dsArr[i].dataSet.filterData(filterFunc, filterOnly);
-	}
+	// Store a copy of the filterFunc so we can apply it
+	// if the data set loads new data.
+
+	this.filterDataFunc = filterFunc;
+
+	// Now set the filterFunc on all of the internal
+	// data sets.
+
+	var dsArr = this.nestedDataSets;
+	var dsArrLen = dsArr.length;
+	for (var i = 0; i &lt; dsArrLen; i++)
+		dsArr[i].dataSet.filterData(filterFunc, filterOnly);
 };
 
 Spry.Data.NestedXMLDataSet.prototype.filter = function(filterFunc, filterOnly)
 {
-	if (columnNames)
+	// Store a copy of the filterFunc so we can apply it
+	// if the data set loads new data.
+
+	this.filterFunc = filterFunc;
+
+	// Now set the filterFunc on all of the internal
+	// data sets.
+
+	var dsArr = this.nestedDataSets;
+	var dsArrLen = dsArr.length;
+	for (var i = 0; i &lt; dsArrLen; i++)
+		dsArr[i].dataSet.filter(filterFunc, filterOnly);
+};
+
+Spry.Data.NestedXMLDataSet.prototype.setXPath = function(path)
+{
+	if (this.xpath != path)
 	{
-		var dsArr = this.nestedDataSets;
-		var dsArrLen = dsArr.length;
-		for (var i = 0; i &lt; dsArrLen; i++)
-			dsArr[i].dataSet.filter(filterFunc, filterOnly);
+		this.xpath = path;
+		this.loadData();
 	}
-};
+};
\ No newline at end of file</diff>
      <filename>resources/script/spry/includes/SpryNestedXMLDataSet.js</filename>
    </modified>
    <modified>
      <diff>@@ -1,4 +1,4 @@
-// SpryPagedView.js - version 0.5 - Spry Pre-Release 1.5
+// SpryPagedView.js - version 0.7 - Spry Pre-Release 1.6.1
 //
 // Copyright (c) 2006. Adobe Systems Incorporated.
 // All rights reserved.
@@ -96,7 +96,7 @@ Spry.Data.PagedView = function(ds, options)
 
 	if (this.pageSize &gt; 0)
 		this.filter(this.getFilterFunc());
-}
+};
 
 Spry.Data.PagedView.prototype = new Spry.Data.DataSet();
 Spry.Data.PagedView.prototype.constructor = Spry.Data.PagedView;
@@ -119,6 +119,63 @@ Spry.Data.PagedView.prototype.setCurrentRowNumber = function(rowNumber)
 		this.ds.setCurrentRowNumber(rowNumber);
 };
 
+Spry.Data.PagedView.prototype.sort = function(columnNames, sortOrder)
+{
+	// We try to discourage developers from sorting the &quot;view&quot;
+	// for a data set instead of the &quot;view itself, but since this
+	// &quot;view&quot; is implemented as a data set, some still insist on
+	// sorting the &quot;view&quot;.
+
+	if (!columnNames)
+		return;
+
+	// We need to calculate the sort order and the set of columnNames
+	// we are going to use so we can pass it when we fire off our
+	// onPreSort and onPostSort notifications.
+
+	if (typeof columnNames == &quot;string&quot;)
+		columnNames = [ columnNames, &quot;ds_RowID&quot; ];
+	else if (columnNames.length &lt; 2 &amp;&amp; columnNames[0] != &quot;ds_RowID&quot;)
+		columnNames.push(&quot;ds_RowID&quot;);
+
+	if (!sortOrder)
+		sortOrder = &quot;toggle&quot;;
+
+	if (sortOrder == &quot;toggle&quot;)
+	{
+		if (this.lastSortColumns.length &gt; 0 &amp;&amp; this.lastSortColumns[0] == columnNames[0] &amp;&amp; this.lastSortOrder == &quot;ascending&quot;)
+			sortOrder = &quot;descending&quot;;
+		else
+			sortOrder = &quot;ascending&quot;;
+	}
+
+	var nData = {
+		oldSortColumns: this.lastSortColumns,
+		oldSortOrder: this.lastSortOrder,
+		newSortColumns: columnNames,
+		newSortOrder: sortOrder
+	};
+
+
+	this.notifyObservers(&quot;onPreSort&quot;, nData);
+
+	// Disable notifications so that when we call our inherited
+	// sort function, no notifications get fired off. We want to
+	// delay any onPostSort notification until *after* we get a chance
+	// to update our pager columns and reset the view to the first
+	// page.
+
+	this.disableNotifications();
+
+	Spry.Data.DataSet.prototype.sort.call(this, columnNames, sortOrder);
+	this.updatePagerColumns();
+	this.firstPage();
+
+	this.enableNotifications();
+
+	this.notifyObservers(&quot;onPostSort&quot;, nData);
+};
+
 Spry.Data.PagedView.prototype.loadData = function()
 {
 	// Pass on any loadData requests to the data set we
@@ -421,7 +478,7 @@ Spry.Data.PagedView.PagingInfo.prototype.onDataChanged = function(notifier, data
 	this.extractInfo();
 };
 
-Spry.Data.PagedView.PagingInfo.prototype.onPostSort = Spry.Data.PagedView.prototype.onDataChanged;
+Spry.Data.PagedView.PagingInfo.prototype.onPostSort = Spry.Data.PagedView.PagingInfo.prototype.onDataChanged;
 
 Spry.Data.PagedView.PagingInfo.prototype.extractInfo = function()
 {</diff>
      <filename>resources/script/spry/includes/SpryPagedView.js</filename>
    </modified>
    <modified>
      <diff>@@ -1,4 +1,4 @@
-// SpryURLUtils.js - version 0.1 - Spry Pre-Release 1.5
+// SpryURLUtils.js - version 0.1 - Spry Pre-Release 1.6.1
 //
 // Copyright (c) 2007. Adobe Systems Incorporated.
 // All rights reserved.</diff>
      <filename>resources/script/spry/includes/SpryURLUtils.js</filename>
    </modified>
    <modified>
      <diff>@@ -1,4 +1,4 @@
-// SpryUtils.js - version 0.2 - Spry Pre-Release 1.5
+// SpryUtils.js - version 0.3 - Spry Pre-Release 1.6.1
 //
 // Copyright (c) 2007. Adobe Systems Incorporated.
 // All rights reserved.
@@ -36,12 +36,12 @@ Spry.Utils.submitForm = function(form, callback, opts)
 		return true;
 
 	if ( typeof form == 'string' )
-		form = document.getElementById(form) || document.forms[form];
+		form = Spry.$(form) || document.forms[form];
 
 	var frmOpts = {};
-	frmOpts.method = form.method;
-	frmOpts.url = form.action || document.location.href;
-	frmOpts.enctype = form.enctype;
+	frmOpts.method = form.getAttribute('method');
+	frmOpts.url = form.getAttribute('action') || document.location.href;
+	frmOpts.enctype = form.getAttribute('enctype');
 
 	Spry.Utils.setOptions(frmOpts, opts);
 
@@ -49,10 +49,10 @@ Spry.Utils.submitForm = function(form, callback, opts)
 	if (frmOpts.additionalData)
 		submitData += &quot;&amp;&quot; + frmOpts.additionalData;
 
-	if (frmOpts.enctype.toLowerCase() != 'multipart/form-data')
+	if (!frmOpts.enctype || frmOpts.enctype.toLowerCase() != 'multipart/form-data')
 	{
 		// Ajax submission of a form doesn't work for multipart/form-data!
-		frmOpts.method = (frmOpts.method.toLowerCase() == &quot;post&quot;) ? 'POST' : 'GET';
+		frmOpts.method = (frmOpts.method &amp;&amp; frmOpts.method.toLowerCase() == &quot;post&quot;) ? 'POST' : 'GET';
 		if (frmOpts.method == &quot;GET&quot;)
 		{
 			// Data will be submitted in the url.</diff>
      <filename>resources/script/spry/includes/SpryUtils.js</filename>
    </modified>
    <modified>
      <diff>@@ -1,4 +1,4 @@
-// SpryXML.js - version 0.4 - Spry Pre-Release 1.5
+// SpryXML.js - version 0.4 - Spry Pre-Release 1.6.1
 //
 // Copyright (c) 2006. Adobe Systems Incorporated.
 // All rights reserved.</diff>
      <filename>resources/script/spry/includes/SpryXML.js</filename>
    </modified>
    <modified>
      <diff>@@ -1,35 +1,294 @@
-// xpath.js - version 0.4 - Revision: Spry Pre-Release 1.5
+// xpath.js - version 0.7 - Spry Pre-Release 1.6.1
 //
-// Copyright (c) 2005, Google Inc.
-// All rights reserved.
-// 
-// Redistribution and use in source and binary forms, with or without
-// modification, are permitted provided that the following conditions are
-// met:
-//         
-//  * Redistributions of source code must retain the above copyright
-//    notice, this list of conditions and the following disclaimer.
-// 
-//  * Redistributions in binary form must reproduce the above copyright
-//    notice, this list of conditions and the following disclaimer in the
-//    documentation and/or other materials provided with the
-//    distribution.
-// 
-//  * Neither the name of Google Inc. nor the names of its contributors
-//    may be used to endorse or promote products derived from this
-//    software without specific prior written permission.
-// 
-// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
-// &quot;AS IS&quot; AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
-// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
-// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
-// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
-// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
-// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
-// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
-// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
-// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
-// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+// Code from xmltoken.js.
+//
+// Copyright 2006 Google Inc.
+// All Rights Reserved
+//
+// Defines regular expression patterns to extract XML tokens from string.
+// See &lt;http://www.w3.org/TR/REC-xml/#sec-common-syn&gt;,
+// &lt;http://www.w3.org/TR/xml11/#sec-common-syn&gt; and
+// &lt;http://www.w3.org/TR/REC-xml-names/#NT-NCName&gt; for the specifications.
+//
+// Author: Junji Takagi &lt;jtakagi@google.com&gt;
+
+// Detect whether RegExp supports Unicode characters or not.
+
+var REGEXP_UNICODE = function() {
+  var tests = [' ', '\u0120', -1,  // Konquerer 3.4.0 fails here.
+               '!', '\u0120', -1,
+               '\u0120', '\u0120', 0,
+               '\u0121', '\u0120', -1,
+               '\u0121', '\u0120|\u0121', 0,
+               '\u0122', '\u0120|\u0121', -1,
+               '\u0120', '[\u0120]', 0,  // Safari 2.0.3 fails here.
+               '\u0121', '[\u0120]', -1,
+               '\u0121', '[\u0120\u0121]', 0,  // Safari 2.0.3 fails here.
+               '\u0122', '[\u0120\u0121]', -1,
+               '\u0121', '[\u0120-\u0121]', 0,  // Safari 2.0.3 fails here.
+               '\u0122', '[\u0120-\u0121]', -1];
+  for (var i = 0; i &lt; tests.length; i += 3) {
+    if (tests[i].search(new RegExp(tests[i + 1])) != tests[i + 2]) {
+      return false;
+    }
+  }
+  return true;
+}();
+
+// Common tokens in XML 1.0 and XML 1.1.
+
+var XML_S = '[ \t\r\n]+';
+var XML_EQ = '(' + XML_S + ')?=(' + XML_S + ')?';
+var XML_CHAR_REF = '&amp;#[0-9]+;|&amp;#x[0-9a-fA-F]+;';
+
+// XML 1.0 tokens.
+
+var XML10_VERSION_INFO = XML_S + 'version' + XML_EQ + '(&quot;1\\.0&quot;|' + &quot;'1\\.0')&quot;;
+var XML10_BASE_CHAR = (REGEXP_UNICODE) ?
+  '\u0041-\u005a\u0061-\u007a\u00c0-\u00d6\u00d8-\u00f6\u00f8-\u00ff' +
+  '\u0100-\u0131\u0134-\u013e\u0141-\u0148\u014a-\u017e\u0180-\u01c3' +
+  '\u01cd-\u01f0\u01f4-\u01f5\u01fa-\u0217\u0250-\u02a8\u02bb-\u02c1\u0386' +
+  '\u0388-\u038a\u038c\u038e-\u03a1\u03a3-\u03ce\u03d0-\u03d6\u03da\u03dc' +
+  '\u03de\u03e0\u03e2-\u03f3\u0401-\u040c\u040e-\u044f\u0451-\u045c' +
+  '\u045e-\u0481\u0490-\u04c4\u04c7-\u04c8\u04cb-\u04cc\u04d0-\u04eb' +
+  '\u04ee-\u04f5\u04f8-\u04f9\u0531-\u0556\u0559\u0561-\u0586\u05d0-\u05ea' +
+  '\u05f0-\u05f2\u0621-\u063a\u0641-\u064a\u0671-\u06b7\u06ba-\u06be' +
+  '\u06c0-\u06ce\u06d0-\u06d3\u06d5\u06e5-\u06e6\u0905-\u0939\u093d' +
+  '\u0958-\u0961\u0985-\u098c\u098f-\u0990\u0993-\u09a8\u09aa-\u09b0\u09b2' +
+  '\u09b6-\u09b9\u09dc-\u09dd\u09df-\u09e1\u09f0-\u09f1\u0a05-\u0a0a' +
+  '\u0a0f-\u0a10\u0a13-\u0a28\u0a2a-\u0a30\u0a32-\u0a33\u0a35-\u0a36' +
+  '\u0a38-\u0a39\u0a59-\u0a5c\u0a5e\u0a72-\u0a74\u0a85-\u0a8b\u0a8d' +
+  '\u0a8f-\u0a91\u0a93-\u0aa8\u0aaa-\u0ab0\u0ab2-\u0ab3\u0ab5-\u0ab9' +
+  '\u0abd\u0ae0\u0b05-\u0b0c\u0b0f-\u0b10\u0b13-\u0b28\u0b2a-\u0b30' +
+  '\u0b32-\u0b33\u0b36-\u0b39\u0b3d\u0b5c-\u0b5d\u0b5f-\u0b61\u0b85-\u0b8a' +
+  '\u0b8e-\u0b90\u0b92-\u0b95\u0b99-\u0b9a\u0b9c\u0b9e-\u0b9f\u0ba3-\u0ba4' +
+  '\u0ba8-\u0baa\u0bae-\u0bb5\u0bb7-\u0bb9\u0c05-\u0c0c\u0c0e-\u0c10' +
+  '\u0c12-\u0c28\u0c2a-\u0c33\u0c35-\u0c39\u0c60-\u0c61\u0c85-\u0c8c' +
+  '\u0c8e-\u0c90\u0c92-\u0ca8\u0caa-\u0cb3\u0cb5-\u0cb9\u0cde\u0ce0-\u0ce1' +
+  '\u0d05-\u0d0c\u0d0e-\u0d10\u0d12-\u0d28\u0d2a-\u0d39\u0d60-\u0d61' +
+  '\u0e01-\u0e2e\u0e30\u0e32-\u0e33\u0e40-\u0e45\u0e81-\u0e82\u0e84' +
+  '\u0e87-\u0e88\u0e8a\u0e8d\u0e94-\u0e97\u0e99-\u0e9f\u0ea1-\u0ea3\u0ea5' +
+  '\u0ea7\u0eaa-\u0eab\u0ead-\u0eae\u0eb0\u0eb2-\u0eb3\u0ebd\u0ec0-\u0ec4' +
+  '\u0f40-\u0f47\u0f49-\u0f69\u10a0-\u10c5\u10d0-\u10f6\u1100\u1102-\u1103' +
+  '\u1105-\u1107\u1109\u110b-\u110c\u110e-\u1112\u113c\u113e\u1140\u114c' +
+  '\u114e\u1150\u1154-\u1155\u1159\u115f-\u1161\u1163\u1165\u1167\u1169' +
+  '\u116d-\u116e\u1172-\u1173\u1175\u119e\u11a8\u11ab\u11ae-\u11af' +
+  '\u11b7-\u11b8\u11ba\u11bc-\u11c2\u11eb\u11f0\u11f9\u1e00-\u1e9b' +
+  '\u1ea0-\u1ef9\u1f00-\u1f15\u1f18-\u1f1d\u1f20-\u1f45\u1f48-\u1f4d' +
+  '\u1f50-\u1f57\u1f59\u1f5b\u1f5d\u1f5f-\u1f7d\u1f80-\u1fb4\u1fb6-\u1fbc' +
+  '\u1fbe\u1fc2-\u1fc4\u1fc6-\u1fcc\u1fd0-\u1fd3\u1fd6-\u1fdb\u1fe0-\u1fec' +
+  '\u1ff2-\u1ff4\u1ff6-\u1ffc\u2126\u212a-\u212b\u212e\u2180-\u2182' +
+  '\u3041-\u3094\u30a1-\u30fa\u3105-\u312c\uac00-\ud7a3' :
+  'A-Za-z';
+var XML10_IDEOGRAPHIC = (REGEXP_UNICODE) ?
+  '\u4e00-\u9fa5\u3007\u3021-\u3029' :
+  '';
+var XML10_COMBINING_CHAR = (REGEXP_UNICODE) ?
+  '\u0300-\u0345\u0360-\u0361\u0483-\u0486\u0591-\u05a1\u05a3-\u05b9' +
+  '\u05bb-\u05bd\u05bf\u05c1-\u05c2\u05c4\u064b-\u0652\u0670\u06d6-\u06dc' +
+  '\u06dd-\u06df\u06e0-\u06e4\u06e7-\u06e8\u06ea-\u06ed\u0901-\u0903\u093c' +
+  '\u093e-\u094c\u094d\u0951-\u0954\u0962-\u0963\u0981-\u0983\u09bc\u09be' +
+  '\u09bf\u09c0-\u09c4\u09c7-\u09c8\u09cb-\u09cd\u09d7\u09e2-\u09e3\u0a02' +
+  '\u0a3c\u0a3e\u0a3f\u0a40-\u0a42\u0a47-\u0a48\u0a4b-\u0a4d\u0a70-\u0a71' +
+  '\u0a81-\u0a83\u0abc\u0abe-\u0ac5\u0ac7-\u0ac9\u0acb-\u0acd\u0b01-\u0b03' +
+  '\u0b3c\u0b3e-\u0b43\u0b47-\u0b48\u0b4b-\u0b4d\u0b56-\u0b57\u0b82-\u0b83' +
+  '\u0bbe-\u0bc2\u0bc6-\u0bc8\u0bca-\u0bcd\u0bd7\u0c01-\u0c03\u0c3e-\u0c44' +
+  '\u0c46-\u0c48\u0c4a-\u0c4d\u0c55-\u0c56\u0c82-\u0c83\u0cbe-\u0cc4' +
+  '\u0cc6-\u0cc8\u0cca-\u0ccd\u0cd5-\u0cd6\u0d02-\u0d03\u0d3e-\u0d43' +
+  '\u0d46-\u0d48\u0d4a-\u0d4d\u0d57\u0e31\u0e34-\u0e3a\u0e47-\u0e4e\u0eb1' +
+  '\u0eb4-\u0eb9\u0ebb-\u0ebc\u0ec8-\u0ecd\u0f18-\u0f19\u0f35\u0f37\u0f39' +
+  '\u0f3e\u0f3f\u0f71-\u0f84\u0f86-\u0f8b\u0f90-\u0f95\u0f97\u0f99-\u0fad' +
+  '\u0fb1-\u0fb7\u0fb9\u20d0-\u20dc\u20e1\u302a-\u302f\u3099\u309a' :
+  '';
+var XML10_DIGIT = (REGEXP_UNICODE) ?
+  '\u0030-\u0039\u0660-\u0669\u06f0-\u06f9\u0966-\u096f\u09e6-\u09ef' +
+  '\u0a66-\u0a6f\u0ae6-\u0aef\u0b66-\u0b6f\u0be7-\u0bef\u0c66-\u0c6f' +
+  '\u0ce6-\u0cef\u0d66-\u0d6f\u0e50-\u0e59\u0ed0-\u0ed9\u0f20-\u0f29' :
+  '0-9';
+var XML10_EXTENDER = (REGEXP_UNICODE) ?
+  '\u00b7\u02d0\u02d1\u0387\u0640\u0e46\u0ec6\u3005\u3031-\u3035' +
+  '\u309d-\u309e\u30fc-\u30fe' :
+  '';
+var XML10_LETTER = XML10_BASE_CHAR + XML10_IDEOGRAPHIC;
+var XML10_NAME_CHAR = XML10_LETTER + XML10_DIGIT + '\\._:' +
+                      XML10_COMBINING_CHAR + XML10_EXTENDER + '-';
+var XML10_NAME = '[' + XML10_LETTER + '_:][' + XML10_NAME_CHAR + ']*';
+
+var XML10_ENTITY_REF = '&amp;' + XML10_NAME + ';';
+var XML10_REFERENCE = XML10_ENTITY_REF + '|' + XML_CHAR_REF;
+var XML10_ATT_VALUE = '&quot;(([^&lt;&amp;&quot;]|' + XML10_REFERENCE + ')*)&quot;|' +
+                      &quot;'(([^&lt;&amp;']|&quot; + XML10_REFERENCE + &quot;)*)'&quot;;
+var XML10_ATTRIBUTE =
+  '(' + XML10_NAME + ')' + XML_EQ + '(' + XML10_ATT_VALUE + ')';
+
+// XML 1.1 tokens.
+// TODO(jtakagi): NameStartChar also includes \u10000-\ueffff.
+// ECMAScript Language Specifiction defines UnicodeEscapeSequence as
+// &quot;\u HexDigit HexDigit HexDigit HexDigit&quot; and we may need to use
+// surrogate pairs, but any browser doesn't support surrogate paris in
+// character classes of regular expression, so avoid including them for now.
+
+var XML11_VERSION_INFO = XML_S + 'version' + XML_EQ + '(&quot;1\\.1&quot;|' + &quot;'1\\.1')&quot;;
+var XML11_NAME_START_CHAR = (REGEXP_UNICODE) ?
+  ':A-Z_a-z\u00c0-\u00d6\u00d8-\u00f6\u00f8-\u02ff\u0370-\u037d' +
+  '\u037f-\u1fff\u200c-\u200d\u2070-\u218f\u2c00-\u2fef\u3001-\ud7ff' +
+  '\uf900-\ufdcf\ufdf0-\ufffd' :
+  ':A-Z_a-z';
+var XML11_NAME_CHAR = XML11_NAME_START_CHAR +
+  ((REGEXP_UNICODE) ? '\\.0-9\u00b7\u0300-\u036f\u203f-\u2040-' : '\\.0-9-');
+var XML11_NAME = '[' + XML11_NAME_START_CHAR + '][' + XML11_NAME_CHAR + ']*';
+
+var XML11_ENTITY_REF = '&amp;' + XML11_NAME + ';';
+var XML11_REFERENCE = XML11_ENTITY_REF + '|' + XML_CHAR_REF;
+var XML11_ATT_VALUE = '&quot;(([^&lt;&amp;&quot;]|' + XML11_REFERENCE + ')*)&quot;|' +
+                      &quot;'(([^&lt;&amp;']|&quot; + XML11_REFERENCE + &quot;)*)'&quot;;
+var XML11_ATTRIBUTE =
+  '(' + XML11_NAME + ')' + XML_EQ + '(' + XML11_ATT_VALUE + ')';
+
+// XML Namespace tokens.
+// Used in XML parser and XPath parser.
+
+var XML_NC_NAME_CHAR = XML10_LETTER + XML10_DIGIT + '\\._' +
+                       XML10_COMBINING_CHAR + XML10_EXTENDER + '-';
+var XML_NC_NAME = '[' + XML10_LETTER + '_][' + XML_NC_NAME_CHAR + ']*';
+
+
+// Code from dom.js.
+//
+// Based on &lt;http://www.w3.org/TR/2000/REC-DOM-Level-2-Core-20001113/
+// core.html#ID-1950641247&gt;
+var DOM_ELEMENT_NODE = 1;
+var DOM_ATTRIBUTE_NODE = 2;
+var DOM_TEXT_NODE = 3;
+var DOM_CDATA_SECTION_NODE = 4;
+var DOM_ENTITY_REFERENCE_NODE = 5;
+var DOM_ENTITY_NODE = 6;
+var DOM_PROCESSING_INSTRUCTION_NODE = 7;
+var DOM_COMMENT_NODE = 8;
+var DOM_DOCUMENT_NODE = 9;
+var DOM_DOCUMENT_TYPE_NODE = 10;
+var DOM_DOCUMENT_FRAGMENT_NODE = 11;
+var DOM_NOTATION_NODE = 12;
+
+// Code from util.js.
+//
+// Copyright 2005 Google
+//
+// Author: Steffen Meschkat &lt;mesch@google.com&gt;
+//
+// Miscellaneous utility and placeholder functions.
+
+// Dummy implmentation for the logging functions. Replace by something
+// useful when you want to debug.
+function xpathLog(msg) {};
+function xsltLog(msg) {};
+function xsltLogXml(msg) {};
+
+// Throws an exception if false.
+function assert(b) {
+  if (!b) {
+    throw &quot;Assertion failed&quot;;
+  }
+}
+
+// Splits a string s at all occurrences of character c. This is like
+// the split() method of the string object, but IE omits empty
+// strings, which violates the invariant (s.split(x).join(x) == s).
+function stringSplit(s, c) {
+  var a = s.indexOf(c);
+  if (a == -1) {
+    return [ s ];
+  }
+  var parts = [];
+  parts.push(s.substr(0,a));
+  while (a != -1) {
+    var a1 = s.indexOf(c, a + 1);
+    if (a1 != -1) {
+      parts.push(s.substr(a + 1, a1 - a - 1));
+    } else {
+      parts.push(s.substr(a + 1));
+    }
+    a = a1;
+  }
+  return parts;
+}
+
+// Applies the given function to each element of the array, preserving
+// this, and passing the index.
+function mapExec(array, func) {
+  for (var i = 0; i &lt; array.length; ++i) {
+    func.call(this, array[i], i);
+  }
+}
+
+// Returns an array that contains the return value of the given
+// function applied to every element of the input array.
+function mapExpr(array, func) {
+  var ret = [];
+  for (var i = 0; i &lt; array.length; ++i) {
+    ret.push(func(array[i]));
+  }
+  return ret;
+};
+
+// Reverses the given array in place.
+function reverseInplace(array) {
+  for (var i = 0; i &lt; array.length / 2; ++i) {
+    var h = array[i];
+    var ii = array.length - i - 1;
+    array[i] = array[ii];
+    array[ii] = h;
+  }
+}
+
+// Removes value from array. Returns the number of instances of value
+// that were removed from array.
+function removeFromArray(array, value, opt_notype) {
+  var shift = 0;
+  for (var i = 0; i &lt; array.length; ++i) {
+    if (array[i] === value || (opt_notype &amp;&amp; array[i] == value)) {
+      array.splice(i--, 1);
+      shift++;
+    }
+  }
+  return shift;
+}
+
+// Shallow-copies an array.
+function copyArray(dst, src) {
+  for (var i = 0; i &lt; src.length; ++i) {
+    dst.push(src[i]);
+  }
+}
+
+// Returns the text value of a node; for nodes without children this
+// is the nodeValue, for nodes with children this is the concatenation
+// of the value of all children.
+function xmlValue(node) {
+  if (!node) {
+    return '';
+  }
+
+  var ret = '';
+  if (node.nodeType == DOM_TEXT_NODE ||
+      node.nodeType == DOM_CDATA_SECTION_NODE ||
+      node.nodeType == DOM_ATTRIBUTE_NODE) {
+    ret += node.nodeValue;
+
+  } else if (node.nodeType == DOM_ELEMENT_NODE ||
+             node.nodeType == DOM_DOCUMENT_NODE ||
+             node.nodeType == DOM_DOCUMENT_FRAGMENT_NODE) {
+    for (var i = 0; i &lt; node.childNodes.length; ++i) {
+      ret += arguments.callee(node.childNodes[i]);
+    }
+  }
+  return ret;
+}
+
+// Code from xpath.js.
+//
+// Copyright 2005 Google Inc.
+// All Rights Reserved
 //
 // An XPath parser and evaluator written in JavaScript. The
 // implementation is complete except for functions handling
@@ -61,9 +320,6 @@
 // that are used internally follow after them.
 //
 //
-// TODO(mesch): add jsdoc comments. Use more coherent naming.
-//
-//
 // Author: Steffen Meschkat &lt;mesch@google.com&gt;
 
 
@@ -74,16 +330,12 @@
 // expression context.
 
 function xpathParse(expr) {
-  if (xpathdebug) {
-    Log.write('XPath parse ' + expr);
-  }
+  xpathLog('parse ' + expr);
   xpathParseInit();
 
   var cached = xpathCacheLookup(expr);
   if (cached) {
-    if (xpathdebug) {
-      Log.write(' ... cached');
-    }
+    xpathLog(' ... cached');
     return cached;
   }
 
@@ -92,29 +344,22 @@ function xpathParse(expr) {
   // ($address), numbers (4), multi-step path expressions where each
   // step is a plain element node test
   // (page/overlay/locations/location).
-  
+
   if (expr.match(/^(\$|@)?\w+$/i)) {
     var ret = makeSimpleExpr(expr);
     xpathParseCache[expr] = ret;
-    if (xpathdebug) {
-      Log.write(' ... simple');
-    }
+    xpathLog(' ... simple');
     return ret;
   }
 
   if (expr.match(/^\w+(\/\w+)*$/i)) {
     var ret = makeSimpleExpr2(expr);
     xpathParseCache[expr] = ret;
-    if (xpathdebug) {
-      Log.write(' ... simple 2');
-    }
+    xpathLog(' ... simple 2');
     return ret;
   }
 
   var cachekey = expr; // expr is modified during parse
-  if (xpathdebug) {
-    Timer.start('XPath parse', cachekey);
-  }
 
   var stack = [];
   var ahead = null;
@@ -124,7 +369,7 @@ function xpathParse(expr) {
   var parse_count = 0;
   var lexer_count = 0;
   var reduce_count = 0;
-  
+
   while (!done) {
     parse_count++;
     expr = expr.replace(/^\s*/, '');
@@ -158,24 +403,22 @@ function xpathParse(expr) {
     // and not explicitly set.
 
     if (rule &amp;&amp;
-        (rule == TOK_DIV || 
+        (rule == TOK_DIV ||
          rule == TOK_MOD ||
-         rule == TOK_AND || 
+         rule == TOK_AND ||
          rule == TOK_OR) &amp;&amp;
-        (!previous || 
-         previous.tag == TOK_AT || 
-         previous.tag == TOK_DSLASH || 
+        (!previous ||
+         previous.tag == TOK_AT ||
+         previous.tag == TOK_DSLASH ||
          previous.tag == TOK_SLASH ||
-         previous.tag == TOK_AXIS || 
+         previous.tag == TOK_AXIS ||
          previous.tag == TOK_DOLLAR)) {
       rule = TOK_QNAME;
     }
 
     if (rule) {
       expr = expr.substr(match.length);
-      if (xpathdebug) {
-        Log.write('token: ' + match + ' -- ' + rule.label);
-      }
+      xpathLog('token: ' + match + ' -- ' + rule.label);
       ahead = {
         tag: rule,
         match: match,
@@ -184,23 +427,17 @@ function xpathParse(expr) {
       };
 
     } else {
-      if (xpathdebug) {
-        Log.write('DONE');
-      }
+      xpathLog('DONE');
       done = true;
     }
 
     while (xpathReduce(stack, ahead)) {
       reduce_count++;
-      if (xpathdebug) {
-        Log.write('stack: ' + stackToString(stack));
-      }
+      xpathLog('stack: ' + stackToString(stack));
     }
   }
 
-  if (xpathdebug) {
-    Log.write(stackToString(stack));
-  }
+  xpathLog('stack: ' + stackToString(stack));
 
   if (stack.length != 1) {
     throw 'XPath parse error ' + cachekey + ':\n' + stackToString(stack);
@@ -209,14 +446,8 @@ function xpathParse(expr) {
   var result = stack[0].expr;
   xpathParseCache[cachekey] = result;
 
-  if (xpathdebug) {
-    Timer.end('XPath parse', cachekey);
-  }
-
-  if (xpathdebug) {
-    Log.write('XPath parse: ' + parse_count + ' / ' + 
-              lexer_count + ' / ' + reduce_count);
-  }
+  xpathLog('XPath parse: ' + parse_count + ' / ' +
+           lexer_count + ' / ' + reduce_count);
 
   return result;
 }
@@ -252,18 +483,16 @@ function xpathReduce(stack, ahead) {
   }
 
   var ret;
-  if (cand &amp;&amp; (!ahead || cand.prec &gt; ahead.prec || 
+  if (cand &amp;&amp; (!ahead || cand.prec &gt; ahead.prec ||
                (ahead.tag.left &amp;&amp; cand.prec &gt;= ahead.prec))) {
     for (var i = 0; i &lt; cand.match.matchlength; ++i) {
       stack.pop();
     }
 
-    if (xpathdebug) {
-      Log.write('reduce ' + cand.tag.label + ' ' + cand.prec +
-                ' ahead ' + (ahead ? ahead.tag.label + ' ' + ahead.prec + 
-                             (ahead.tag.left ? ' left' : '')
-                             : ' none '));
-    }
+    xpathLog('reduce ' + cand.tag.label + ' ' + cand.prec +
+             ' ahead ' + (ahead ? ahead.tag.label + ' ' + ahead.prec +
+                          (ahead.tag.left ? ' left' : '')
+                          : ' none '));
 
     var matchexpr = mapExpr(cand.match, function(m) { return m.expr; });
     cand.expr = cand.rule[3].apply(null, matchexpr);
@@ -273,12 +502,10 @@ function xpathReduce(stack, ahead) {
 
   } else {
     if (ahead) {
-      if (xpathdebug) {
-        Log.write('shift ' + ahead.tag.label + ' ' + ahead.prec + 
-                  (ahead.tag.left ? ' left' : '') +
-                  ' over ' + (cand ? cand.tag.label + ' ' + 
-                              cand.prec : ' none'));
-      }
+      xpathLog('shift ' + ahead.tag.label + ' ' + ahead.prec +
+               (ahead.tag.left ? ' left' : '') +
+               ' over ' + (cand ? cand.tag.label + ' ' +
+                           cand.prec : ' none'));
       stack.push(ahead);
     }
     ret = false;
@@ -428,25 +655,35 @@ function stackToString(stack) {
 //
 //   getVariable(name) -- what the name says.
 //
-//   setNode(node, position) -- sets the context to the new node and
-//   its corresponding position. Needed to implement scoping rules for
-//   variables in XPath. (A variable is visible to all subsequent
-//   siblings, not only to its children.)
+//   setNode(position) -- sets the context to the node at the given
+//   position. Needed to implement scoping rules for variables in
+//   XPath. (A variable is visible to all subsequent siblings, not
+//   only to its children.)
 
-function ExprContext(node, position, nodelist, parent) {
+function ExprContext(node, opt_position, opt_nodelist, opt_parent) {
   this.node = node;
-  this.position = position || 0;
-  this.nodelist = nodelist || [ node ];
+  this.position = opt_position || 0;
+  this.nodelist = opt_nodelist || [ node ];
   this.variables = {};
-  this.parent = parent || null;
-  this.root = parent ? parent.root : node.ownerDocument;
+  this.parent = opt_parent || null;
+  if (opt_parent) {
+    this.root = opt_parent.root;
+  } else if (this.node.nodeType == DOM_DOCUMENT_NODE) {
+    // NOTE(mesch): DOM Spec stipulates that the ownerDocument of a
+    // document is null. Our root, however is the document that we are
+    // processing, so the initial context is created from its document
+    // node, which case we must handle here explcitly.
+    this.root = node;
+  } else {
+    this.root = node.ownerDocument;
+  }
 }
 
-ExprContext.prototype.clone = function(node, position, nodelist) {
-  return new
-  ExprContext(node || this.node,
-              typeof position != 'undefined' ? position : this.position,
-              nodelist || this.nodelist, this);
+ExprContext.prototype.clone = function(opt_node, opt_position, opt_nodelist) {
+  return new ExprContext(
+      opt_node || this.node,
+      typeof opt_position != 'undefined' ? opt_position : this.position,
+      opt_nodelist || this.nodelist, this);
 };
 
 ExprContext.prototype.setVariable = function(name, value) {
@@ -463,12 +700,16 @@ ExprContext.prototype.getVariable = function(name) {
   } else {
     return null;
   }
-}
+};
 
-ExprContext.prototype.setNode = function(node, position) {
-  this.node = node;
+ExprContext.prototype.setNode = function(position) {
+  this.node = this.nodelist[position];
   this.position = position;
-}
+};
+
+ExprContext.prototype.contextSize = function() {
+  return this.nodelist.length;
+};
 
 
 // XPath expression values. They are what XPath expressions evaluate
@@ -511,19 +752,19 @@ function StringValue(value) {
 
 StringValue.prototype.stringValue = function() {
   return this.value;
-}
+};
 
 StringValue.prototype.booleanValue = function() {
   return this.value.length &gt; 0;
-}
+};
 
 StringValue.prototype.numberValue = function() {
   return this.value - 0;
-}
+};
 
 StringValue.prototype.nodeSetValue = function() {
-  throw this + ' ' + Error().stack;
-}
+  throw this;
+};
 
 function BooleanValue(value) {
   this.value = value;
@@ -532,19 +773,19 @@ function BooleanValue(value) {
 
 BooleanValue.prototype.stringValue = function() {
   return '' + this.value;
-}
+};
 
 BooleanValue.prototype.booleanValue = function() {
   return this.value;
-}
+};
 
 BooleanValue.prototype.numberValue = function() {
   return this.value ? 1 : 0;
-}
+};
 
 BooleanValue.prototype.nodeSetValue = function() {
-  throw this + ' ' + Error().stack;
-}
+  throw this;
+};
 
 function NumberValue(value) {
   this.value = value;
@@ -553,19 +794,19 @@ function NumberValue(value) {
 
 NumberValue.prototype.stringValue = function() {
   return '' + this.value;
-}
+};
 
 NumberValue.prototype.booleanValue = function() {
   return !!this.value;
-}
+};
 
 NumberValue.prototype.numberValue = function() {
   return this.value - 0;
-}
+};
 
 NumberValue.prototype.nodeSetValue = function() {
-  throw this + ' ' + Error().stack;
-}
+  throw this;
+};
 
 function NodeSetValue(value) {
   this.value = value;
@@ -578,15 +819,15 @@ NodeSetValue.prototype.stringValue = function() {
   } else {
     return xmlValue(this.value[0]);
   }
-}
+};
 
 NodeSetValue.prototype.booleanValue = function() {
   return this.value.length &gt; 0;
-}
+};
 
 NodeSetValue.prototype.numberValue = function() {
   return this.stringValue() - 0;
-}
+};
 
 NodeSetValue.prototype.nodeSetValue = function() {
   return this.value;
@@ -624,7 +865,7 @@ function LocationExpr() {
 
 LocationExpr.prototype.appendStep = function(s) {
   this.steps.push(s);
-}
+};
 
 LocationExpr.prototype.prependStep = function(s) {
   var steps0 = this.steps;
@@ -662,15 +903,15 @@ function xPathStep(nodes, steps, step, input, ctx) {
   }
 }
 
-function StepExpr(axis, nodetest, predicate) {
+function StepExpr(axis, nodetest, opt_predicate) {
   this.axis = axis;
   this.nodetest = nodetest;
-  this.predicate = predicate || [];
+  this.predicate = opt_predicate || [];
 }
 
 StepExpr.prototype.appendPredicate = function(p) {
   this.predicate.push(p);
-}
+};
 
 StepExpr.prototype.evaluate = function(ctx) {
   var input = ctx.node;
@@ -682,12 +923,12 @@ StepExpr.prototype.evaluate = function(ctx) {
 
   if (this.axis ==  xpathAxis.ANCESTOR_OR_SELF) {
     nodelist.push(input);
-    for (var n = input.parentNode; n; n = input.parentNode) {
+    for (var n = input.parentNode; n; n = n.parentNode) {
       nodelist.push(n);
     }
 
   } else if (this.axis == xpathAxis.ANCESTOR) {
-    for (var n = input.parentNode; n; n = input.parentNode) {
+    for (var n = input.parentNode; n; n = n.parentNode) {
       nodelist.push(n);
     }
 
@@ -705,7 +946,7 @@ StepExpr.prototype.evaluate = function(ctx) {
     xpathCollectDescendants(nodelist, input);
 
   } else if (this.axis == xpathAxis.FOLLOWING) {
-    for (var n = input.parentNode; n; n = n.parentNode) {
+    for (var n = input; n; n = n.parentNode) {
       for (var nn = n.nextSibling; nn; nn = nn.nextSibling) {
         nodelist.push(nn);
         xpathCollectDescendants(nodelist, nn);
@@ -713,7 +954,7 @@ StepExpr.prototype.evaluate = function(ctx) {
     }
 
   } else if (this.axis == xpathAxis.FOLLOWING_SIBLING) {
-    for (var n = input.nextSibling; n; n = input.nextSibling) {
+    for (var n = input.nextSibling; n; n = n.nextSibling) {
       nodelist.push(n);
     }
 
@@ -726,7 +967,7 @@ StepExpr.prototype.evaluate = function(ctx) {
     }
 
   } else if (this.axis == xpathAxis.PRECEDING) {
-    for (var n = input.parentNode; n; n = n.parentNode) {
+    for (var n = input; n; n = n.parentNode) {
       for (var nn = n.previousSibling; nn; nn = nn.previousSibling) {
         nodelist.push(nn);
         xpathCollectDescendantsReverse(nodelist, nn);
@@ -734,7 +975,7 @@ StepExpr.prototype.evaluate = function(ctx) {
     }
 
   } else if (this.axis == xpathAxis.PRECEDING_SIBLING) {
-    for (var n = input.previousSibling; n; n = input.previousSibling) {
+    for (var n = input.previousSibling; n; n = n.previousSibling) {
       nodelist.push(n);
     }
 
@@ -778,23 +1019,25 @@ NodeTestAny.prototype.evaluate = function(ctx) {
   return this.value;
 };
 
-function NodeTestElement() {}
+function NodeTestElementOrAttribute() {}
 
-NodeTestElement.prototype.evaluate = function(ctx) {
-  return new BooleanValue(ctx.node.nodeType == DOM_ELEMENT_NODE);
-}
+NodeTestElementOrAttribute.prototype.evaluate = function(ctx) {
+  return new BooleanValue(
+      ctx.node.nodeType == DOM_ELEMENT_NODE ||
+      ctx.node.nodeType == DOM_ATTRIBUTE_NODE);
+};
 
 function NodeTestText() {}
 
 NodeTestText.prototype.evaluate = function(ctx) {
   return new BooleanValue(ctx.node.nodeType == DOM_TEXT_NODE);
-}
+};
 
 function NodeTestComment() {}
 
 NodeTestComment.prototype.evaluate = function(ctx) {
   return new BooleanValue(ctx.node.nodeType == DOM_COMMENT_NODE);
-}
+};
 
 function NodeTestPI(target) {
   this.target = target;
@@ -804,7 +1047,7 @@ NodeTestPI.prototype.evaluate = function(ctx) {
   return new
   BooleanValue(ctx.node.nodeType == DOM_PROCESSING_INSTRUCTION_NODE &amp;&amp;
                (!this.target || ctx.node.nodeName == this.target));
-}
+};
 
 function NodeTestNC(nsprefix) {
   this.regex = new RegExp(&quot;^&quot; + nsprefix + &quot;:&quot;);
@@ -814,7 +1057,7 @@ function NodeTestNC(nsprefix) {
 NodeTestNC.prototype.evaluate = function(ctx) {
   var n = ctx.node;
   return new BooleanValue(this.regex.match(n.nodeName));
-}
+};
 
 function NodeTestName(name) {
   this.name = name;
@@ -823,7 +1066,7 @@ function NodeTestName(name) {
 NodeTestName.prototype.evaluate = function(ctx) {
   var n = ctx.node;
   return new BooleanValue(n.nodeName == this.name);
-}
+};
 
 function PredicateExpr(expr) {
   this.expr = expr;
@@ -856,7 +1099,7 @@ FunctionCallExpr.prototype.evaluate = function(ctx) {
   if (f) {
     return f.call(this, ctx);
   } else {
-    Log.write('XPath NO SUCH FUNCTION ' + fn);
+    xpathLog('XPath NO SUCH FUNCTION ' + fn);
     return new BooleanValue(false);
   }
 };
@@ -865,7 +1108,7 @@ FunctionCallExpr.prototype.xpathfunctions = {
   'last': function(ctx) {
     assert(this.args.length == 0);
     // NOTE(mesch): XPath position starts at 1.
-    return new NumberValue(ctx.nodelist.length);
+    return new NumberValue(ctx.contextSize());
   },
 
   'position': function(ctx) {
@@ -882,19 +1125,20 @@ FunctionCallExpr.prototype.xpathfunctions = {
 
   'id': function(ctx) {
     assert(this.args.length == 1);
-    var e = this.args.evaluate(ctx);
+    var e = this.args[0].evaluate(ctx);
     var ret = [];
     var ids;
     if (e.type == 'node-set') {
       ids = [];
-      for (var i = 0; i &lt; e.length; ++i) {
-        var v = xmlValue(e[i]).split(/\s+/);
+      var en = e.nodeSetValue();
+      for (var i = 0; i &lt; en.length; ++i) {
+        var v = xmlValue(en[i]).split(/\s+/);
         for (var ii = 0; ii &lt; v.length; ++ii) {
           ids.push(v[ii]);
         }
       }
     } else {
-      ids = e.split(/\s+/);
+      ids = e.stringValue().split(/\s+/);
     }
     var d = ctx.node.ownerDocument;
     for (var i = 0; i &lt; ids.length; ++i) {
@@ -1154,19 +1398,10 @@ FunctionCallExpr.prototype.xpathfunctions = {
     }
   },
 
-  'ext-sprintf': function(ctx) {
-    assert(this.args.length &gt;= 1);
-    var args = [];
-    for (var i = 0; i &lt; this.args.length; ++i) {
-      args.push(this.args[i].evaluate(ctx).stringValue());
-    }
-    return new StringValue(sprintf.apply(null, args));
-  },
-
   // ext-cardinal() evaluates its single argument as a number, and
   // returns the current node that many times. It can be used in the
   // select attribute to iterate over an integer range.
-  
+
   'ext-cardinal': function(ctx) {
     assert(this.args.length &gt;= 1);
     var c = this.args[0].evaluate(ctx).numberValue();
@@ -1188,16 +1423,19 @@ UnionExpr.prototype.evaluate = function(ctx) {
   var nodes2 = this.expr2.evaluate(ctx).nodeSetValue();
   var I1 = nodes1.length;
   for (var i2 = 0; i2 &lt; nodes2.length; ++i2) {
+    var n = nodes2[i2];
+    var inBoth = false;
     for (var i1 = 0; i1 &lt; I1; ++i1) {
-      if (nodes1[i1] == nodes2[i2]) {
-        // break inner loop and continue outer loop, labels confuse
-        // the js compiler, so we don't use them here.
-        i1 = I1;
+      if (nodes1[i1] == n) {
+        inBoth = true;
+        i1 = I1; // break inner loop
       }
     }
-    nodes1.push(nodes2[i2]);
+    if (!inBoth) {
+      nodes1.push(n);
+    }
   }
-  return new NodeSetValue(nodes2);
+  return new NodeSetValue(nodes1);
 };
 
 function PathExpr(filter, rel) {
@@ -1236,7 +1474,7 @@ FilterExpr.prototype.evaluate = function(ctx) {
   }
 
   return new NodeSetValue(nodes);
-}
+};
 
 function UnaryMinusExpr(expr) {
   this.expr = expr;
@@ -1410,7 +1648,7 @@ BinaryExpr.prototype.compare = function(ctx, cmp) {
   }
 
   return new BooleanValue(ret);
-}
+};
 
 function LiteralExpr(value) {
   this.value = value;
@@ -1434,7 +1672,7 @@ function VariableExpr(name) {
 
 VariableExpr.prototype.evaluate = function(ctx) {
   return ctx.getVariable(this.name);
-}
+};
 
 // Factory functions for semantic values (i.e. Expressions) of the
 // productions in the grammar. When a production is matched to reduce
@@ -1533,7 +1771,7 @@ function makeAbbrevStep(abbrev) {
 }
 
 function makeNodeTestExpr1(asterisk) {
-  return new NodeTestElement;
+  return new NodeTestElementOrAttribute;
 }
 
 function makeNodeTestExpr2(ncname, colon, asterisk) {
@@ -1557,14 +1795,14 @@ function makeNodeTestExpr4(typeo, parenc) {
     return new NodeTestComment;
 
   case 'processing-instruction':
-    return new NodeTestPI;
+    return new NodeTestPI('');
   }
 }
 
 function makeNodeTestExpr5(typeo, target, parenc) {
   var type = typeo.replace(/\s*\($/, '');
   if (type != 'processing-instruction') {
-    throw type + ' ' + Error().stack;
+    throw type;
   }
   return new NodeTestPI(target.value);
 }
@@ -1660,9 +1898,9 @@ function makeSimpleExpr(expr) {
 }
 
 function makeSimpleExpr2(expr) {
-  var steps = expr.split('/');
+  var steps = stringSplit(expr, '/');
   var c = new LocationExpr();
-  for (var i = 0; i &lt; steps.length; i++) {
+  for (var i = 0; i &lt; steps.length; ++i) {
     var a = new NodeTestName(steps[i]);
     var b = new StepExpr('child', a);
     c.appendStep(b);
@@ -1745,7 +1983,7 @@ var TOK_BRACKO = { label: &quot;[&quot;,   prec:   32, re: new RegExp(&quot;^\\[&quot;) };
 var TOK_BRACKC = { label: &quot;]&quot;,               re: new RegExp(&quot;^\\]&quot;) };
 var TOK_DOLLAR = { label: &quot;$&quot;,               re: new RegExp(&quot;^\\$&quot;) };
 
-var TOK_NCNAME = { label: &quot;[ncname]&quot;, re: new RegExp('^[a-z][-\\w]*','i') };
+var TOK_NCNAME = { label: &quot;[ncname]&quot;, re: new RegExp('^' + XML_NC_NAME) };
 
 var TOK_ASTERISK = { label: &quot;*&quot;, prec: 15, re: new RegExp(&quot;^\\*&quot;), left: true };
 var TOK_LITERALQ = { label: &quot;[litq]&quot;, prec: 20, re: new RegExp(&quot;^'[^\\']*'&quot;) };
@@ -1762,7 +2000,7 @@ var TOK_NUMBER  = {
 
 var TOK_QNAME = {
   label: &quot;[qname]&quot;,
-  re: new RegExp('^([a-z][-\\w]*:)?[a-z][-\\w]*','i')
+  re: new RegExp('^(' + XML_NC_NAME + ':)?' + XML_NC_NAME)
 };
 
 var TOK_NODEO = {
@@ -1892,7 +2130,7 @@ var xpathGrammarRules =
    [ XPathLocationPath, [ XPathAbsoluteLocationPath ], 18,
      passExpr ],
 
-   [ XPathAbsoluteLocationPath, [ TOK_SLASH, XPathRelativeLocationPath ], 18, 
+   [ XPathAbsoluteLocationPath, [ TOK_SLASH, XPathRelativeLocationPath ], 18,
      makeLocationExpr1 ],
    [ XPathAbsoluteLocationPath, [ TOK_DSLASH, XPathRelativeLocationPath ], 18,
      makeLocationExpr2 ],
@@ -1964,11 +2202,11 @@ var xpathGrammarRules =
    [ XPathUnionExpr, [ XPathUnionExpr, TOK_PIPE, XPathPathExpr ], 20,
      makeUnionExpr ],
 
-   [ XPathPathExpr, [ XPathLocationPath ], 20, 
-     passExpr ], 
-   [ XPathPathExpr, [ XPathFilterExpr ], 19, 
-     passExpr ], 
-   [ XPathPathExpr, 
+   [ XPathPathExpr, [ XPathLocationPath ], 20,
+     passExpr ],
+   [ XPathPathExpr, [ XPathFilterExpr ], 19,
+     passExpr ],
+   [ XPathPathExpr,
      [ XPathFilterExpr, TOK_SLASH, XPathRelativeLocationPath ], 20,
      makePathExpr1 ],
    [ XPathPathExpr,
@@ -1976,7 +2214,7 @@ var xpathGrammarRules =
      makePathExpr2 ],
 
    [ XPathFilterExpr, [ XPathPrimaryExpr, XPathPredicate, Q_MM ], 20,
-     makeFilterExpr ], 
+     makeFilterExpr ],
 
    [ XPathExpr, [ XPathPrimaryExpr ], 16,
      passExpr ],
@@ -2065,8 +2303,7 @@ function xpathParseInit() {
     xpathTokenRules[i].key = k++;
   }
 
-  if (xpathdebug)
-  Log.write('XPath parse INIT: ' + k + ' rules');
+  xpathLog('XPath parse INIT: ' + k + ' rules');
 
   // Another slight optimization: sort the rules into bins according
   // to the last element (observing quantifiers), so we can restrict
@@ -2092,7 +2329,7 @@ function xpathParseInit() {
       if (pattern[j] == Q_1M) {
         push_(xpathRules, pattern[j-1].key, rule);
         break;
-        
+
       } else if (pattern[j] == Q_MM || pattern[j] == Q_01) {
         push_(xpathRules, pattern[j-1].key, rule);
         --j;
@@ -2104,18 +2341,17 @@ function xpathParseInit() {
     }
   }
 
-  if (xpathdebug)
-  Log.write('XPath parse INIT: ' + xpathRules.length + ' rule bins');
-  
+  xpathLog('XPath parse INIT: ' + xpathRules.length + ' rule bins');
+
   var sum = 0;
   mapExec(xpathRules, function(i) {
     if (i) {
       sum += i.length;
     }
   });
-  
-  if (xpathdebug)
-  Log.write('XPath parse INIT: ' + (sum / xpathRules.length) + ' average bin size');
+
+  xpathLog('XPath parse INIT: ' + (sum / xpathRules.length) +
+           ' average bin size');
 }
 
 // Local utility functions that are used by the lexer or parser.
@@ -2152,11 +2388,11 @@ function xpathSort(input, sort) {
 
   var sortlist = [];
 
-  for (var i = 0; i &lt; input.nodelist.length; ++i) {
+  for (var i = 0; i &lt; input.contextSize(); ++i) {
     var node = input.nodelist[i];
     var sortitem = { node: node, key: [] };
     var context = input.clone(node, 0, [ node ]);
-    
+
     for (var j = 0; j &lt; sort.length; ++j) {
       var s = sort[j];
       var value = s.expr.evaluate(context);
@@ -2185,7 +2421,7 @@ function xpathSort(input, sort) {
     nodes.push(sortlist[i].node);
   }
   input.nodelist = nodes;
-  input.setNode(nodes[0], 0);
+  input.setNode(0);
 }
 
 
@@ -2196,7 +2432,6 @@ function xpathSort(input, sort) {
 // NOTE: In browsers which do not follow the spec, this breaks only in
 // the case that numbers should be sorted as strings, which is very
 // uncommon.
-
 function xpathSortByKey(v1, v2) {
   // NOTE: Sort key vectors of different length never occur in
   // xsltSort.
@@ -2214,286 +2449,10 @@ function xpathSortByKey(v1, v2) {
 }
 
 
-// Copyright (c) 2005, Google Inc.
-// All rights reserved.
-// 
-// Redistribution and use in source and binary forms, with or without
-// modification, are permitted provided that the following conditions are
-// met:
-//         
-//  * Redistributions of source code must retain the above copyright
-//    notice, this list of conditions and the following disclaimer.
-// 
-//  * Redistributions in binary form must reproduce the above copyright
-//    notice, this list of conditions and the following disclaimer in the
-//    documentation and/or other materials provided with the
-//    distribution.
-// 
-//  * Neither the name of Google Inc. nor the names of its contributors
-//    may be used to endorse or promote products derived from this
-//    software without specific prior written permission.
-// 
-// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
-// &quot;AS IS&quot; AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
-// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
-// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
-// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
-// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
-// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
-// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
-// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
-// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
-// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
-//
-// Miscellania that support the ajaxslt implementation.
-//
-// Author: Steffen Meschkat &lt;mesch@google.com&gt;
-//
-
-function el(i) {
-  return document.getElementById(i);
-}
-
-function px(x) {
-  return x + 'px';
-}
-
-// Split a string s at all occurrences of character c. This is like
-// the split() method of the string object, but IE omits empty
-// strings, which violates the invariant (s.split(x).join(x) == s).
-function stringSplit(s, c) {
-  var a = s.indexOf(c);
-  if (a == -1) {
-    return [ s ];
-  }
-  
-  var parts = [];
-  parts.push(s.substr(0,a));
-  while (a != -1) {
-    var a1 = s.indexOf(c, a + 1);
-    if (a1 != -1) {
-      parts.push(s.substr(a + 1, a1 - a - 1));
-    } else {
-      parts.push(s.substr(a + 1));
-    } 
-    a = a1;
-  }
-
-  return parts;
-}
-
-// Returns the text value if a node; for nodes without children this
-// is the nodeValue, for nodes with children this is the concatenation
-// of the value of all children.
-function xmlValue(node) {
-  if (!node) {
-    return '';
-  }
-
-  var ret = '';
-  if (node.nodeType == DOM_TEXT_NODE ||
-      node.nodeType == DOM_CDATA_SECTION_NODE ||
-      node.nodeType == DOM_ATTRIBUTE_NODE) {
-    ret += node.nodeValue;
-
-  } else if (node.nodeType == DOM_ELEMENT_NODE ||
-             node.nodeType == DOM_DOCUMENT_NODE ||
-             node.nodeType == DOM_DOCUMENT_FRAGMENT_NODE) {
-    for (var i = 0; i &lt; node.childNodes.length; ++i) {
-      ret += arguments.callee(node.childNodes[i]);
-    }
-  }
-  return ret;
-}
-
-// Returns the representation of a node as XML text.
-function xmlText(node) {
-  var ret = '';
-  if (node.nodeType == DOM_TEXT_NODE) {
-    ret += xmlEscapeText(node.nodeValue);
-    
-  } else if (node.nodeType == DOM_ELEMENT_NODE) {
-    ret += '&lt;' + node.nodeName;
-    for (var i = 0; i &lt; node.attributes.length; ++i) {
-      var a = node.attributes[i];
-      if (a &amp;&amp; a.nodeName &amp;&amp; a.nodeValue) {
-        ret += ' ' + a.nodeName;
-        ret += '=&quot;' + xmlEscapeAttr(a.nodeValue) + '&quot;';
-      }
-    }
-
-    if (node.childNodes.length == 0) {
-      ret += '/&gt;';
-
-    } else {
-      ret += '&gt;';
-      for (var i = 0; i &lt; node.childNodes.length; ++i) {
-        ret += arguments.callee(node.childNodes[i]);
-      }
-      ret += '&lt;/' + node.nodeName + '&gt;';
-    }
-    
-  } else if (node.nodeType == DOM_DOCUMENT_NODE || 
-             node.nodeType == DOM_DOCUMENT_FRAGMENT_NODE) {
-    for (var i = 0; i &lt; node.childNodes.length; ++i) {
-      ret += arguments.callee(node.childNodes[i]);
-    }
-  }
-  
-  return ret;
-}
-
-// Applies the given function to each element of the array.
-function mapExec(array, func) {
-  for (var i = 0; i &lt; array.length; ++i) {
-    func(array[i]);
-  }
-}
-
-// Returns an array that contains the return value of the given
-// function applied to every element of the input array.
-function mapExpr(array, func) {
-  var ret = [];
-  for (var i = 0; i &lt; array.length; ++i) {
-    ret.push(func(array[i]));
-  }
+// Parses and then evaluates the given XPath expression in the given
+// input context. Notice that parsed xpath expressions are cached.
+function xpathEval(select, context) {
+  var expr = xpathParse(select);
+  var ret = expr.evaluate(context);
   return ret;
-};
-
-// Reverses the given array in place.
-function reverseInplace(array) {
-  for (var i = 0; i &lt; array.length / 2; ++i) {
-    var h = array[i];
-    var ii = array.length - i - 1;
-    array[i] = array[ii];
-    array[ii] = h;
-  }
 }
-
-// Shallow-copies an array.
-function copyArray(dst, src) { 
-  for (var i = 0; i &lt; src.length; ++i) {
-    dst.push(src[i]);
-  }
-}
-
-function assert(b) {
-  if (!b) {
-    throw 'assertion failed';
-  }
-}
-
-// Based on
-// &lt;http://www.w3.org/TR/2000/REC-DOM-Level-2-Core-20001113/core.html#ID-1950641247&gt;
-var DOM_ELEMENT_NODE = 1;
-var DOM_ATTRIBUTE_NODE = 2;
-var DOM_TEXT_NODE = 3;
-var DOM_CDATA_SECTION_NODE = 4;
-var DOM_ENTITY_REFERENCE_NODE = 5;
-var DOM_ENTITY_NODE = 6;
-var DOM_PROCESSING_INSTRUCTION_NODE = 7;
-var DOM_COMMENT_NODE = 8;
-var DOM_DOCUMENT_NODE = 9;
-var DOM_DOCUMENT_TYPE_NODE = 10;
-var DOM_DOCUMENT_FRAGMENT_NODE = 11;
-var DOM_NOTATION_NODE = 12;
-
-
-var xpathdebug = false; // trace xpath parsing
-var xsltdebug = false; // trace xslt processing
-
-
-// Escape XML special markup chracters: tag delimiter &lt; &gt; and entity
-// reference start delimiter &amp;. The escaped string can be used in XML
-// text portions (i.e. between tags).
-function xmlEscapeText(s) {
-  return s.replace(/&amp;/g, '&amp;amp;').replace(/&lt;/g, '&amp;lt;').replace(/&gt;/g, '&amp;gt;');
-}
-
-// Escape XML special markup characters: tag delimiter &lt; &gt; entity
-// reference start delimiter &amp; and quotes &quot;. The escaped string can be
-// used in double quoted XML attribute value portions (i.e. in
-// attributes within start tags).
-function xmlEscapeAttr(s) {
-  return xmlEscapeText(s).replace(/\&quot;/g, '&amp;quot;');
-}
-
-// Escape markup in XML text, but don't touch entity references. The
-// escaped string can be used as XML text (i.e. between tags).
-function xmlEscapeTags(s) {
-  return s.replace(/&lt;/g, '&amp;lt;').replace(/&gt;/g, '&amp;gt;');
-}
-
-// An implementation of the debug log. 
-
-var logging__ = true;
-
-function Log() {};
-
-Log.lines = [];
-
-Log.write = function(s) {
-  if (logging__) {
-    this.lines.push(xmlEscapeText(s));
-    this.show();
-  }
-};
-
-// Writes the given XML with every tag on a new line.
-Log.writeXML = function(xml) {
-  if (logging__) {
-    var s0 = xml.replace(/&lt;/g, '\n&lt;');
-    var s1 = xmlEscapeText(s0);
-    var s2 = s1.replace(/\s*\n(\s|\n)*/g, '&lt;br/&gt;');
-    this.lines.push(s2);
-    this.show();
-  }
-}
-
-// Writes without any escaping
-Log.writeRaw = function(s) {
-  if (logging__) {
-    this.lines.push(s);
-    this.show();
-  }
-}
-
-Log.clear = function() {
-  if (logging__) {
-    var l = this.div();
-    l.innerHTML = '';
-    this.lines = [];
-  }
-}
-
-Log.show = function() {
-  var l = this.div();
-  l.innerHTML += this.lines.join('&lt;br/&gt;') + '&lt;br/&gt;';
-  this.lines = [];
-  l.scrollTop = l.scrollHeight;
-}
-
-Log.div = function() {
-  var l = document.getElementById('log');
-  if (!l) {
-    l = document.createElement('div');
-    l.id = 'log';
-    l.style.position = 'absolute';
-    l.style.right = '5px';
-    l.style.top = '5px';
-    l.style.width = '250px';
-    l.style.height = '150px';
-    l.style.overflow = 'auto';
-    l.style.backgroundColor = '#f0f0f0';
-    l.style.border = '1px solid gray';
-    l.style.fontSize = '10px';
-    l.style.padding = '5px';
-    document.body.appendChild(l);
-  }
-  return l;
-}
-
-
-function Timer() {}
-Timer.start = function() {}
-Timer.end = function() {}</diff>
      <filename>resources/script/spry/includes/xpath.js</filename>
    </modified>
    <modified>
      <diff>@@ -1,6 +1,6 @@
 @charset &quot;UTF-8&quot;;
 
-/* SpryAccordion.css - version 0.4 - Spry Pre-Release 1.5 */
+/* SpryAccordion.css - version 0.4 - Spry Pre-Release 1.6.1 */
 
 /* Copyright (c) 2006. Adobe Systems Incorporated. All rights reserved. */
 </diff>
      <filename>resources/script/spry/widgets/accordion/SpryAccordion.css</filename>
    </modified>
    <modified>
      <diff>@@ -1,4 +1,4 @@
-// SpryAccordion.js - version 0.13 - Spry Pre-Release 1.5
+// SpryAccordion.js - version 0.15 - Spry Pre-Release 1.6.1
 //
 // Copyright (c) 2006. Adobe Systems Incorporated.
 // All rights reserved.
@@ -140,7 +140,7 @@ Spry.Widget.Accordion.prototype.openPanel = function(elementOrIndex)
 			contentA.style.height = &quot;0px&quot;;
 		}
 		contentB.style.display = &quot;block&quot;;
-		contentB.style.height = (this.useFixedPanelHeights ? this.fixedPanelHeight : contentB.scrollHeight) + &quot;px&quot;;
+		contentB.style.height = this.useFixedPanelHeights ? this.fixedPanelHeight + &quot;px&quot; : &quot;auto&quot;;
 	}
 
 	if(panelA)
@@ -221,7 +221,7 @@ Spry.Widget.Accordion.prototype.onPanelTabClick = function(e, panel)
 		this.focus();
 
 	if (e.preventDefault) e.preventDefault();
-	else e.returnResult = false;
+	else e.returnValue = false;
 	if (e.stopPropagation) e.stopPropagation();
 	else e.cancelBubble = true;
 
@@ -268,7 +268,7 @@ Spry.Widget.Accordion.prototype.onKeyDown = function(e)
 		this.openPanel(nextPanel);
 
 	if (e.preventDefault) e.preventDefault();
-	else e.returnResult = false;
+	else e.returnValue = false;
 	if (e.stopPropagation) e.stopPropagation();
 	else e.cancelBubble = true;
 
@@ -453,6 +453,7 @@ Spry.Widget.Accordion.PanelAnimator = function(accordion, panel, opts)
 	this.panel = panel;
 	this.panelToOpen = accordion.getElement(panel);
 	this.panelData = [];
+	this.useFixedPanelHeights = accordion.useFixedPanelHeights;
 
 	Spry.Widget.Accordion.setOptions(this, opts, true);
 
@@ -538,7 +539,7 @@ Spry.Widget.Accordion.PanelAnimator.prototype.stepAnimation = function()
 				obj.content.style.height = &quot;0px&quot;;
 			}
 			obj.content.style.overflow = obj.overflow;
-			obj.content.style.height = obj.toHeight + &quot;px&quot;;
+			obj.content.style.height = (this.useFixedPanelHeights || obj.toHeight == 0) ? obj.toHeight + &quot;px&quot; : &quot;auto&quot;;
 		}
 		if (this.onComplete)
 			this.onComplete();</diff>
      <filename>resources/script/spry/widgets/accordion/SpryAccordion.js</filename>
    </modified>
    <modified>
      <diff>@@ -1,6 +1,6 @@
 @charset &quot;UTF-8&quot;;
 
-/* SpryAutoSuggest.css - version 0.1 - Spry Pre-Release 1.5 */
+/* SpryAutoSuggest.css - version 0.2 - Spry Pre-Release 1.6.1 */
 
 /* Copyright (c) 2006. Adobe Systems Incorporated. All rights reserved. */
 
@@ -13,6 +13,7 @@
 	width: 250px;
 	margin: 0px;
 	cursor: pointer;
+	z-index: 1011;
 }
 
 .showSuggestClass .hideSuggestClass{
@@ -22,3 +23,10 @@
 .showSuggestClass .hoverSuggestClass{
 	background-color: #CCCCCC !important;
 }
+
+.iframeSuggest
+{
+	position: absolute;
+	z-index: 1010;
+	filter:alpha(opacity:0.1);
+}</diff>
      <filename>resources/script/spry/widgets/autosuggest/SpryAutoSuggest.css</filename>
    </modified>
    <modified>
      <diff>@@ -1,4 +1,4 @@
-// SpryAutoSuggest.js - version 0.5 - Spry Pre-Release 1.5
+// SpryAutoSuggest.js - version 0.91 - Spry Pre-Release 1.6.1
 //
 // Copyright (c) 2006. Adobe Systems Incorporated.
 // All rights reserved.
@@ -37,23 +37,22 @@ Spry.Widget.BrowserSniff = function()
 	var up = navigator.platform.toString();
 	var ua = navigator.userAgent.toString();
 
-	this.mozilla = this.ie = this.opera = r = false;
+	this.mozilla = this.ie = this.opera = this.safari = false;
 	var re_opera = /Opera.([0-9\.]*)/i;
 	var re_msie = /MSIE.([0-9\.]*)/i;
 	var re_gecko = /gecko/i;
-	var re_safari = /safari\/([\d\.]*)/i;
+	var re_safari = /(applewebkit|safari)\/([\d\.]*)/i;
+	var r = false;
 
-	if (ua.match(re_opera)) {
-		r = ua.match(re_opera);
+	if ( (r = ua.match(re_opera))) {
 		this.opera = true;
 		this.version = parseFloat(r[1]);
-	} else if (ua.match(re_msie)) {
-		r = ua.match(re_msie);
+	} else if ( (r = ua.match(re_msie))) {
 		this.ie = true;
 		this.version = parseFloat(r[1]);
-	} else if (ua.match(re_safari)) {
+	} else if ( (r = ua.match(re_safari))) {
 		this.safari = true;
-		this.version = 1.4;
+		this.version = parseFloat(r[2]);
 	} else if (ua.match(re_gecko)) {
 		var re_gecko_version = /rv:\s*([0-9\.]+)/i;
 		r = ua.match(re_gecko_version);
@@ -76,114 +75,39 @@ Spry.Widget.BrowserSniff = function()
 
 Spry.is = new Spry.Widget.BrowserSniff();
 
-Spry.Widget.AutoSuggest = function(region, suggestRegion, dataset, field, options)
+Spry.Widget.AutoSuggest = function(region, sRegion, dataset, field, options)
 {
-	options = options || {};
-	this.options = {};
-
 	if (!this.isBrowserSupported())
 		return;
 
-	this.region = document.getElementById(region);
-	if (!this.region)
-		return;
-
-	this.textElement = Spry.Widget.Utils.getFirstChildWithNodeNameAtAnyLevel(this.region, &quot;INPUT&quot;);
-	this.textElement.setAttribute('AutoComplete', 'off');
-	this.suggestRegion = document.getElementById(suggestRegion);
-	this.timerID = null;
-	if (typeof dataset == &quot;string&quot;){
-		this.dataset = window[dataset];
-	}else{
-		this.dataset = dataset;
-	}
-	this.field = field;
-	if (typeof field == 'string' &amp;&amp; field.indexOf(',') != -1)
-	{
-		field = field.replace(/\s*,\s*/ig, ',');
-		this.field = field.split(',');
-	}
-
-	this.showSuggestClass = 'showSuggestClass';
-	this.hideSuggestClass = 'hideSuggestClass';
-	this.hoverSuggestClass = 'hoverSuggestClass';
-	this.minCharsType = false;
-	this.containsString = false;
-	this.loadFromServer = false;
-	this.urlParam = '';
-	this.suggestionIsVisible = false;
-	this.stopFocus = false;
-	this.hasFocus = false;
+	options = options || {};
 
+	this.init(region, sRegion, dataset, field);
 	Spry.Widget.Utils.setOptions(this, options);
-	Spry.Widget.Utils.setOptions(this.options, options);
 
-	var self = this;
-	// adding listeners to the text input to catch the text changes
-	this._notifyKeyUp = function(e){ self.handleKeyUp(e)}
-	this._notifyFocus = function(e){ if (self.stopFocus){ self.handleKeyUp(e);} self.hasFocus = true; self.stopFocus = false;}
-	this._notifyMDown = function(e){ self.clickInList = true;}
-	Spry.Widget.Utils.addEventListener(this.textElement, &quot;keydown&quot;, this._notifyKeyUp, false); 
-	Spry.Widget.Utils.addEventListener(this.textElement, &quot;focus&quot;, this._notifyFocus, false);
-	Spry.Widget.Utils.addEventListener(this.textElement, &quot;drop&quot;, this._notifyKeyUp, false);
-	Spry.Widget.Utils.addEventListener(this.textElement, &quot;dragdrop&quot;, this._notifyKeyUp, false);
-	
-	// on opera the blur is triggered before onclick
-	if (Spry.is.opera){
-		this._notifyBlur = function(e) { setTimeout(function(){if (!self.clickInList){ self.showSuggestions(false); }else{ self.stopFocus = true; self.textElement.focus();} self.clickInList = false; self.hasFocus = false;}, 100)}
-	}else{
-		this._notifyBlur = function(e) { if (!self.clickInList){ self.showSuggestions(false); }else{ self.stopFocus = true; self.textElement.focus();} self.clickInList = false; self.hasFocus = false;}
-	}
-	Spry.Widget.Utils.addEventListener(this.textElement, &quot;blur&quot;, this._notifyBlur, false);
+	if (Spry.Widget.AutoSuggest.onloadDidFire)
+		this.attachBehaviors();
+	else 
+		Spry.Widget.AutoSuggest.loadQueue.push(this);
 
-	// we listen on the suggest region too
-	Spry.Widget.Utils.addEventListener(this.suggestRegion, &quot;mousedown&quot;, this._notifyMDown, false);
-	
 	// when data is changing we will decide if we will have to show the suggestions
-	this.dataset.addObserver(
-	{
-		onDataChanged: function(el)
-		{
-				var data = el.getData();
-				var val = self.getValue();
-				if (data &amp;&amp; (!self.minCharsType || val.length &gt;= self.minCharsType) &amp;&amp; (data.length &gt; 1 || (data.length == 1 &amp;&amp; self.childs[0] &amp;&amp; self.childs[0].attributes.getNamedItem(&quot;spry:suggest&quot;).value != self.getValue())))
-				{
-					self.showSuggestions(true);
-					return;
-				}
-				self.showSuggestions(false);
-		}
-	});
+	this.dataset.addObserver(this);
 	
-	// define some notification functions used later for each row in the list
-	this._notifyNodeMOver = function(e, node)
-	{ 
-		var l = self.childs.length;
-		for (var i=0; i&lt;l; i++)
-			if (self.childs[i] != node &amp;&amp; Spry.Widget.Utils.hasClassName(self.childs[i], self.hoverSuggestClass))
-			{
-				Spry.Widget.Utils.removeClassName(self.childs[i], self.hoverSuggestClass);
-				break;
-			}
-	};
-	this._notifyNodeClick = function(e, value) {if (value){self.setValue(value);}};
-
-	// prepare the suggest region
-	Spry.Widget.Utils.makePositioned(this.suggestRegion);
-	Spry.Widget.Utils.addClassName(this.suggestRegion, this.hideSuggestClass);
-
 	// Set up an observer so we can attach our click behaviors whenever
 	// the region is regenerated.
-	var regionID = Spry.Widget.Utils.getElementID(suggestRegion);
+	var regionID = Spry.Widget.Utils.getElementID(sRegion);
+
+	var self = this;
 	this._notifyDataset = { onPostUpdate: function() {
 			self.attachClickBehaviors();
 	}, onPreUpdate: function(){
 			self.removeClickBehaviours();
 	}};
+
 	Spry.Data.Region.addObserver(regionID,this._notifyDataset);
 
 	// clean up the widget when on page unload
-	// Spry.Widget.Utils.addEventListener(window, 'beforeunload', function(){self.destroy()}, false);
+	Spry.Widget.Utils.addEventListener(window, 'unload', function(){self.destroy()}, false);
 
 	// make the first computation in case the textfield is not empty
 	this.attachClickBehaviors();
@@ -191,6 +115,48 @@ Spry.Widget.AutoSuggest = function(region, suggestRegion, dataset, field, option
 	this.showSuggestions(false);
 };
 
+Spry.Widget.AutoSuggest.prototype.init = function(region, sRegion, dataset, field)
+{
+	this.region = Spry.Widget.Utils.getElement(region);
+	
+	if (!this.region)
+		return;
+	
+	this.minCharsType = false;
+	this.containsString = false;
+	this.loadFromServer = false;
+	this.urlParam = '';
+	this.suggestionIsVisible = false;
+	this.stopFocus = false;
+	this.hasFocus = false;
+	this.showSuggestClass = 'showSuggestClass';
+	this.hideSuggestClass = 'hideSuggestClass';
+	this.hoverSuggestClass = 'hoverSuggestClass';
+	this.movePrevKeyCode = Spry.Widget.AutoSuggest.KEY_UP;
+	this.moveNextKeyCode = Spry.Widget.AutoSuggest.KEY_DOWN;
+
+	this.textElement = Spry.Widget.Utils.getFirstChildWithNodeNameAtAnyLevel(this.region, &quot;INPUT&quot;);
+	this.textElement.setAttribute('AutoComplete', 'off');
+	
+	this.suggestRegion = Spry.Widget.Utils.getElement(sRegion);
+	// prepare the suggest region
+	Spry.Widget.Utils.makePositioned(this.suggestRegion);
+	Spry.Widget.Utils.addClassName(this.suggestRegion, this.hideSuggestClass);
+
+	this.timerID = null;
+	if (typeof dataset == &quot;string&quot;){
+		this.dataset = window[dataset];
+	}else{
+		this.dataset = dataset;
+	}
+	this.field = field;
+	if (typeof field == 'string' &amp;&amp; field.indexOf(',') != -1)
+	{
+		field = field.replace(/\s*,\s*/ig, ',');
+		this.field = field.split(',');
+	}
+};
+
 Spry.Widget.AutoSuggest.prototype.isBrowserSupported = function()
 {
 	return Spry.is.ie &amp;&amp; Spry.is.v &gt;= 5 &amp;&amp; Spry.is.windows
@@ -229,9 +195,17 @@ Spry.Widget.AutoSuggest.prototype.showSuggestions = function(doShow)
 	if (this.region &amp;&amp; this.isVisibleSuggestion() != doShow)
 	{
 		if (doShow &amp;&amp; this.hasFocus)
+		{
 				Spry.Widget.Utils.addClassName(this.region, this.showSuggestClass);
+				if (Spry.is.ie &amp;&amp; Spry.is.version &lt; 7)
+					this.createIframeLayer(this.suggestRegion);
+		}
 		else
+		{
+				if (Spry.is.ie &amp;&amp; Spry.is.version &lt; 7)
+					this.removeIframeLayer();
 				Spry.Widget.Utils.removeClassName(this.region, this.showSuggestClass);
+		}
 	}
 	this.suggestionIsVisible = Spry.Widget.Utils.hasClassName(this.region, this.showSuggestClass);
 };
@@ -241,6 +215,27 @@ Spry.Widget.AutoSuggest.prototype.isVisibleSuggestion = function()
 	return this.suggestionIsVisible;
 };
 
+Spry.Widget.AutoSuggest.prototype.onDataChanged = function(el)
+{
+		var data = el.getData(true);
+		var val = this.getValue();
+		this.showSuggestions(data &amp;&amp; (!this.minCharsType || val.length &gt;= this.minCharsType) &amp;&amp; (data.length &gt; 1 || (data.length == 1 &amp;&amp; this.childs[0] &amp;&amp; this.childs[0].attributes.getNamedItem(&quot;spry:suggest&quot;).value != this.getValue())));
+};
+Spry.Widget.AutoSuggest.prototype.nodeMouseOver = function(e, node)
+{
+	var l = this.childs.length;
+	for (var i=0; i&lt;l; i++)
+		if (this.childs[i] != node &amp;&amp; Spry.Widget.Utils.hasClassName(this.childs[i], this.hoverSuggestClass))
+		{
+			Spry.Widget.Utils.removeClassName(this.childs[i], this.hoverSuggestClass);
+			break;
+		}
+};
+Spry.Widget.AutoSuggest.prototype.nodeClick = function(e, value) 
+{
+	if (value)
+		this.setValue(value);
+};
 Spry.Widget.AutoSuggest.prototype.handleKeyUp = function(e)
 {
 	if (this.timerID)
@@ -250,16 +245,19 @@ Spry.Widget.AutoSuggest.prototype.handleKeyUp = function(e)
 	}
 
 	// If the user hit the escape key, hide the auto suggest menu!
-	if (e &amp;&amp; Spry.Widget.Utils.isSpecialKey(e))
+	if (e &amp;&amp; this.isSpecialKey(e))
 	{
 		this.handleSpecialKeys(e);
 		return;
 	}
+
 	var self = this;
+	var func = function() { self.timerID = null; self.loadDataSet();};
 	if (!this.loadFromServer)
-		this.timerID = setTimeout(function() { self.timerID = null; self.filterDataSet()}, 200);
-	else
-		this.timerID = setTimeout(function() { self.timerID = null; self.loadDataSet()}, 200); 
+		func = function() { self.timerID = null; self.filterDataSet();};
+
+	this.timerID = setTimeout(func, 200);
+	
 };
 
 Spry.Widget.AutoSuggest.prototype.scrollVisible = function(el)
@@ -299,11 +297,15 @@ Spry.Widget.AutoSuggest.prototype.scrollVisible = function(el)
 	}
 };
 
+Spry.Widget.AutoSuggest.KEY_UP = 38;
+Spry.Widget.AutoSuggest.KEY_DOWN = 40;
+
 Spry.Widget.AutoSuggest.prototype.handleSpecialKeys = function(e){
+
  	switch (e.keyCode)
 	{
-		case 40: // Down key  
- 		case 38: // Up Key
+		case this.moveNextKeyCode: // Down key  
+ 		case this.movePrevKeyCode: // Up Key
 			if (!(this.childs.length &gt; 0) || !this.getValue())
 				return;	
 
@@ -318,26 +320,27 @@ Spry.Widget.AutoSuggest.prototype.handleSpecialKeys = function(e){
 			else
 				return;	
 			
+			var utils = Spry.Widget.Utils;
 			for (var k=0; k &lt; this.childs.length; k++)
 			{
 				if (next)
 				{
-					Spry.Widget.Utils.addClassName(this.childs[k], this.hoverSuggestClass);
+					utils.addClassName(this.childs[k], this.hoverSuggestClass);
 					this.scrollVisible(this.childs[k]);
 					break;
 				}
-				if (Spry.Widget.Utils.hasClassName(this.childs[k], this.hoverSuggestClass))
+				if (utils.hasClassName(this.childs[k], this.hoverSuggestClass))
 				{
-					Spry.Widget.Utils.removeClassName(this.childs[k], this.hoverSuggestClass);
+					utils.removeClassName(this.childs[k], this.hoverSuggestClass);
 					found = true;
-					if (e.keyCode == 40)
+					if (e.keyCode == this.moveNextKeyCode)
 					{
 						next = true;
 						continue;
 					}
 					else
 					{
-						Spry.Widget.Utils.addClassName(this.childs[prev], this.hoverSuggestClass);
+						utils.addClassName(this.childs[prev], this.hoverSuggestClass);
 						this.scrollVisible(this.childs[prev]);
 						break;
 					}
@@ -346,10 +349,10 @@ Spry.Widget.AutoSuggest.prototype.handleSpecialKeys = function(e){
 			}
 			if (!found || (next &amp;&amp; k == this.childs.length))
 			{
-				Spry.Widget.Utils.addClassName(this.childs[0], this.hoverSuggestClass);
+				utils.addClassName(this.childs[0], this.hoverSuggestClass);
 				this.scrollVisible(this.childs[0]);
 			}
-			Spry.Widget.Utils.stopEvent(e);
+			utils.stopEvent(e);
 			break;
 		case 27: // ESC key
 			this.showSuggestions(false);
@@ -389,7 +392,7 @@ Spry.Widget.AutoSuggest.prototype.filterDataSet = function()
 
 	if (!val || (this.minCharsType &amp;&amp; this.minCharsType &gt; val.length))
 	{
-		this.dataset.filter(function(ds, row, rowNumber) { return null; });
+		this.dataset.filter(function(ds, row, rowNumber) {return null;});
 		this.showSuggestions(false);
 		return;
 	}
@@ -430,19 +433,16 @@ Spry.Widget.AutoSuggest.prototype.filterDataSet = function()
 
 	this.dataset.filter(filterFunc);
 	var data = this.dataset.getData();
-	if (data &amp;&amp; (!this.minCharsType || val.length &gt;= this.minCharsType) &amp;&amp; (data.length &gt; 1 || (data.length == 1 &amp;&amp; this.childs[0] &amp;&amp; this.childs[0].attributes.getNamedItem('spry:suggest').value != val ))){
-		this.showSuggestions(true);
-		return;
-	}
-	this.showSuggestions(false);
+	this.showSuggestions(data &amp;&amp; (!this.minCharsType || val.length &gt;= this.minCharsType) &amp;&amp; (data.length &gt; 1 || (data.length == 1 &amp;&amp; this.childs[0] &amp;&amp; this.childs[0].attributes.getNamedItem('spry:suggest').value != val )));
 };
 
 Spry.Widget.AutoSuggest.prototype.loadDataSet = function()
 {
-	this.dataset.cancelLoadData();
-	this.dataset.useCache = false;
-	
 	var val = this.getValue();
+	var ds = this.dataset;
+	ds.cancelLoadData();
+	ds.useCache = false;
+	
 	if (!val || (this.minCharsType &amp;&amp; this.minCharsType &gt; val.length))
 	{
 		this.showSuggestions(false);
@@ -451,35 +451,33 @@ Spry.Widget.AutoSuggest.prototype.loadDataSet = function()
 	
 	if (this.previousString &amp;&amp; this.previousString == val)
 	{
-		var data = this.dataset.getData();
-		if (data &amp;&amp; (data.length &gt; 1 || (data.length == 1 &amp;&amp; this.childs[0].attributes.getNamedItem(&quot;spry:suggest&quot;).value != val))){
-			this.showSuggestions(true);
-		}else{
-			this.showSuggestions(false);
-		}
+		var data = ds.getData();
+		this.showSuggestions(data &amp;&amp; (data.length &gt; 1 || (data.length == 1 &amp;&amp; this.childs[0].attributes.getNamedItem(&quot;spry:suggest&quot;).value != val)));
 		return;
 	}
 
 	this.previousString = val;
 
-	var url = Spry.Widget.Utils.addReplaceParam(this.dataset.url, this.urlParam, val);
-	this.dataset.setURL(url);
-	this.dataset.loadData();
+	var url = Spry.Widget.Utils.addReplaceParam(ds.url, this.urlParam, val);
+	ds.setURL(url);
+	ds.loadData();
 };
 
 Spry.Widget.AutoSuggest.prototype.addMouseListener =  function(node, value)
 {
 	var self = this;
-	Spry.Widget.Utils.addEventListener(node, &quot;click&quot;, function(e){ return self._notifyNodeClick(e, value); self.handleKeyUp(null);}, false); 
-	Spry.Widget.Utils.addEventListener(node, &quot;mouseover&quot;, function(e){ Spry.Widget.Utils.addClassName(node, self.hoverSuggestClass); self._notifyNodeMOver(e, node)}, false); 
-	Spry.Widget.Utils.addEventListener(node, &quot;mouseout&quot;, function(e){ Spry.Widget.Utils.removeClassName(node, self.hoverSuggestClass); self._notifyNodeMOver(e, node)}, false); 
+	var addListener = Spry.Widget.Utils.addEventListener;
+	addListener(node, &quot;click&quot;, function(e){ return self.nodeClick(e, value); self.handleKeyUp(null);}, false); 
+	addListener(node, &quot;mouseover&quot;, function(e){ Spry.Widget.Utils.addClassName(node, self.hoverSuggestClass); self.nodeMouseOver(e, node)}, false); 
+	addListener(node, &quot;mouseout&quot;, function(e){ Spry.Widget.Utils.removeClassName(node, self.hoverSuggestClass); self.nodeMouseOver(e, node)}, false); 
 };
 Spry.Widget.AutoSuggest.prototype.removeMouseListener =  function(node, value)
 {
 	var self = this;
-	Spry.Widget.Utils.removeEventListener(node, &quot;click&quot;, function (e){ self._notifyNodeClick(e, value); self.handleKeyUp(null);}, false); 
-	Spry.Widget.Utils.removeEventListener(node, &quot;mouseover&quot;, function(e){ Spry.Widget.Utils.addClassName(node, self.hoverSuggestClass); self._notifyNodeMOver(e, node)}, false); 
-	Spry.Widget.Utils.removeEventListener(node, &quot;mouseout&quot;, function(e){ Spry.Widget.Utils.removeClassName(node, self.hoverSuggestClass); self._notifyNodeMOver(e, node)}, false); 
+	var removeListener = Spry.Widget.Utils.removeEventListener;
+	removeListener(node, &quot;click&quot;, function (e){ self.nodeClick(e, value); self.handleKeyUp(null);}, false); 
+	removeListener(node, &quot;mouseover&quot;, function(e){ Spry.Widget.Utils.addClassName(node, self.hoverSuggestClass); self.nodeMouseOver(e, node)}, false); 
+	removeListener(node, &quot;mouseout&quot;, function(e){ Spry.Widget.Utils.removeClassName(node, self.hoverSuggestClass); self.nodeMouseOver(e, node)}, false); 
 };
 Spry.Widget.AutoSuggest.prototype.attachClickBehaviors =  function()
 {
@@ -515,20 +513,104 @@ Spry.Widget.AutoSuggest.prototype.removeClickBehaviours = function()
 	});
 };
 Spry.Widget.AutoSuggest.prototype.destroy = function(){
+
 	this.removeClickBehaviours();
 	Spry.Data.Region.removeObserver(Spry.Widget.Utils.getElementID(this.suggestRegion),this._notifyDataset);
-	Spry.Widget.Utils.removeEventListener(this.textElement, &quot;keydown&quot;, this._notifyKeyUp, false); 
-	Spry.Widget.Utils.removeEventListener(this.textElement, &quot;focus&quot;, this._notifyFocus, false);
-	Spry.Widget.Utils.removeEventListener(this.textElement, &quot;drop&quot;, this._notifyKeyUp, false);
-	Spry.Widget.Utils.removeEventListener(this.textElement, &quot;dragdrop&quot;, this._notifyKeyUp, false);
-	Spry.Widget.Utils.removeEventListener(this.suggestRegion, &quot;mousedown&quot;, this._notifyMDown, false);
-	Spry.Widget.Utils.removeEventListener(this.textElement, &quot;blur&quot;, this._notifyBlur, false);
-	for (var k in this){
+	
+	if (this.event_handlers)
+		for (var i=0; i&lt;this.event_handlers.length; i++) {
+			Spry.Widget.Utils.removeEventListener(this.event_handlers[i][0], this.event_handlers[i][1], this.event_handlers[i][2], false);
+		}
+
+	for (var k in this)
+	{
 		if (typeof this[k] != 'function'){
-			try { delete this.textElement; } catch(err) {}
+			try { delete this[k]; } catch(err) {}
 		}
 	}
 };
+
+Spry.Widget.AutoSuggest.onloadDidFire = false;
+Spry.Widget.AutoSuggest.loadQueue = [];
+
+Spry.Widget.AutoSuggest.processLoadQueue = function(handler)
+{
+	Spry.Widget.AutoSuggest.onloadDidFire = true;
+	var q = Spry.Widget.AutoSuggest.loadQueue;
+	var qlen = q.length;
+	for (var i = 0; i &lt; qlen; i++)
+		q[i].attachBehaviors();
+};
+
+Spry.Widget.AutoSuggest.addLoadListener = function(handler)
+{
+	if (typeof window.addEventListener != 'undefined')
+		window.addEventListener('load', handler, false);
+	else if (typeof document.addEventListener != 'undefined')
+		document.addEventListener('load', handler, false);
+	else if (typeof window.attachEvent != 'undefined')
+		window.attachEvent('onload', handler);
+};
+
+Spry.Widget.AutoSuggest.addLoadListener(Spry.Widget.AutoSuggest.processLoadQueue);
+
+Spry.Widget.AutoSuggest.prototype.attachBehaviors = function()
+{
+	this.event_handlers = [];
+	var self = this;
+	// adding listeners to the text input to catch the text changes
+	var _notifyKeyUp = function(e){ self.handleKeyUp(e)};
+	this.event_handlers.push([this.textElement, &quot;keydown&quot;, _notifyKeyUp]); 
+	this.event_handlers.push([this.textElement, &quot;focus&quot;, function(e){ if (self.stopFocus){ self.handleKeyUp(e);} self.hasFocus = true; self.stopFocus = false;}]);
+	this.event_handlers.push([this.textElement, &quot;drop&quot;, _notifyKeyUp]);
+	this.event_handlers.push([this.textElement, &quot;dragdrop&quot;, _notifyKeyUp]);
+	
+	var _notifyBlur = false;
+	// on opera the blur is triggered before onclick
+	if (Spry.is.opera){
+		_notifyBlur = function(e) { setTimeout(function(){if (!self.clickInList){ self.showSuggestions(false); }else{ self.stopFocus = true; self.textElement.focus();} self.clickInList = false; self.hasFocus = false;}, 100); };
+	}else{
+		_notifyBlur = function(e) { if (!self.clickInList){ self.showSuggestions(false); }else{ self.stopFocus = true; self.textElement.focus();} self.clickInList = false; self.hasFocus = false;};
+	}
+	this.event_handlers.push([this.textElement, &quot;blur&quot;, _notifyBlur]);
+
+	// we listen on the suggest region too
+	this.event_handlers.push([this.suggestRegion, &quot;mousedown&quot;, function(e){ self.clickInList = true;}]);
+
+	for (var i=0; i&lt;this.event_handlers.length; i++) 
+		Spry.Widget.Utils.addEventListener(this.event_handlers[i][0], this.event_handlers[i][1], this.event_handlers[i][2], false);
+};
+
+// createIframeLayer for Tooltip
+// creates an IFRAME underneath a tooltip element so that it will show above form controls and ActiveX
+Spry.Widget.AutoSuggest.prototype.createIframeLayer = function(element)
+{
+	if (typeof this.iframeLayer == 'undefined')
+	{
+		var layer = document.createElement('iframe');
+		layer.tabIndex = '-1';
+		layer.src = 'javascript:&quot;&quot;;';
+		layer.scrolling = 'no';
+		layer.frameBorder = '0';
+		layer.className = 'iframeSuggest';
+		element.parentNode.appendChild(layer);
+		this.iframeLayer = layer;
+	}
+	this.iframeLayer.style.left = element.offsetLeft + 'px';
+	this.iframeLayer.style.top = element.offsetTop + 'px';
+	this.iframeLayer.style.width = element.offsetWidth + 'px';
+	this.iframeLayer.style.height = element.offsetHeight + 'px';
+	this.iframeLayer.style.display = 'block';	
+};
+
+// removeIframeLayer for Tooltip Element
+// removes an IFRAME underneath a tooltip to reveal any form controls and ActiveX
+Spry.Widget.AutoSuggest.prototype.removeIframeLayer =  function()
+{
+	if (this.iframeLayer)
+		this.iframeLayer.style.display = 'none';
+};
+
 //////////////////////////////////////////////////////////////////////
 //
 // Spry.Widget.Utils
@@ -536,13 +618,16 @@ Spry.Widget.AutoSuggest.prototype.destroy = function(){
 //////////////////////////////////////////////////////////////////////
 if (!Spry.Widget.Utils)	Spry.Widget.Utils = {};
 
-Spry.Widget.Utils.specialSafariNavKeys = &quot;,63232,63233,63234,63235,63272,63273,63275,63276,63277,63289,&quot;;
-Spry.Widget.Utils.specialCharacters = &quot;,9,13,27,38,40,&quot;;
+Spry.Widget.Utils.specialSafariNavKeys = &quot;,63232,63233,63234,63235,63272,63273,63275,63276,63277,63289,&quot;; //left,up,rigtht,down arrows,delete,home,end,page up,page down,num lock
+Spry.Widget.Utils.specialCharacters = &quot;,9,13,27,38,40,&quot;;              //suggest keys: tab,enter,escape,up arrow,down arrow
+Spry.Widget.Utils.specialCharacters += &quot;,33,34,35,36,37,39,45,46,&quot;;   //edit keys: insert,delete,home,end,left arrow,right arrow,page up,page down
+Spry.Widget.Utils.specialCharacters += &quot;,16,17,18,19,20,144,145,&quot;;    //control keys: shift,control,alt,pause,caps lock,num lock,scroll lock
+Spry.Widget.Utils.specialCharacters += &quot;,112,113,114,115,116,117,118,119,120,121,122,123,&quot;; //F1-F12
 Spry.Widget.Utils.specialCharacters += Spry.Widget.Utils.specialSafariNavKeys;
 
-Spry.Widget.Utils.isSpecialKey = function (ev)
+Spry.Widget.AutoSuggest.prototype.isSpecialKey = function (ev)
 {
-	return Spry.Widget.Utils.specialCharacters.indexOf(&quot;,&quot; + ev.keyCode + &quot;,&quot;) != -1;
+	return Spry.Widget.Utils.specialCharacters.indexOf(&quot;,&quot; + ev.keyCode + &quot;,&quot;) != -1 || this.moveNextKeyCode == ev.keyCode || this.movePrevKeyCode == ev.keyCode;
 };
 Spry.Widget.Utils.getElementID = function(el)
 {
@@ -550,11 +635,17 @@ Spry.Widget.Utils.getElementID = function(el)
 		return el;
 	return el.getAttribute('id');
 };
+Spry.Widget.Utils.getElement = function(ele)
+{
+	if (ele &amp;&amp; typeof ele == &quot;string&quot;)
+		return document.getElementById(ele);
+	return ele;
+};
 Spry.Widget.Utils.addReplaceParam = function(url, param, paramValue)
 {
 	var uri ='';
 	var qstring = '';
-	var i = url.indexOf('?')
+	var i = url.indexOf('?');
 	if ( i != -1)
 	{
 		uri = url.slice(0, i);
@@ -579,7 +670,7 @@ Spry.Widget.Utils.addReplaceParam = function(url, param, paramValue)
 			arg[i] = null;
 	}
 
-	arg[arg.length] = encodeURI(param) + '=' + encodeURI(paramValue);
+	arg[arg.length] = encodeURIComponent(param) + '=' + encodeURIComponent(paramValue);
 	qstring = '';
 	// reconstruct the qstring
 	for (i=0; i &lt; arg.length; i++)</diff>
      <filename>resources/script/spry/widgets/autosuggest/SpryAutoSuggest.js</filename>
    </modified>
    <modified>
      <diff>@@ -1,6 +1,6 @@
 @charset &quot;UTF-8&quot;;
 
-/* SpryValidationCheckbox.css - version 0.4 - Spry Pre-Release 1.5 */
+/* SpryValidationCheckbox.css - version 0.4 - Spry Pre-Release 1.6.1 */
 
 /* Copyright (c) 2006. Adobe Systems Incorporated. All rights reserved. */
 </diff>
      <filename>resources/script/spry/widgets/checkboxvalidation/SpryValidationCheckbox.css</filename>
    </modified>
    <modified>
      <diff>@@ -1,4 +1,4 @@
-// SpryValidationCheckbox.js - version 0.10 - Spry Pre-Release 1.5
+// SpryValidationCheckbox.js - version 0.10 - Spry Pre-Release 1.6.1
 //
 // Copyright (c) 2006. Adobe Systems Incorporated.
 // All rights reserved.
@@ -96,13 +96,17 @@ Spry.Widget.ValidationCheckbox.prototype.init = function(element)
 };
 
 Spry.Widget.ValidationCheckbox.prototype.destroy = function() {
-	for (var i=0; i&lt;this.event_handlers.length; i++) {
-		Spry.Widget.Utils.removeEventListener(this.event_handlers[i][0], this.event_handlers[i][1], this.event_handlers[i][2], false);
-	}
+	if (this.event_handlers)
+		for (var i=0; i&lt;this.event_handlers.length; i++)
+		{
+			Spry.Widget.Utils.removeEventListener(this.event_handlers[i][0], this.event_handlers[i][1], this.event_handlers[i][2], false);
+		}
 	try { delete this.element; } catch(err) {}
-	for(var i=0; i&lt;this.checkboxElements.length; i++) {
-		try { delete this.checkboxElements[i];} catch(err) {}
-	}
+	if (this.checkboxElements)
+		for(var i=0; i&lt;this.checkboxElements.length; i++)
+		{
+			try { delete this.checkboxElements[i];} catch(err) {}
+		}
 	try { delete this.checkboxElements; } catch(err) {}
 	try { delete this.form; } catch(err) {}
 	try { delete this.event_handlers; } catch(err) {}
@@ -153,6 +157,8 @@ Spry.Widget.ValidationCheckbox.addLoadListener(function(){
 
 Spry.Widget.ValidationCheckbox.prototype.attachBehaviors = function()
 {
+	if (!this.element)
+		return;
 	// find the INPUT type=&quot;checkbox&quot; element(s) inside current container
 	if (this.element.nodeName == &quot;INPUT&quot;) {
 		this.checkboxElements = [this.element];
@@ -212,7 +218,7 @@ Spry.Widget.ValidationCheckbox.prototype.getCheckboxes = function() {
 		return arrCheckboxes;
 	}
 	return null;
-}
+};
 
 Spry.Widget.ValidationCheckbox.prototype.addClassName = function(ele, className)
 {
@@ -312,7 +318,7 @@ Spry.Widget.ValidationCheckbox.prototype.validate = function() {
 	this.addClassName(this.element, this.validClass);
 	this.addClassName(this.additionalError, this.validClass);
 	return true;
-}
+};
 
 Spry.Widget.ValidationCheckbox.prototype.isDisabled = function() {
 	var ret = true;
@@ -326,7 +332,7 @@ Spry.Widget.ValidationCheckbox.prototype.isDisabled = function() {
 		}
 	}
 	return ret;
-}
+};
 
 //////////////////////////////////////////////////////////////////////
 //</diff>
      <filename>resources/script/spry/widgets/checkboxvalidation/SpryValidationCheckbox.js</filename>
    </modified>
    <modified>
      <diff>@@ -1,6 +1,6 @@
 @charset &quot;UTF-8&quot;;
 
-/* SpryCollapsiblePanel.css - version 0.5 - Spry Pre-Release 1.5 */
+/* SpryCollapsiblePanel.css - version 0.5 - Spry Pre-Release 1.6.1 */
 
 /* Copyright (c) 2006. Adobe Systems Incorporated. All rights reserved. */
 
@@ -76,6 +76,15 @@
 	background-color: #EEE;
 }
 
+/* This is an example of how to change the appearance of the panel tab when the
+ * CollapsiblePanel is closed. The &quot;CollapsiblePanelClosed&quot; class is programatically added and removed
+ * whenever the CollapsiblePanel is closed.
+ */
+
+.CollapsiblePanelClosed .CollapsiblePanelTab {
+ /* background-color: #EFEFEF */
+}
+
 /* This is an example of how to change the appearance of the panel tab as the
  * mouse hovers over it. The class &quot;CollapsiblePanelTabHover&quot; is programatically added
  * and removed from panel tab containers as the mouse enters and exits the tab container.</diff>
      <filename>resources/script/spry/widgets/collapsiblepanel/SpryCollapsiblePanel.css</filename>
    </modified>
    <modified>
      <diff>@@ -1,4 +1,4 @@
-// SpryCollapsiblePanel.js - version 0.5 - Spry Pre-Release 1.5
+// SpryCollapsiblePanel.js - version 0.7 - Spry Pre-Release 1.6.1
 //
 // Copyright (c) 2006. Adobe Systems Incorporated.
 // All rights reserved.
@@ -45,6 +45,9 @@ Spry.Widget.CollapsiblePanel = function(element, opts)
 	this.hasFocus = false;
 	this.contentIsOpen = true;
 
+	this.openPanelKeyCode = Spry.Widget.CollapsiblePanel.KEY_DOWN;
+	this.closePanelKeyCode = Spry.Widget.CollapsiblePanel.KEY_UP;
+
 	Spry.Widget.CollapsiblePanel.setOptions(this, opts);
 
 	this.attachBehaviors();
@@ -151,12 +154,7 @@ Spry.Widget.CollapsiblePanel.prototype.onTabClick = function(e)
 
 	this.focus();
 
-	if (e.preventDefault) e.preventDefault();
-	else e.returnResult = false;
-	if (e.stopPropagation) e.stopPropagation();
-	else e.cancelBubble = true;
-
-	return false;
+	return this.stopPropagation(e);
 };
 
 Spry.Widget.CollapsiblePanel.prototype.onFocus = function(e)
@@ -173,25 +171,29 @@ Spry.Widget.CollapsiblePanel.prototype.onBlur = function(e)
 	return false;
 };
 
-Spry.Widget.CollapsiblePanel.ENTER_KEY = 13;
-Spry.Widget.CollapsiblePanel.SPACE_KEY = 32;
+Spry.Widget.CollapsiblePanel.KEY_UP = 38;
+Spry.Widget.CollapsiblePanel.KEY_DOWN = 40;
 
 Spry.Widget.CollapsiblePanel.prototype.onKeyDown = function(e)
 {
 	var key = e.keyCode;
-	if (!this.hasFocus || (key != Spry.Widget.CollapsiblePanel.ENTER_KEY &amp;&amp; key != Spry.Widget.CollapsiblePanel.SPACE_KEY))
+	if (!this.hasFocus || (key != this.openPanelKeyCode &amp;&amp; key != this.closePanelKeyCode))
 		return true;
-	
-	if (this.isOpen())
+
+	if (this.isOpen() &amp;&amp; key == this.closePanelKeyCode)
 		this.close();
-	else
+	else if ( key == this.openPanelKeyCode)
 		this.open();
+	
+	return this.stopPropagation(e);
+};
 
+Spry.Widget.CollapsiblePanel.prototype.stopPropagation = function(e)
+{
 	if (e.preventDefault) e.preventDefault();
-	else e.returnResult = false;
+	else e.returnValue = false;
 	if (e.stopPropagation) e.stopPropagation();
 	else e.cancelBubble = true;
-
 	return false;
 };
 
@@ -286,6 +288,7 @@ Spry.Widget.CollapsiblePanel.prototype.attachBehaviors = function()
 
 	if (this.contentIsOpen || this.hasClassName(panel, this.openClass))
 	{
+		this.addClassName(panel, this.openClass);
 		this.removeClassName(panel, this.closedClass);
 		this.setDisplay(content, &quot;block&quot;);
 		this.contentIsOpen = true;</diff>
      <filename>resources/script/spry/widgets/collapsiblepanel/SpryCollapsiblePanel.js</filename>
    </modified>
    <modified>
      <diff>@@ -1,4 +1,4 @@
-// SpryMenuBar.js - version 0.4 - Spry Pre-Release 1.5
+// SpryMenuBar.js - version 0.12 - Spry Pre-Release 1.6.1
 //
 // Copyright (c) 2006. Adobe Systems Incorporated.
 // All rights reserved.
@@ -38,15 +38,51 @@
 
  *******************************************************************************/
 
-var Spry;
-if(!Spry)
-{
-	Spry = {};
-}
-if(!Spry.Widget)
+var Spry; if (!Spry) Spry = {}; if (!Spry.Widget) Spry.Widget = {};
+
+Spry.BrowserSniff = function()
 {
-	Spry.Widget = {};
-}
+	var b = navigator.appName.toString();
+	var up = navigator.platform.toString();
+	var ua = navigator.userAgent.toString();
+
+	this.mozilla = this.ie = this.opera = this.safari = false;
+	var re_opera = /Opera.([0-9\.]*)/i;
+	var re_msie = /MSIE.([0-9\.]*)/i;
+	var re_gecko = /gecko/i;
+	var re_safari = /(applewebkit|safari)\/([\d\.]*)/i;
+	var r = false;
+
+	if ( (r = ua.match(re_opera))) {
+		this.opera = true;
+		this.version = parseFloat(r[1]);
+	} else if ( (r = ua.match(re_msie))) {
+		this.ie = true;
+		this.version = parseFloat(r[1]);
+	} else if ( (r = ua.match(re_safari))) {
+		this.safari = true;
+		this.version = parseFloat(r[2]);
+	} else if (ua.match(re_gecko)) {
+		var re_gecko_version = /rv:\s*([0-9\.]+)/i;
+		r = ua.match(re_gecko_version);
+		this.mozilla = true;
+		this.version = parseFloat(r[1]);
+	}
+	this.windows = this.mac = this.linux = false;
+
+	this.Platform = ua.match(/windows/i) ? &quot;windows&quot; :
+					(ua.match(/linux/i) ? &quot;linux&quot; :
+					(ua.match(/mac/i) ? &quot;mac&quot; :
+					ua.match(/unix/i)? &quot;unix&quot; : &quot;unknown&quot;));
+	this[this.Platform] = true;
+	this.v = this.version;
+
+	if (this.safari &amp;&amp; this.mac &amp;&amp; this.mozilla) {
+		this.mozilla = false;
+	}
+};
+
+Spry.is = new Spry.BrowserSniff();
 
 // Constructor for Menu Bar
 // element should be an ID of an unordered list (&lt;ul&gt; tag)
@@ -62,40 +98,81 @@ Spry.Widget.MenuBar.prototype.init = function(element, opts)
 
 	// represents the current (sub)menu we are operating on
 	this.currMenu = null;
-
-	var isie = (typeof document.all != 'undefined' &amp;&amp; typeof window.opera == 'undefined' &amp;&amp; navigator.vendor != 'KDE');
-	if(typeof document.getElementById == 'undefined' || (navigator.vendor == 'Apple Computer, Inc.' &amp;&amp; typeof window.XMLHttpRequest == 'undefined') || (isie &amp;&amp; typeof document.uniqueID == 'undefined'))
+	this.showDelay = 250;
+	this.hideDelay = 600;
+	if(typeof document.getElementById == 'undefined' || (navigator.vendor == 'Apple Computer, Inc.' &amp;&amp; typeof window.XMLHttpRequest == 'undefined') || (Spry.is.ie &amp;&amp; typeof document.uniqueID == 'undefined'))
 	{
 		// bail on older unsupported browsers
 		return;
 	}
 
+	// Fix IE6 CSS images flicker
+	if (Spry.is.ie &amp;&amp; Spry.is.version &lt; 7){
+		try {
+			document.execCommand(&quot;BackgroundImageCache&quot;, false, true);
+		} catch(err) {}
+	}
+
+	this.upKeyCode = Spry.Widget.MenuBar.KEY_UP;
+	this.downKeyCode = Spry.Widget.MenuBar.KEY_DOWN;
+	this.leftKeyCode = Spry.Widget.MenuBar.KEY_LEFT;
+	this.rightKeyCode = Spry.Widget.MenuBar.KEY_RIGHT;
+	this.escKeyCode = Spry.Widget.MenuBar.KEY_ESC;
+
+	this.hoverClass = 'MenuBarItemHover';
+	this.subHoverClass = 'MenuBarItemSubmenuHover';
+	this.subVisibleClass ='MenuBarSubmenuVisible';
+	this.hasSubClass = 'MenuBarItemSubmenu';
+	this.activeClass = 'MenuBarActive';
+	this.isieClass = 'MenuBarItemIE';
+	this.verticalClass = 'MenuBarVertical';
+	this.horizontalClass = 'MenuBarHorizontal';
+	this.enableKeyboardNavigation = true;
+
+	this.hasFocus = false;
 	// load hover images now
 	if(opts)
 	{
 		for(var k in opts)
 		{
-			var rollover = new Image;
-			rollover.src = opts[k];
+			if (typeof this[k] == 'undefined')
+			{
+				var rollover = new Image;
+				rollover.src = opts[k];
+			}
 		}
+		Spry.Widget.MenuBar.setOptions(this, opts);
 	}
 
+	// safari doesn't support tabindex
+	if (Spry.is.safari)
+		this.enableKeyboardNavigation = false;
+
 	if(this.element)
 	{
 		this.currMenu = this.element;
 		var items = this.element.getElementsByTagName('li');
 		for(var i=0; i&lt;items.length; i++)
 		{
-			this.initialize(items[i], element, isie);
-			if(isie)
+			if (i &gt; 0 &amp;&amp; this.enableKeyboardNavigation)
+				items[i].getElementsByTagName('a')[0].tabIndex='-1';
+
+			this.initialize(items[i], element);
+			if(Spry.is.ie)
 			{
-				this.addClassName(items[i], &quot;MenuBarItemIE&quot;);
+				this.addClassName(items[i], this.isieClass);
 				items[i].style.position = &quot;static&quot;;
 			}
 		}
-		if(isie)
+		if (this.enableKeyboardNavigation)
+		{
+			var self = this;
+			this.addEventListener(document, 'keydown', function(e){self.keyDown(e); }, false);
+		}
+
+		if(Spry.is.ie)
 		{
-			if(this.hasClassName(this.element, &quot;MenuBarVertical&quot;))
+			if(this.hasClassName(this.element, this.verticalClass))
 			{
 				this.element.style.position = &quot;relative&quot;;
 			}
@@ -107,6 +184,11 @@ Spry.Widget.MenuBar.prototype.init = function(element, opts)
 		}
 	}
 };
+Spry.Widget.MenuBar.KEY_ESC = 27;
+Spry.Widget.MenuBar.KEY_UP = 38;
+Spry.Widget.MenuBar.KEY_DOWN = 40;
+Spry.Widget.MenuBar.KEY_LEFT = 37;
+Spry.Widget.MenuBar.KEY_RIGHT = 39;
 
 Spry.Widget.MenuBar.prototype.getElement = function(ele)
 {
@@ -162,7 +244,9 @@ Spry.Widget.MenuBar.prototype.createIframeLayer = function(menu)
 {
 	var layer = document.createElement('iframe');
 	layer.tabIndex = '-1';
-	layer.src = 'javascript:false;';
+	layer.src = 'javascript:&quot;&quot;';
+	layer.frameBorder = '0';
+	layer.scrolling = 'no';
 	menu.parentNode.appendChild(layer);
 	
 	layer.style.left = menu.offsetLeft + 'px';
@@ -175,7 +259,7 @@ Spry.Widget.MenuBar.prototype.createIframeLayer = function(menu)
 // removes an IFRAME underneath a menu to reveal any form controls and ActiveX
 Spry.Widget.MenuBar.prototype.removeIframeLayer =  function(menu)
 {
-	var layers = menu.parentNode.getElementsByTagName('iframe');
+	var layers = ((menu == this.element) ? menu : menu.parentNode).getElementsByTagName('iframe');
 	while(layers.length &gt; 0)
 	{
 		layers[0].parentNode.removeChild(layers[0]);
@@ -188,17 +272,16 @@ Spry.Widget.MenuBar.prototype.clearMenus = function(root)
 {
 	var menus = root.getElementsByTagName('ul');
 	for(var i=0; i&lt;menus.length; i++)
-	{
 		this.hideSubmenu(menus[i]);
-	}
-	this.removeClassName(this.element, &quot;MenuBarActive&quot;);
+
+	this.removeClassName(this.element, this.activeClass);
 };
 
 // bubbledTextEvent for Menu Bar
 // identify bubbled up text events in Safari so we can ignore them
 Spry.Widget.MenuBar.prototype.bubbledTextEvent = function()
 {
-	return (navigator.vendor == 'Apple Computer, Inc.' &amp;&amp; (event.target == event.relatedTarget.parentNode || (event.eventPhase == 3 &amp;&amp; event.target.parentNode == event.relatedTarget)));
+	return Spry.is.safari &amp;&amp; (event.target == event.relatedTarget.parentNode || (event.eventPhase == 3 &amp;&amp; event.target.parentNode == event.relatedTarget));
 };
 
 // showSubmenu for Menu Bar
@@ -213,20 +296,20 @@ Spry.Widget.MenuBar.prototype.showSubmenu = function(menu)
 	
 	if(menu)
 	{
-		this.addClassName(menu, &quot;MenuBarSubmenuVisible&quot;);
-		if(typeof document.all != 'undefined' &amp;&amp; typeof window.opera == 'undefined' &amp;&amp; navigator.vendor != 'KDE')
+		this.addClassName(menu, this.subVisibleClass);
+		if(typeof document.all != 'undefined' &amp;&amp; !Spry.is.opera &amp;&amp; navigator.vendor != 'KDE')
 		{
-			if(!this.hasClassName(this.element, &quot;MenuBarHorizontal&quot;) || menu.parentNode.parentNode != this.element)
+			if(!this.hasClassName(this.element, this.horizontalClass) || menu.parentNode.parentNode != this.element)
 			{
 				menu.style.top = menu.parentNode.offsetTop + 'px';
 			}
 		}
-		if(typeof document.uniqueID != &quot;undefined&quot;)
+		if(Spry.is.ie &amp;&amp; Spry.is.version &lt; 7)
 		{
 			this.createIframeLayer(menu);
 		}
 	}
-	this.addClassName(this.element, &quot;MenuBarActive&quot;);
+	this.addClassName(this.element, this.activeClass);
 };
 
 // hideSubmenu for Menu Bar
@@ -235,34 +318,31 @@ Spry.Widget.MenuBar.prototype.hideSubmenu = function(menu)
 {
 	if(menu)
 	{
-		this.removeClassName(menu, &quot;MenuBarSubmenuVisible&quot;);
-		if(typeof document.all != 'undefined' &amp;&amp; typeof window.opera == 'undefined' &amp;&amp; navigator.vendor != 'KDE')
+		this.removeClassName(menu, this.subVisibleClass);
+		if(typeof document.all != 'undefined' &amp;&amp; !Spry.is.opera &amp;&amp; navigator.vendor != 'KDE')
 		{
 			menu.style.top = '';
 			menu.style.left = '';
 		}
-		this.removeIframeLayer(menu);
+		if(Spry.is.ie &amp;&amp; Spry.is.version &lt; 7)
+			this.removeIframeLayer(menu);
 	}
 };
 
 // initialize for Menu Bar
 // create event listeners for the Menu Bar widget so we can properly
 // show and hide submenus
-Spry.Widget.MenuBar.prototype.initialize = function(listitem, element, isie)
+Spry.Widget.MenuBar.prototype.initialize = function(listitem, element)
 {
 	var opentime, closetime;
 	var link = listitem.getElementsByTagName('a')[0];
 	var submenus = listitem.getElementsByTagName('ul');
 	var menu = (submenus.length &gt; 0 ? submenus[0] : null);
 
-	var hasSubMenu = false;
 	if(menu)
-	{
-		this.addClassName(link, &quot;MenuBarItemSubmenu&quot;);
-		hasSubMenu = true;
-	}
+		this.addClassName(link, this.hasSubClass);
 
-	if(!isie)
+	if(!Spry.is.ie)
 	{
 		// define a simple function that comes standard in IE to determine
 		// if a node is within another node
@@ -270,63 +350,410 @@ Spry.Widget.MenuBar.prototype.initialize = function(listitem, element, isie)
 		{
 			// this refers to the list item
 			if(testNode == null)
-			{
 				return false;
-			}
+
 			if(testNode == this)
-			{
 				return true;
-			}
 			else
-			{
 				return this.contains(testNode.parentNode);
-			}
 		};
 	}
-	
+
 	// need to save this for scope further down
 	var self = this;
+	this.addEventListener(listitem, 'mouseover', function(e){self.mouseOver(listitem, e);}, false);
+	this.addEventListener(listitem, 'mouseout', function(e){if (self.enableKeyboardNavigation) self.clearSelection(); self.mouseOut(listitem, e);}, false);
 
-	this.addEventListener(listitem, 'mouseover', function(e)
+	if (this.enableKeyboardNavigation)
 	{
-		if(self.bubbledTextEvent())
+		this.addEventListener(link, 'blur', function(e){self.onBlur(listitem);}, false);
+		this.addEventListener(link, 'focus', function(e){self.keyFocus(listitem, e);}, false);
+	}
+};
+Spry.Widget.MenuBar.prototype.keyFocus = function (listitem, e)
+{
+	this.lastOpen = listitem.getElementsByTagName('a')[0];
+	this.addClassName(this.lastOpen, listitem.getElementsByTagName('ul').length &gt; 0 ? this.subHoverClass : this.hoverClass);
+	this.hasFocus = true;
+};
+Spry.Widget.MenuBar.prototype.onBlur = function (listitem)
+{
+	this.clearSelection(listitem);
+};
+Spry.Widget.MenuBar.prototype.clearSelection = function(el){
+	//search any intersection with the current open element
+	if (!this.lastOpen)
+		return;
+
+	if (el)
+	{
+		el = el.getElementsByTagName('a')[0];
+		
+		// check children
+		var item = this.lastOpen;
+		while (item != this.element)
 		{
-			// ignore bubbled text events
-			return;
+			var tmp = el;
+			while (tmp != this.element)
+			{
+				if (tmp == item)
+					return;
+				try{
+					tmp = tmp.parentNode;
+				}catch(err){break;}
+			}
+			item = item.parentNode;
 		}
-		clearTimeout(closetime);
-		if(self.currMenu == listitem)
+	}
+	var item = this.lastOpen;
+	while (item != this.element)
+	{
+		this.hideSubmenu(item.parentNode);
+		var link = item.getElementsByTagName('a')[0];
+		this.removeClassName(link, this.hoverClass);
+		this.removeClassName(link, this.subHoverClass);
+		item = item.parentNode;
+	}
+	this.lastOpen = false;
+};
+Spry.Widget.MenuBar.prototype.keyDown = function (e)
+{
+	if (!this.hasFocus)
+		return;
+
+	if (!this.lastOpen)
+	{
+		this.hasFocus = false;
+		return;
+	}
+
+	var e = e|| event;
+	var listitem = this.lastOpen.parentNode;
+	var link = this.lastOpen;
+	var submenus = listitem.getElementsByTagName('ul');
+	var menu = (submenus.length &gt; 0 ? submenus[0] : null);
+	var hasSubMenu = (menu) ? true : false;
+
+	var opts = [listitem, menu, null, this.getSibling(listitem, 'previousSibling'), this.getSibling(listitem, 'nextSibling')];
+	
+	if (!opts[3])
+		opts[2] = (listitem.parentNode.parentNode.nodeName.toLowerCase() == 'li')?listitem.parentNode.parentNode:null;
+
+	var found = 0;
+	switch (e.keyCode){
+		case this.upKeyCode:
+			found = this.getElementForKey(opts, 'y', 1);
+			break;
+		case this.downKeyCode:
+			found = this.getElementForKey(opts, 'y', -1);
+			break;
+		case this.leftKeyCode:
+			found = this.getElementForKey(opts, 'x', 1);
+			break;
+		case this.rightKeyCode:
+			found = this.getElementForKey(opts, 'x', -1);
+			break;
+		case this.escKeyCode:
+		case 9:
+			this.clearSelection();
+			this.hasFocus = false;
+		default: return;
+	}
+	switch (found)
+	{
+		case 0: return;
+		case 1:
+			//subopts
+			this.mouseOver(listitem, e);
+			break;
+		case 2:
+			//parent
+			this.mouseOut(opts[2], e);
+			break;
+		case 3:
+		case 4:
+			// left - right
+			this.removeClassName(link, hasSubMenu ? this.subHoverClass : this.hoverClass);
+			break;
+	}
+	var link = opts[found].getElementsByTagName('a')[0];
+	if (opts[found].nodeName.toLowerCase() == 'ul')
+		opts[found] = opts[found].getElementsByTagName('li')[0];
+
+	this.addClassName(link, opts[found].getElementsByTagName('ul').length &gt; 0 ? this.subHoverClass : this.hoverClass);
+	this.lastOpen = link;
+	opts[found].getElementsByTagName('a')[0].focus();
+  
+        //stop further event handling by the browser
+	return Spry.Widget.MenuBar.stopPropagation(e);
+};
+Spry.Widget.MenuBar.prototype.mouseOver = function (listitem, e)
+{
+	var link = listitem.getElementsByTagName('a')[0];
+	var submenus = listitem.getElementsByTagName('ul');
+	var menu = (submenus.length &gt; 0 ? submenus[0] : null);
+	var hasSubMenu = (menu) ? true : false;
+	if (this.enableKeyboardNavigation)
+		this.clearSelection(listitem);
+
+	if(this.bubbledTextEvent())
+	{
+		// ignore bubbled text events
+		return;
+	}
+
+	if (listitem.closetime)
+		clearTimeout(listitem.closetime);
+
+	if(this.currMenu == listitem)
+	{
+		this.currMenu = null;
+	}
+
+	// move the focus too
+	if (this.hasFocus)
+		link.focus();
+
+	// show menu highlighting
+	this.addClassName(link, hasSubMenu ? this.subHoverClass : this.hoverClass);
+	this.lastOpen = link;
+	if(menu &amp;&amp; !this.hasClassName(menu, this.subHoverClass))
+	{
+		var self = this;
+		listitem.opentime = window.setTimeout(function(){self.showSubmenu(menu);}, this.showDelay);
+	}
+};
+Spry.Widget.MenuBar.prototype.mouseOut = function (listitem, e)
+{
+	var link = listitem.getElementsByTagName('a')[0];
+	var submenus = listitem.getElementsByTagName('ul');
+	var menu = (submenus.length &gt; 0 ? submenus[0] : null);
+	var hasSubMenu = (menu) ? true : false;
+	if(this.bubbledTextEvent())
+	{
+		// ignore bubbled text events
+		return;
+	}
+
+	var related = (typeof e.relatedTarget != 'undefined' ? e.relatedTarget : e.toElement);
+	if(!listitem.contains(related))
+	{
+		if (listitem.opentime) 
+			clearTimeout(listitem.opentime);
+		this.currMenu = listitem;
+
+		// remove menu highlighting
+		this.removeClassName(link, hasSubMenu ? this.subHoverClass : this.hoverClass);
+		if(menu)
 		{
-			self.currMenu = null;
+			var self = this;
+			listitem.closetime = window.setTimeout(function(){self.hideSubmenu(menu);}, this.hideDelay);
 		}
-		// show menu highlighting
-		self.addClassName(link, hasSubMenu ? &quot;MenuBarItemSubmenuHover&quot; : &quot;MenuBarItemHover&quot;);
-		if(menu &amp;&amp; !self.hasClassName(menu, &quot;MenuBarSubmenuVisible&quot;))
+		if (this.hasFocus)
+			link.blur();
+	}
+};
+Spry.Widget.MenuBar.prototype.getSibling = function(element, sibling)
+{
+	var child = element[sibling];
+	while (child &amp;&amp; child.nodeName.toLowerCase() !='li')
+		child = child[sibling];
+
+	return child;
+};
+Spry.Widget.MenuBar.prototype.getElementForKey = function(els, prop, dir)
+{
+	var found = 0;
+	var rect = Spry.Widget.MenuBar.getPosition;
+	var ref = rect(els[found]);
+
+	var hideSubmenu = false;
+	//make the subelement visible to compute the position
+	if (els[1] &amp;&amp; !this.hasClassName(els[1], this.MenuBarSubmenuVisible))
+	{
+		els[1].style.visibility = 'hidden';
+		this.showSubmenu(els[1]);
+		hideSubmenu = true;
+	}
+
+	var isVert = this.hasClassName(this.element, this.verticalClass);
+	var hasParent = els[0].parentNode.parentNode.nodeName.toLowerCase() == 'li' ? true : false;
+	
+	for (var i = 1; i &lt; els.length; i++){
+		//when navigating on the y axis in vertical menus, ignore children and parents
+		if(prop=='y' &amp;&amp; isVert &amp;&amp; (i==1 || i==2))
+		{
+			continue;
+		}
+		//when navigationg on the x axis in the FIRST LEVEL of horizontal menus, ignore children and parents
+		if(prop=='x' &amp;&amp; !isVert &amp;&amp; !hasParent &amp;&amp; (i==1 || i==2))
 		{
-			opentime = window.setTimeout(function(){self.showSubmenu(menu);}, 250);
+			continue;
 		}
-	}, false);
+			
+		if (els[i])
+		{
+			var tmp = rect(els[i]); 
+			if ( (dir * tmp[prop]) &lt; (dir * ref[prop]))
+			{
+				ref = tmp;
+				found = i;
+			}
+		}
+	}
+	
+	// hide back the submenu
+	if (els[1] &amp;&amp; hideSubmenu){
+		this.hideSubmenu(els[1]);
+		els[1].style.visibility =  '';
+	}
+
+	return found;
+};
+Spry.Widget.MenuBar.camelize = function(str)
+{
+	if (str.indexOf('-') == -1){
+		return str;	
+	}
+	var oStringList = str.split('-');
+	var isFirstEntry = true;
+	var camelizedString = '';
 
-	this.addEventListener(listitem, 'mouseout', function(e)
+	for(var i=0; i &lt; oStringList.length; i++)
 	{
-		if(self.bubbledTextEvent())
+		if(oStringList[i].length&gt;0)
 		{
-			// ignore bubbled text events
-			return;
+			if(isFirstEntry)
+			{
+				camelizedString = oStringList[i];
+				isFirstEntry = false;
+			}
+			else
+			{
+				var s = oStringList[i];
+				camelizedString += s.charAt(0).toUpperCase() + s.substring(1);
+			}
 		}
+	}
 
-		var related = (typeof e.relatedTarget != 'undefined' ? e.relatedTarget : e.toElement);
-		if(!listitem.contains(related))
-		{
-			clearTimeout(opentime);
-			self.currMenu = listitem;
+	return camelizedString;
+};
 
-			// remove menu highlighting
-			self.removeClassName(link, hasSubMenu ? &quot;MenuBarItemSubmenuHover&quot; : &quot;MenuBarItemHover&quot;);
-			if(menu)
+Spry.Widget.MenuBar.getStyleProp = function(element, prop)
+{
+	var value;
+	try
+	{
+		if (element.style)
+			value = element.style[Spry.Widget.MenuBar.camelize(prop)];
+
+		if (!value)
+			if (document.defaultView &amp;&amp; document.defaultView.getComputedStyle)
 			{
-				closetime = window.setTimeout(function(){self.hideSubmenu(menu);}, 600);
+				var css = document.defaultView.getComputedStyle(element, null);
+				value = css ? css.getPropertyValue(prop) : null;
 			}
+			else if (element.currentStyle) 
+			{
+					value = element.currentStyle[Spry.Widget.MenuBar.camelize(prop)];
+			}
+	}
+	catch (e) {}
+
+	return value == 'auto' ? null : value;
+};
+Spry.Widget.MenuBar.getIntProp = function(element, prop)
+{
+	var a = parseInt(Spry.Widget.MenuBar.getStyleProp(element, prop),10);
+	if (isNaN(a))
+		return 0;
+	return a;
+};
+
+Spry.Widget.MenuBar.getPosition = function(el, doc)
+{
+	doc = doc || document;
+	if (typeof(el) == 'string') {
+		el = doc.getElementById(el);
+	}
+
+	if (!el) {
+		return false;
+	}
+
+	if (el.parentNode === null || Spry.Widget.MenuBar.getStyleProp(el, 'display') == 'none') {
+		//element must be visible to have a box
+		return false;
+	}
+
+	var ret = {x:0, y:0};
+	var parent = null;
+	var box;
+
+	if (el.getBoundingClientRect) { // IE
+		box = el.getBoundingClientRect();
+		var scrollTop = doc.documentElement.scrollTop || doc.body.scrollTop;
+		var scrollLeft = doc.documentElement.scrollLeft || doc.body.scrollLeft;
+		ret.x = box.left + scrollLeft;
+		ret.y = box.top + scrollTop;
+	} else if (doc.getBoxObjectFor) { // gecko
+		box = doc.getBoxObjectFor(el);
+		ret.x = box.x;
+		ret.y = box.y;
+	} else { // safari/opera
+		ret.x = el.offsetLeft;
+		ret.y = el.offsetTop;
+		parent = el.offsetParent;
+		if (parent != el) {
+			while (parent) {
+				ret.x += parent.offsetLeft;
+				ret.y += parent.offsetTop;
+				parent = parent.offsetParent;
+			}
+		}
+		// opera &amp; (safari absolute) incorrectly account for body offsetTop
+		if (Spry.is.opera || Spry.is.safari &amp;&amp; Spry.Widget.MenuBar.getStyleProp(el, 'position') == 'absolute')
+			ret.y -= doc.body.offsetTop;
+	}
+	if (el.parentNode)
+			parent = el.parentNode;
+	else
+		parent = null;
+	if (parent.nodeName){
+		var cas = parent.nodeName.toUpperCase();
+		while (parent &amp;&amp; cas != 'BODY' &amp;&amp; cas != 'HTML') {
+			cas = parent.nodeName.toUpperCase();
+			ret.x -= parent.scrollLeft;
+			ret.y -= parent.scrollTop;
+			if (parent.parentNode)
+				parent = parent.parentNode;
+			else
+				parent = null;
 		}
-	}, false);
+	}
+	return ret;
+};
+
+Spry.Widget.MenuBar.stopPropagation = function(ev)
+{
+	if (ev.stopPropagation)
+		ev.stopPropagation();
+	else
+		ev.cancelBubble = true;
+	if (ev.preventDefault) 
+		ev.preventDefault();
+	else 
+		ev.returnValue = false;
+};
+
+Spry.Widget.MenuBar.setOptions = function(obj, optionsObj, ignoreUndefinedProps)
+{
+	if (!optionsObj)
+		return;
+	for (var optionName in optionsObj)
+	{
+		if (ignoreUndefinedProps &amp;&amp; optionsObj[optionName] == undefined)
+			continue;
+		obj[optionName] = optionsObj[optionName];
+	}
 };</diff>
      <filename>resources/script/spry/widgets/menubar/SpryMenuBar.js</filename>
    </modified>
    <modified>
      <diff>@@ -1,6 +1,6 @@
 @charset &quot;UTF-8&quot;;
 
-/* SpryMenuBarHorizontal.css - version 0.6 - Spry Pre-Release 1.5 */
+/* SpryMenuBarHorizontal.css - version 0.6 - Spry Pre-Release 1.6.1 */
 
 /* Copyright (c) 2006. Adobe Systems Incorporated. All rights reserved. */
 
@@ -154,6 +154,7 @@ ul.MenuBarHorizontal iframe
 {
 	position: absolute;
 	z-index: 1010;
+	filter:alpha(opacity:0.1);
 }
 /* HACK FOR IE: to stabilize appearance of menu items; the slash in float is to keep IE 5.0 from parsing */
 @media screen, projection</diff>
      <filename>resources/script/spry/widgets/menubar/SpryMenuBarHorizontal.css</filename>
    </modified>
    <modified>
      <diff>@@ -1,6 +1,6 @@
 @charset &quot;UTF-8&quot;;
 
-/* SpryMenuBarVertical.css - version 0.6 - Spry Pre-Release 1.5 */
+/* SpryMenuBarVertical.css - version 0.6 - Spry Pre-Release 1.6.1 */
 
 /* Copyright (c) 2006. Adobe Systems Incorporated. All rights reserved. */
 
@@ -134,6 +134,7 @@ ul.MenuBarVertical iframe
 {
 	position: absolute;
 	z-index: 1010;
+	filter:alpha(opacity:0.1);
 }
 /* HACK FOR IE: to stabilize appearance of menu items; the slash in float is to keep IE 5.0 from parsing */
 @media screen, projection</diff>
      <filename>resources/script/spry/widgets/menubar/SpryMenuBarVertical.css</filename>
    </modified>
    <modified>
      <diff>@@ -1,6 +1,6 @@
 @charset &quot;UTF-8&quot;;
 
-/* SpryValidationRadio.css - version 0.1 - Spry Pre-Release 1.5 */
+/* SpryValidationRadio.css - version 0.1 - Spry Pre-Release 1.6.1 */
 
 /* Copyright (c) 2007. Adobe Systems Incorporated. All rights reserved. */
 
@@ -22,5 +22,4 @@
 .radioInvalidState .radioInvalidMsg{
 	display: inline;
 	color: #CC3333;
-	border: 1px solid #CC3333;
 }</diff>
      <filename>resources/script/spry/widgets/radiovalidation/SpryValidationRadio.css</filename>
    </modified>
    <modified>
      <diff>@@ -1,4 +1,4 @@
-// SpryValidationRadio.js - version 0.1 - Spry Pre-Release 1.5
+// SpryValidationRadio.js - version 0.1 - Spry Pre-Release 1.6.1
 //
 // Copyright (c) 2007. Adobe Systems Incorporated.
 // All rights reserved.
@@ -120,6 +120,8 @@ Spry.Widget.ValidationRadio.addLoadListener(function(){
 
 Spry.Widget.ValidationRadio.prototype.attachBehaviors = function()
 {
+	if (!this.element)
+		return;
 	// find the INPUT type=&quot;Radio&quot; element(s) inside current container
 	if (this.element.nodeName == &quot;INPUT&quot;) {
 		this.radioElements = [this.element];
@@ -295,13 +297,17 @@ Spry.Widget.ValidationRadio.prototype.isDisabled = function()
 
 Spry.Widget.ValidationRadio.prototype.destroy = function()
 {
-	for (var i=0; i&lt;this.event_handlers.length; i++) {
-		Spry.Widget.Utils.removeEventListener(this.event_handlers[i][0], this.event_handlers[i][1], this.event_handlers[i][2], false);
-	}
+	if (this.event_handlers)
+		for (var i=0; i&lt;this.event_handlers.length; i++)
+		{
+			Spry.Widget.Utils.removeEventListener(this.event_handlers[i][0], this.event_handlers[i][1], this.event_handlers[i][2], false);
+		}
 	try { delete this.element; } catch(err) {}
-	for(var i=0; i &lt; this.radioElements.length; i++) {
-		try { delete this.radioElements[i];} catch(err) {}
-	}
+	if (this.radioElements)
+		for(var i=0; i &lt; this.radioElements.length; i++)
+		{
+			try { delete this.radioElements[i];} catch(err) {}
+		}
 	try { delete this.radioElements; } catch(err) {}
 	try { delete this.form; } catch(err) {}
 	try { delete this.event_handlers; } catch(err) {}</diff>
      <filename>resources/script/spry/widgets/radiovalidation/SpryValidationRadio.js</filename>
    </modified>
    <modified>
      <diff>@@ -1,6 +1,6 @@
 @charset &quot;UTF-8&quot;;
 
-/* SpryValidationSelect.css - version 0.4 - Spry Pre-Release 1.5 */
+/* SpryValidationSelect.css - version 0.4 - Spry Pre-Release 1.6.1 */
 
 /* Copyright (c) 2006. Adobe Systems Incorporated. All rights reserved. */
 </diff>
      <filename>resources/script/spry/widgets/selectvalidation/SpryValidationSelect.css</filename>
    </modified>
    <modified>
      <diff>@@ -1,4 +1,4 @@
-// SpryValidationSelect.js - version 0.10 - Spry Pre-Release 1.5
+// SpryValidationSelect.js - version 0.10 - Spry Pre-Release 1.6.1
 //
 // Copyright (c) 2006. Adobe Systems Incorporated.
 // All rights reserved.
@@ -90,9 +90,10 @@ Spry.Widget.ValidationSelect.prototype.init = function(element)
 };
 
 Spry.Widget.ValidationSelect.prototype.destroy = function() {
-	for (var i=0; i&lt;this.event_handlers.length; i++) {
-		Spry.Widget.Utils.removeEventListener(this.event_handlers[i][0], this.event_handlers[i][1], this.event_handlers[i][2], false);
-	}
+	if (this.event_handlers)
+		for (var i=0; i&lt;this.event_handlers.length; i++) {
+			Spry.Widget.Utils.removeEventListener(this.event_handlers[i][0], this.event_handlers[i][1], this.event_handlers[i][2], false);
+		}
 	try { delete this.element; } catch(err) {}
 	try { delete this.selectElement; } catch(err) {}
 	try { delete this.form; } catch(err) {}
@@ -295,11 +296,11 @@ Spry.Widget.ValidationSelect.prototype.validate = function() {
 	this.addClassName(this.element, this.validClass);
 	this.addClassName(this.additionalError, this.validClass);
 	return true;
-}
+};
 
 Spry.Widget.ValidationSelect.prototype.isDisabled = function() {
 	return this.selectElement.disabled;	
-}
+};
 
 //////////////////////////////////////////////////////////////////////
 //</diff>
      <filename>resources/script/spry/widgets/selectvalidation/SpryValidationSelect.js</filename>
    </modified>
    <modified>
      <diff>@@ -1,6 +1,6 @@
 @charset &quot;UTF-8&quot;;
 
-/* SprySlidingPanels.css - version 0.1 - Spry Pre-Release 1.5 */
+/* SprySlidingPanels.css - version 0.1 - Spry Pre-Release 1.6.1 */
 
 /* Copyright (c) 2006. Adobe Systems Incorporated. All rights reserved. */
 </diff>
      <filename>resources/script/spry/widgets/slidingpanels/SprySlidingPanels.css</filename>
    </modified>
    <modified>
      <diff>@@ -1,4 +1,4 @@
-// SprySlidingPanels.js - version 0.4 - Spry Pre-Release 1.5
+// SprySlidingPanels.js - version 0.5 - Spry Pre-Release 1.6.1
 //
 // Copyright (c) 2006. Adobe Systems Incorporated.
 // All rights reserved.
@@ -111,7 +111,7 @@ Spry.Widget.SlidingPanels.prototype.onKeyDown = function(e)
 		this.showPreviousPanel();
 
 	if (e.preventDefault) e.preventDefault();
-	else e.returnResult = false;
+	else e.returnValue = false;
 	if (e.stopPropagation) e.stopPropagation();
 	else e.cancelBubble = true;
 
@@ -362,7 +362,7 @@ Spry.Widget.SlidingPanels.PanelAnimator = function(ele, curX, curY, dstX, dstY,
 	this.finish = null;
 
 	var self = this;
-	this.intervalFunc = function() { self.step(); }
+	this.intervalFunc = function() { self.step(); };
 	
 	Spry.Widget.SlidingPanels.setOptions(this, opts, true);
 </diff>
      <filename>resources/script/spry/widgets/slidingpanels/SprySlidingPanels.js</filename>
    </modified>
    <modified>
      <diff>@@ -1,6 +1,6 @@
 @charset &quot;UTF-8&quot;;
 
-/* SpryTabbedPanels.css - version 0.4 - Spry Pre-Release 1.5 */
+/* SpryTabbedPanels.css - version 0.4 - Spry Pre-Release 1.6.1 */
 
 /* Copyright (c) 2006. Adobe Systems Incorporated. All rights reserved. */
 </diff>
      <filename>resources/script/spry/widgets/tabbedpanels/SpryTabbedPanels.css</filename>
    </modified>
    <modified>
      <diff>@@ -1,4 +1,4 @@
-// SpryTabbedPanels.js - version 0.4 - Spry Pre-Release 1.5
+// SpryTabbedPanels.js - version 0.6 - Spry Pre-Release 1.6.1
 //
 // Copyright (c) 2006. Adobe Systems Incorporated.
 // All rights reserved.
@@ -43,6 +43,8 @@ Spry.Widget.TabbedPanels = function(element, opts)
 	this.hasFocus = false;
 	this.currentTabIndex = 0;
 	this.enableKeyboardNavigation = true;
+	this.nextPanelKeyCode = Spry.Widget.TabbedPanels.KEY_RIGHT;
+	this.previousPanelKeyCode = Spry.Widget.TabbedPanels.KEY_LEFT;
 
 	Spry.Widget.TabbedPanels.setOptions(this, opts);
 
@@ -78,7 +80,7 @@ Spry.Widget.TabbedPanels.prototype.getElement = function(ele)
 	if (ele &amp;&amp; typeof ele == &quot;string&quot;)
 		return document.getElementById(ele);
 	return ele;
-}
+};
 
 Spry.Widget.TabbedPanels.prototype.getElementChildren = function(element)
 {
@@ -203,18 +205,22 @@ Spry.Widget.TabbedPanels.addEventListener = function(element, eventType, handler
 	catch (e) {}
 };
 
-Spry.Widget.TabbedPanels.prototype.onTabClick = function(e, tab)
+Spry.Widget.TabbedPanels.prototype.cancelEvent = function(e)
 {
-	this.showPanel(tab);
-
 	if (e.preventDefault) e.preventDefault();
-	else e.returnResult = false;
+	else e.returnValue = false;
 	if (e.stopPropagation) e.stopPropagation();
 	else e.cancelBubble = true;
 
 	return false;
 };
 
+Spry.Widget.TabbedPanels.prototype.onTabClick = function(e, tab)
+{
+	this.showPanel(tab);
+	return this.cancelEvent(e);
+};
+
 Spry.Widget.TabbedPanels.prototype.onTabMouseOver = function(e, tab)
 {
 	this.addClassName(tab, this.tabHoverClass);
@@ -241,23 +247,38 @@ Spry.Widget.TabbedPanels.prototype.onTabBlur = function(e, tab)
 	return false;
 };
 
-Spry.Widget.TabbedPanels.ENTER_KEY = 13;
-Spry.Widget.TabbedPanels.SPACE_KEY = 32;
+Spry.Widget.TabbedPanels.KEY_UP = 38;
+Spry.Widget.TabbedPanels.KEY_DOWN = 40;
+Spry.Widget.TabbedPanels.KEY_LEFT = 37;
+Spry.Widget.TabbedPanels.KEY_RIGHT = 39;
+
+
 
 Spry.Widget.TabbedPanels.prototype.onTabKeyDown = function(e, tab)
 {
 	var key = e.keyCode;
-	if (!this.hasFocus || (key != Spry.Widget.TabbedPanels.ENTER_KEY &amp;&amp; key != Spry.Widget.TabbedPanels.SPACE_KEY))
+	if (!this.hasFocus || (key != this.previousPanelKeyCode &amp;&amp; key != this.nextPanelKeyCode))
 		return true;
 
-	this.showPanel(tab);
+	var tabs = this.getTabs();
+	for (var i =0; i &lt; tabs.length; i++)
+		if (tabs[i] == tab)
+		{
+			var el = false;
+			if (key == this.previousPanelKeyCode &amp;&amp; i &gt; 0)
+				el = tabs[i-1];
+			else if (key == this.nextPanelKeyCode &amp;&amp; i &lt; tabs.length-1)
+				el = tabs[i+1];
 
-	if (e.preventDefault) e.preventDefault();
-	else e.returnResult = false;
-	if (e.stopPropagation) e.stopPropagation();
-	else e.cancelBubble = true;
+			if (el)
+			{
+				this.showPanel(el);
+				el.focus();
+				break;
+			}
+		}
 
-	return false;
+	return this.cancelEvent(e);
 };
 
 Spry.Widget.TabbedPanels.prototype.preorderTraversal = function(root, func)</diff>
      <filename>resources/script/spry/widgets/tabbedpanels/SpryTabbedPanels.js</filename>
    </modified>
    <modified>
      <diff>@@ -1,6 +1,6 @@
 @charset &quot;UTF-8&quot;;
 
-/* SpryFormValidation.css - version 0.5 - Spry Pre-Release 1.5 */
+/* SpryFormValidation.css - version 0.5 - Spry Pre-Release 1.6.1 */
 
 /* Copyright (c) 2006. Adobe Systems Incorporated. All rights reserved. */
 </diff>
      <filename>resources/script/spry/widgets/textareavalidation/SpryValidationTextarea.css</filename>
    </modified>
    <modified>
      <diff>@@ -1,4 +1,4 @@
-// SpryValidationTextarea.js - version 0.15 - Spry Pre-Release 1.5
+// SpryValidationTextarea.js - version 0.17 - Spry Pre-Release 1.6.1
 //
 // Copyright (c) 2006. Adobe Systems Incorporated.
 // All rights reserved.
@@ -31,28 +31,28 @@ var Spry;
 if (!Spry) Spry = {};
 if (!Spry.Widget) Spry.Widget = {};
 
-Spry.Widget.BrowserSniff = function() {
+Spry.Widget.BrowserSniff = function()
+{
 	var b = navigator.appName.toString();
 	var up = navigator.platform.toString();
 	var ua = navigator.userAgent.toString();
 
-	this.mozilla = this.ie = this.opera = r = false;
+	this.mozilla = this.ie = this.opera = this.safari = false;
 	var re_opera = /Opera.([0-9\.]*)/i;
 	var re_msie = /MSIE.([0-9\.]*)/i;
 	var re_gecko = /gecko/i;
-	var re_safari = /safari\/([\d\.]*)/i;
-	
-	if (ua.match(re_opera)) {
-		r = ua.match(re_opera);
+	var re_safari = /(applewebkit|safari)\/([\d\.]*)/i;
+	var r = false;
+
+	if ( (r = ua.match(re_opera))) {
 		this.opera = true;
 		this.version = parseFloat(r[1]);
-	} else if (ua.match(re_msie)) {
-		r = ua.match(re_msie);
+	} else if ( (r = ua.match(re_msie))) {
 		this.ie = true;
 		this.version = parseFloat(r[1]);
-	} else if (ua.match(re_safari)) {
+	} else if ( (r = ua.match(re_safari))) {
 		this.safari = true;
-		this.version = 1.4;
+		this.version = parseFloat(r[2]);
 	} else if (ua.match(re_gecko)) {
 		var re_gecko_version = /rv:\s*([0-9\.]+)/i;
 		r = ua.match(re_gecko_version);
@@ -78,7 +78,7 @@ Spry.is = new Spry.Widget.BrowserSniff();
 
 Spry.Widget.ValidationTextarea = function(element, options){
 	
-	options = options || {};
+	options = Spry.Widget.Utils.firstValid(options, {});
 	this.flags = {locked: false};
 	this.options = {};
 	this.element = element;
@@ -147,9 +147,10 @@ Spry.Widget.ValidationTextarea.prototype.init = function(element)
 };
 
 Spry.Widget.ValidationTextarea.prototype.destroy = function() {
-	for (var i=0; i&lt;this.event_handlers.length; i++) {
-		Spry.Widget.Utils.removeEventListener(this.event_handlers[i][0], this.event_handlers[i][1], this.event_handlers[i][2], false);
-	}
+	if (this.event_handlers)
+		for (var i=0; i&lt;this.event_handlers.length; i++) {
+			Spry.Widget.Utils.removeEventListener(this.event_handlers[i][0], this.event_handlers[i][1], this.event_handlers[i][2], false);
+		}
 	try { delete this.element; } catch(err) {}
 	try { delete this.input; } catch(err) {}
 	try { delete this.counterEl; } catch(err) {}
@@ -485,6 +486,10 @@ Spry.Widget.ValidationTextarea.prototype.validate = function(){
 			return true;	
 	}
 
+  if (this.validateOn &amp; Spry.Widget.ValidationTextarea.ONSUBMIT) {
+    this.removeHint();
+  }
+  
 	var val = this.input.value;
 	this.validateMinRequired(val);
 
@@ -814,32 +819,31 @@ Spry.Widget.SelectionDescriptor = function (element)
 Spry.Widget.SelectionDescriptor.prototype.update = function()
 {
 	if (Spry.is.ie &amp;&amp; Spry.is.windows) {
+		var sel = this.element.ownerDocument.selection;
 		if (this.element.nodeName == &quot;TEXTAREA&quot;) {
-            var sel = this.element.ownerDocument.selection;
-            if (sel.type != 'None') {
-		var range = this.element.ownerDocument.selection.createRange();
-		if (range.parentElement() == this.element){
-			var range_all = this.element.ownerDocument.body.createTextRange();
-			range_all.moveToElementText(this.element);
-			for (var sel_start = 0; range_all.compareEndPoints('StartToStart', range) &lt; 0; sel_start ++){
-			 	range_all.moveStart('character', 1);
-			}
-			this.start = sel_start;
-			// create a selection of the whole this.element
-			range_all = this.element.ownerDocument.body.createTextRange();
-			range_all.moveToElementText(this.element);
-			for (var sel_end = 0; range_all.compareEndPoints('StartToEnd', range) &lt; 0; sel_end++){
-				range_all.moveStart('character', 1);
-			}
-			this.end = sel_end;
-			this.length = this.end - this.start;
-			// get selected and surrounding text
-			this.text = range.text;
-		 }
-            }        
+			if (sel.type != 'None') {
+				try{var range = sel.createRange();}catch(err){return;}
+				if (range.parentElement() == this.element){
+					var range_all = this.element.ownerDocument.body.createTextRange();
+					range_all.moveToElementText(this.element);
+					for (var sel_start = 0; range_all.compareEndPoints('StartToStart', range) &lt; 0; sel_start ++){
+						range_all.moveStart('character', 1);
+					}
+					this.start = sel_start;
+					// create a selection of the whole this.element
+					range_all = this.element.ownerDocument.body.createTextRange();
+					range_all.moveToElementText(this.element);
+					for (var sel_end = 0; range_all.compareEndPoints('StartToEnd', range) &lt; 0; sel_end++){
+						range_all.moveStart('character', 1);
+					}
+					this.end = sel_end;
+					this.length = this.end - this.start;
+					// get selected and surrounding text
+					this.text = range.text;
+		 		}
+			}        
 		} else if (this.element.nodeName == &quot;INPUT&quot;){
-            var sel = this.element.ownerDocument.selection;
-			this.range = this.element.ownerDocument.selection.createRange();
+			try{this.range = sel.createRange();}catch(err){return;}
 			this.length = this.range.text.length;
 			var clone = this.range.duplicate();
 			this.start = -clone.moveStart(&quot;character&quot;, -10000);
@@ -852,9 +856,9 @@ Spry.Widget.SelectionDescriptor.prototype.update = function()
 		var tmp = this.element;
 		var selectionStart = 0;
 		var selectionEnd = 0;
-
-		try { selectionStart = tmp.selectionStart; } catch(err) {}
-		try { selectionEnd = tmp.selectionEnd; } catch(err) {}
+        
+		try { selectionStart = tmp.selectionStart;} catch(err) {}
+		try { selectionEnd = tmp.selectionEnd;} catch(err) {}
 
 		if (Spry.is.safari) {
 			if (selectionStart == 2147483647) {
@@ -870,7 +874,6 @@ Spry.Widget.SelectionDescriptor.prototype.update = function()
 		this.text = this.element.value.substring(selectionStart, selectionEnd);
 	}
 };
-
 Spry.Widget.SelectionDescriptor.prototype.destroy = function() {
 	try { delete this.range} catch(err) {}
 	try { delete this.element} catch(err) {}</diff>
      <filename>resources/script/spry/widgets/textareavalidation/SpryValidationTextarea.js</filename>
    </modified>
    <modified>
      <diff>@@ -1,6 +1,6 @@
 @charset &quot;UTF-8&quot;;
 
-/* SpryFormValidation.css - version 0.4 - Spry Pre-Release 1.5 */
+/* SpryValidationTextField.css - version 0.4 - Spry Pre-Release 1.6.1 */
 
 /* Copyright (c) 2006. Adobe Systems Incorporated. All rights reserved. */
 </diff>
      <filename>resources/script/spry/widgets/textfieldvalidation/SpryValidationTextField.css</filename>
    </modified>
    <modified>
      <diff>@@ -1,4 +1,4 @@
-// SpryValidationTextField.js - version 0.33 - Spry Pre-Release 1.5
+// SpryValidationTextField.js - version 0.37 - Spry Pre-Release 1.6.1
 //
 // Copyright (c) 2006. Adobe Systems Incorporated.
 // All rights reserved.
@@ -31,28 +31,28 @@ var Spry;
 if (!Spry) Spry = {};
 if (!Spry.Widget) Spry.Widget = {};
 
-Spry.Widget.BrowserSniff = function() {
+Spry.Widget.BrowserSniff = function()
+{
 	var b = navigator.appName.toString();
 	var up = navigator.platform.toString();
 	var ua = navigator.userAgent.toString();
 
-	this.mozilla = this.ie = this.opera = r = false;
+	this.mozilla = this.ie = this.opera = this.safari = false;
 	var re_opera = /Opera.([0-9\.]*)/i;
 	var re_msie = /MSIE.([0-9\.]*)/i;
 	var re_gecko = /gecko/i;
-	var re_safari = /safari\/([\d\.]*)/i;
-	
-	if (ua.match(re_opera)) {
-		r = ua.match(re_opera);
+	var re_safari = /(applewebkit|safari)\/([\d\.]*)/i;
+	var r = false;
+
+	if ( (r = ua.match(re_opera))) {
 		this.opera = true;
 		this.version = parseFloat(r[1]);
-	} else if (ua.match(re_msie)) {
-		r = ua.match(re_msie);
+	} else if ( (r = ua.match(re_msie))) {
 		this.ie = true;
 		this.version = parseFloat(r[1]);
-	} else if (ua.match(re_safari)) {
+	} else if ( (r = ua.match(re_safari))) {
 		this.safari = true;
-		this.version = 1.4;
+		this.version = parseFloat(r[2]);
 	} else if (ua.match(re_gecko)) {
 		var re_gecko_version = /rv:\s*([0-9\.]+)/i;
 		r = ua.match(re_gecko_version);
@@ -79,9 +79,11 @@ Spry.Widget.ValidationTextField = function(element, type, options)
 {
 	type = Spry.Widget.Utils.firstValid(type, &quot;none&quot;);
 	if (typeof type != 'string') {
+		this.showError('The second parameter in the constructor should be the validation type, the options are the third parameter.');
 		return;
 	}
 	if (typeof Spry.Widget.ValidationTextField.ValidationDescriptors[type] == 'undefined') {
+		this.showError('Unknown validation type received as the second parameter.');
 		return;
 	}
 	options = Spry.Widget.Utils.firstValid(options, {});
@@ -208,7 +210,7 @@ Spry.Widget.ValidationTextField.ValidationDescriptors = {
 	'email': {
 		characterMasking: /[^\s]/,
 		validation: function(value, options) {
-			var rx = /^[\w\.\+-]+@[\w\.-]+\.\w+$/i;
+			var rx = /^[\w\.-]+@[\w\.-]+\.\w+$/i;
 			return rx.test(value);
 		}
 	},
@@ -259,7 +261,7 @@ Spry.Widget.ValidationTextField.ValidationDescriptors = {
 							maxDay = 31;
 							break;
 						case 4:	// April
-						case 6: // Juna
+						case 6: // June
 						case 9: // September
 						case 11: // November
 							maxDay = 30;
@@ -279,7 +281,7 @@ Spry.Widget.ValidationTextField.ValidationDescriptors = {
 					}
 					
 					// If successfull we'll return the date object
-					return (new Date(theYear, theMonth, theDay));
+					return (new Date(theYear, theMonth - 1, theDay));   //JavaScript requires a month between 0 and 11
 				}
 			} else {
 				return false;
@@ -362,12 +364,12 @@ Spry.Widget.ValidationTextField.ValidationDescriptors = {
 			var regExp = null;
 			options.format = options.format || 'ALL';
 			switch (options.format.toUpperCase()) {
-				case 'ALL': regExp = /^[3-6]{1}[0-9]{12,15}$/; break;
-				case 'VISA': regExp = /^4[0-9]{12,15}$/; break;
+				case 'ALL': regExp = /^[3-6]{1}[0-9]{12,18}$/; break;
+				case 'VISA': regExp = /^4(?:[0-9]{12}|[0-9]{15})$/; break;
 				case 'MASTERCARD': regExp = /^5[1-5]{1}[0-9]{14}$/; break;
 				case 'AMEX': regExp = /^3(4|7){1}[0-9]{13}$/; break;
 				case 'DISCOVER': regExp = /^6011[0-9]{12}$/; break;
-				case 'DINERSCLUB': regExp = /^3((0[0-5]{1}[0-9]{11})|(6[0-9]{12})|(8[0-9]{12}))$/; break;
+				case 'DINERSCLUB': regExp = /^3(?:(0[0-5]{1}[0-9]{11})|(6[0-9]{12})|(8[0-9]{12}))$/; break;
 			}
 			if (!regExp.test(value)) {
 				return false;
@@ -729,9 +731,10 @@ Spry.Widget.ValidationTextField.prototype.init = function(element, options)
 };
 
 Spry.Widget.ValidationTextField.prototype.destroy = function() {
-	for (var i=0; i&lt;this.event_handlers.length; i++) {
-		Spry.Widget.Utils.removeEventListener(this.event_handlers[i][0], this.event_handlers[i][1], this.event_handlers[i][2], false);
-	}
+	if (this.event_handlers)
+		for (var i=0; i&lt;this.event_handlers.length; i++) {
+			Spry.Widget.Utils.removeEventListener(this.event_handlers[i][0], this.event_handlers[i][1], this.event_handlers[i][2], false);
+		}
 	try { delete this.element; } catch(err) {}
 	try { delete this.input; } catch(err) {}
 	try { delete this.form; } catch(err) {}
@@ -1016,7 +1019,7 @@ Spry.Widget.ValidationTextField.prototype.doValidations = function(testValue, re
 	}
 
 	if(!mustRevert &amp;&amp; this.validation &amp;&amp; this.minValue !== null &amp;&amp; continueValidations) {
-		var minValue = this.validation(this.minValue, this.options);
+		var minValue = this.validation(this.minValue.toString(), this.options);
 		if (minValue !== false) {
 			if (this.typedValue &lt; minValue) {
 				errors = errors | Spry.Widget.ValidationTextField.ERROR_RANGE_MIN;
@@ -1026,7 +1029,7 @@ Spry.Widget.ValidationTextField.prototype.doValidations = function(testValue, re
 	}
 
 	if(!mustRevert &amp;&amp; this.validation &amp;&amp; this.maxValue !== null &amp;&amp; continueValidations) {
-		var maxValue = this.validation(this.maxValue, this.options);
+		var maxValue = this.validation(this.maxValue.toString(), this.options);
 		if (maxValue !== false) {
 			if( this.typedValue &gt; maxValue) {
 				errors = errors | Spry.Widget.ValidationTextField.ERROR_RANGE_MAX;
@@ -1160,12 +1163,12 @@ Spry.Widget.ValidationTextField.prototype.compileDatePattern = function ()
 			}
 		}
 	}
-	this.dateValidationPattern = new RegExp(&quot;^&quot; + dateValidationPatternString + &quot;$&quot; , &quot;&quot;)
+	this.dateValidationPattern = new RegExp(&quot;^&quot; + dateValidationPatternString + &quot;$&quot; , &quot;&quot;);
 	this.dateAutocompleteCharacters = autocompleteCharacters;
 	this.dateGroupPatterns = groupPatterns;
 	this.dateFullGroupPatterns = fullGroupPatterns;
 	this.lastDateGroup = formatGroups.length-2;
-}
+};
 
 Spry.Widget.ValidationTextField.prototype.getRegExpForGroup = function (group) 
 {
@@ -1173,7 +1176,7 @@ Spry.Widget.ValidationTextField.prototype.getRegExpForGroup = function (group)
 	for (var j = 0; j &lt;= group; j++) ret += this.dateGroupPatterns[j];
 	ret += '$';
 	return new RegExp(ret, &quot;&quot;);	
-}
+};
 
 Spry.Widget.ValidationTextField.prototype.getRegExpForFullGroup = function (group) 
 {
@@ -1181,7 +1184,7 @@ Spry.Widget.ValidationTextField.prototype.getRegExpForFullGroup = function (grou
 	for (var j = 0; j &lt; group; j++) ret += this.dateGroupPatterns[j];
 	ret += this.dateFullGroupPatterns[group];
 	return new RegExp(ret, &quot;&quot;);	
-}
+};
 
 Spry.Widget.ValidationTextField.prototype.getDateGroup = function(value, pos) 
 {
@@ -1196,20 +1199,20 @@ Spry.Widget.ValidationTextField.prototype.getDateGroup = function(value, pos)
 Spry.Widget.ValidationTextField.prototype.isDateGroupFull = function(value, group) 
 {
 	return this.getRegExpForFullGroup(group).test(value);
-}
+};
 
 Spry.Widget.ValidationTextField.prototype.isValueValid = function(value, pos, group) 
 {
 	var test_value = value.substring(0, pos);
 	return this.getRegExpForGroup(group).test(test_value);
-	}
+};
 
 
 Spry.Widget.ValidationTextField.prototype.isPositionAtEndOfGroup = function (value, pos, group)
 {
 	var test_value = value.substring(0, pos);
 	return this.getRegExpForFullGroup(group).test(test_value);
-}
+};
 
 Spry.Widget.ValidationTextField.prototype.nextDateDelimiterExists = function (value, pos, group)
 {
@@ -1223,7 +1226,7 @@ Spry.Widget.ValidationTextField.prototype.nextDateDelimiterExists = function (va
 			return true;
 	}
 	return false;
-}
+};
 
 
 
@@ -1718,7 +1721,7 @@ Spry.Widget.ValidationTextField.prototype.patternToRegExp = function(len) {
 };
 
 Spry.Widget.ValidationTextField.prototype.resetClasses = function() {
-	var classes = [this.requiredClass, this.invalidFormatClass, this.invalidRangeMinClass, this.invalidRangeMaxClass, this.invalidCharsMinClass, this.invalidCharsMaxClass, this.validClass]
+	var classes = [this.requiredClass, this.invalidFormatClass, this.invalidRangeMinClass, this.invalidRangeMaxClass, this.invalidCharsMinClass, this.invalidCharsMaxClass, this.validClass];
 	for (var i=0; i &lt; classes.length; i++)
 	{
 		this.removeClassName(this.element, classes[i]);
@@ -1796,7 +1799,7 @@ Spry.Widget.ValidationTextField.prototype.validate = function() {
 	this.addClassName(this.element, this.validClass);
 	this.addClassName(this.additionalError, this.validClass);
 	return true;
-}
+};
 
 Spry.Widget.ValidationTextField.prototype.addClassName = function(ele, className)
 {
@@ -1811,7 +1814,10 @@ Spry.Widget.ValidationTextField.prototype.removeClassName = function(ele, classN
 		return;
 	ele.className = ele.className.replace(new RegExp(&quot;\\s*\\b&quot; + className + &quot;\\b&quot;, &quot;g&quot;), &quot;&quot;);
 };
-
+Spry.Widget.ValidationTextField.prototype.showError = function(msg)
+{
+	alert('Spry.Widget.TextField ERR: ' + msg);
+};
 /**
  * SelectionDescriptor is a wrapper for input type text selection methods and properties 
  * as implemented by various  browsers
@@ -1825,32 +1831,31 @@ Spry.Widget.SelectionDescriptor = function (element)
 Spry.Widget.SelectionDescriptor.prototype.update = function()
 {
 	if (Spry.is.ie &amp;&amp; Spry.is.windows) {
+		var sel = this.element.ownerDocument.selection;
 		if (this.element.nodeName == &quot;TEXTAREA&quot;) {
-            var sel = this.element.ownerDocument.selection;
-            if (sel.type != 'None') {
-		var range = this.element.ownerDocument.selection.createRange();
-		if (range.parentElement() == this.element){
-			var range_all = this.element.ownerDocument.body.createTextRange();
-			range_all.moveToElementText(this.element);
-			for (var sel_start = 0; range_all.compareEndPoints('StartToStart', range) &lt; 0; sel_start ++){
-			 	range_all.moveStart('character', 1);
-			}
-			this.start = sel_start;
-			// create a selection of the whole this.element
-			range_all = this.element.ownerDocument.body.createTextRange();
-			range_all.moveToElementText(this.element);
-			for (var sel_end = 0; range_all.compareEndPoints('StartToEnd', range) &lt; 0; sel_end++){
-				range_all.moveStart('character', 1);
-			}
-			this.end = sel_end;
-			this.length = this.end - this.start;
-			// get selected and surrounding text
-			this.text = range.text;
-		 }
-            }        
+			if (sel.type != 'None') {
+				try{var range = sel.createRange();}catch(err){return;}
+				if (range.parentElement() == this.element){
+					var range_all = this.element.ownerDocument.body.createTextRange();
+					range_all.moveToElementText(this.element);
+					for (var sel_start = 0; range_all.compareEndPoints('StartToStart', range) &lt; 0; sel_start ++){
+						range_all.moveStart('character', 1);
+					}
+					this.start = sel_start;
+					// create a selection of the whole this.element
+					range_all = this.element.ownerDocument.body.createTextRange();
+					range_all.moveToElementText(this.element);
+					for (var sel_end = 0; range_all.compareEndPoints('StartToEnd', range) &lt; 0; sel_end++){
+						range_all.moveStart('character', 1);
+					}
+					this.end = sel_end;
+					this.length = this.end - this.start;
+					// get selected and surrounding text
+					this.text = range.text;
+		 		}
+			}        
 		} else if (this.element.nodeName == &quot;INPUT&quot;){
-            var sel = this.element.ownerDocument.selection;
-			this.range = this.element.ownerDocument.selection.createRange();
+			try{this.range = sel.createRange();}catch(err){return;}
 			this.length = this.range.text.length;
 			var clone = this.range.duplicate();
 			this.start = -clone.moveStart(&quot;character&quot;, -10000);
@@ -2120,7 +2125,7 @@ Spry.Widget.Utils.punycode_encode = function (input, max_out) {
 			}
 
 			if (input[j] == n) {
-				for (q = delta, k = this.punycode_constants.base;; k += this.punycode_constants.base) {
+				for (q = delta, k = this.punycode_constants.base; true; k += this.punycode_constants.base) {
 					if (out &gt;= max_out) {
 						return false;
 					}</diff>
      <filename>resources/script/spry/widgets/textfieldvalidation/SpryValidationTextField.js</filename>
    </modified>
  </modified>
  <removed type="array"/>
  <parents type="array">
    <parent>
      <id>0c28c68b1a39c6a698ba12f658f311f944379816</id>
    </parent>
  </parents>
  <author>
    <name>Nicholas 'OwlManAtt' Evans</name>
    <email>owlmanatt@gmail.com</email>
  </author>
  <url>http://github.com/OwlManAtt/kittokittokitto/commit/da608fe6127c4e19fb7536a72dc4877a75482f69</url>
  <id>da608fe6127c4e19fb7536a72dc4877a75482f69</id>
  <committed-date>2009-07-13T16:26:31-07:00</committed-date>
  <authored-date>2009-07-13T16:26:30-07:00</authored-date>
  <message>Updated Spry to 1.6.1. This should play nice with jQuery.</message>
  <tree>10557e32e8f137a2a5228900c25f6753bcc01954</tree>
  <committer>
    <name>Nicholas 'OwlManAtt' Evans</name>
    <email>owlmanatt@gmail.com</email>
  </committer>
</commit>
