/* SpryData.js - Revision: Spry Preview Release 1.4 */// Copyright (c) 2006. Adobe Systems Incorporated.// 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 Adobe Systems Incorporated 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 "AS IS"// 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.var Spry; if (!Spry) Spry = {};////////////////////////////////////////////////////////////////////////// Spry.Utils////////////////////////////////////////////////////////////////////////if (!Spry.Utils) Spry.Utils = {};Spry.Utils.msProgIDs = ["MSXML2.XMLHTTP.5.0", "MSXML2.XMLHTTP.4.0", "MSXML2.XMLHTTP.3.0", "MSXML2.XMLHTTP", "Microsoft.XMLHTTP"];Spry.Utils.createXMLHttpRequest = function(){	var req = null;	try	{		if (window.XMLHttpRequest)			req = new XMLHttpRequest();		else if (window.ActiveXObject)		{			while (!req && Spry.Utils.msProgIDs.length)			{				try { req = new ActiveXObject(Spry.Utils.msProgIDs[0]); } catch (e) { req = null; }				if (!req)					Spry.Utils.msProgIDs.splice(0, 1);			}		}	}	catch (e) { req = null;	}	if (!req)		Spry.Debug.reportError("Failed to create an XMLHttpRequest object!" );	return req;};Spry.Utils.loadURL = function(method, url, async, callback, opts){	var req = new Spry.Utils.loadURL.Request();	req.method = method;	req.url = url;	req.async = async;	req.successCallback = callback;	Spry.Utils.setOptions(req, opts);		try	{		req.xhRequest = Spry.Utils.createXMLHttpRequest();		if (!req.xhRequest)			return null;		if (req.async)			req.xhRequest.onreadystatechange = function() { Spry.Utils.loadURL.callback(req); };		req.xhRequest.open(req.method, req.url, req.async, req.username, req.password);				if (req.headers)		{			for (var name in req.headers)				req.xhRequest.setRequestHeader(name, req.headers[name]);		}		req.xhRequest.send(req.postData);		if (!req.async)			Spry.Utils.loadURL.callback(req);	}	catch(e) { req = null; Spry.Debug.reportError("Exception caught while loading " + url + ": " + e); }	return req;};Spry.Utils.loadURL.callback = function(req){	if (!req || req.xhRequest.readyState != 4)		return;	if (req.successCallback && (req.xhRequest.status == 200 || req.xhRequest.status == 0))		req.successCallback(req);	else if (req.errorCallback)		req.errorCallback(req);};Spry.Utils.loadURL.Request = function(){	var props = Spry.Utils.loadURL.Request.props;	var numProps = props.length;	for (var i = 0; i < numProps; i++)		this[props[i]] = null;	this.method = "GET";	this.async = true;	this.headers = {};};Spry.Utils.loadURL.Request.props = [ "method", "url", "async", "username", "password", "postData", "successCallback", "errorCallback", "headers", "userData", "xhRequest" ];Spry.Utils.loadURL.Request.prototype.extractRequestOptions = function(opts, undefineRequestProps){	if (!opts)		return;	var props = Spry.Utils.loadURL.Request.props;	var numProps = props.length;	for (var i = 0; i < numProps; i++)	{		var prop = props[i];		if (opts[prop] != undefined)		{			this[prop] = opts[prop];			if (undefineRequestProps)				opts[prop] = undefined;		}	}};Spry.Utils.loadURL.Request.prototype.clone = function(){	var props = Spry.Utils.loadURL.Request.props;	var numProps = props.length;	var req = new Spry.Utils.loadURL.Request;	for (var i = 0; i < numProps; i++)		req[props[i]] = this[props[i]];	if (this.headers)	{		req.headers = {};		Spry.Utils.setOptions(req.headers, this.headers);	}	return req;};Spry.Utils.setInnerHTML = function(ele, str, preventScripts){	if (!ele)		return;	ele = $(ele);	var scriptExpr = "<script[^>]*>(.|\s|\n|\r)*?</script>";	ele.innerHTML = str.replace(new RegExp(scriptExpr, "img"), "");	if (preventScripts)		return;			var matches = str.match(new RegExp(scriptExpr, "img"));	if (matches)	{		var numMatches = matches.length;		for (var i = 0; i < numMatches; i++)		{			var s = matches[i].replace(/<script[^>]*>[\s\r\n]*(<\!--)?|(-->)?[\s\r\n]*<\/script>/img, "");			Spry.Utils.eval(s);		}	}};Spry.Utils.updateContent = function (ele, url, finishFunc, opts){	var method = (opts && opts.method) ? opts.method : "GET";	Spry.Utils.loadURL(method, url, false, function(req)	{		Spry.Utils.setInnerHTML(ele, req.xhRequest.responseText);		if (finishFunc)			finishFunc(ele, url);	}, opts);};Spry.Utils.addEventListener = function(element, eventType, handler, capture){	try	{		element = $(element);		if (element.addEventListener)			element.addEventListener(eventType, handler, capture);		else if (element.attachEvent)			element.attachEvent("on" + eventType, handler);	}	catch (e) {}};Spry.Utils.removeEventListener = function(element, eventType, handler, capture){	try	{		element = $(element);		if (element.removeEventListener)			element.removeEventListener(eventType, handler, capture);		else if (element.detachEvent)			element.detachEvent("on" + eventType, handler);	}	catch (e) {}};Spry.Utils.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.Utils.eval = function(str){	// Call this method from your JS function when	// you don't want the JS expression to access or	// interfere with any local variables in your JS	// function.	return eval(str);};Spry.Utils.escapeQuotesAndLineBreaks = function(str){	if (str)	{		str = str.replace(/\\/g, "\\\\");		str = str.replace(/["']/g, "\\$&");		str = str.replace(/\n/g, "\\n");		str = str.replace(/\r/g, "\\r");	}	return str;};Spry.Utils.encodeEntities = function(str){	if (str && str.search(/[&<>"]/) != -1)	{		str = str.replace(/&/g, "&amp;");		str = str.replace(/</g, "&lt;");		str = str.replace(/>/g, "&gt;");		str = str.replace(/"/g, "&quot;");	}	return str};Spry.Utils.decodeEntities = function(str){	var d = Spry.Utils.decodeEntities.div;	if (!d)	{		d = document.createElement('div');		Spry.Utils.decodeEntities.div = d;		if (!d) return str;	}	d.innerHTML = str;	if (d.childNodes.length == 1 && d.firstChild.nodeType == 3 /* Node.TEXT_NODE */ && d.firstChild.nextSibling == null)		str = d.firstChild.data;	else	{		// Hmmm, innerHTML processing of str produced content		// we weren't expecting, so just replace entities we		// expect folks will use in node attributes that contain		// JavaScript.		str = str.replace(/&lt;/, "<");		str = str.replace(/&gt;/, ">");		str = str.replace(/&quot;/, "\"");		str = str.replace(/&amp;/, "&");	}	return str;};Spry.Utils.fixupIETagAttributes = function(inStr){	var outStr = "";	// Break the tag string into 3 pieces.	var tagStart = inStr.match(/^<[^\s>]+\s*/)[0];	var tagEnd = inStr.match(/\s*\/?>$/)[0];	var tagAttrs = inStr.replace(/^<[^\s>]+\s*|\s*\/?>/g, "");	// Write out the start of the tag.	outStr += tagStart;	// If the tag has attributes, parse it out manually to avoid accidentally fixing up	// attributes that contain JavaScript expressions.	if (tagAttrs)	{		var startIndex = 0;		var endIndex = 0;		while (startIndex < tagAttrs.length)		{			// Find the '=' char of the attribute.			while (tagAttrs.charAt(endIndex) != '=' && endIndex < tagAttrs.length)				++endIndex;			// If we are at the end of the string, just write out what we've			// collected.			if (endIndex >= tagAttrs.length)			{				outStr += tagAttrs.substring(startIndex, endIndex);				break;			}			// Step past the '=' character and write out what we've			// collected so far.			++endIndex;			outStr += tagAttrs.substring(startIndex, endIndex);			startIndex = endIndex;			if (tagAttrs.charAt(endIndex) == '"' || tagAttrs.charAt(endIndex) == "'")			{				// Attribute is quoted. Advance us past the quoted value!				var savedIndex = endIndex++;				while (endIndex < tagAttrs.length)				{					if (tagAttrs.charAt(endIndex) == tagAttrs.charAt(savedIndex))					{						endIndex++;						break;					}					else if (tagAttrs.charAt(endIndex) == "\\")						endIndex++;					endIndex++;				}				outStr += tagAttrs.substring(startIndex, endIndex);				startIndex = endIndex;			}			else			{				// This attribute value wasn't quoted! Wrap it with quotes and				// write out everything till we hit a space, or the end of the				// string.				outStr += "\"";								var sIndex = tagAttrs.slice(endIndex).search(/\s/);				endIndex = (sIndex != -1) ? (endIndex + sIndex) : tagAttrs.length;				outStr += tagAttrs.slice(startIndex, endIndex);								outStr += "\"";								startIndex = endIndex;			}		}	}	outStr += tagEnd;	// Write out the end of the tag.	return outStr;}Spry.Utils.fixUpIEInnerHTML = function(inStr){	var outStr = "";	// Create a regular expression that will match:	//     <!--	//     <![CDATA[	//     <tag>	//     -->	//     ]]>	//     ]]&gt;   // Yet another workaround for an IE innerHTML bug.	//	// The idea here is that we only want to fix up attribute values on tags that	// are not in any comments or CDATA.	var regexp = new RegExp("<\\!--|<\\!\\[CDATA\\[|<\\w+[^<>]*>|-->|\\]\\](>|\&gt;)", "g");	var searchStartIndex = 0;	var skipFixUp = 0;		while (inStr.length)	{		var results = regexp.exec(inStr);		if (!results || !results[0])		{			outStr += inStr.substr(searchStartIndex, inStr.length - searchStartIndex);			break;		}		if (results.index != searchStartIndex)		{			// We found a match but it's not at the start of the inStr.			// Create a string token for everything that precedes the match.			outStr += inStr.substr(searchStartIndex, results.index - searchStartIndex);		}		if (results[0] == "<!--" || results[0] == "<![CDATA[")		{			++skipFixUp;			outStr += results[0];		}		else if (results[0] == "-->" || results[0] == "]]>" || (skipFixUp && results[0] == "]]&gt;"))		{			--skipFixUp;			outStr += results[0];		}		else if (!skipFixUp && results[0].charAt(0) == '<')			outStr += Spry.Utils.fixupIETagAttributes(results[0]);		else			outStr += results[0];		searchStartIndex = regexp.lastIndex;	}		return outStr;};Spry.Utils.stringToXMLDoc = function(str){	var xmlDoc = null;	try	{		// Attempt to parse the string using the IE method.		var xmlDOMObj = new ActiveXObject("Microsoft.XMLDOM");		xmlDOMObj.async = false;		xmlDOMObj.loadXML(str);		xmlDoc = xmlDOMObj;	}	catch (e)	{		// The IE method didn't work. Try the Mozilla way.		try		{			var domParser = new DOMParser;			xmlDoc = domParser.parseFromString(str, 'text/xml');		}		catch (e)		{			Spry.Debug.reportError("Caught exception in Spry.Utils.stringToXMLDoc(): " + e + "\n");			xmlDoc = null;		}	}	return xmlDoc;};Spry.Utils.serializeObject = function(obj){	// Create a JSON representation of a given object.	var str = "";	var firstItem = true;	if (obj == null || obj == undefined)		return str + obj;	var objType = typeof obj;	if (objType == "number" || objType == "boolean")		str += obj;	else if (objType == "string")		str += "\"" + Spry.Utils.escapeQuotesAndLineBreaks(obj) + "\"";	else if (obj.constructor == Array)	{		str += "[";		for (var i = 0; i < obj.length; i++)		{			if (!firstItem)				str += ", ";			str += Spry.Utils.serializeObject(obj[i]);			firstItem = false;		}		str += "]";	}	else if (objType == "object")	{		str += "{";		for (var p in obj)		{			if (!firstItem)				str += ", ";			str += "\"" + p + "\": " + Spry.Utils.serializeObject(obj[p]);			firstItem = false;		}		str += "}";	}	return str;};Spry.Utils.getNodesByFunc = function(root, func){	var nodeStack = new Array;	var resultArr = new Array;	var node = root;	while (node)	{		if (func(node))			resultArr.push(node);		if (node.hasChildNodes())		{			nodeStack.push(node);			node = node.firstChild;		}		else		{			if (node == root)				node = null;			else				try { node = node.nextSibling; } catch (e) { node = null; };		}				while (!node && nodeStack.length > 0)		{			node = nodeStack.pop();			if (node == root)				node = null;			else				try { node = node.nextSibling; } catch (e) { node = null; }		}	}		if (nodeStack && nodeStack.length > 0)		Spry.Debug.trace("-- WARNING: Spry.Utils.getNodesByFunc() failed to traverse all nodes!\n");	return resultArr;};Spry.Utils.addClassName = function(ele, className){	ele = $(ele);	if (!ele || !className || (ele.className && ele.className.search(new RegExp("\\b" + className + "\\b")) != -1))		return;	ele.className += (ele.className ? " " : "") + className;};Spry.Utils.removeClassName = function(ele, className){	ele = $(ele);	if (!ele || !className || (ele.className && ele.className.search(new RegExp("\\b" + className + "\\b")) == -1))		return;	ele.className = ele.className.replace(new RegExp("\\s*\\b" + className + "\\b", "g"), "");};Spry.Utils.getFirstChildWithNodeName = function(node, nodeName){	var child = node.firstChild;	while (child)	{		if (child.nodeName == nodeName)			return child;		child = child.nextSibling;	} 	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 = "";  	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("Spry.Utils.getNodeText() exception caught: " + e + "\n"); }			child = child.nextSibling;		}	}	catch (e) { Spry.Debug.reportError("Spry.Utils.getNodeText() exception caught: " + e + "\n"); }  	return txt;};Spry.Utils.CreateObjectForNode = function(node){	if (!node)		return null;	var obj = null;	var i = 0;	var attr = null;	try	{		for (i = 0; i < node.attributes.length; i++)		{			attr = node.attributes[i];			if (attr && attr.nodeType == 2 /* Node.ATTRIBUTE_NODE */)			{				if (!obj)				{					obj = new Object();					if (!obj)					{						Spry.Debug.reportError("Spry.Utils.CreateObjectForNode(): Object creation failed!");						return null;					}				}						obj["@" + attr.name] = attr.value;			}		}	}	catch (e)	{		Spry.Debug.reportError("Spry.Utils.CreateObjectForNode() caught exception while accessing attributes: " + e + "\n");	}  	var child = node.firstChild;		if (child && !child.nextSibling && 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.		if (!obj)		{			obj = new Object();			if (!obj)			{				Spry.Debug.reportError("Spry.Utils.CreateObjectForNode(): Object creation failed!");				return null;			}		}		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))			{				var txt = Spry.Utils.getNodeText(child);				if (!obj)				{					obj = new Object();					if (!obj)					{						Spry.Debug.reportError("Spry.Utils.CreateObjectForNode(): Object creation failed!");						return null;					}				}	  				obj[child.nodeName] = txt;				// Now add properties for any attributes on the child. The property				// name will be of the form "<child.nodeName>/@<attr.name>".				try				{					var namePrefix = child.nodeName + "/@";										for (i = 0; i < child.attributes.length; i++)					{						attr = child.attributes[i];						if (attr && attr.nodeType == 2 /* Node.ATTRIBUTE_NODE */)							obj[namePrefix + attr.name] = attr.value;					}				}				catch (e)				{					Spry.Debug.reportError("Spry.Utils.CreateObjectForNode() caught exception while accessing attributes: " + e + "\n");				}        			}			// else Spry.Debug.trace("WARNING: Skipping '" + child.nodeName + "' node! Multi-level data sets are not supported right now!\n");		}		child = child.nextSibling;	}  	return obj;};Spry.Utils.getRecordSetFromXMLDoc = function(xmlDoc, path){	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 && nodeArray.length > 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 < nodeArray.length; i++)	{		var rowObj = null;			if (isDOMNodeArray)			rowObj = Spry.Utils.CreateObjectForNode(nodeArray[i]);		else // Must be a Node.ATTRIBUTE_NODE array.		{			rowObj = new Object;			rowObj["@" + 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++;			recordSet.dataHash[rowObj['ds_RowID']] = rowObj;			recordSet.data.push(rowObj);		}	}  	return recordSet;};Spry.Utils.setOptions = function(obj, optionsObj, ignoreUndefinedProps){	if (!optionsObj)		return;	for (var optionName in optionsObj)	{		if (ignoreUndefinedProps && optionsObj[optionName] == undefined)			continue;		obj[optionName] = optionsObj[optionName];	}};Spry.Utils.SelectionManager = {};Spry.Utils.SelectionManager.selectionGroups = new Object;Spry.Utils.SelectionManager.SelectionGroup = function(){	this.selectedElements = new Array;};Spry.Utils.SelectionManager.SelectionGroup.prototype.select = function(element, className, multiSelect){	var selObj = null;	if (!multiSelect)	{		// Multiple selection is not enabled, so clear any		// selected elements from our list.		this.clearSelection();	}	else	{		// Multiple selection is enabled, so check to see if element		// is already in the array. If it is, make sure the className		// is the className that was passed in.		for (var i = 0; i < this.selectedElements.length; i++)		{			selObj = this.selectedElements[i].element;			if (selObj.element == element)			{				if (selObj.className != className)				{					Spry.Utils.removeClassName(element, selObj.className);					Spry.Utils.addClassName(element, className);				}				return;			}		}	}	// Add the element to our list of selected elements.	selObj = new Object;	selObj.element = element;	selObj.className = className;	this.selectedElements.push(selObj);	Spry.Utils.addClassName(element, className);};Spry.Utils.SelectionManager.SelectionGroup.prototype.unSelect = function(element){	for (var i = 0; i < this.selectedElements.length; i++)	{		var selObj = this.selectedElements[i].element;			if (selObj.element == element)		{			Spry.Utils.removeClassName(selObj.element, selObj.className);			return;		}	}};Spry.Utils.SelectionManager.SelectionGroup.prototype.clearSelection = function(){	var selObj = null;	do	{		selObj = this.selectedElements.shift();		if (selObj)			Spry.Utils.removeClassName(selObj.element, selObj.className);	}	while (selObj);};Spry.Utils.SelectionManager.getSelectionGroup = function(selectionGroupName){	if (!selectionGroupName)		return null;	var groupObj = Spry.Utils.SelectionManager.selectionGroups[selectionGroupName];	if (!groupObj)	{		groupObj = new Spry.Utils.SelectionManager.SelectionGroup();		Spry.Utils.SelectionManager.selectionGroups[selectionGroupName] = groupObj;	}	return groupObj;};Spry.Utils.SelectionManager.select = function(selectionGroupName, element, className, multiSelect){	var groupObj = Spry.Utils.SelectionManager.getSelectionGroup(selectionGroupName);	if (!groupObj)		return;	groupObj.select(element, className, multiSelect);};Spry.Utils.SelectionManager.unSelect = function(selectionGroupName, element){	var groupObj = Spry.Utils.SelectionManager.getSelectionGroup(selectionGroupName);	if (!groupObj)		return;	groupObj.unSelect(element, className);};Spry.Utils.SelectionManager.clearSelection = function(selectionGroupName){	var groupObj = Spry.Utils.SelectionManager.getSelectionGroup(selectionGroupName);	if (!groupObj)		return;	groupObj.clearSelection();};////////////////////////////////////////////////////////////////////////// Define Prototype's $() convenience function.////////////////////////////////////////////////////////////////////////function $(){	var elements = new Array();		for (var i = 0; i < arguments.length; i++)	{		var element = arguments[i];		if (typeof element == 'string')			element = document.getElementById(element);				if (arguments.length == 1)			return element;				elements.push(element);	}		return elements;}Spry.Utils.Notifier = function(){	this.observers = [];	this.suppressNotifications = 0;};Spry.Utils.Notifier.prototype.addObserver = function(observer){	if (!observer)		return;	// Make sure the observer isn't already on the list.	var len = this.observers.length;	for (var i = 0; i < len; i++)	{		if (this.observers[i] == observer)			return;	}	this.observers[len] = observer;};Spry.Utils.Notifier.prototype.removeObserver = function(observer){	if (!observer)		return;	for (var i = 0; i < this.observers.length; i++)	{		if (this.observers[i] == observer)		{			this.observers.splice(i, 1);			break;		}	}};Spry.Utils.Notifier.prototype.notifyObservers = function(methodName, data){	if (!methodName)		return;	if (!this.suppressNotifications)	{		var len = this.observers.length;		for (var i = 0; i < len; i++)		{			var obs = this.observers[i];			if (obs)			{				if (typeof obs == "function")					obs(methodName, this, data);				else if (obs[methodName])					obs[methodName](this, data);			}		}	}};Spry.Utils.Notifier.prototype.enableNotifications = function(){	if (--this.suppressNotifications < 0)	{		this.suppressNotifications = 0;		Spry.Debug.reportError("Unbalanced enableNotifications() call!\n");	}};Spry.Utils.Notifier.prototype.disableNotifications = function(){	++this.suppressNotifications;};////////////////////////////////////////////////////////////////////////// Spry.Debug////////////////////////////////////////////////////////////////////////Spry.Debug = {};Spry.Debug.enableTrace = true;Spry.Debug.debugWindow = null;Spry.Debug.createDebugWindow = function(){	if (!Spry.Debug.enableTrace || Spry.Debug.debugWindow)		return;	try	{		Spry.Debug.debugWindow = document.createElement("div");		var div = Spry.Debug.debugWindow;		div.style.fontSize = "12px";		div.style.fontFamily = "console";		div.style.position = "absolute";		div.style.width = "400px";		div.style.height = "300px";		div.style.overflow = "auto";		div.style.border = "solid 1px black";		div.style.backgroundColor = "white";		div.style.color = "black";		div.style.bottom = "0px";		div.style.right = "0px";		// div.style.opacity = "0.5";		// div.style.filter = "alpha(opacity=50)";		div.setAttribute("id", "SpryDebugWindow");		document.body.appendChild(Spry.Debug.debugWindow);	}	catch (e) {}};Spry.Debug.debugOut = function(str, bgColor){	if (!Spry.Debug.debugWindow)	{		Spry.Debug.createDebugWindow();		if (!Spry.Debug.debugWindow)			return;	}	var d = document.createElement("div");	if (bgColor)		d.style.backgroundColor = bgColor;	d.innerHTML = str;	Spry.Debug.debugWindow.appendChild(d);	};Spry.Debug.trace = function(str){	Spry.Debug.debugOut(str);};Spry.Debug.reportError = function(str){	Spry.Debug.debugOut(str, "red");};////////////////////////////////////////////////////////////////////////// Spry.Data////////////////////////////////////////////////////////////////////////Spry.Data = {};Spry.Data.regionsArray = {};Spry.Data.initRegions = function(rootNode){	if (!rootNode)		rootNode = document.body;	var lastRegionFound = null;	var regions = Spry.Utils.getNodesByFunc(rootNode, function(node)	{		try		{			if (node.nodeType != 1 /* Node.ELEMENT_NODE */)				return false;			// Region elements must have an spryregion attribute with a			// non-empty value. An id attribute is also required so we can			// reference the region by name if necessary.			var attrName = "spry:region";			var attr = node.attributes.getNamedItem(attrName);			if (!attr)			{				attrName = "spry:detailregion";				attr = node.attributes.getNamedItem(attrName);			}			if (attr)			{				if (lastRegionFound)				{					var parent = node.parentNode;					while (parent)					{						if (parent == lastRegionFound)						{							Spry.Debug.reportError("Found a nested " + attrName + " in the following markup. Nested regions are currently not supported.<br/><pre>" + Spry.Utils.encodeEntities(parent.innerHTML) + "</pre>");							return false;						}						parent = parent.parentNode;					}				}				if (attr.value)				{					attr = node.attributes.getNamedItem("id");					if (!attr || !attr.value)					{						// The node is missing an id attribute so add one.						node.setAttribute("id", "spryregion" + (++Spry.Data.initRegions.nextUniqueRegionID));					}					lastRegionFound = node;					return true;				}				else					Spry.Debug.reportError(attrName + " attributes require one or more data set names as values!");			}		}		catch(e) {}		return false;	});	var name, dataSets, i;  	for (i = 0; i < regions.length; i++)	{		var rgn = regions[i];		var isDetailRegion = false;		// Get the region name.		name = rgn.attributes.getNamedItem("id").value;		attr = rgn.attributes.getNamedItem("spry:region");		if (!attr)		{			attr = rgn.attributes.getNamedItem("spry:detailregion");			isDetailRegion = true;		}		if (!attr.value)		{			Spry.Debug.reportError("spry:region and spry:detailregion attributes require one or more data set names as values!");			continue;		}		// Remove the spry:region or spry:detailregion attribute so it doesn't appear in		// the output generated by our processing of the dynamic region.		rgn.attributes.removeNamedItem(attr.nodeName);		// Remove the hiddenRegionCSS class from the rgn.		Spry.Utils.removeClassName(rgn, Spry.Data.Region.hiddenRegionClassName);		// Get the DataSets that should be bound to the region.		dataSets = Spry.Data.Region.strToDataSetsArray(attr.value);		if (!dataSets.length)		{			Spry.Debug.reportError("spry:region or spry:detailregion attribute has no data set!");			continue;		}			var hasBehaviorAttributes = false;		var hasSpryContent = false;		var dataStr = "";		var parent = null;		var regionStates = {};		var regionStateMap = {};		// Check if there are any attributes on the region node that remap		// the default states.		attr = rgn.attributes.getNamedItem("spry:readystate");		if (attr && attr.value)			regionStateMap["ready"] = attr.value;		attr = rgn.attributes.getNamedItem("spry:errorstate");		if (attr && attr.value)			regionStateMap["error"] = attr.value;		attr = rgn.attributes.getNamedItem("spry:loadingstate");		if (attr && attr.value)			regionStateMap["loading"] = attr.value;				// Find all of the processing instruction regions in the region.		// Insert comments around the regions we find so we can identify them		// easily when tokenizing the region html string.		var piRegions = Spry.Utils.getNodesByFunc(rgn, function(node)		{			try			{				if (node.nodeType == 1 /* ELEMENT_NODE */)				{					var attributes = node.attributes;					var numPI = Spry.Data.Region.PI.orderedInstructions.length;					var lastStartComment = null;					var lastEndComment = null;					for (var i = 0; i < numPI; i++)					{						var piName = Spry.Data.Region.PI.orderedInstructions[i];						var attr = attributes.getNamedItem(piName);						if (!attr)							continue;							var piDesc = Spry.Data.Region.PI.instructions[piName];						var childrenOnly = (node == rgn) ? true : piDesc.childrenOnly;						var openTag = piDesc.getOpenTag(node, piName);						var closeTag = piDesc.getCloseTag(node, piName);							if (childrenOnly)						{								var oComment = document.createComment(openTag);								var cComment = document.createComment(closeTag)								if (!lastStartComment)									node.insertBefore(oComment, node.firstChild);								else									node.insertBefore(oComment, lastStartComment.nextSibling);								lastStartComment = oComment;								if (!lastEndComment)									node.appendChild(cComment);								else									node.insertBefore(cComment, lastEndComment);								lastEndComment = cComment;						}						else						{							var parent = node.parentNode;							parent.insertBefore(document.createComment(openTag), node);							parent.insertBefore(document.createComment(closeTag), node.nextSibling);						}						// If this is a spry:state processing instruction, record the state name						// so we know that we should re-generate the region if we ever see that state.						if (piName == "spry:state")							regionStates[attr.value] = true;						node.removeAttribute(piName);					}					if (Spry.Data.Region.enableBehaviorAttributes)					{						var bAttrs = Spry.Data.Region.behaviorAttrs;						for (var behaviorAttrName in bAttrs)						{							var bAttr = attributes.getNamedItem(behaviorAttrName);							if (bAttr)							{								hasBehaviorAttributes = true;								if (bAttrs[behaviorAttrName].setup)									bAttrs[behaviorAttrName].setup(node, bAttr.value);							}						}					}				}			}			catch(e) {}			return false;		});			// Get the data in the region.		dataStr = rgn.innerHTML;		// Argh! IE has an innerHTML bug where it will remove the quotes around any		// attribute value that it thinks is a single word. This includes removing quotes		// around our data references which is problematic since a single data reference		// can be replaced with multiple words. If we are running in IE, we have to call		// fixUpIEInnerHTML to get around this problem.		if (window.ActiveXObject && !Spry.Data.Region.disableIEInnerHTMLFixUp && dataStr.search(/=\{/) != -1)		{			if (Spry.Data.Region.debug)				Spry.Debug.trace("<hr />Performing IE innerHTML fix up of Region: " + name + "<br /><br />" + Spry.Utils.encodeEntities(dataStr));			dataStr = Spry.Utils.fixUpIEInnerHTML(dataStr);		}		if (Spry.Data.Region.debug)			Spry.Debug.trace("<hr />Region template markup for '" + name + "':<br /><br />" + Spry.Utils.encodeEntities(dataStr));		if (!hasSpryContent)		{			// Clear the region.			rgn.innerHTML = "";		}		// 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;	}	Spry.Data.updateAllRegions();};Spry.Data.initRegions.nextUniqueRegionID = 0;Spry.Data.updateRegion = function(regionName){	if (!regionName || !Spry.Data.regionsArray || !Spry.Data.regionsArray[regionName])		return;	try { Spry.Data.regionsArray[regionName].updateContent(); }	catch(e) { Spry.Debug.reportError("Spry.Data.updateRegion(" + regionName + ") caught an exception: " + e + "\n"); }};Spry.Data.getRegion = function(regionName){	return Spry.Data.regionsArray[regionName];};Spry.Data.updateAllRegions = function(){	if (!Spry.Data.regionsArray)		return;	for (var regionName in Spry.Data.regionsArray)		Spry.Data.updateRegion(regionName);};////////////////////////////////////////////////////////////////////////// Spry.Data.DataSet////////////////////////////////////////////////////////////////////////Spry.Data.DataSet = function(){	Spry.Utils.Notifier.call(this);	this.name = "";	this.internalID = Spry.Data.DataSet.nextDataSetID++;	this.curRowID = 0;	this.data = null;	this.unfilteredData = null;	this.dataHash = null;	this.columnTypes = new Object;	this.filterFunc = null;		// non-destructive filter function	this.filterDataFunc = null;	// destructive filter function	this.distinctOnLoad = false;	this.sortOnLoad = null;	this.sortOrderOnLoad = "ascending";	this.keepSorted = false;	this.dataWasLoaded = false;	this.pendingRequest = null;	this.lastSortColumns = [];	this.lastSortOrder = "";	this.loadIntervalID = 0;};Spry.Data.DataSet.prototype = new Spry.Utils.Notifier();Spry.Data.DataSet.prototype.constructor = Spry.Data.DataSet;Spry.Data.DataSet.prototype.getData = function(unfiltered){	return (unfiltered && this.unfilteredData) ? this.unfilteredData : this.data;};Spry.Data.DataSet.prototype.getUnfilteredData = function(){	// XXX: Deprecated.	return this.getData(true);};Spry.Data.DataSet.prototype.getLoadDataRequestIsPending = function(){	return this.pendingRequest != null;};Spry.Data.DataSet.prototype.getDataWasLoaded = function(){	return this.dataWasLoaded;};Spry.Data.DataSet.prototype.loadData = function(){	// The idea here is that folks using the base class DataSet directly	// would change the data in the DataSet manually and then call loadData()	// to fire off an async notifications to say that it was ready for consumption.	//	// Firing off data changed notificataions synchronously from this method	// can wreak havoc with complicated master/detail regions that use data sets	// that have master/detail relationships with other data sets. Our data set	// logic already handles async data loading nicely so we use a timer to fire	// off the data changed notification to insure that it happens after this	// function is finished and the JS stack unwinds.	//	// Other classes that derive from this class and load data synchronously	// inside their loadData() implementation should also fire off an async	// notification in this same manner to avoid this same problem.	var self = this;	this.pendingRequest = new Object;	this.dataWasLoaded = false;	this.pendingRequest.timer = setTimeout(function()	{		self.pendingRequest = null;		self.dataWasLoaded = true;		if (self.filterDataFunc)			self.filterData(self.filterDataFunc, true);		if (self.distinctOnLoad)			self.distinct();		if (self.keepSorted && self.getSortColumn())			self.sort(self.lastSortColumns, self.lastSortOrder)		else if (self.sortOnLoad)			self.sort(self.sortOnLoad, self.sortOrderOnLoad);			if (self.filterFunc)			self.filter(self.filterFunc, true);			self.notifyObservers("onDataChanged");	}, 0);  };Spry.Data.DataSet.prototype.cancelLoadData = function(){	if (this.pendingRequest && this.pendingRequest.timer)		clearTimeout(this.pendingRequest.timer);	this.pendingRequest = null;};Spry.Data.DataSet.prototype.getRowCount = function(unfiltered){	var rows = this.getData(unfiltered);	return rows ? rows.length : 0;};Spry.Data.DataSet.prototype.getRowByID = function(rowID){	if (!this.data)		return null;	return this.dataHash[rowID];};Spry.Data.DataSet.prototype.getRowByRowNumber = function(rowNumber, unfiltered){	var rows = this.getData(unfiltered);	if (rows && rowNumber >= 0 && rowNumber < rows.length)		return rows[rowNumber];	return null;};Spry.Data.DataSet.prototype.getCurrentRow = function(){	return this.getRowByID(this.curRowID);};Spry.Data.DataSet.prototype.setCurrentRow = function(rowID){	if (this.curRowID == rowID)		return;	var nData = { oldRowID: this.curRowID, newRowID: rowID };	this.curRowID = rowID;	this.notifyObservers("onCurrentRowChanged", nData);};Spry.Data.DataSet.prototype.getRowNumber = function(row){	if (row && this.data && this.data.length)	{		var numRows = this.data.length;		for (var i = 0; i < numRows; i++)		{			if (this.data[i] == row)				return i;		}	}	return 0;};Spry.Data.DataSet.prototype.getCurrentRowNumber = function(){	return this.getRowNumber(this.getCurrentRow());};Spry.Data.DataSet.prototype.setCurrentRowNumber = function(rowNumber){	if (!this.data || rowNumber >= this.data.length)	{		Spry.Debug.trace("Invalid row number: " + rowNumber + "\n");		return;	}	var rowID = this.data[rowNumber]["ds_RowID"];	if (rowID == undefined || this.curRowID == rowID)		return;	this.setCurrentRow(rowID);};Spry.Data.DataSet.prototype.findRowsWithColumnValues = function(valueObj, firstMatchOnly, unfiltered){	var results = [];	var rows = this.getData(unfiltered);	if (rows)	{		var numRows = rows.length;		for (var i = 0; i < numRows; i++)		{			var row = rows[i];			var matched = true;			for (var colName in valueObj)			{				if (valueObj[colName] != row[colName])				{					matched = false;					break;				}			}						if (matched)			{				if (firstMatchOnly)					return row;				results.push(row);			}		}	}	return firstMatchOnly ? null : results;};Spry.Data.DataSet.prototype.setColumnType = function(columnName, columnType){	if (columnName)		this.columnTypes[columnName] = columnType;};Spry.Data.DataSet.prototype.getColumnType = function(columnName){	if (this.columnTypes[columnName])		return this.columnTypes[columnName];	return "string";};Spry.Data.DataSet.prototype.distinct = function(){	if (this.data)	{		var oldData = this.data;		this.data = [];		this.dataHash = {};		var alreadySeenHash = {};		var i = 0;		for (var i = 0; i < oldData.length; i++)		{			var rec = oldData[i];			var hashStr = "";			for (var recField in rec)			{				if (recField != "ds_RowID")				{					if (hashStr)						hashStr += ",";					hashStr += recField + ":" + "\"" + rec[recField] + "\"";				}			}			if (!alreadySeenHash[hashStr])			{				this.data.push(rec);				this.dataHash[rec['ds_RowID']] = rec;				alreadySeenHash[hashStr] = true;			}		}	}};Spry.Data.DataSet.prototype.getSortColumn = function() {	return (this.lastSortColumns && this.lastSortColumns.length > 0) ? this.lastSortColumns[0] : "";};Spry.Data.DataSet.prototype.getSortOrder = function() {	return this.lastSortOrder ? this.lastSortOrder : "";};Spry.Data.DataSet.prototype.sort = function(columnNames, sortOrder){	// columnNames can be either the name of a column to	// sort on, or an array of column names, but it can't be	// null/undefined.	if (!columnNames)		return;	// If only one column name was specified for sorting, do a	// secondary sort on ds_RowID so we get a stable sort order.	if (typeof columnNames == "string")		columnNames = [ columnNames, "ds_RowID" ];	else if (columnNames.length < 2 && columnNames[0] != "ds_RowID")		columnNames.push("ds_RowID");	if (!sortOrder)		sortOrder = "toggle";	if (sortOrder == "toggle")	{		if (this.lastSortColumns.length > 0 && this.lastSortColumns[0] == columnNames[0] && this.lastSortOrder == "ascending")			sortOrder = "descending";		else			sortOrder = "ascending";	}	if (sortOrder != "ascending" && sortOrder != "descending")	{		Spry.Debug.reportError("Invalid sort order type specified: " + sortOrder + "\n");		return;	}	var nData = {		oldSortColumns: this.lastSortColumns,		oldSortOrder: this.lastSortOrder,		newSortColumns: columnNames,		newSortOrder: sortOrder	};	this.notifyObservers("onPreSort", nData);	var cname = columnNames[columnNames.length - 1];	var sortfunc = Spry.Data.DataSet.prototype.sort.getSortFunc(cname, this.getColumnType(cname), sortOrder);	for (var i = columnNames.length - 2; i >= 0; i--)	{		cname = columnNames[i];		sortfunc = Spry.Data.DataSet.prototype.sort.buildSecondarySortFunc(Spry.Data.DataSet.prototype.sort.getSortFunc(cname, this.getColumnType(cname), sortOrder), sortfunc);	}	if (this.unfilteredData)	{		this.unfilteredData.sort(sortfunc);		if (this.filterFunc)			this.filter(this.filterFunc, true);	}	else		this.data.sort(sortfunc);	this.lastSortColumns = columnNames.slice(0); // Copy the array.	this.lastSortOrder = sortOrder;	this.notifyObservers("onPostSort", nData);};Spry.Data.DataSet.prototype.sort.getSortFunc = function(prop, type, order){	var sortfunc = null;	if (type == "number")	{		if (order == "ascending")			sortfunc = function(a, b){ return a[prop]-b[prop]; };		else // order == "descending"			sortfunc = function(a, b){ return b[prop]-a[prop]; };	}	else if (type == "date")	{		if (order == "ascending")			sortfunc = function(a, b)			{				var dA = a[prop];				var dB = b[prop];							dA = dA ? (new Date(dA)) : 0;				dB = dB ? (new Date(dB)) : 0;				return dA - dB;			};		else // order == "descending"			sortfunc = function(a, b)			{				var dA = a[prop];				var dB = b[prop];							dA = dA ? (new Date(dA)) : 0;				dB = dB ? (new Date(dB)) : 0;				return dB - dA;			};	}	else // type == "string"	{		if (order == "ascending")			sortfunc = function(a, b){				var tA = a[prop].toString();				var tB = b[prop].toString();				var tA_l = tA.toLowerCase();				var tB_l = tB.toLowerCase();				var min_len = tA.length > tB.length ? tB.length : tA.length;				for (var i=0; i < min_len; i++){					var a_l_c = tA_l.charAt(i);					var b_l_c = tB_l.charAt(i);					var a_c = tA.charAt(i);					var b_c = tB.charAt(i);					if (a_l_c > b_l_c){							return 1;					}else if (a_l_c < b_l_c){						 return -1;					}else if (a_c > b_c){						 return 1;					}else if (a_c < b_c){						 return -1;					}				}				if(tA.length == tB.length){					return 0;				}else if (tA.length > tB.length){					return 1;				}else{					return -1;					}			};		else // order == "descending"			sortfunc = function(a, b){				var tA = a[prop].toString();				var tB = b[prop].toString();				var tA_l = tA.toLowerCase();				var tB_l = tB.toLowerCase();				var min_len = tA.length > tB.length ? tB.length : tA.length;				for (var i=0; i < min_len; i++){					var a_l_c = tA_l.charAt(i);					var b_l_c = tB_l.charAt(i);					var a_c = tA.charAt(i);					var b_c = tB.charAt(i);					if (a_l_c > b_l_c){							return -1;					}else if (a_l_c < b_l_c){						 return 1;					}else if (a_c > b_c){						 return -1;					}else if (a_c < b_c){						 return 1;					}				}				if(tA.length == tB.length){					return 0;				}else if (tA.length > tB.length){					return -1;				}else{					return 1;					}			};	     }	return sortfunc;};Spry.Data.DataSet.prototype.sort.buildSecondarySortFunc = function(funcA, funcB){	return function(a, b)	{		var ret = funcA(a, b);		if (ret == 0)			ret = funcB(a, b);		return ret;	};};Spry.Data.DataSet.prototype.filterData = function(filterFunc, filterOnly){	// This is a destructive filter function.		var dataChanged = false;	if (!filterFunc)	{		// Caller wants to remove the filter.		this.filterDataFunc = null;		dataChanged = true;	}	else	{		this.filterDataFunc = filterFunc;				if (this.dataWasLoaded && ((this.unfilteredData && this.unfilteredData.length) || (this.data && this.data.length)))		{			if (this.unfilteredData)			{				this.data = this.unfilteredData;				this.unfilteredData = null;			}				var oldData = this.data;			this.data = [];			this.dataHash = {};				for (var i = 0; i < oldData.length; i++)			{				var newRow = filterFunc(this, oldData[i], i);				if (newRow)				{					this.data.push(newRow);					this.dataHash[newRow["ds_RowID"]] = newRow;				}			}				dataChanged = true;		}	}	if (dataChanged)	{		if (!filterOnly)		{			this.disableNotifications();			if (this.filterFunc)				this.filter(this.filterFunc, true);			this.enableNotifications();		}		this.notifyObservers("onDataChanged");	}};Spry.Data.DataSet.prototype.filter = function(filterFunc, filterOnly){	// This is a non-destructive filter function.	var dataChanged = false;	if (!filterFunc)	{		if (this.filterFunc && this.unfilteredData)		{			// Caller wants to remove the filter. Restore the unfiltered			// data and trigger a data changed notification.				this.data = this.unfilteredData;			this.unfilteredData = null;			this.filterFunc = null;			dataChanged = true;		}	}	else	{		this.filterFunc = filterFunc;				if (this.dataWasLoaded && (this.unfilteredData || (this.data && this.data.length)))		{			if (!this.unfilteredData)				this.unfilteredData = this.data;				var udata = this.unfilteredData;			this.data = [];				for (var i = 0; i < udata.length; i++)			{				var newRow = filterFunc(this, udata[i], i);					if (newRow)					this.data.push(newRow);			}				dataChanged = true;		}	}	if (dataChanged)		this.notifyObservers("onDataChanged");};Spry.Data.DataSet.prototype.startLoadInterval = function(interval){	this.stopLoadInterval();	if (interval > 0)	{		var self = this;		this.loadInterval = interval;		this.loadIntervalID = setInterval(function() { self.loadData(); }, interval);	}};Spry.Data.DataSet.prototype.stopLoadInterval = function(){	if (this.loadIntervalID)		clearInterval(this.loadIntervalID);	this.loadInterval = 0;	this.loadIntervalID = null;};Spry.Data.DataSet.nextDataSetID = 0;////////////////////////////////////////////////////////////////////////// Spry.Data.XMLDataSet////////////////////////////////////////////////////////////////////////Spry.Data.XMLDataSet = function(dataSetURL, dataSetPath, dataSetOptions){	// Call the constructor for our DataSet base class so that	// our base class properties get defined. We'll call setOptions	// manually after we set up our XMLDataSet properties.	Spry.Data.DataSet.call(this);		// XMLDataSet Properties:	this.url = dataSetURL;	this.xpath = dataSetPath;	this.doc = null;	this.dataSetsForDataRefStrings = new Array;	this.hasDataRefStrings = false;	this.useCache = true;	// Create a loadURL request object to store any load options	// the caller specified. We'll fill in the URL at the last minute	// before we make the actual load request because our URL needs	// to be processed at the last possible minute in case it contains	// data references.	this.requestInfo = new Spry.Utils.loadURL.Request();	this.requestInfo.extractRequestOptions(dataSetOptions, true);	// If the caller wants to use "POST" to fetch the data, but didn't	// provide the content type, default to x-www-form-urlencoded.	if (this.requestInfo.method == "POST")	{		if (!this.requestInfo.headers)			this.requestInfo.headers = {};		if (!this.requestInfo.headers['Content-Type'])			this.requestInfo.headers['Content-Type'] = "application/x-www-form-urlencoded; charset=UTF-8";	}	Spry.Utils.setOptions(this, dataSetOptions, true);		this.recalculateDataSetDependencies();	if (this.loadInterval > 0)		this.startLoadInterval(this.loadInterval);}; // End of Spry.Data.XMLDataSet() constructor.Spry.Data.XMLDataSet.prototype = new Spry.Data.DataSet();Spry.Data.XMLDataSet.prototype.constructor = Spry.Data.XMLDataSet;Spry.Data.XMLDataSet.prototype.recalculateDataSetDependencies = function(){	this.hasDataRefStrings = false;	if (!this.url)		return;	// Clear all old callbacks that may have been registered.	var i = 0;	for (i = 0; i < this.dataSetsForDataRefStrings.length; i++)	{		var ds = this.dataSetsForDataRefStrings[i];		if (ds)			ds.removeObserver(this);	}	// Now run through the strings that may contain data references and figure	// out what data sets they require. Note that the data references in these	// strings must be fully qualified with a data set name. (ex: {dsDataSetName::columnName})	this.dataSetsForDataRefStrings = new Array();	var regionStrs = [ this.url, this.xpath, this.requestInfo.postData ];	// If postData exists, and is a string, we want to check it for data refs.	var postData = this.requestInfo.postData;	if (postData && (typeof postData) == "string")		regionStrs.push(postData);	var dsCount = 0;	for (var n = 0; n < regionStrs.length; n++)	{		var tokens = Spry.Data.Region.getTokensFromStr(regionStrs[n]);		for (i = 0; tokens && i < tokens.length; i++)		{			if (tokens[i].search(/{[^}:]+::[^}]+}/) != -1)			{				var dsName = tokens[i].replace(/^\{|::.*\}/g, "");				var ds = null;				if (!this.dataSetsForDataRefStrings[dsName])				{					try { ds = eval(dsName); } catch (e) { ds = null; }						if (dsName && ds)					{						// The dataSetsForDataRefStrings array serves as both an						// array of data sets and a hash lookup by name.						this.dataSetsForDataRefStrings[dsName] = ds;						this.dataSetsForDataRefStrings[dsCount++] = ds;						this.hasDataRefStrings = true;					}				}			}		}	}	// Set up observers on any data sets our URL depends on.	for (i = 0; i < this.dataSetsForDataRefStrings.length; i++)	{		var ds = this.dataSetsForDataRefStrings[i];		ds.addObserver(this);	}};Spry.Data.XMLDataSet.prototype.attemptLoadData = function(){	// We only want to trigger a load when all of our data sets have data!	for (var i = 0; i < this.dataSetsForDataRefStrings.length; i++)	{		var ds = this.dataSetsForDataRefStrings[i];		if (ds.getLoadDataRequestIsPending() || !ds.getDataWasLoaded())			return;	}	this.loadData();};Spry.Data.XMLDataSet.prototype.onCurrentRowChanged = function(ds, data){	this.attemptLoadData();};Spry.Data.XMLDataSet.prototype.onPostSort = function(ds, data){	this.attemptLoadData();};			Spry.Data.XMLDataSet.prototype.onDataChanged = function(ds, data){	this.attemptLoadData();};			Spry.Data.XMLDataSet.prototype.loadData = function(){	if (!this.url || !this.xpath)		return;	this.cancelLoadData();	var url = this.url;	var postData = this.requestInfo.postData;	if (this.hasDataRefStrings)	{		var allDataSetsReady = true;		for (var i = 0; i < this.dataSetsForDataRefStrings.length; i++)		{			var ds = this.dataSetsForDataRefStrings[i];			if (ds.getLoadDataRequestIsPending())				allDataSetsReady = false;			else if (!ds.getDataWasLoaded())			{				// Kick off the load of this data set!				ds.loadData();				allDataSetsReady = false;			}		}		// If our data sets aren't ready, just return. We'll		// get called back to load our data when they are all		// done.		if (!allDataSetsReady)			return;		url = Spry.Data.Region.processDataRefString(null, this.url, this.dataSetsForDataRefStrings);		if (!url)			return;					if (postData && (typeof postData) == "string")			postData = Spry.Data.Region.processDataRefString(null, postData, this.dataSetsForDataRefStrings);	}	this.notifyObservers("onPreLoad");	this.data = null;	this.dataWasLoaded = false;	this.unfilteredData = null;	this.dataHash = null;	this.curRowID = 0;	// At this point the url should've been processed if it contained any	// data references. Set the url of the requestInfo structure and pass it	// to LoadManager.loadData().	var req = this.requestInfo.clone();	req.url = url;	req.postData = postData;	this.pendingRequest = new Object;	this.pendingRequest.data = Spry.Data.XMLDataSet.LoadManager.loadData(req, this, this.useCache);};Spry.Data.XMLDataSet.prototype.cancelLoadData = function(){	if (this.pendingRequest)	{		Spry.Data.XMLDataSet.LoadManager.cancelLoadData(this.pendingRequest.data, this);		this.pendingRequest = null;	}};Spry.Data.XMLDataSet.prototype.getURL = function() { return this.url; };Spry.Data.XMLDataSet.prototype.setURL = function(url, requestOptions){	if (this.url == url)		return;	this.url = url;	if (requestOptions)		this.requestInfo.extractRequestOptions(requestOptions);	this.cancelLoadData();	this.recalculateDataSetDependencies();	this.dataWasLoaded = false;};Spry.Data.XMLDataSet.prototype.getDocument = function() { return this.doc; };Spry.Data.XMLDataSet.prototype.getXPath = function() { return this.xpath; };Spry.Data.XMLDataSet.prototype.setXPath = function(path){	if (this.xpath != path)	{		this.xpath = path;		if (this.dataWasLoaded && this.doc)			this.setDataFromDoc(this.doc);	}};Spry.Data.XMLDataSet.prototype.setDataFromDoc = function(doc){	this.pendingRequest = null;	var rs = null;	rs = Spry.Utils.getRecordSetFromXMLDoc(doc, Spry.Data.Region.processDataRefString(null, this.xpath, this.dataSetsForDataRefStrings));	if (!rs)	{		Spry.Debug.reportError("Spry.Data.XMLDataSet.setDataFromDoc() failed to create dataSet '" + this.name + "'for '" + this.xpath + "' - " + this.url + "\n");		return;	}	this.doc = rs.xmlDoc;	this.data = rs.data;	this.dataHash = rs.dataHash;	this.dataWasLoaded = (this.doc != null);	// 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();	// If sortOnLoad was set, sort the data based on the columns	// specified in sortOnLoad.	if (this.keepSorted && 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);	// The default "current" row is the first row of the data set.	if (this.data && this.data.length > 0)		this.curRowID = this.data[0]['ds_RowID'];	else		this.curRowID = 0;	this.notifyObservers("onPostLoad");	this.notifyObservers("onDataChanged");};Spry.Data.XMLDataSet.prototype.onRequestResponse = function(cachedRequest, req){	this.setDataFromDoc(cachedRequest.doc);};Spry.Data.XMLDataSet.prototype.onRequestError = function(cachedRequest, req){	this.notifyObservers("onLoadError", req);	// Spry.Debug.reportError("Spry.Data.XMLDataSet.LoadManager.CachedRequest.loadDataCallback(" + req.xhRequest.status + ") failed to load: " + req.url + "\n");};Spry.Data.XMLDataSet.LoadManager = {};Spry.Data.XMLDataSet.LoadManager.cache = [];Spry.Data.XMLDataSet.LoadManager.CachedRequest = function(reqInfo){	Spry.Utils.Notifier.call(this);	this.reqInfo = reqInfo;	this.doc = null;	this.timer = null;	this.state = Spry.Data.XMLDataSet.LoadManager.CachedRequest.NOT_LOADED;};Spry.Data.XMLDataSet.LoadManager.CachedRequest.prototype = new Spry.Utils.Notifier();Spry.Data.XMLDataSet.LoadManager.CachedRequest.prototype.constructor = Spry.Data.XMLDataSet.LoadManager.CachedRequest;Spry.Data.XMLDataSet.LoadManager.CachedRequest.NOT_LOADED      = 1;Spry.Data.XMLDataSet.LoadManager.CachedRequest.LOAD_REQUESTED  = 2;Spry.Data.XMLDataSet.LoadManager.CachedRequest.LOAD_FAILED     = 3;Spry.Data.XMLDataSet.LoadManager.CachedRequest.LOAD_SUCCESSFUL = 4;Spry.Data.XMLDataSet.LoadManager.CachedRequest.prototype.loadDataCallback = function(req){	if (req.xhRequest.readyState != 4)		return;	var xmlDoc = req.xhRequest.responseXML;	if (req.xhRequest.status != 200)	{		if (req.xhRequest.status == 0)		{			// The page that is attempting to load data was probably loaded with			// a file:// url. Mozilla based browsers will actually provide the complete DOM			// tree for the data, but IE provides an empty document node so try to parse			// the xml text manually to create a dom tree we can use.			if (req.xhRequest.responseText && (!xmlDoc || !xmlDoc.firstChild))				xmlDoc = Spry.Utils.stringToXMLDoc(req.xhRequest.responseText);		}	}	if (!xmlDoc  || !xmlDoc.firstChild || xmlDoc.firstChild.nodeName == "parsererror")	{		this.state = Spry.Data.XMLDataSet.LoadManager.CachedRequest.LOAD_FAILED;		this.notifyObservers("onRequestError", req);		this.observers.length = 0; // Clear the observers list.		return;	}	this.doc = xmlDoc;	this.state = Spry.Data.XMLDataSet.LoadManager.CachedRequest.LOAD_SUCCESSFUL;	// Notify all of the cached request's observers!	this.notifyObservers("onRequestResponse", req);	// Clear the observers list.	this.observers.length = 0;};Spry.Data.XMLDataSet.LoadManager.CachedRequest.prototype.loadData = function(){	// IE will synchronously fire our loadDataCallback() during the call	// to an async Spry.Utils.loadURL() if the data for the url is already	// in the browser's local cache. This can wreak havoc with complicated master/detail	// regions that use data sets that have master/detail relationships with other	// data sets. Our data set logic already handles async data loading nicely so we	// use a timer to fire off the async Spry.Utils.loadURL() call to insure that any	// data loading happens asynchronously after this function is finished.	var self = this;	this.cancelLoadData();	this.doc = null;	this.state = Spry.Data.XMLDataSet.LoadManager.CachedRequest.LOAD_REQUESTED;	var reqInfo = this.reqInfo.clone();	reqInfo.successCallback = function(req) { self.loadDataCallback(req); };	reqInfo.errorCallback = reqInfo.successCallback;	this.timer = setTimeout(function()	{		self.timer = null;		Spry.Utils.loadURL(reqInfo.method, reqInfo.url, reqInfo.async, reqInfo.successCallback, reqInfo);	}, 0);  };Spry.Data.XMLDataSet.LoadManager.CachedRequest.prototype.cancelLoadData = function(){	if (this.state == Spry.Data.XMLDataSet.LoadManager.CachedRequest.LOAD_REQUESTED)	{		if (this.timer)		{			this.timer.clearTimeout();			this.timer = null;		}		this.doc = null;		this.state = Spry.Data.XMLDataSet.LoadManager.CachedRequest.NOT_LOADED;	}};Spry.Data.XMLDataSet.LoadManager.getCacheKey = function(reqInfo){	return reqInfo.method + "::" + reqInfo.url + "::" + reqInfo.postData + "::" + reqInfo.username;};Spry.Data.XMLDataSet.LoadManager.loadData = function(reqInfo, ds, useCache){	if (!reqInfo)		return null;	var cacheObj = null;	var cacheKey = null;	if (useCache)	{		cacheKey = Spry.Data.XMLDataSet.LoadManager.getCacheKey(reqInfo);		cacheObj = Spry.Data.XMLDataSet.LoadManager.cache[cacheKey];	}	if (cacheObj)	{		if (cacheObj.state == Spry.Data.XMLDataSet.LoadManager.CachedRequest.LOAD_REQUESTED)		{			if (ds)				cacheObj.addObserver(ds);			return cacheObj;		}		else if (cacheObj.state == Spry.Data.XMLDataSet.LoadManager.CachedRequest.LOAD_SUCCESSFUL)		{			// Data is already cached so if we have a data set, trigger an async call			// that tells it to load its data.			if (ds)				setTimeout(function() { ds.setDataFromDoc(cacheObj.doc); }, 0);			return cacheObj;		}	}	// We're either loading this url for the first time, or an error occurred when	// we last tried to load it, or the caller requested a forced load.	if (!cacheObj)	{		cacheObj = new Spry.Data.XMLDataSet.LoadManager.CachedRequest(reqInfo);		if (useCache)		{			Spry.Data.XMLDataSet.LoadManager.cache[cacheKey] = cacheObj;			// Add an observer that will remove the cacheObj from the cache			// if there is a load request failure.			cacheObj.addObserver({ onRequestError: function() { Spry.Data.XMLDataSet.LoadManager.cache[cacheKey] = undefined; }});		}	}	if (ds)		cacheObj.addObserver(ds);	cacheObj.loadData();	return cacheObj;};Spry.Data.XMLDataSet.LoadManager.cancelLoadData = function(cacheObj, ds){	if (cacheObj)	{		if (ds)			cacheObj.removeObserver(ds);		else			cacheObj.cancelLoadData();	}};////////////////////////////////////////////////////////////////////////// Spry.Data.Region//////////////////////////////////////////////////////////////////////// Spry.Data.Region = function(regionNode, name, isDetailRegion, data, dataSets, regionStates, regionStateMap, hasBehaviorAttributes){	this.regionNode = regionNode;	this.name = name;	this.isDetailRegion = isDetailRegion;	this.data = data;	this.dataSets = dataSets;	this.hasBehaviorAttributes = hasBehaviorAttributes;	this.tokens = null;	this.currentState = null;	this.states = { ready: true };	this.stateMap = {};	Spry.Utils.setOptions(this.states, regionStates);	Spry.Utils.setOptions(this.stateMap, regionStateMap);	// Add the region as an observer to the dataSet!	for (var i = 0; i < this.dataSets.length; i++)	{		var ds = this.dataSets[i];		try 		{			if (ds)				ds.addObserver(this);		}		catch(e) { Spry.Debug.reportError("Failed to add '" + this.name + "' as a dataSet observer!\n"); }	}}; // End of Spry.Data.Region() constructor.Spry.Data.Region.hiddenRegionClassName = "SpryHiddenRegion";Spry.Data.Region.evenRowClassName = "even";Spry.Data.Region.oddRowClassName = "odd";Spry.Data.Region.notifiers = {};Spry.Data.Region.evalScripts = true;Spry.Data.Region.addObserver = function(regionID, observer){	var n = Spry.Data.Region.notifiers[regionID];	if (!n)	{		n = new Spry.Utils.Notifier();		Spry.Data.Region.notifiers[regionID] = n;	}	n.addObserver(observer);};Spry.Data.Region.removeObserver = function(regionID, observer){	var n = Spry.Data.Region.notifiers[regionID];	if (n)		n.removeObserver(observer);};Spry.Data.Region.notifyObservers = function(methodName, region, data){	var n = Spry.Data.Region.notifiers[region.name];	if (n)	{		var dataObj = {};		if (data && typeof data == "object")			dataObj = data;		else			dataObj.data = data;		dataObj.region = region;		dataObj.regionID = region.name;		dataObj.regionNode = region.regionNode;		n.notifyObservers(methodName, dataObj);	}};Spry.Data.Region.RS_Error = 0x01;Spry.Data.Region.RS_LoadingData = 0x02;Spry.Data.Region.RS_PreUpdate = 0x04;Spry.Data.Region.RS_PostUpdate = 0x08;Spry.Data.Region.prototype.getState = function(){	return this.currentState;};Spry.Data.Region.prototype.mapState = function(stateName, newStateName){	this.stateMap[stateName] = newStateName;};Spry.Data.Region.prototype.getMappedState = function(stateName){	var mappedState = this.stateMap[stateName];	return mappedState ? mappedState : stateName;};Spry.Data.Region.prototype.setState = function(stateName, suppressNotfications){	var stateObj = { state: stateName, mappedState: this.getMappedState(stateName) };	if (!suppressNotfications)		Spry.Data.Region.notifyObservers("onPreStateChange", this, stateObj);	this.currentState = stateObj.mappedState ? stateObj.mappedState : stateName;	// If the region has content that is specific to this	// state, regenerate the region so that its markup is updated.	if (this.states[stateName])	{		if (!suppressNotfications)			Spry.Data.Region.notifyObservers("onPreUpdate", this, { state: this.currentState });			// Make the region transform the xml data. The result is		// a string that we need to parse and insert into the document.		var str = this.transform();			// Clear out any previous transformed content.		// this.clearContent();			if (Spry.Data.Region.debug)			Spry.Debug.trace("<hr />Generated region markup for '" + this.name + "':<br /><br />" + Spry.Utils.encodeEntities(str));		// Now insert the new transformed content into the document.		Spry.Utils.setInnerHTML(this.regionNode, str, !Spry.Data.Region.evalScripts);			// Now run through the content looking for attributes		// that tell us what behaviors to attach to each element.		if (this.hasBehaviorAttributes)			this.attachBehaviors();			if (!suppressNotfications)			Spry.Data.Region.notifyObservers("onPostUpdate", this, { state: this.currentState });	}	if (!suppressNotfications)		Spry.Data.Region.notifyObservers("onPostStateChange", this, stateObj);};Spry.Data.Region.prototype.getDataSets = function(){	return this.dataSets;};Spry.Data.Region.prototype.addDataSet = function(aDataSet){	if (!aDataSet)		return;	if (!this.dataSets)		this.dataSets = new Array;	// Check to see if the data set is already in our list.	for (var i = 0; i < this.dataSets.length; i++)	{		if (this.dataSets[i] == aDataSet)			return; // It's already in our list!	}	this.dataSets.push(aDataSet);	aDataSet.addObserver(this);};Spry.Data.Region.prototype.removeDataSet = function(aDataSet){	if (!aDataSet || this.dataSets)		return;	for (var i = 0; i < this.dataSets.length; i++)	{		if (this.dataSets[i] == aDataSet)		{			this.dataSets.splice(i, 1);			aDataSet.removeObserver(this);			return;		}	}};Spry.Data.Region.prototype.onPreLoad = function(dataSet){	if (this.currentState != "loading")		this.setState("loading");};Spry.Data.Region.prototype.onLoadError = function(dataSet){	if (this.currentState != "error")		this.setState("error");	Spry.Data.Region.notifyObservers("onError", this);};Spry.Data.Region.prototype.onCurrentRowChanged = function(dataSet, data){	if (this.isDetailRegion)		this.updateContent();};Spry.Data.Region.prototype.onPostSort = function(dataSet, data){	this.updateContent();};Spry.Data.Region.prototype.onDataChanged = function(dataSet, data){	this.updateContent();};Spry.Data.Region.enableBehaviorAttributes = true;Spry.Data.Region.behaviorAttrs = {};Spry.Data.Region.behaviorAttrs["spry:select"] ={	attach: function(rgn, node, value)	{		var selectGroupName = null;		try { selectGroupName = node.attributes.getNamedItem("spry:selectgroup").value; } catch (e) {}		if (!selectGroupName)			selectGroupName = "default";		Spry.Utils.addEventListener(node, "click", function(event) { Spry.Utils.SelectionManager.select(selectGroupName, node, value); }, false);				if (node.attributes.getNamedItem("spry:selected"))			Spry.Utils.SelectionManager.select(selectGroupName, node, value);	}};Spry.Data.Region.behaviorAttrs["spry:hover"] ={	attach: function(rgn, node, value)	{		Spry.Utils.addEventListener(node, "mouseover", function(event){ Spry.Utils.addClassName(node, value); }, false);		Spry.Utils.addEventListener(node, "mouseout", function(event){ Spry.Utils.removeClassName(node, value); }, false);	}};Spry.Data.Region.setUpRowNumberForEvenOddAttr = function(node, attr, value, rowNumAttrName){	// The format for the spry:even and spry:odd attributes are as follows:	//	// <div spry:even="dataSetName cssEvenClassName" spry:odd="dataSetName cssOddClassName">	//	// The dataSetName is optional, and if not specified, the first data set	// listed for the region is used.	//	// cssEvenClassName and cssOddClassName are required and *must* be specified. They can be	// any user defined CSS class name.	if (!value)	{		Spry.Debug.showError("The " + attr + " attribute requires a CSS class name as its value!");		node.attributes.removeNamedItem(attr);		return;	}	var dsName = "";	var valArr = value.split(/\s/);	if (valArr.length > 1)	{		// Extract out the data set name and reset the attribute so		// that it only contains the CSS class name to use.		dsName = valArr[0];		node.setAttribute(attr, valArr[1]);	}	// Tag the node with an attribute that will allow us to fetch the row	// number used when it is written out during the re-generation process.	node.setAttribute(rowNumAttrName, "{" + (dsName ? (dsName + "::") : "") + "ds_RowNumber}");};Spry.Data.Region.behaviorAttrs["spry:even"] ={	setup: function(node, value)	{		Spry.Data.Region.setUpRowNumberForEvenOddAttr(node, "spry:even", value, "spryevenrownumber");	},	attach: function(rgn, node, value)	{		if (value)		{			rowNumAttr = node.attributes.getNamedItem("spryevenrownumber");			if (rowNumAttr && rowNumAttr.value)			{				var rowNum = parseInt(rowNumAttr.value);				if (rowNum % 2)					Spry.Utils.addClassName(node, value);			}		}		node.removeAttribute("spry:even");		node.removeAttribute("spryevenrownumber");	}};Spry.Data.Region.behaviorAttrs["spry:odd"] ={	setup: function(node, value)	{		Spry.Data.Region.setUpRowNumberForEvenOddAttr(node, "spry:odd", value, "spryoddrownumber");	},	attach: function(rgn, node, value)	{		if (value)		{			rowNumAttr = node.attributes.getNamedItem("spryoddrownumber");			if (rowNumAttr && rowNumAttr.value)			{				var rowNum = parseInt(rowNumAttr.value);				if (rowNum % 2 == 0)					Spry.Utils.addClassName(node, value);			}		}		node.removeAttribute("spry:odd");		node.removeAttribute("spryoddrownumber");	}};Spry.Data.Region.setRowAttrClickHandler = function(node, dsName, rowAttr, funcName){		if (dsName)		{			var ds = null;			try { ds = Spry.Utils.eval(dsName); } catch(e) { ds = null; };			if (ds)			{				rowIDAttr = node.attributes.getNamedItem(rowAttr);				if (rowIDAttr)				{					var rowAttrVal = rowIDAttr.value;					if (rowAttrVal)						Spry.Utils.addEventListener(node, "click", function(event){ ds[funcName](rowAttrVal); }, false);				}			}		}};Spry.Data.Region.behaviorAttrs["spry:setrow"] ={	setup: function(node, value)	{		if (!value)		{			Spry.Debug.reportError("The spry:setrow attribute requires a data set name as its value!");			node.removeAttribute("spry:setrow");			return;		}		// Tag the node with an attribute that will allow us to fetch the id of the		// row used when it is written out during the re-generation process.		node.setAttribute("spryrowid", "{" + value + "::ds_RowID}");	},	attach: function(rgn, node, value)	{		Spry.Data.Region.setRowAttrClickHandler(node, value, "spryrowid", "setCurrentRow");		node.removeAttribute("spry:setrow");		node.removeAttribute("spryrowid");	}};Spry.Data.Region.behaviorAttrs["spry:setrownumber"] ={	setup: function(node, value)	{		if (!value)		{			Spry.Debug.reportError("The spry:setrownumber attribute requires a data set name as its value!");			node.removeAttribute("spry:setrownumber");			return;		}		// Tag the node with an attribute that will allow us to fetch the row number		// of the row used when it is written out during the re-generation process.		node.setAttribute("spryrownumber", "{" + value + "::ds_RowID}");	},	attach: function(rgn, node, value)	{		Spry.Data.Region.setRowAttrClickHandler(node, value, "spryrownumber", "setCurrentRowNumber");		node.removeAttribute("spry:setrownumber");		node.removeAttribute("spryrownumber");	}};Spry.Data.Region.behaviorAttrs["spry:sort"] ={	attach: function(rgn, node, value)	{		if (!value)			return;		// The format of a spry:sort attribute is as follows:		//		// <div spry:sort="dataSetName column1Name column2Name ... sortOrderName">		//		// The dataSetName and sortOrderName are optional, but when specified, they		// must appear in the order mentioned above. If the dataSetName is not specified,		// the first data set listed for the region is used. If the sortOrderName is not		// specified, the sort defaults to "toggle".		//		// The user *must* specify at least one column name.		var ds = rgn.getDataSets()[0];		var sortOrder = "toggle";		var colArray = value.split(/\s/);		if (colArray.length > 1)		{			// 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 = eval(colArray[0]);				if (specifiedDS && (typeof specifiedDS) == "object")				{					ds = specifiedDS;					colArray.shift();				}			} catch(e) {}			// 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.			if (colArray.length > 1)			{				var str = colArray[colArray.length - 1];				if (str == "ascending" || str == "descending" || str == "toggle")				{					sortOrder = str;					colArray.pop();				}			}		}		// If we have a data set and some column names, add a non-destructive		// onclick handler that will perform a toggle sort on the data set.		if (ds && colArray.length > 0)			Spry.Utils.addEventListener(node, "click", function(event){ ds.sort(colArray, sortOrder); }, false);		node.removeAttribute("spry:sort");	}};Spry.Data.Region.prototype.attachBehaviors = function(){	var rgn = this;	Spry.Utils.getNodesByFunc(this.regionNode, function(node)	{		if (!node || node.nodeType != 1 /* Node.ELEMENT_NODE */)			return false;		try		{			var bAttrs = Spry.Data.Region.behaviorAttrs;			for (var bAttrName in bAttrs)			{				var attr = node.attributes.getNamedItem(bAttrName);				if (attr)				{					var behavior = bAttrs[bAttrName];					if (behavior && behavior.attach)						behavior.attach(rgn, node, attr.value);				}			}		} catch(e) {}		return false;	});};Spry.Data.Region.prototype.updateContent = function(){	var allDataSetsReady = true;	var dsArray = this.getDataSets();	if (!dsArray || dsArray.length < 1)	{		Spry.Debug.reportError("updateContent(): Region '" + this.name + "' has no data set!\n");		return;	}	for (var i = 0; i < dsArray.length; i++)	{		var ds = dsArray[i];		if (ds)		{			if (ds.getLoadDataRequestIsPending())				allDataSetsReady = false;			else if (!ds.getDataWasLoaded())			{				// Kick off the loading of the data if it hasn't happened yet.				ds.loadData();				allDataSetsReady = false;			}		}	}	if (!allDataSetsReady)	{		Spry.Data.Region.notifyObservers("onLoadingData", this);		// Just return, this method will get called again automatically		// as each data set load completes!		return;	}	this.setState("ready");};Spry.Data.Region.prototype.clearContent = function(){	this.regionNode.innerHTML = "";};Spry.Data.Region.processContentPI = function(inStr){	var outStr = "";	var regexp = /<!--\s*<\/?spry:content\s*[^>]*>\s*-->/mg;	var searchStartIndex = 0;	var processingContentTag = 0;	while (inStr.length)	{		var results = regexp.exec(inStr);		if (!results || !results[0])		{			outStr += inStr.substr(searchStartIndex, inStr.length - searchStartIndex);			break;		}		if (!processingContentTag && results.index != searchStartIndex)		{			// We found a match but it's not at the start of the inStr.			// Create a string token for everything that precedes the match.			outStr += inStr.substr(searchStartIndex, results.index - searchStartIndex);		}		if (results[0].search(/<\//) != -1)		{			--processingContentTag;			if (processingContentTag)				Spry.Debug.reportError("Nested spry:content regions are not allowed!\n");		}		else		{			++processingContentTag;			var dataRefStr = results[0].replace(/.*\bdataref="/, "");			outStr += dataRefStr.replace(/".*$/, "");		}				searchStartIndex = regexp.lastIndex;	}	return outStr;}Spry.Data.Region.prototype.tokenizeData = function(dataStr){	// If there is no data, there's nothing to do.	if (!dataStr)		return null;	var rootToken = new Spry.Data.Region.Token(Spry.Data.Region.Token.LIST_TOKEN, null, null, null);	var tokenStack = new Array;	var parseStr = Spry.Data.Region.processContentPI(dataStr);	tokenStack.push(rootToken);	// Create a regular expression that will match one of the following:	//	//   <spry:repeat select="regionName" test="true">	//   </spry:repeat>	//   {valueReference}	var regexp = /((<!--\s*){0,1}<\/{0,1}spry:[^>]+>(\s*-->){0,1})|((\{|%7[bB])[^\}\s%]+(\}|%7[dD]))/mg;	var searchStartIndex = 0;	while(parseStr.length)	{		var results = regexp.exec(parseStr);		var token = null;				if (!results || !results[0])		{			// If we get here, the rest of the parseStr should be			// just a plain string. Create a token for it and then			// break out of the list.			var str = parseStr.substr(searchStartIndex, parseStr.length - searchStartIndex);			token = new Spry.Data.Region.Token(Spry.Data.Region.Token.STRING_TOKEN, null, str, str);			tokenStack[tokenStack.length - 1].addChild(token);			break;		}		if (results.index != searchStartIndex)		{			// We found a match but it's not at the start of the parseStr.			// Create a string token for everything that precedes the match.			var str = parseStr.substr(searchStartIndex, results.index - searchStartIndex);			token = new Spry.Data.Region.Token(Spry.Data.Region.Token.STRING_TOKEN, null, str, str);			tokenStack[tokenStack.length - 1].addChild(token);		}		// We found a string that needs to be turned into a token. Create a token		// for it and then update parseStr for the next iteration.		if (results[0].search(/^({|%7[bB])/) != -1 /* results[0].charAt(0) == '{' */)		{			var valueName = results[0];			var regionStr = results[0];						// Strip off brace and url encode brace chars inside the valueName.			valueName = valueName.replace(/^({|%7[bB])/, "");			valueName = valueName.replace(/(}|%7[dD])$/, "");			// Check to see if our value begins with the name of a data set.			// For example: {dataSet:tokenValue}. If it is, we need to save			// the data set name so we know which data set to use to get the			// value for the token during the region transform.			var dataSetName = null;			var splitArray = valueName.split(/::/);			if (splitArray.length > 1)			{				dataSetName = splitArray[0];				valueName = splitArray[1];			}			// Convert any url encoded braces to regular brace chars.			regionStr = regionStr.replace(/^%7[bB]/, "{");			regionStr = regionStr.replace(/%7[dD]$/, "}");			// Now create a token for the placeholder.			token = new Spry.Data.Region.Token(Spry.Data.Region.Token.VALUE_TOKEN, dataSetName, valueName, new String(regionStr));			tokenStack[tokenStack.length - 1].addChild(token);		}		else if (results[0].charAt(0) == '<')		{			// Extract out the name of the processing instruction.			var piName = results[0].replace(/^(<!--\s*){0,1}<\/?/, "");			piName = piName.replace(/>(\s*-->){0,1}|\s.*$/, "");						if (results[0].search(/<\//) != -1 /* results[0].charAt(1) == '/' */)			{				// We found a processing instruction close tag. Pop the top of the				// token stack!				//				// XXX: We need to make sure that the close tag name matches the one				//      on the top of the token stack!				if (tokenStack[tokenStack.length - 1].tokenType != Spry.Data.Region.Token.PROCESSING_INSTRUCTION_TOKEN)				{					Spry.Debug.reportError("Invalid processing instruction close tag: " + piName + " -- " + results[0] + "\n");					return null;				}				tokenStack.pop();			}			else			{				// Create the processing instruction token, add it as a child of the token				// at the top of the token stack, and then push it on the stack so that it				// becomes the parent of any tokens between it and its close tag.				var piDesc = Spry.Data.Region.PI.instructions[piName];				if (piDesc)				{					var dataSet = null;					var selectedDataSetName = "";					if (results[0].search(/^.*\bselect=\"/) != -1)					{						selectedDataSetName = results[0].replace(/^.*\bselect=\"/, "");						selectedDataSetName = selectedDataSetName.replace(/".*$/, "");							if (selectedDataSetName)						{							try							{								dataSet = eval(selectedDataSetName);							}							catch (e)							{								Spry.Debug.reportError("Caught exception in tokenizeData() while trying to retrieve data set (" + selectedDataSetName + "): " + e + "\n");								dataSet = null;								selectedDataSetName = "";							}						}					}					// Check if the repeat has a test attribute.					var jsExpr = null;					if (results[0].search(/^.*\btest=\"/) != -1)					{						jsExpr = results[0].replace(/^.*\btest=\"/, "");						jsExpr = jsExpr.replace(/".*$/, "");						jsExpr = Spry.Utils.decodeEntities(jsExpr);					}					// Check if the instruction has a state name specified.					var regionState = null;					if (results[0].search(/^.*\bname=\"/) != -1)					{						regionState = results[0].replace(/^.*\bname=\"/, "");						regionState = regionState.replace(/".*$/, "");						regionState = Spry.Utils.decodeEntities(regionState);					}					var piData = new Spry.Data.Region.Token.PIData(piName, selectedDataSetName, jsExpr, regionState);					token = new Spry.Data.Region.Token(Spry.Data.Region.Token.PROCESSING_INSTRUCTION_TOKEN, dataSet, piData, new String(results[0]));					tokenStack[tokenStack.length - 1].addChild(token);					tokenStack.push(token);				}				else				{					Spry.Debug.reportError("Unsupported region processing instruction: " + results[0] + "\n");					return null;				}			}		}		else		{			Spry.Debug.reportError("Invalid region token: " + results[0] + "\n");			return null;		}		searchStartIndex = regexp.lastIndex;	}	return rootToken;};Spry.Data.Region.prototype.processTokenChildren = function(token, processContext){	// The use of an array to gather the strings returned from processing	// the child tokens is actually a performance enhancement for IE.	// The original code:	//	//     for (var i = 0; i < token.children.length; i++)	//       outputStr += this.processTokens(token.children[i], processContext);	//	// seemed to cause an n-square problem in IE. Using an array with	// a final join reduced one of our test cases (SelectExample.html) from over	// a minute to about 15 seconds.		var strArr = [ "" ];	var len = token.children.length;	var children = token.children;		for (var i = 0; i < len; i++)		strArr.push(this.processTokens(children[i], processContext));	return strArr.join("");};Spry.Data.Region.prototype.processTokens = function(token, processContext){	if (!processContext)	{		processContext = new Spry.Data.Region.ProcessingContext(this);		if (!processContext)			return "";	}	var outputStr = "";	var i = 0;	switch(token.tokenType)	{		case Spry.Data.Region.Token.LIST_TOKEN:			outputStr += this.processTokenChildren(token, processContext);			break;		case Spry.Data.Region.Token.STRING_TOKEN:			outputStr += token.data;			break;		case Spry.Data.Region.Token.PROCESSING_INSTRUCTION_TOKEN:			if (token.data.name == "spry:repeat")			{				var dataSet = null;				if (token.dataSet)					dataSet = token.dataSet;				else					dataSet = this.dataSets[0];				if (dataSet)				{					var dsContext = processContext.getDataSetContext(dataSet);					if (!dsContext)					{						Spry.Debug.reportError("processTokens() failed to get a data set context!\n");						break;					}					var numRows = dsContext.getNumRows();					var dataSetRows = dataSet.getData();					dsContext.pushState();					for (i = 0; i < numRows; i++)					{						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("Caught exception in Spry.Data.Region.prototype.processTokens while evaluating: " + jsExpr + "\n    Exception:" + e + "\n");								testVal = true;							}						}						if (testVal)							outputStr += this.processTokenChildren(token, processContext);					}					dsContext.popState();				}			}			else if (token.data.name == "spry:if")			{				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("Caught exception in Spry.Data.Region.prototype.processTokens while evaluating: " + jsExpr + "\n    Exception:" + e + "\n");						testVal = true;					}				}					if (testVal)					outputStr += this.processTokenChildren(token, processContext);			}			else if (token.data.name == "spry:choose")			{				var defaultChild = null;				var childToProcess = null;				var testVal = false;				var j = 0;				// All of the children of the spry:choose token should be of the type spry:when or spry:default.				// Run through all of the spry:when children and see if any of their test expressions return true.				// If one does, then process its children tokens. If none of the test expressions return true,				// process the spry:default token's children, if it exists.				for (j = 0; j < token.children.length; j++)				{					var child = token.children[j];					if (child.tokenType == Spry.Data.Region.Token.PROCESSING_INSTRUCTION_TOKEN)					{						if (child.data.name == "spry:when")						{							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("Caught exception in Spry.Data.Region.prototype.processTokens while evaluating: " + jsExpr + "\n    Exception:" + e + "\n");									testVal = false;								}								if (testVal)								{									childToProcess = child;									break;								}							}						}						else if (child.data.name == "spry:default")							defaultChild = child;					}				}				// If we didn't find a match, use the token for the default case.				if (!childToProcess && defaultChild)					childToProcess = defaultChild;				if (childToProcess)					outputStr += this.processTokenChildren(childToProcess, processContext);			}			else if (token.data.name == "spry:state")			{				var testVal = true;								if (!token.data.regionState || token.data.regionState == this.currentState)					outputStr += this.processTokenChildren(token, processContext);			}			else			{				Spry.Debug.reportError("processTokens(): Unknown processing instruction: " + token.data.name + "\n");				return "";			}			break;		case Spry.Data.Region.Token.VALUE_TOKEN:			var dataSet = token.dataSet;			if (!dataSet && this.dataSets && this.dataSets.length > 0 && 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("processTokens(): Value reference has no data set specified: " + token.regionStr + "\n");				return "";			}			var dsContext = processContext.getDataSetContext(dataSet);			if (!dsContext)			{				Spry.Debug.reportError("processTokens: Failed to get a data set context!\n");				return "";			}			var ds = dsContext.getDataSet();			if (token.data == "ds_RowNumber")				outputStr += dsContext.getRowIndex();			else if (token.data == "ds_RowNumberPlus1")				outputStr += (dsContext.getRowIndex() + 1);			else if (token.data == "ds_RowCount")				outputStr += dsContext.getNumRows();			else if (token.data == "ds_UnfilteredRowCount")				outputStr += dsContext.getNumRows(true);			else if (token.data == "ds_CurrentRowNumber")				outputStr += ds.getRowNumber(ds.getCurrentRow());			else if (token.data == "ds_CurrentRowID")				outputStr += ds.curRowID;			else if (token.data == "ds_EvenOddRow")				outputStr += (dsContext.getRowIndex() % 2) ? Spry.Data.Region.evenRowClassName : Spry.Data.Region.oddRowClassName;			else if (token.data == "ds_SortOrder")				outputStr += ds.getSortOrder();			else if (token.data == "ds_SortColumn")				outputStr += ds.getSortColumn();			else			{				var curDataSetRow = dsContext.getCurrentRow();				if (curDataSetRow)					outputStr += curDataSetRow[token.data];			}			break;		default:			Spry.Debug.reportError("processTokens(): Invalid token type: " + token.regionStr + "\n");			break;	}	return outputStr;};Spry.Data.Region.prototype.transform = function(){	if (this.data && !this.tokens)		this.tokens = this.tokenizeData(this.data);	if (!this.tokens)		return "";	return this.processTokens(this.tokens, null);};Spry.Data.Region.PI = {};Spry.Data.Region.PI.instructions = {};Spry.Data.Region.PI.buildOpenTagForValueAttr = function(ele, piName, attrName){	if (!ele || !piName)		return "";	var jsExpr = "";	try	{		var testAttr = ele.attributes.getNamedItem(piName);		if (testAttr && testAttr.value)			jsExpr = Spry.Utils.encodeEntities(testAttr.value);	}	catch (e) { jsExpr = ""; }	if (!jsExpr)	{		Spry.Debug.reportError(piName + " attribute requires a JavaScript expression that returns true or false!\n");		return "";	}	return "<" + Spry.Data.Region.PI.instructions[piName].tagName + " " + attrName +"=\"" + jsExpr + "\">";};Spry.Data.Region.PI.buildOpenTagForTest = function(ele, piName){	return Spry.Data.Region.PI.buildOpenTagForValueAttr(ele, piName, "test");};Spry.Data.Region.PI.buildOpenTagForState = function(ele, piName){	return Spry.Data.Region.PI.buildOpenTagForValueAttr(ele, piName, "name");};Spry.Data.Region.PI.buildOpenTagForRepeat = function(ele, piName){	if (!ele || !piName)		return "";	var selectAttrStr = "";	try	{		var selectAttr = ele.attributes.getNamedItem(piName);		if (selectAttr && selectAttr.value)		{			selectAttrStr = selectAttr.value;			selectAttrStr = selectAttrStr.replace(/\s/g, "");		}	}	catch (e) { selectAttrStr = ""; }	if (!selectAttrStr)	{		Spry.Debug.reportError(piName + " attribute requires a data set name!\n");		return "";	}	var testAttrStr = "";	try	{		var testAttr = ele.attributes.getNamedItem("spry:test");		if (testAttr)		{			if (testAttr.value)				testAttrStr = " test=\"" + Spry.Utils.encodeEntities(testAttr.value) + "\"";			ele.attributes.removeNamedItem(testAttr.nodeName);		}	}	catch (e) { testAttrStr = ""; }	return "<" + Spry.Data.Region.PI.instructions[piName].tagName + " select=\"" + selectAttrStr + "\"" + testAttrStr + ">";};Spry.Data.Region.PI.buildOpenTagForContent = function(ele, piName){	if (!ele || !piName)		return "";	var dataRefStr = "";	try	{		var contentAttr = ele.attributes.getNamedItem(piName);		if (contentAttr && contentAttr.value)			dataRefStr = Spry.Utils.encodeEntities(contentAttr.value);	}	catch (e) { dataRefStr = ""; }	if (!dataRefStr)	{		Spry.Debug.reportError(piName + " attribute requires a data reference!\n");		return "";	}	return "<" + Spry.Data.Region.PI.instructions[piName].tagName + " dataref=\"" + dataRefStr + "\">";};Spry.Data.Region.PI.buildOpenTag = function(ele, piName){	return "<" + Spry.Data.Region.PI.instructions[piName].tagName + ">";};Spry.Data.Region.PI.buildCloseTag = function(ele, piName){	return "</" + Spry.Data.Region.PI.instructions[piName].tagName + ">";};Spry.Data.Region.PI.instructions["spry:state"] = { tagName: "spry:state", childrenOnly: false, getOpenTag: Spry.Data.Region.PI.buildOpenTagForState, getCloseTag: Spry.Data.Region.PI.buildCloseTag };Spry.Data.Region.PI.instructions["spry:if"] = { tagName: "spry:if", childrenOnly: false, getOpenTag: Spry.Data.Region.PI.buildOpenTagForTest, getCloseTag: Spry.Data.Region.PI.buildCloseTag };Spry.Data.Region.PI.instructions["spry:repeat"] = { tagName: "spry:repeat", childrenOnly: false, getOpenTag: Spry.Data.Region.PI.buildOpenTagForRepeat, getCloseTag: Spry.Data.Region.PI.buildCloseTag };Spry.Data.Region.PI.instructions["spry:repeatchildren"] = { tagName: "spry:repeat", childrenOnly: true, getOpenTag: Spry.Data.Region.PI.buildOpenTagForRepeat, getCloseTag: Spry.Data.Region.PI.buildCloseTag };Spry.Data.Region.PI.instructions["spry:choose"] = { tagName: "spry:choose", childrenOnly: true, getOpenTag: Spry.Data.Region.PI.buildOpenTag, getCloseTag: Spry.Data.Region.PI.buildCloseTag };Spry.Data.Region.PI.instructions["spry:when"] = { tagName: "spry:when", childrenOnly: false, getOpenTag: Spry.Data.Region.PI.buildOpenTagForTest, getCloseTag: Spry.Data.Region.PI.buildCloseTag };Spry.Data.Region.PI.instructions["spry:default"] = { tagName: "spry:default", childrenOnly: false, getOpenTag: Spry.Data.Region.PI.buildOpenTag, getCloseTag: Spry.Data.Region.PI.buildCloseTag };Spry.Data.Region.PI.instructions["spry:content"] = { tagName: "spry:content", childrenOnly: true, getOpenTag: Spry.Data.Region.PI.buildOpenTagForContent, getCloseTag: Spry.Data.Region.PI.buildCloseTag };Spry.Data.Region.PI.orderedInstructions = [ "spry:state", "spry:if", "spry:repeat", "spry:repeatchildren", "spry:choose", "spry:when", "spry:default", "spry:content" ];Spry.Data.Region.getTokensFromStr = function(str){	// XXX: This will need to be modified if we support	// tokens that use javascript between the braces!	if (!str)		return null;	return str.match(/{[^}]+}/g);};Spry.Data.Region.processDataRefString = function(processingContext, regionStr, dataSetsToUse, isJSExpr){	if (!regionStr)		return "";	if (!processingContext && !dataSetsToUse)		return regionStr;	var resultStr = "";	var re = new RegExp("\\{([^\\}:]+::)?[^\\}]+\\}", "g");	var startSearchIndex = 0;	while (startSearchIndex < regionStr.length)	{		var reArray = re.exec(regionStr);		if (!reArray || !reArray[0])		{			resultStr += regionStr.substr(startSearchIndex, regionStr.length - startSearchIndex);			return resultStr;		}		if (reArray.index != startSearchIndex)			resultStr += regionStr.substr(startSearchIndex, reArray.index - startSearchIndex);		var dsName = "";		if (reArray[0].search(/^\{[^}:]+::/) != -1)			dsName = reArray[0].replace(/^\{|::.*/g, "");		var fieldName = reArray[0].replace(/^\{|.*::|\}/g, "");		var row = null;		if (processingContext)		{			var dsContext = processingContext.getDataSetContext(dsName);			if (fieldName == "ds_RowNumber")			{				resultStr += dsContext.getRowIndex();				row = null;			}			else if (fieldName == "ds_RowNumberPlus1")			{				resultStr += (dsContext.getRowIndex() + 1);				row = null;			}			else if (fieldName == "ds_RowCount")			{				resultStr += dsContext.getNumRows();				row = null;			}			else if (fieldName == "ds_UnfilteredRowCount")			{				resultStr += dsContext.getNumRows(true);				row = null;			}			else if (fieldName == "ds_CurrentRowNumber")			{				var ds = dsContext.getDataSet();				resultStr += ds.getRowNumber(ds.getCurrentRow());				row = null;			}			else if (fieldName == "ds_CurrentRowID")			{				var ds = dsContext.getDataSet();				resultStr += "" + ds.curRowID;				row = null;			}			else if (fieldName == "ds_EvenOddRow")			{				resultStr += (dsContext.getRowIndex() % 2) ? Spry.Data.Region.evenRowClassName : Spry.Data.Region.oddRowClassName;				row = null;			}			else if (fieldName == "ds_SortOrder")			{				resultStr += dsContext.getDataSet().getSortOrder();;				row = null;			}			else if (fieldName == "ds_SortColumn")			{				resultStr += dsContext.getDataSet().getSortColumn();				row = null;			}			else				row = processingContext.getCurrentRowForDataSet(dsName);		}		else		{			var ds = dsName ? dataSetsToUse[dsName] : dataSetsToUse[0];			if (ds)				row = ds.getCurrentRow();		}		if (row)			resultStr += isJSExpr ? Spry.Utils.escapeQuotesAndLineBreaks("" + row[fieldName]) : row[fieldName];		if (startSearchIndex == re.lastIndex)		{			// On IE if there was a match near the end of the string, it sometimes			// leaves re.lastIndex pointing to the value it had before the last time			// we called re.exec. We check for this case to prevent an infinite loop!			// We need to write out any text in regionStr that comes after the last			// match.			var leftOverIndex = reArray.index + reArray[0].length;			if (leftOverIndex < regionStr.length)				resultStr += regionStr.substr(leftOverIndex);			break;		}		startSearchIndex = re.lastIndex;	}	return resultStr;};Spry.Data.Region.strToDataSetsArray = function(str, returnRegionNames){	var dataSetsArr = new Array;	var foundHash = {};	if (!str)		return dataSetsArr;	str = str.replace(/\s+/g, " ");	str = str.replace(/^\s|\s$/g, "");	var arr = str.split(/ /);	for (var i = 0; i < arr.length; i++)	{		if (arr[i] && !Spry.Data.Region.PI.instructions[arr[i]])		{			try {				var dataSet = eval(arr[i]);				if (!foundHash[arr[i]])				{					if (returnRegionNames)						dataSetsArr.push(arr[i]);					else						dataSetsArr.push(dataSet);					foundHash[arr[i]] = true;				}			}			catch (e) { /* Spry.Debug.trace("Caught exception: " + e + "\n"); */ }		}	}	return dataSetsArr;};Spry.Data.Region.DSContext = function(dataSet){	var m_self = this;	var m_dataSet = dataSet;	var m_curRowIndexArray = [ -1 ]; // -1 means return whatever the current row is inside the data set.	// Private Methods:	function getInternalRowIndex() { return m_curRowIndexArray[m_curRowIndexArray.length - 1]; }	// Public Methods:	this.resetAll = function() { m_curRowIndexArray = [ m_dataSet.getCurrentRow() ] };	this.getDataSet = function() { return m_dataSet; };	this.getNumRows = function(unfiltered)	{		return m_dataSet.getRowCount(unfiltered);	};	this.getCurrentRow = function()	{		if (m_curRowIndexArray.length < 2 || getInternalRowIndex() < 0)			return m_dataSet.getCurrentRow();			var data = m_dataSet.getData();		var curRowIndex = getInternalRowIndex();			if (curRowIndex < 0 || curRowIndex > data.length)		{			Spry.Debug.reportError("Invalid index used in Spry.Data.Region.DSContext.getCurrentRow()!\n");			return null;		}			return data[curRowIndex];	};	this.getRowIndex = function()	{		var curRowIndex = getInternalRowIndex();		if (curRowIndex >= 0)			return curRowIndex;		return m_dataSet.getRowNumber(m_dataSet.getCurrentRow());	};	this.setRowIndex = function(rowIndex) { m_curRowIndexArray[m_curRowIndexArray.length - 1] = rowIndex; };	this.pushState = function() { m_curRowIndexArray.push( getInternalRowIndex()); };	this.popState = function()	{		if (m_curRowIndexArray.length < 2)		{			// Our array should always have at least one element in it!			Spry.Debug.reportError("Stack underflow in Spry.Data.Region.DSContext.popState()!\n");			return;		}		m_curRowIndexArray.pop();	};};Spry.Data.Region.ProcessingContext = function(region){	var m_self = this;	var m_region = region;	var m_dataSetContexts = [];		if (region && region.dataSets)	{		for (var i = 0; i < region.dataSets.length; i++)			m_dataSetContexts.push(new Spry.Data.Region.DSContext(region.dataSets[i]));	}	this.getDataSetContext = function(dataSet)	{		if (!dataSet)		{			// We were called without a specified data set or			// data set name. Assume the caller wants the first			// data set in the processing context.			if (m_dataSetContexts.length > 0)				return m_dataSetContexts[0];			return null;		}		if (typeof dataSet == 'string')		{			try { dataSet = eval(dataSet); } catch (e) { dataSet = null; }			if (!dataSet)				return null;		}			for (var i = 0; i < m_dataSetContexts.length; i++)		{			var dsc = m_dataSetContexts[i];			if (dsc.getDataSet() == dataSet)				return dsc;		}			return null;	};	this.getCurrentRowForDataSet = function(dataSet)	{		var dsc = m_self.getDataSetContext(dataSet);		if (dsc)			return dsc.getCurrentRow();		return null;	};};Spry.Data.Region.Token = function(tokenType, dataSet, data, regionStr){	var self = this;	this.tokenType = tokenType;	this.dataSet = dataSet;	this.data = data;	this.regionStr = regionStr;	this.parent = null;	this.children = null;};Spry.Data.Region.Token.prototype.addChild = function(child){	if (!child)		return;		if (!this.children)		this.children = new Array;		this.children.push(child);	child.parent = this;};Spry.Data.Region.Token.LIST_TOKEN                   = 0;Spry.Data.Region.Token.STRING_TOKEN                 = 1;Spry.Data.Region.Token.PROCESSING_INSTRUCTION_TOKEN = 2;Spry.Data.Region.Token.VALUE_TOKEN                  = 3;Spry.Data.Region.Token.PIData = function(piName, data, jsExpr, regionState){	var self = this;	this.name = piName;	this.data = data;	this.jsExpr = jsExpr;	this.regionState = regionState;};Spry.Utils.addLoadListener(function() { setTimeout(function() { Spry.Data.initRegions(); }, 0); });
