<!-- HTML DATA SET PHOTO GALLERY -->

//                                                                     
// Compiled HTML Data Set Photo Gallery scipts - Spry Pre-Release 1.6.1
//                                                                     

// 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.

//                              
// CONTENTS:                    
// 0032 SpryEffects.js          
// 2429 SpryDOMUtils.js         
// 3432 SpryImageLoader.js      
// 3524 SpryNotifier.js         
// 3601 SpryThumbViewer.js      
// 3820 SpryImageViewer.js      
// 3903 SprySlideShowControl.js 
// 4039 gallery_init.js         
// 4096 SpryData.js             
// 8238 SpryHTMLDataSet.js      
// 8771 DynamicGallery.js       
//                              

// 0032                         
// Spry.Effect.js - version 0.38
//                              

var Spry;

if (!Spry) Spry = {};

Spry.forwards = 1;
Spry.backwards = 2;

if (!Spry.Effect) Spry.Effect = {};

Spry.Effect.Transitions = {
	linearTransition: function(time, begin, change, duration)
	{
		if (time > duration) return change+begin;
		return begin + (time / duration) * change;
	},
	sinusoidalTransition: function(time, begin, change, duration)
	{
		if (time > duration) return change+begin;
		return begin + ((-Math.cos((time/duration)*Math.PI)/2) + 0.5) * change;
	},
	squareTransition: function(time, begin, change, duration)
	{
		if (time > duration) return change+begin;
		return begin + Math.pow(time/duration, 2) * change;
	},
	squarerootTransition: function(time, begin, change, duration)
	{
		if (time > duration) return change+begin;
		return begin + Math.sqrt(time/duration) * change;
	},
	fifthTransition: function(time, begin, change, duration)
	{
		if (time > duration) return change+begin;
		return begin + Math.sqrt((-Math.cos((time/duration)*Math.PI)/2) + 0.5) * change;
	},
	circleTransition: function(time, begin, change, duration)
	{
		if (time > duration) return change+begin;
		var pos = time/duration;
		return begin + Math.sqrt(1 - Math.pow((pos-1), 2))* change;
	},
	pulsateTransition: function(time, begin, change, duration)
	{
		if (time > duration) return change+begin;
		return begin + (0.5 + Math.sin(17*time/duration)/2) * change;
	},
	growSpecificTransition: function(time, begin, change, duration)
	{
		if (time > duration) return change+begin;
		var pos = time/duration;
		return begin + (5 * Math.pow(pos, 3) - 6.4 * Math.pow(pos, 2) + 2 * pos) * change;
	}
};
for (var trans in Spry.Effect.Transitions)
{
	Spry[trans] = Spry.Effect.Transitions[trans];
}
//
// Spry.Effect.Registry
//

Spry.Effect.Registry = function()
{
	this.effects = [];
};

Spry.Effect.Registry.prototype.getRegisteredEffect = function(element, options) 
{
	var a = {};
	a.element = Spry.Effect.getElement(element);
	a.options = options;

	for (var i=0; i<this.effects.length; i++)
		if (this.effectsAreTheSame(this.effects[i], a))
			return this.effects[i].effect;

	return false;
};

Spry.Effect.Registry.prototype.addEffect = function(effect, element, options)
{
	if (!this.getRegisteredEffect(element, options))
	{
		var len = this.effects.length;
		this.effects[len] = {};
		var eff = this.effects[len];
		eff.effect = effect;
		eff.element = Spry.Effect.getElement(element);
		eff.options = options;
	}
};

Spry.Effect.Registry.prototype.effectsAreTheSame = function(effectA, effectB)
{
	if (effectA.element != effectB.element)
		return false;

	var compare = Spry.Effect.Utils.optionsAreIdentical(effectA.options, effectB.options);
	if (compare)
	{
		if (typeof effectB.options.setup == 'function')
			effectA.options.setup = effectB.options.setup;

		if (typeof effectB.options.finish == 'function')
			effectA.options.finish = effectB.options.finish;
	}		

	return compare;
};

var SpryRegistry = new Spry.Effect.Registry;

//
// Spry.Effect.Utils
//

if (!Spry.Effect.Utils) Spry.Effect.Utils = {};

Spry.Effect.Utils.showError = function(msg)
{
	alert('Spry.Effect ERR: ' + msg);
};
Spry.Effect.Utils.showInitError = function(effect){
	Spry.Effect.Utils.showError('The ' + effect + ' class can\'t be accessed as a static function anymore. '+ "\n" + 'Please read Spry Effects migration documentation.');
	return false;
};
Spry.Effect.Utils.Position = function()
{
	this.x = 0;
	this.y = 0;
	this.units = "px";
};

Spry.Effect.Utils.Rectangle = function()
{
	this.width = 0;
	this.height = 0;
	this.units = "px";
};

Spry.Effect.Utils.intToHex = function(integerNum) 
{
	var result = integerNum.toString(16);
	if (result.length == 1)
		result = "0" + result;
	return result;
};

Spry.Effect.Utils.hexToInt = function(hexStr)
{
	return parseInt(hexStr, 16);
};

Spry.Effect.Utils.rgb = function(redInt, greenInt, blueInt)
{
	var intToHex = Spry.Effect.Utils.intToHex;
	var redHex = intToHex(redInt);
	var greenHex = intToHex(greenInt);
	var blueHex = intToHex(blueInt);
	compositeColorHex = redHex.concat(greenHex, blueHex).toUpperCase();
	compositeColorHex = '#' + compositeColorHex;
	return compositeColorHex;
};

Spry.Effect.Utils.longColorVersion = function(color){
	if ( color.match(/^#[0-9a-f]{3}$/i) ){
		var tmp = color.split('');
		var color = '#';
		for (var i = 1; i < tmp.length; i++){
			color += tmp[i] + '' + tmp[i];	
		}
	}
	return color;
};

Spry.Effect.Utils.camelize = function(stringToCamelize)
{
	if (stringToCamelize.indexOf('-') == -1){
		return stringToCamelize;	
	}
	var oStringList = stringToCamelize.split('-');
	var isFirstEntry = true;
	var camelizedString = '';

	for(var i=0; i < oStringList.length; i++)
	{
		if(oStringList[i].length>0)
		{
			if(isFirstEntry)
			{
				camelizedString = oStringList[i];
				isFirstEntry = false;
			}
			else
			{
				var s = oStringList[i];
				camelizedString += s.charAt(0).toUpperCase() + s.substring(1);
			}
		}
	}

	return camelizedString;
};

Spry.Effect.Utils.isPercentValue = function(value)
{
	var result = false;
	if (typeof value == 'string' && value.length > 0 && value.lastIndexOf("%") > 0)
		result = true;

	return result;
};

Spry.Effect.Utils.getPercentValue = function(value)
{
	var result = 0;
	try
	{
		result = Number(value.substring(0, value.lastIndexOf("%")));
	}
	catch (e) {Spry.Effect.Utils.showError('Spry.Effect.Utils.getPercentValue: ' + e);}
	return result;
};

Spry.Effect.Utils.getPixelValue = function(value)
{
	var result = 0;
	if (typeof value == 'number') return value;
	var unitIndex = value.lastIndexOf("px");
	if ( unitIndex == -1)
		unitIndex = value.length;
	try
	{
		result = parseInt(value.substring(0, unitIndex), 10);
	}
	catch (e){}
	return result;
};

Spry.Effect.Utils.getFirstChildElement = function(node)
{
	if (node)
	{
		var childCurr = node.firstChild;
		while (childCurr)
		{
			if (childCurr.nodeType == 1)
				return childCurr;

			childCurr = childCurr.nextSibling;
		}
	}

	return null;
};

Spry.Effect.Utils.fetchChildImages = function(startEltIn, targetImagesOut)
{
	if(!startEltIn  || startEltIn.nodeType != 1 || !targetImagesOut)
		return;

	if(startEltIn.hasChildNodes())
	{
		var childImages = startEltIn.getElementsByTagName('img');
		var imageCnt = childImages.length;
		for(var i=0; i<imageCnt; i++)
		{
			var imgCurr = childImages[i];
			var dimensionsCurr = Spry.Effect.getDimensions(imgCurr);
			targetImagesOut.push([imgCurr,dimensionsCurr.width,dimensionsCurr.height]);
		}
	}
};

Spry.Effect.Utils.optionsAreIdentical = function(optionsA, optionsB)
{
	if(optionsA == null && optionsB == null)
		return true;

	if(optionsA != null && optionsB != null)
	{
		var objectCountA = 0;
		var objectCountB = 0;

		for (var propA in optionsA) objectCountA++;
		for (var propB in optionsB) objectCountB++;

		if(objectCountA != objectCountB)
			return false;

		for (var prop in optionsA)
		{
			var typeA = typeof optionsA[prop];
			var typeB = typeof optionsB[prop];
			if ( typeA != typeB || (typeA != 'undefined' && optionsA[prop] != optionsB[prop]))
				return false;
		}

		return true;
	}

	return false;
};

Spry.Effect.Utils.DoEffect = function (effectName, element, options)
{
	if (!options)
		var options = {};

	options.name = effectName;
	var ef = SpryRegistry.getRegisteredEffect(element, options);
	if (!ef)
	{
		ef = new Spry.Effect[effectName](element, options);
		SpryRegistry.addEffect(ef, element, options);
	}
	ef.start();
	return true;
};
//
//  The notification class
//
if (!Spry.Utils) Spry.Utils = {};

Spry.Utils.Notifier = function()
{
	this.observers = [];
	this.suppressNotifications = 0;
};

Spry.Utils.Notifier.prototype.addObserver = function(observer)
{
	if (!observer)
		return;

	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.Effect.Utils.showError("Unbalanced enableNotifications() call!\n");
	}
};

Spry.Utils.Notifier.prototype.disableNotifications = function()
{
	++this.suppressNotifications;
};

//
// DHTML manipulation
//

Spry.Effect.getElement = function(ele)
{
	var element = ele;
	if (typeof ele == "string")
		element = document.getElementById(ele);

	if (element == null) 
		Spry.Effect.Utils.showError('Element "' + ele + '" not found.');
	
	return element;
};

Spry.Effect.getStyleProp = function(element, prop)
{
	var value;
	var camelized = Spry.Effect.Utils.camelize(prop);
	try
	{
		if (element.style)
			value = element.style[camelized];

		if (!value)
		{
			if (document.defaultView && document.defaultView.getComputedStyle)
			{
				var css = document.defaultView.getComputedStyle(element, null);
				value = css ? css.getPropertyValue(prop) : null;
			}
			else if (element.currentStyle) 
			{
					value = element.currentStyle[camelized];
			}
		}
	}
	catch (e) {Spry.Effect.Utils.showError('Spry.Effect.getStyleProp: ' + e);}

	return value == 'auto' ? null : value;
};

Spry.Effect.setStyleProp = function(element, prop, value)
{
	try
	{
		element.style[Spry.Effect.Utils.camelize(prop)] = value;
	}
	catch (e) {Spry.Effect.Utils.showError('Spry.Effect.setStyleProp: ' + e);}
};

Spry.Effect.getStylePropRegardlessOfDisplayState = function(element, prop, displayElement)
{
	var refElement = displayElement ? displayElement : element;
	var displayOrig = Spry.Effect.getStyleProp(refElement, 'display');
	var visibilityOrig = Spry.Effect.getStyleProp(refElement, 'visibility');

	if(displayOrig == 'none')
	{
		Spry.Effect.setStyleProp(refElement, 'visibility', 'hidden');
		Spry.Effect.setStyleProp(refElement, 'display', 'block');

		if(window.opera)
			refElement.focus();
	}

	var styleProp = Spry.Effect.getStyleProp(element, prop);

	if(displayOrig == 'none')
	{
		Spry.Effect.setStyleProp(refElement, 'display', 'none');
		Spry.Effect.setStyleProp(refElement, 'visibility', visibilityOrig);
	}
	return styleProp;
};

Spry.Effect.makePositioned = function(element)
{
	var pos = Spry.Effect.getStyleProp(element, 'position');
	if (!pos || pos == 'static')
	{
		element.style.position = 'relative';

		if (window.opera)
		{
			element.style.top = 0;
			element.style.left = 0;
		}
	}
};

Spry.Effect.isInvisible = function(element)
{
	var propDisplay = Spry.Effect.getStyleProp(element, 'display');
	if (propDisplay && propDisplay.toLowerCase() == 'none')
		return true;

	var propVisible = Spry.Effect.getStyleProp(element, 'visibility');
	if (propVisible && propVisible.toLowerCase() == 'hidden')
		return true;

	return false;
};

Spry.Effect.enforceVisible = function(element)
{
	var propDisplay = Spry.Effect.getStyleProp(element, 'display');
	if (propDisplay && propDisplay.toLowerCase() == 'none')
		Spry.Effect.setStyleProp(element, 'display', 'block');

	var propVisible = Spry.Effect.getStyleProp(element, 'visibility');
	if (propVisible && propVisible.toLowerCase() == 'hidden')
		Spry.Effect.setStyleProp(element, 'visibility', 'visible');
};

Spry.Effect.makeClipping = function(element)
{
	var overflow = Spry.Effect.getStyleProp(element, 'overflow');
	if (!overflow || (overflow.toLowerCase() != 'hidden' && overflow.toLowerCase() != 'scroll'))
	{
		var heightCache = 0;
		var needsCache = /MSIE 7.0/.test(navigator.userAgent) && /Windows NT/.test(navigator.userAgent);
		if(needsCache)
			heightCache = Spry.Effect.getDimensionsRegardlessOfDisplayState(element).height;

		Spry.Effect.setStyleProp(element, 'overflow', 'hidden');

		if(needsCache)
			Spry.Effect.setStyleProp(element, 'height', heightCache+'px');
	}
};

Spry.Effect.cleanWhitespace = function(element) 
{
	var childCountInit = element.childNodes.length;
  for (var i = childCountInit - 1; i >= 0; i--) {
  	var node = element.childNodes[i];
		if (node.nodeType == 3 && !/\S/.test(node.nodeValue))
			try
			{
				element.removeChild(node);
			}
			catch (e) {Spry.Effect.Utils.showError('Spry.Effect.cleanWhitespace: ' + e);}
	}
};

Spry.Effect.getComputedStyle = function(element)
{
	return /MSIE/.test(navigator.userAgent) ? element.currentStyle : document.defaultView.getComputedStyle(element, null);
};

Spry.Effect.getDimensions = function(element)
{
	var dimensions = new Spry.Effect.Utils.Rectangle;
	var computedStyle = null;

	if (element.style.width && /px/i.test(element.style.width))
		dimensions.width = parseInt(element.style.width, 10);
	else
	{
		computedStyle = Spry.Effect.getComputedStyle(element);
		var tryComputedStyle = computedStyle && computedStyle.width && /px/i.test(computedStyle.width);

		if (tryComputedStyle)
			dimensions.width = parseInt(computedStyle.width, 10);

		if (!tryComputedStyle || dimensions.width == 0)
			dimensions.width = element.offsetWidth;
	}

	if (element.style.height && /px/i.test(element.style.height))
		dimensions.height = parseInt(element.style.height, 10);
	else
	{
		if (!computedStyle)
			computedStyle = Spry.Effect.getComputedStyle(element);

		var tryComputedStyle = computedStyle && computedStyle.height && /px/i.test(computedStyle.height);

		if (tryComputedStyle)
			dimensions.height = parseInt(computedStyle.height, 10);

		if(!tryComputedStyle || dimensions.height == 0)
			dimensions.height = element.offsetHeight;
	}
	return dimensions;
};

Spry.Effect.getDimensionsRegardlessOfDisplayState = function(element, displayElement)
{

	var refElement = displayElement ? displayElement : element;
	var displayOrig = Spry.Effect.getStyleProp(refElement, 'display');
	var visibilityOrig = Spry.Effect.getStyleProp(refElement, 'visibility');

	if(displayOrig == 'none')
	{
		Spry.Effect.setStyleProp(refElement, 'visibility', 'hidden');
		Spry.Effect.setStyleProp(refElement, 'display', 'block');

		if(window.opera)
			refElement.focus();
	}

	var dimensions = Spry.Effect.getDimensions(element);

	if(displayOrig == 'none')
	{
		Spry.Effect.setStyleProp(refElement, 'display', 'none');
		Spry.Effect.setStyleProp(refElement, 'visibility', visibilityOrig);
	}
	return dimensions;
};

Spry.Effect.getOpacity = function(element)
{
  var o = Spry.Effect.getStyleProp(element, "opacity");
  if (typeof o == 'undefined' || o == null)
    o = 1.0;
  return o;
};

Spry.Effect.getBgColor = function(ele)
{
  return Spry.Effect.getStyleProp(ele, "background-color");
};

Spry.Effect.intPropStyle = function(e, prop){
		var i = parseInt(Spry.Effect.getStyleProp(e, prop), 10);
		if (isNaN(i))
			return 0;
		return i;
};

Spry.Effect.getPosition = function(element)
{
	var position = new Spry.Effect.Utils.Position;
	var computedStyle = null;

	if (element.style.left  && /px/i.test(element.style.left))
		position.x = parseInt(element.style.left, 10);
	else
	{
		computedStyle = Spry.Effect.getComputedStyle(element);
		var tryComputedStyle = computedStyle && computedStyle.left && /px/i.test(computedStyle.left);

		if (tryComputedStyle)
			position.x = parseInt(computedStyle.left, 10);

		if(!tryComputedStyle || position.x == 0)
			position.x = element.offsetLeft;
	}

	if (element.style.top && /px/i.test(element.style.top))
		position.y = parseInt(element.style.top, 10);
	else
	{
		if (!computedStyle)
			computedStyle = Spry.Effect.getComputedStyle(element);

    var tryComputedStyle = computedStyle && computedStyle.top && /px/i.test(computedStyle.top);

		if (tryComputedStyle)
			position.y = parseInt(computedStyle.top, 10);

		if(!tryComputedStyle || position.y == 0)
			position.y = element.offsetTop;
	}
	return position;
};

Spry.Effect.getOffsetPosition = Spry.Effect.getPosition;

//
// Spry.Effect.Animator
// (base class)
//

Spry.Effect.Animator = function(options)
{
	Spry.Utils.Notifier.call(this);
	
	this.name = 'Animator';
	this.element = null;
	this.startMilliseconds = 0;
	this.repeat = 'none';
	this.isRunning = false;
	this.timer = null;
	this.cancelRemaining = 0;

	if (!options)
		var options = {};

	if (options.toggle)
		this.direction = false;
	else
		this.direction = Spry.forwards;
	
	var self = this;
	if (options.setup != null)
		this.addObserver({onPreEffect: function(){try{self.options.setup(self.element, self);}catch(e){Spry.Effect.Utils.showError('Spry.Effect.Animator.prototype.start: setup callback: ' + e);}}});

	if (options.finish != null)
		this.addObserver({onPostEffect: function(){try{self.options.finish(self.element, self);}catch(e){Spry.Effect.Utils.showError('Spry.Effect.Animator.prototype.stop: finish callback: ' + e);}}});

	this.options = {
		duration: 1000,
		toggle: false,
		transition: Spry.linearTransition,
		interval: 16
	};

	this.setOptions(options);
	if (options.transition)
		this.setTransition(options.transition);

	if (options.fps)
		this.setFps(options.fps);
};
Spry.Effect.Animator.prototype = new Spry.Utils.Notifier();
Spry.Effect.Animator.prototype.constructor = Spry.Utils.Animator;

Spry.Effect.Animator.prototype.notStaticAnimator = true;

Spry.Effect.Animator.prototype.setOptions = function(options)
{
	if (!options)
		return;
	for (var prop in options)
		this.options[prop] = options[prop];
};
Spry.Effect.Animator.prototype.setTransition = function(transition){
	if (typeof transition == 'number' || transition == "1" || transition == "2")
		switch (parseInt(transition,10))
		{
			case 1: transition = Spry.linearTransition; break;
			case 2: transition = Spry.sinusoidalTransition; break;
			default: Spry.Effect.Utils.showError('unknown transition');
		}

	else if (typeof transition == 'string')
	{
		if (typeof window[transition] == 'function')
			transition = window[transition];
		else if (typeof Spry[transition] == 'function')
			transition = Spry[transition];
		else
			Spry.Effect.Utils.showError('unknown transition');
	}

	this.options.transition = transition;
	if (typeof this.effectsArray != 'undefined'){
		var l = this.effectsArray.length;
		for (var i = 0; i < l; i++)
				this.effectsArray[i].effect.setTransition(transition);
	}
};

Spry.Effect.Animator.prototype.setDuration = function(duration){
	this.options.duration = duration;
	if (typeof this.effectsArray != 'undefined')
	{
		var l = this.effectsArray.length;
		for (var i = 0; i < l; i++)
		{
			this.effectsArray[i].effect.setDuration(duration);
		}
	}
};

Spry.Effect.Animator.prototype.setFps = function(fps){
	this.options.interval = parseInt(1000 / fps, 10);
	this.options.fps = fps;
	if (typeof this.effectsArray != 'undefined')
	{
		var l = this.effectsArray.length;
		for (var i = 0; i < l; i++)
		{
			this.effectsArray[i].effect.setFps(fps);
		}
	}
};

Spry.Effect.Animator.prototype.start = function(withoutTimer)
{
	if (!this.element)
		return;

	if (arguments.length == 0)
		withoutTimer = false;

	if (this.isRunning)
		this.cancel();

	this.prepareStart();
	var currDate = new Date();
	this.startMilliseconds = currDate.getTime();

	if (this.element.id)
		this.element = document.getElementById(this.element.id);

	if (this.cancelRemaining != 0 && this.options.toggle)
	{
		if (this.cancelRemaining < 1 && typeof this.options.transition == 'function')
		{
			var startTime = 0;
			var stopTime = this.options.duration;
			var start = 0;
			var stop = 1;
			var emergency = 0;
			this.cancelRemaining = Math.round(this.cancelRemaining * 1000) / 1000;
			var found = false;
			var middle = 0;
			while (!found)
			{
				if (emergency++ > this.options.duration) break;
				var half = startTime + ((stopTime - startTime) / 2);
				middle = Math.round(this.options.transition(half, 1, -1, this.options.duration) * 1000) / 1000;
				if (middle == this.cancelRemaining)
				{
					this.startMilliseconds -= half;
					found = true;
				}
				if (middle < this.cancelRemaining)
				{
					stopTime = half;
					stop = middle;
				}
				else
				{
					startTime = half;
					start = middle;
				}
			}
		}
		this.cancelRemaining = 0;
	}
	this.notifyObservers('onPreEffect', this);

	if (withoutTimer == false)
	{
		var self = this;
		this.timer = setInterval(function() { self.drawEffect(); }, this.options.interval);
	}
	this.isRunning = true;
};
Spry.Effect.Animator.prototype.stopFlagReset = function()
{
	if (this.timer)
	{
		clearInterval(this.timer);
		this.timer = null;
	}
	this.startMilliseconds = 0;
};
Spry.Effect.Animator.prototype.stop = function()
{
	this.stopFlagReset();
	this.notifyObservers('onPostEffect', this);
	this.isRunning = false;
};

Spry.Effect.Animator.prototype.cancel = function()
{
	var elapsed = this.getElapsedMilliseconds();
	if (this.startMilliseconds > 0 && elapsed < this.options.duration)
		this.cancelRemaining = this.options.transition(elapsed, 0, 1, this.options.duration);

	this.stopFlagReset();
	this.notifyObservers('onCancel', this);
	this.isRunning = false;
};

Spry.Effect.Animator.prototype.drawEffect = function()
{
	var isRunning = true;

	this.notifyObservers('onStep', this);
	var timeElapsed = this.getElapsedMilliseconds();

	if (typeof this.options.transition != 'function'){
		Spry.Effect.Utils.showError('unknown transition');
		return;
	}
	this.animate();

	if (timeElapsed > this.options.duration)
	{
		isRunning = false;
		this.stop();
	}
	return isRunning;
};

Spry.Effect.Animator.prototype.getElapsedMilliseconds = function()
{
	if (this.startMilliseconds > 0)
	{
		var currDate = new Date();
		return (currDate.getTime() - this.startMilliseconds);
	}
	return 0;
};

Spry.Effect.Animator.prototype.doToggle = function()
{
	if (!this.direction)
	{
		this.direction = Spry.forwards;
		return;
	}
	if (this.options.toggle == true)
	{
		if (this.direction == Spry.forwards)
		{
			this.direction = Spry.backwards;
			this.notifyObservers('onToggle', this);
		} 
		else if (this.direction == Spry.backwards)
		{
			this.direction = Spry.forwards;
		}
	}
};

Spry.Effect.Animator.prototype.prepareStart = function()
{
		if (this.options && this.options.toggle)
			this.doToggle();
};

Spry.Effect.Animator.prototype.animate = function(){};
Spry.Effect.Animator.prototype.onStep = function(el)
{
	if (el != this)
		this.notifyObservers('onStep', this);
};
//
// Spry.Effect.Move
//

Spry.Effect.Move = function(element, fromPos, toPos, options)
{
	this.dynamicFromPos = false;
	if (arguments.length == 3)
	{
		options = toPos;
		toPos = fromPos;
		fromPos = Spry.Effect.getPosition(element);
		this.dynamicFromPos = true;
	}

	Spry.Effect.Animator.call(this, options);

	this.name = 'Move';
	this.element = Spry.Effect.getElement(element);
	if (!this.element)
		return;

	if (fromPos.units != toPos.units)
		Spry.Effect.Utils.showError('Spry.Effect.Move: Conflicting units (' + fromPos.units + ', ' + toPos.units + ')');

	this.units = fromPos.units;
	this.startX = Number(fromPos.x);
	this.stopX = Number(toPos.x);
	this.startY = Number(fromPos.y);
	this.stopY = Number(toPos.y);
};

Spry.Effect.Move.prototype = new Spry.Effect.Animator();
Spry.Effect.Move.prototype.constructor = Spry.Effect.Move;

Spry.Effect.Move.prototype.animate = function()
{
	var left = 0;
	var top = 0;
	var floor = Math.floor;
	var elapsed = this.getElapsedMilliseconds();
	if (this.direction == Spry.forwards)
	{
		left = floor(this.options.transition(elapsed, this.startX, this.stopX - this.startX, this.options.duration));
		top = floor(this.options.transition(elapsed, this.startY, this.stopY - this.startY, this.options.duration));
	}
	else if (this.direction == Spry.backwards)
	{
		left = floor(this.options.transition(elapsed, this.stopX, this.startX - this.stopX, this.options.duration));
		top = floor(this.options.transition(elapsed, this.stopY, this.startY - this.stopY, this.options.duration));
	}

	this.element.style.left = left + this.units;
	this.element.style.top = top + this.units;
};

Spry.Effect.Move.prototype.prepareStart = function()
{
	if (this.options && this.options.toggle)
		this.doToggle();

	if (this.dynamicFromPos == true)
	{
		var fromPos = Spry.Effect.getPosition(this.element);
		this.startX = fromPos.x;
		this.startY = fromPos.y;
		
		this.rangeMoveX = this.startX - this.stopX;
		this.rangeMoveY= this.startY - this.stopY;
	}
};

//
// Spry.Effect.Size
//

Spry.Effect.Size = function(element, fromRect, toRect, options)
{
	this.dynamicFromRect = false;

	if (arguments.length == 3)
	{
		options = toRect;
		toRect = fromRect;
		fromRect = Spry.Effect.getDimensionsRegardlessOfDisplayState(element);
		this.dynamicFromRect = true;
	}

	Spry.Effect.Animator.call(this, options);

	this.name = 'Size';
	this.element = Spry.Effect.getElement(element);
	if (!this.element)
		return;

	element = this.element;

	if (fromRect.units != toRect.units)
	{
		Spry.Effect.Utils.showError('Spry.Effect.Size: Conflicting units (' + fromRect.units + ', ' + toRect.units + ')');
		return false;
	}

	this.units = fromRect.units;

	var originalRect = Spry.Effect.getDimensionsRegardlessOfDisplayState(element);
	this.originalWidth = originalRect.width;
	this.originalHeight = originalRect.height;

	this.startWidth = fromRect.width;
	this.startHeight = fromRect.height;
	this.stopWidth = toRect.width;
	this.stopHeight = toRect.height;
	this.childImages = new Array();

	if (this.options.useCSSBox){
		Spry.Effect.makePositioned(this.element);
		var intProp = Spry.Effect.intPropStyle;
		this.startFromBorder_top = intProp(element, 'border-top-width');
		this.startFromBorder_bottom = intProp(element, 'border-bottom-width');
		this.startFromBorder_left = intProp(element, 'border-left-width');
		this.startFromBorder_right = intProp(element, 'border-right-width');
		this.startFromPadding_top = intProp(element, 'padding-top');
		this.startFromPadding_bottom = intProp(element, 'padding-bottom');
		this.startFromPadding_left = intProp(element, 'padding-left');
		this.startFromPadding_right = intProp(element, 'padding-right');
		this.startFromMargin_top = intProp(element, 'margin-top');
		this.startFromMargin_bottom = intProp(element, 'margin-bottom');
		this.startFromMargin_right = intProp(element, 'margin-right');
		this.startFromMargin_left = intProp(element, 'margin-left');
		this.startLeft = intProp(element, 'left');
		this.startTop = intProp(element, 'top');
	}

	if(this.options.scaleContent)
		Spry.Effect.Utils.fetchChildImages(element, this.childImages);

	this.fontFactor = 1.0;
	var fontSize = Spry.Effect.getStyleProp(this.element, 'font-size');
	if(fontSize && /em\s*$/.test(fontSize))
		this.fontFactor = parseFloat(fontSize);

	var isPercent = Spry.Effect.Utils.isPercentValue;

	if (isPercent(this.startWidth))
	{
		var startWidthPercent = Spry.Effect.Utils.getPercentValue(this.startWidth);
		this.startWidth = originalRect.width * (startWidthPercent / 100);
	}

	if (isPercent(this.startHeight))
	{
		var startHeightPercent = Spry.Effect.Utils.getPercentValue(this.startHeight);
		this.startHeight = originalRect.height * (startHeightPercent / 100);
	}

	if (isPercent(this.stopWidth))
	{
		var stopWidthPercent = Spry.Effect.Utils.getPercentValue(this.stopWidth);
		this.stopWidth = originalRect.width * (stopWidthPercent / 100);
	}

	if (isPercent(this.stopHeight))
	{
		var stopHeightPercent = Spry.Effect.Utils.getPercentValue(this.stopHeight);
		this.stopHeight = originalRect.height * (stopHeightPercent / 100);
	}

	this.enforceVisible = Spry.Effect.isInvisible(this.element);
};

Spry.Effect.Size.prototype = new Spry.Effect.Animator();
Spry.Effect.Size.prototype.constructor = Spry.Effect.Size;

Spry.Effect.Size.prototype.animate = function()
{
	var width = 0;
	var height = 0;
	var fontSize = 0;
	var direction = 0;
	var floor = Math.floor;
	var elapsed = this.getElapsedMilliseconds();

	if (this.direction == Spry.forwards) {
		width = floor(this.options.transition(elapsed, this.startWidth, this.stopWidth - this.startWidth, this.options.duration));
		height = floor(this.options.transition(elapsed, this.startHeight, this.stopHeight - this.startHeight, this.options.duration));
		direction = 1;
	} else if (this.direction == Spry.backwards) {
		width = floor(this.options.transition(elapsed, this.stopWidth, this.startWidth - this.stopWidth, this.options.duration));
		height = floor(this.options.transition(elapsed, this.stopHeight, this.startHeight - this.stopHeight, this.options.duration));
		direction = -1;
	}

	var propFactor = width/this.originalWidth;
	fontSize = this.fontFactor * propFactor;

	var elStyle = this.element.style;
	if (width < 0)
		width = 0;
	
	if (height < 0)
		height = 0;

	elStyle.width = width + this.units;
	elStyle.height = height + this.units;

	if (typeof this.options.useCSSBox != 'undefined' && this.options.useCSSBox == true)
	{
		var intProp = Spry.Effect.intPropStyle;
		var origTop = intProp(this.element, 'top');
		var origLeft = intProp(this.element, 'left');
		var origMarginTop = intProp(this.element, 'margin-top');
		var origMarginLeft = intProp(this.element, 'margin-left');

		var widthFactor = propFactor;
		var heightFactor = height / this.originalHeight;
		var border_top = floor(this.startFromBorder_top * heightFactor);
		var border_bottom = floor(this.startFromBorder_bottom * heightFactor);
		var border_left = floor(this.startFromBorder_left * widthFactor);
		var border_right = floor(this.startFromBorder_right * widthFactor);
		var padding_top = floor(this.startFromPadding_top * heightFactor);
		var padding_bottom = floor(this.startFromPadding_bottom * heightFactor);
		var padding_left = floor(this.startFromPadding_left * widthFactor);
		var padding_right = floor(this.startFromPadding_right * widthFactor);
		var margin_top = floor(this.startFromMargin_top * heightFactor);
		var margin_bottom = floor(this.startFromMargin_bottom * heightFactor);
		var margin_right = floor(this.startFromMargin_right * widthFactor);
		var margin_left = floor(this.startFromMargin_left * widthFactor);

		elStyle.borderTopWidth = border_top + this.units;
		elStyle.borderBottomWidth = border_bottom + this.units;
		elStyle.borderLeftWidth = border_left + this.units;
		elStyle.borderRightWidth = border_right + this.units;
		elStyle.paddingTop = padding_top + this.units;
		elStyle.paddingBottom = padding_bottom + this.units;
		elStyle.paddingLeft = padding_left + this.units;
		elStyle.paddingRight = padding_right + this.units;
		elStyle.marginTop  = margin_top + this.units;
		elStyle.marginBottom = margin_bottom + this.units;
		elStyle.marginLeft = margin_left + this.units;
		elStyle.marginRight = margin_right + this.units;

		elStyle.left = floor(origLeft + origMarginLeft - margin_left) + this.units;
		elStyle.top = floor(origTop + origMarginTop - margin_top) + this.units;
	}

	if (this.options.scaleContent)
	{

		for(var i=0; i < this.childImages.length; i++)
		{
			this.childImages[i][0].style.width = propFactor * this.childImages[i][1] + this.units;
			this.childImages[i][0].style.height = propFactor * this.childImages[i][2] + this.units;
		}
		this.element.style.fontSize = fontSize + 'em';
	}

	if(this.enforceVisible)
	{
		Spry.Effect.enforceVisible(this.element);
		this.enforceVisible = false;
	}
};

Spry.Effect.Size.prototype.prepareStart = function()
{
	if (this.options && this.options.toggle)
		this.doToggle();	

	if (this.dynamicFromRect == true)
	{
		var fromRect = Spry.Effect.getDimensions(this.element);
		this.startWidth = fromRect.width;
		this.startHeight = fromRect.height;

		this.widthRange = this.startWidth - this.stopWidth;
		this.heightRange = this.startHeight - this.stopHeight;
	}
};

//
// Spry.Effect.Opacity
//

Spry.Effect.Opacity = function(element, startOpacity, stopOpacity, options)
{
	this.dynamicStartOpacity = false;
	if (arguments.length == 3)
	{
		options = stopOpacity;
		stopOpacity = startOpacity;
		startOpacity = Spry.Effect.getOpacity(element);
		this.dynamicStartOpacity = true;
	}

	Spry.Effect.Animator.call(this, options);

	this.name = 'Opacity';
	this.element = Spry.Effect.getElement(element);
	if (!this.element)
		return;

	if(/MSIE/.test(navigator.userAgent) && (!this.element.hasLayout))
		Spry.Effect.setStyleProp(this.element, 'zoom', '1');

	this.startOpacity = startOpacity;
	this.stopOpacity = stopOpacity;
	this.enforceVisible = Spry.Effect.isInvisible(this.element);
};

Spry.Effect.Opacity.prototype = new Spry.Effect.Animator();
Spry.Effect.Opacity.prototype.constructor = Spry.Effect.Opacity;

Spry.Effect.Opacity.prototype.animate = function()
{
	var opacity = 0;
	var elapsed = this.getElapsedMilliseconds();
	if (this.direction == Spry.forwards) 
		opacity = this.options.transition(elapsed, this.startOpacity, this.stopOpacity - this.startOpacity, this.options.duration);
	else if (this.direction == Spry.backwards) 
		opacity = this.options.transition(elapsed, this.stopOpacity, this.startOpacity - this.stopOpacity, this.options.duration);

	if (opacity < 0)
		opacity = 0;

	if(/MSIE/.test(navigator.userAgent))
	{
		var tmpval = Spry.Effect.getStyleProp(this.element,'filter');
		if (tmpval){
			tmpval = tmpval.replace(/alpha\(opacity=[0-9]{1,3}\)/g, '');
		}
		this.element.style.filter = tmpval + "alpha(opacity=" + Math.floor(opacity * 100) + ")";
	}
	else
		this.element.style.opacity = opacity;

	if(this.enforceVisible)
	{
		Spry.Effect.enforceVisible(this.element);
		this.enforceVisible = false;
	}
};

Spry.Effect.Opacity.prototype.prepareStart = function()
{
	if (this.options && this.options.toggle)
		this.doToggle();	

	if (this.dynamicStartOpacity == true)
	{
		this.startOpacity = Spry.Effect.getOpacity(this.element);
		this.opacityRange = this.startOpacity - this.stopOpacity;
	}
};

//
// Spry.Effect.Color
//

Spry.Effect.Color = function(element, startColor, stopColor, options)
{
	this.dynamicStartColor = false;
	if (arguments.length == 3)
	{
		options = stopColor;
		stopColor = startColor;
		startColor = Spry.Effect.getBgColor(element);
		this.dynamicStartColor = true;
	}
	
	Spry.Effect.Animator.call(this, options);

	this.name = 'Color';
	this.element = Spry.Effect.getElement(element);
	if (!this.element)
		return;

	this.startColor = startColor;
	this.stopColor = stopColor;
	this.startRedColor = Spry.Effect.Utils.hexToInt(startColor.substr(1,2));
	this.startGreenColor = Spry.Effect.Utils.hexToInt(startColor.substr(3,2));
	this.startBlueColor = Spry.Effect.Utils.hexToInt(startColor.substr(5,2));
	this.stopRedColor = Spry.Effect.Utils.hexToInt(stopColor.substr(1,2));
	this.stopGreenColor = Spry.Effect.Utils.hexToInt(stopColor.substr(3,2));
	this.stopBlueColor = Spry.Effect.Utils.hexToInt(stopColor.substr(5,2));
};

Spry.Effect.Color.prototype = new Spry.Effect.Animator();
Spry.Effect.Color.prototype.constructor = Spry.Effect.Color;

Spry.Effect.Color.prototype.animate = function()
{
	var redColor = 0;
	var greenColor = 0;
	var blueColor = 0;
	var floor = Math.floor;
	var elapsed = this.getElapsedMilliseconds();

	if (this.direction == Spry.forwards)
	{
		redColor = floor(this.options.transition(elapsed, this.startRedColor, this.stopRedColor - this.startRedColor, this.options.duration));
		greenColor = floor(this.options.transition(elapsed, this.startGreenColor, this.stopGreenColor - this.startGreenColor, this.options.duration));
		blueColor = floor(this.options.transition(elapsed, this.startBlueColor, this.stopBlueColor - this.startBlueColor, this.options.duration));
	}
	else if (this.direction == Spry.backwards)
	{
		redColor = floor(this.options.transition(elapsed, this.stopRedColor, this.startRedColor - this.stopRedColor, this.options.duration));
		greenColor = floor(this.options.transition(elapsed, this.stopGreenColor, this.startGreenColor - this.stopGreenColor, this.options.duration));
		blueColor = floor(this.options.transition(elapsed, this.stopBlueColor, this.startBlueColor - this.stopBlueColor, this.options.duration));
	}

	this.element.style.backgroundColor = Spry.Effect.Utils.rgb(redColor, greenColor, blueColor);
};

Spry.Effect.Color.prototype.prepareStart = function() 
{
	if (this.options && this.options.toggle)
		this.doToggle();

	if (this.dynamicStartColor == true)
	{
		this.startColor = Spry.Effect.getBgColor(element);
		this.startRedColor = Spry.Effect.Utils.hexToInt(startColor.substr(1,2));
		this.startGreenColor = Spry.Effect.Utils.hexToInt(startColor.substr(3,2));
		this.startBlueColor = Spry.Effect.Utils.hexToInt(startColor.substr(5,2));
		this.redColorRange = this.startRedColor - this.stopRedColor;
		this.greenColorRange = this.startGreenColor - this.stopGreenColor;
		this.blueColorRange = this.startBlueColor - this.stopBlueColor;
	}
};

//
// Spry.Effect.Cluster
//

Spry.Effect.Cluster = function(options)
{
	Spry.Effect.Animator.call(this, options);

	this.name = 'Cluster';
	this.effectsArray = new Array();
	this.currIdx = -1;
	var _ClusteredEffect = function(effect, kind)
	{
		this.effect = effect;
		this.kind = kind;
		this.isRunning = false;
	};

	this.ClusteredEffect = _ClusteredEffect;
};

Spry.Effect.Cluster.prototype = new Spry.Effect.Animator();
Spry.Effect.Cluster.prototype.constructor = Spry.Effect.Cluster;

Spry.Effect.Cluster.prototype.setInterval = function(interval){
	var l = this.effectsArray.length;
	this.options.interval = interval;
	for (var i = 0; i < l; i++)
	{
		this.effectsArray[i].effect.setInterval(interval);
	}
};
Spry.Effect.Cluster.prototype.drawEffect = function()
{
	var isRunning = true;
	var allEffectsDidRun = false;
	var baseEffectIsStillRunning = false;
	var evalNextEffectsRunning = false;

	if ((this.currIdx == -1 && this.direction == Spry.forwards) || (this.currIdx == this.effectsArray.length && this.direction == Spry.backwards))
		this.initNextEffectsRunning();

	var start = this.direction == Spry.forwards ? 0 : this.effectsArray.length-1;
	var stop = this.direction == Spry.forwards ? this.effectsArray.length : -1;
	var step = this.direction == Spry.forwards ? 1 : -1;
	for (var i = start; i != stop; i+=step)
	{
		if (this.effectsArray[i].isRunning == true)
		{
			baseEffectIsStillRunning = this.effectsArray[i].effect.drawEffect();
			if (baseEffectIsStillRunning == false && i == this.currIdx)
			{
				this.effectsArray[i].isRunning = false;
				evalNextEffectsRunning = true;
			}
		}
	}

	if (evalNextEffectsRunning == true)
		allEffectsDidRun = this.initNextEffectsRunning();

	if (allEffectsDidRun == true) {
		this.stop();
		isRunning = false;
		for (var i = 0; i < this.effectsArray.length; i++)
			this.effectsArray[i].isRunning = false;

		this.currIdx = this.direction == Spry.forwards ? this.effectsArray.length: -1;
	}
	return isRunning;
};

Spry.Effect.Cluster.prototype.initNextEffectsRunning = function()
{
	var allEffectsDidRun = false;
	var step = this.direction == Spry.forwards ? 1 : -1;
	var stop = this.direction == Spry.forwards ? this.effectsArray.length : -1;
	this.currIdx+=step;
	if ( (this.currIdx > (this.effectsArray.length - 1) && this.direction == Spry.forwards) || (this.currIdx < 0 && this.direction == Spry.backwards))
		allEffectsDidRun = true;
	else
		for (var i = this.currIdx; i != stop; i+=step)
		{
			if ((i > this.currIdx && this.direction == Spry.forwards || i < this.currIdx && this.direction == Spry.backwards) && this.effectsArray[i].kind == "queue")
				break;
			this.effectsArray[i].effect.start(true);
			this.effectsArray[i].isRunning = true;
			this.currIdx = i;
		}

	return allEffectsDidRun;
};

Spry.Effect.Cluster.prototype.toggleCluster = function()
{
	if (!this.direction)
	{
		this.direction = Spry.forwards;
		return;
	}

	if (this.options.toggle == true)
	{
		if (this.direction == Spry.forwards)
		{
			this.direction = Spry.backwards;
			this.notifyObservers('onToggle', this);
			this.currIdx = this.effectsArray.length;
		}
		else if (this.direction == Spry.backwards)
		{
			this.direction = Spry.forwards;
			this.currIdx = -1;
		}
	}
	else
	{
		if (this.direction == Spry.forwards)
			this.currIdx = -1;
		else if (this.direction == Spry.backwards)
			this.currIdx = this.effectsArray.length;
	}
};

Spry.Effect.Cluster.prototype.doToggle = function()
{
	this.toggleCluster();

	for (var i = 0; i < this.effectsArray.length; i++)
	{
		if (this.effectsArray[i].effect.options && (this.effectsArray[i].effect.options.toggle != null))
			if (this.effectsArray[i].effect.options.toggle == true)
				this.effectsArray[i].effect.doToggle();
	}
};

Spry.Effect.Cluster.prototype.cancel = function()
{
	for (var i = 0; i < this.effectsArray.length; i++)
		if (this.effectsArray[i].effect.isRunning)
			this.effectsArray[i].effect.cancel();
	
	var elapsed = this.getElapsedMilliseconds();
	if (this.startMilliseconds > 0 && elapsed < this.options.duration)
		this.cancelRemaining = this.options.transition(elapsed, 0, 1, this.options.duration);
	this.stopFlagReset();
	this.notifyObservers('onCancel', this);
	this.isRunning = false;
};

Spry.Effect.Cluster.prototype.addNextEffect = function(effect)
{
	effect.addObserver(this);
	this.effectsArray[this.effectsArray.length] = new this.ClusteredEffect(effect, "queue");
	if (this.effectsArray.length == 1)
	{
		this.element = effect.element;
	}
};

Spry.Effect.Cluster.prototype.addParallelEffect = function(effect)
{
	if (this.effectsArray.length == 0 || this.effectsArray[this.effectsArray.length-1].kind != 'parallel')
		effect.addObserver(this);

	this.effectsArray[this.effectsArray.length] = new this.ClusteredEffect(effect, "parallel");
	if (this.effectsArray.length == 1)
	{
		this.element = effect.element;
	}
};

Spry.Effect.Cluster.prototype.prepareStart = function()
{
	this.toggleCluster();
};

//
// Combination effects. Custom effects can be build by combining basic effect bahaviour like Move, Size, Color, Opacity
//

Spry.Effect.Fade = function (element, options) 
{
	if (!this.notStaticAnimator)
		return Spry.Effect.Utils.showInitError('Fade');

	Spry.Effect.Cluster.call(this, options);

	this.name = 'Fade';
	var element = Spry.Effect.getElement(element);
	this.element = element;
	if (!this.element)
		return;
	var durationInMilliseconds = 1000;
	var fromOpacity = 0.0;
	var toOpacity = 100.0;
	var doToggle = false;
	var transition = Spry.fifthTransition;
	var fps = 60;
	var originalOpacity = 0;
	if(/MSIE/.test(navigator.userAgent))
		originalOpacity = parseInt(Spry.Effect.getStylePropRegardlessOfDisplayState(this.element, 'filter').replace(/alpha\(opacity=([0-9]{1,3})\)/g, '$1'), 10);
	else
		originalOpacity = parseInt(Spry.Effect.getStylePropRegardlessOfDisplayState(this.element, 'opacity') * 100, 10);

	if (isNaN(originalOpacity))
		originalOpacity = 100;

	if (options)
	{
		if (options.duration != null) durationInMilliseconds = options.duration;
		if (options.from != null){
			if (Spry.Effect.Utils.isPercentValue(options.from))
				fromOpacity = Spry.Effect.Utils.getPercentValue(options.from) * originalOpacity / 100;
			else
				fromOpacity = options.from;
		}
		if (options.to != null)
		{	
			if (Spry.Effect.Utils.isPercentValue(options.to))
				toOpacity = Spry.Effect.Utils.getPercentValue(options.to) * originalOpacity / 100;
			else
				toOpacity = options.to;
		}
		if (options.toggle != null) doToggle = options.toggle;
		if (options.transition != null) transition = options.transition;
		if (options.fps != null) fps = options.fps;
		else this.options.transition = transition;
	}

	fromOpacity = fromOpacity/ 100.0;
	toOpacity = toOpacity / 100.0;

	options = {duration: durationInMilliseconds, toggle: doToggle, transition: transition, from: fromOpacity, to: toOpacity, fps: fps};
	var fadeEffect = new Spry.Effect.Opacity(element, fromOpacity, toOpacity, options);
	this.addNextEffect(fadeEffect);
};

Spry.Effect.Fade.prototype = new Spry.Effect.Cluster();
Spry.Effect.Fade.prototype.constructor = Spry.Effect.Fade;

Spry.Effect.Blind = function (element, options)
{
	if (!this.notStaticAnimator)
		return Spry.Effect.Utils.showInitError('Blind'); 

	Spry.Effect.Cluster.call(this, options);

	this.name = 'Blind';
	var element = Spry.Effect.getElement(element);
	this.element = element;
	if (!this.element)
		return;
	var durationInMilliseconds = 1000;
	var doToggle = false;
	var kindOfTransition = Spry.circleTransition;
	var fps = 60;
	var doScaleContent = false;

	Spry.Effect.makeClipping(element);

	var originalRect = Spry.Effect.getDimensionsRegardlessOfDisplayState(element);
	var fromHeightPx  = originalRect.height;
	var toHeightPx    = 0;
	var optionFrom = options ? options.from : originalRect.height;
	var optionTo   = options ? options.to : 0;
	var fullCSSBox = false;


	if (options)
	{
		if (options.duration != null) durationInMilliseconds = options.duration;
		if (options.from != null)
		{
			if (Spry.Effect.Utils.isPercentValue(options.from))
				fromHeightPx = Spry.Effect.Utils.getPercentValue(options.from) * originalRect.height / 100;
			else
				fromHeightPx = Spry.Effect.Utils.getPixelValue(options.from);
		}
		if (options.to != null)
		{
			if (Spry.Effect.Utils.isPercentValue(options.to))
				toHeightPx = Spry.Effect.Utils.getPercentValue(options.to) * originalRect.height / 100;
			else
				toHeightPx = Spry.Effect.Utils.getPixelValue(options.to);
		}
		if (options.toggle != null) doToggle = options.toggle;
		if (options.transition != null) kindOfTransition = options.transition;
		if (options.fps != null) fps = options.fps;
		if (options.useCSSBox != null) fullCSSBox = options.useCSSBox;
	}

	var fromRect = new Spry.Effect.Utils.Rectangle;
	fromRect.width = originalRect.width;
	fromRect.height = fromHeightPx;

	var toRect = new Spry.Effect.Utils.Rectangle;
	toRect.width = originalRect.width;
	toRect.height = toHeightPx;

	options = {duration:durationInMilliseconds, toggle:doToggle, transition:kindOfTransition, scaleContent:doScaleContent, useCSSBox: fullCSSBox, from: optionFrom, to: optionTo, fps: fps};
	var blindEffect = new Spry.Effect.Size(element, fromRect, toRect, options);
	this.addNextEffect(blindEffect);
};

Spry.Effect.Blind.prototype = new Spry.Effect.Cluster();
Spry.Effect.Blind.prototype.constructor = Spry.Effect.Blind;

Spry.Effect.Highlight = function (element, options) 
{
	if (!this.notStaticAnimator)
		return Spry.Effect.Utils.showInitError('Highlight'); 

	Spry.Effect.Cluster.call(this, options);

	this.name = 'Highlight';
	var durationInMilliseconds = 1000;
	var toColor = "#ffffff";
	var doToggle = false;
	var kindOfTransition = Spry.sinusoidalTransition;
	var fps = 60;
	var element = Spry.Effect.getElement(element);
	this.element = element;
	if (!this.element)
		return;
	var fromColor = Spry.Effect.getBgColor(element);
	if (fromColor == "transparent") fromColor = "#ffff99";

	if (options)
	{
		if (options.duration != null) durationInMilliseconds = options.duration;
		if (options.from != null) fromColor = options.from;
		if (options.to != null) toColor = options.to;
		if (options.toggle != null) doToggle = options.toggle;
		if (options.transition != null) kindOfTransition = options.transition;
		if (options.fps != null) fps = options.fps;
	}

	if ( fromColor.indexOf('rgb') != -1 )
		var fromColor = Spry.Effect.Utils.rgb(parseInt(fromColor.substring(fromColor.indexOf('(')+1, fromColor.indexOf(',')),10), parseInt(fromColor.substring(fromColor.indexOf(',')+1, fromColor.lastIndexOf(',')),10), parseInt(fromColor.substring(fromColor.lastIndexOf(',')+1, fromColor.indexOf(')')),10));

	if ( toColor.indexOf('rgb') != -1 )
		var toColor = Spry.Effect.Utils.rgb(parseInt(toColor.substring(toColor.indexOf('(')+1, toColor.indexOf(',')),10), parseInt(toColor.substring(toColor.indexOf(',')+1, toColor.lastIndexOf(',')),10), parseInt(toColor.substring(toColor.lastIndexOf(',')+1, toColor.indexOf(')')),10));

	var fromColor = Spry.Effect.Utils.longColorVersion(fromColor);
	var toColor = Spry.Effect.Utils.longColorVersion(toColor);

	this.restoreBackgroundImage = Spry.Effect.getStyleProp(element, 'background-image');

	options = {duration: durationInMilliseconds, toggle: doToggle, transition: kindOfTransition, fps: fps};
	var highlightEffect = new Spry.Effect.Color(element, fromColor, toColor, options);
	this.addNextEffect(highlightEffect);

	this.addObserver({
		onPreEffect:
		function(effect){
			Spry.Effect.setStyleProp(effect.element, 'background-image', 'none');
		},
		onPostEffect:
		function(effect){
			Spry.Effect.setStyleProp(effect.element, 'background-image', effect.restoreBackgroundImage);

			if (effect.direction == Spry.forwards && effect.options.restoreColor)
				Spry.Effect.setStyleProp(element, 'background-color', effect.options.restoreColor);		
		}
	});
};

Spry.Effect.Highlight.prototype = new Spry.Effect.Cluster();
Spry.Effect.Highlight.prototype.constructor = Spry.Effect.Highlight;

Spry.Effect.Slide = function (element, options) 
{
	if (!this.notStaticAnimator)
		return Spry.Effect.Utils.showInitError('Slide'); 

	Spry.Effect.Cluster.call(this, options);

	this.name = 'Slide';
	var element = Spry.Effect.getElement(element);
	this.element = element;
	if (!this.element)
		return;
	var durationInMilliseconds = 1000;
	var doToggle = false;
	var kindOfTransition = Spry.sinusoidalTransition;
	var fps = 60;
	var slideHorizontally = false;
	var firstChildElt = Spry.Effect.Utils.getFirstChildElement(element);
	var direction = -1;

	if(/MSIE 7.0/.test(navigator.userAgent) && /Windows NT/.test(navigator.userAgent))
		Spry.Effect.makePositioned(element);

	Spry.Effect.makeClipping(element);

	if(/MSIE 6.0/.test(navigator.userAgent) && /Windows NT/.test(navigator.userAgent))
	{
		var pos = Spry.Effect.getStyleProp(element, 'position');
		if(pos && (pos == 'static' || pos == 'fixed'))
		{
			Spry.Effect.setStyleProp(element, 'position', 'relative');
			Spry.Effect.setStyleProp(element, 'top', '');
			Spry.Effect.setStyleProp(element, 'left', '');
		}
	}

	if(firstChildElt)
	{
		Spry.Effect.makePositioned(firstChildElt);
		Spry.Effect.makeClipping(firstChildElt);
  
		var childRect = Spry.Effect.getDimensionsRegardlessOfDisplayState(firstChildElt, element);
		Spry.Effect.setStyleProp(firstChildElt, 'width', childRect.width + 'px');
	}

	var fromDim = Spry.Effect.getDimensionsRegardlessOfDisplayState(element);

	var initDim = new Spry.Effect.Utils.Rectangle();
	var toDim = new Spry.Effect.Utils.Rectangle();
	initDim.width = toDim.width = fromDim.width;
	initDim.height = toDim.height = fromDim.height;

	if (!this.options.to){
		if (!options)
			options = {};

		options.to = '0%';
	}

	if (options && options.horizontal !== null && options.horizontal === true)
		slideHorizontally = true;

	if (options.duration != null) durationInMilliseconds = options.duration;

	if (options.from != null)
	{
		if(slideHorizontally)
		{
				if (Spry.Effect.Utils.isPercentValue(options.from))
					fromDim.width = initDim.width * Spry.Effect.Utils.getPercentValue(options.from) / 100;
				else
					fromDim.width = Spry.Effect.Utils.getPixelValue(options.from);
		}
		else
		{
				if (Spry.Effect.Utils.isPercentValue(options.from))
					fromDim.height = initDim.height * Spry.Effect.Utils.getPercentValue(options.from) / 100;
				else
					fromDim.height = Spry.Effect.Utils.getPixelValue(options.from);
		}
	}

	if (options.to != null)
	{
			if(slideHorizontally)
			{
				if (Spry.Effect.Utils.isPercentValue(options.to))
					toDim.width = initDim.width * Spry.Effect.Utils.getPercentValue(options.to) / 100;
				else
					toDim.width = Spry.Effect.Utils.getPixelValue(options.to);
			}
			else
			{
				if (Spry.Effect.Utils.isPercentValue(options.to))
					toDim.height = initDim.height * Spry.Effect.Utils.getPercentValue(options.to) / 100;
				else
					toDim.height = Spry.Effect.Utils.getPixelValue(options.to);
		}
	}
	if (options.toggle != null) doToggle = options.toggle;
	if (options.transition != null) kindOfTransition = options.transition;
	if (options.fps != null) fps = options.fps;

	options = {duration: durationInMilliseconds, transition: kindOfTransition, scaleContent: false, toggle:doToggle, fps: fps};
	var size = new Spry.Effect.Size(element, fromDim, toDim, options);
	this.addParallelEffect(size);

	if ( (fromDim.width < toDim.width && slideHorizontally) || (fromDim.height < toDim.height && !slideHorizontally))
		direction = 1;
	
	var fromPos = new Spry.Effect.Utils.Position();
	var toPos = new Spry.Effect.Utils.Position();
	toPos.x = fromPos.x = Spry.Effect.intPropStyle(firstChildElt, 'left');
	toPos.y = fromPos.y = Spry.Effect.intPropStyle(firstChildElt, 'top');
	toPos.units = fromPos.units;

	if (slideHorizontally)
		toPos.x = parseInt(fromPos.x + direction * (fromDim.width - toDim.width), 10);
	else
		toPos.y = parseInt(fromPos.y + direction * (fromDim.height - toDim.height), 10);

	if (direction == 1){
		var tmp = fromPos;
		var fromPos = toPos;
		var toPos = tmp;
	}

	options = {duration: durationInMilliseconds, transition: kindOfTransition, toggle:doToggle, from: fromPos, to: toPos, fps: fps};
	var move = new Spry.Effect.Move(firstChildElt, fromPos, toPos, options);
	this.addParallelEffect(move);
};

Spry.Effect.Slide.prototype = new Spry.Effect.Cluster();
Spry.Effect.Slide.prototype.constructor = Spry.Effect.Slide;

Spry.Effect.Grow = function (element, options) 
{
	if (!element)
		return;
	if (!this.notStaticAnimator)
		return Spry.Effect.Utils.showInitError('Grow');

	Spry.Effect.Cluster.call(this, options);

	this.name = 'Grow';
	var durationInMilliseconds = 1000;
	var doToggle = false;
	var doScaleContent = true;
	var calcHeight = false;
	var growFromCenter = true;
	var fullCSSBox = false;
	var kindOfTransition = Spry.squareTransition;
	var fps = 60;
	var element = Spry.Effect.getElement(element);
	this.element = element;
	if (!this.element)
		return;

	Spry.Effect.makeClipping(element);

	var dimRect = Spry.Effect.getDimensionsRegardlessOfDisplayState(element);
	var originalWidth = dimRect.width;
	var originalHeight = dimRect.height;
	var propFactor = (originalWidth == 0) ? 1 :originalHeight/originalWidth;

	var fromRect = new Spry.Effect.Utils.Rectangle;
	fromRect.width = 0;
	fromRect.height = 0;

	var toRect = new Spry.Effect.Utils.Rectangle;
	toRect.width = originalWidth;
	toRect.height = originalHeight;

	var optionFrom = options ? options.from : dimRect.width;

	var optionTo   = options ? options.to : 0;
	var pixelValue = Spry.Effect.Utils.getPixelValue;

	if (options)
	{
		if (options.growCenter != null) growFromCenter = options.growCenter;
		if (options.duration != null) durationInMilliseconds = options.duration;
		if (options.useCSSBox != null) fullCSSBox = options.useCSSBox;
		if (options.scaleContent != null) doScaleContent = options.scaleContent;
		if (options.from != null) 
		{
			if (Spry.Effect.Utils.isPercentValue(options.from))
			{
				fromRect.width = originalWidth * (Spry.Effect.Utils.getPercentValue(options.from) / 100);
				fromRect.height = originalHeight * (Spry.Effect.Utils.getPercentValue(options.from) / 100);
			}
			else
			{
				if(calcHeight)
				{
					fromRect.height = pixelValue(options.from);
					fromRect.width  = pixelValue(options.from) / propFactor;
				}
				else
				{
					fromRect.width = pixelValue(options.from);
					fromRect.height = propFactor * pixelValue(options.from);
				}
			}
		}
		if (options.to != null)
		{
			if (Spry.Effect.Utils.isPercentValue(options.to))
			{
				toRect.width = originalWidth * (Spry.Effect.Utils.getPercentValue(options.to) / 100);
				toRect.height = originalHeight * (Spry.Effect.Utils.getPercentValue(options.to) / 100);
			}
			else
			{
				if(calcHeight)
				{
					toRect.height = pixelValue(options.to);
					toRect.width  = pixelValue(options.to) / propFactor;
				}
				else
				{
					toRect.width = pixelValue(options.to);
					toRect.height = propFactor * pixelValue(options.to);
				}
			}
		}
		if (options.toggle != null) doToggle = options.toggle;
		if (options.transition != null) kindOfTransition = options.transition;
		if (options.fps != null) fps = options.fps;
	}

	options = {duration:durationInMilliseconds, toggle:doToggle, transition:kindOfTransition, scaleContent:doScaleContent, useCSSBox: fullCSSBox, fps: fps};
	var sizeEffect = new Spry.Effect.Size(element, fromRect, toRect, options);
	this.addParallelEffect(sizeEffect);

	if(growFromCenter)
	{
		Spry.Effect.makePositioned(element);

		var startOffsetPosition = new Spry.Effect.Utils.Position();
		startOffsetPosition.x = parseInt(Spry.Effect.getStylePropRegardlessOfDisplayState(element, "left"), 10);
		startOffsetPosition.y = parseInt(Spry.Effect.getStylePropRegardlessOfDisplayState(element, "top"), 10);	
		if (!startOffsetPosition.x) startOffsetPosition.x = 0;
		if (!startOffsetPosition.y) startOffsetPosition.y = 0;

		options = {duration:durationInMilliseconds, toggle:doToggle, transition:kindOfTransition, from: optionFrom, to: optionTo, fps: fps};
		var fromPos = new Spry.Effect.Utils.Position;
		fromPos.x = startOffsetPosition.x + (originalWidth - fromRect.width) / 2.0;
		fromPos.y = startOffsetPosition.y + (originalHeight - fromRect.height) / 2.0;

		var toPos = new Spry.Effect.Utils.Position;
		toPos.x = startOffsetPosition.x + (originalWidth - toRect.width) / 2.0;
		toPos.y = startOffsetPosition.y + (originalHeight - toRect.height) / 2.0;

		var moveEffect = new Spry.Effect.Move(element, fromPos, toPos, options);
		this.addParallelEffect(moveEffect);
	}
};

Spry.Effect.Grow.prototype = new Spry.Effect.Cluster();
Spry.Effect.Grow.prototype.constructor = Spry.Effect.Grow;

Spry.Effect.Shake = function (element, options) 
{
	if (!this.notStaticAnimator)
		return Spry.Effect.Utils.showInitError('Shake'); 

	Spry.Effect.Cluster.call(this, options);

	this.options.direction = false;
	if (this.options.toggle)
		this.options.toggle = false;

	this.name = 'Shake';

	var element = Spry.Effect.getElement(element);
	this.element = element;
	if (!this.element)
		return;
	var durationInMilliseconds = 100;
	var kindOfTransition = Spry.linearTransition;
	var fps = 60;
	var steps = 4;

	if (options)
	{
		if (options.duration != null) steps = Math.ceil(this.options.duration / durationInMilliseconds) - 1;
		if (options.fps != null) fps = options.fps;
		if (options.transition != null) kindOfTransition = options.transition;
	}

	Spry.Effect.makePositioned(element);
	
	var startOffsetPosition = new Spry.Effect.Utils.Position();
	startOffsetPosition.x = parseInt(Spry.Effect.getStyleProp(element, "left"), 10);
	startOffsetPosition.y = parseInt(Spry.Effect.getStyleProp(element, "top"), 10);
	if (!startOffsetPosition.x) startOffsetPosition.x = 0;
	if (!startOffsetPosition.y) startOffsetPosition.y = 0;

	var centerPos = new Spry.Effect.Utils.Position;
	centerPos.x = startOffsetPosition.x;
	centerPos.y = startOffsetPosition.y;

	var rightPos = new Spry.Effect.Utils.Position;
	rightPos.x = startOffsetPosition.x + 20;
	rightPos.y = startOffsetPosition.y + 0;

	var leftPos = new Spry.Effect.Utils.Position;
	leftPos.x = startOffsetPosition.x + -20;
	leftPos.y = startOffsetPosition.y + 0;

	options = {duration:Math.ceil(durationInMilliseconds / 2), toggle:false, fps: fps, transition: kindOfTransition};
	var effect = new Spry.Effect.Move(element, centerPos, rightPos, options);
	this.addNextEffect(effect);

	options = {duration:durationInMilliseconds, toggle:false, fps:fps, transition: kindOfTransition};
	var effectToRight = new Spry.Effect.Move(element, rightPos, leftPos, options);
	var effectToLeft = new Spry.Effect.Move(element, leftPos, rightPos, options);

	for (var i=0; i < steps; i++)
	{
		if (i % 2 == 0)
			this.addNextEffect(effectToRight);
		else
			this.addNextEffect(effectToLeft);
	}
	var pos = (steps % 2 == 0) ? rightPos: leftPos;

	options = {duration:Math.ceil(durationInMilliseconds / 2), toggle:false, fps: fps, transition: kindOfTransition};
	var effect = new Spry.Effect.Move(element, pos, centerPos, options);
	this.addNextEffect(effect);
};
Spry.Effect.Shake.prototype = new Spry.Effect.Cluster();
Spry.Effect.Shake.prototype.constructor = Spry.Effect.Shake;
Spry.Effect.Shake.prototype.doToggle = function(){};

Spry.Effect.Squish = function (element, options) 
{
	if (!this.notStaticAnimator)
		return Spry.Effect.Utils.showInitError('Squish');

	if (!options)
		options = {};
	if (!options.to)
		options.to = '0%';
	if (!options.from)
		options.from = '100%';

	options.growCenter = false;
	Spry.Effect.Grow.call(this, element, options);
	this.name = 'Squish';
};
Spry.Effect.Squish.prototype = new Spry.Effect.Grow();
Spry.Effect.Squish.prototype.constructor = Spry.Effect.Squish;

Spry.Effect.Pulsate = function (element, options) 
{
	if (!this.notStaticAnimator)
		return Spry.Effect.Utils.showInitError('Pulsate');

	Spry.Effect.Cluster.call(this, options);

	this.options.direction = false;
	if (this.options.toggle)
		this.options.toggle = false;

	var element = Spry.Effect.getElement(element);
	var originalOpacity = 0;
	this.element = element;
	if (!this.element)
		return;

	this.name = 'Pulsate';
	var durationInMilliseconds = 100;
	var fromOpacity = 100.0;
	var toOpacity = 0.0;
	var doToggle = false;
	var kindOfTransition = Spry.linearTransition;
	var fps = 60;
	if(/MSIE/.test(navigator.userAgent))
		originalOpacity = parseInt(Spry.Effect.getStylePropRegardlessOfDisplayState(this.element, 'filter').replace(/alpha\(opacity=([0-9]{1,3})\)/g, '$1'), 10);
	else
		originalOpacity = parseInt(Spry.Effect.getStylePropRegardlessOfDisplayState(this.element, 'opacity') * 100, 10);

	if (isNaN(originalOpacity)){
		originalOpacity = 100;
	}

	if (options)
	{
		if (options.from != null){
			if (Spry.Effect.Utils.isPercentValue(options.from))
				fromOpacity = Spry.Effect.Utils.getPercentValue(options.from) * originalOpacity / 100;
			else
				fromOpacity = options.from;
		}
		if (options.to != null)
		{	
			if (Spry.Effect.Utils.isPercentValue(options.to))
				toOpacity = Spry.Effect.Utils.getPercentValue(options.to) * originalOpacity / 100;
			else
				toOpacity = options.to;
		}
		if (options.transition != null) kindOfTransition = options.transition;
		if (options.fps != null) fps = options.fps;
	}

	options = {duration:durationInMilliseconds, toggle:doToggle, transition:kindOfTransition, fps:fps};
	fromOpacity = fromOpacity / 100.0;
	toOpacity = toOpacity / 100.0;

	var fadeEffect = new Spry.Effect.Opacity(element, fromOpacity, toOpacity, options);
	var appearEffect = new Spry.Effect.Opacity(element, toOpacity, fromOpacity, options);
	var steps = parseInt(this.options.duration / 200, 10);
	for (var i=0; i < steps; i++){ 
		this.addNextEffect(fadeEffect);
		this.addNextEffect(appearEffect);
	}
};
Spry.Effect.Pulsate.prototype = new Spry.Effect.Cluster();
Spry.Effect.Pulsate.prototype.constructor = Spry.Effect.Pulsate;
Spry.Effect.Pulsate.prototype.doToggle = function(){};

Spry.Effect.Puff = function (element, options)
{
	if (!this.notStaticAnimator)
		return Spry.Effect.Utils.showInitError('Puff'); 

	Spry.Effect.Cluster.call(this, options);

	var element = Spry.Effect.getElement(element);
	this.element = element;	
	if (!this.element)
		return;
	this.name = 'Puff';
	var doToggle = false;
	var doScaleContent = false;
	var durationInMilliseconds = 1000;
	var kindOfTransition = Spry.fifthTransition;
	var fps = 60;

	Spry.Effect.makePositioned(element);

	if (options){
		if (options.toggle != null) doToggle = options.toggle;
		if (options.duration != null) durationInMilliseconds = options.duration;
		if (options.transition != null) kindOfTransition = options.transition;
		if (options.fps != null) fps = options.fps;
	}
	var originalRect = Spry.Effect.getDimensions(element);
	var startWidth = originalRect.width;
	var startHeight = originalRect.height;

	options = {duration:durationInMilliseconds, toggle:doToggle, transition: kindOfTransition, fps: fps};

	var fromOpacity = 1.0;
	var toOpacity = 0.0;
	var opacityEffect = new Spry.Effect.Opacity(element, fromOpacity, toOpacity, options);
	this.addParallelEffect(opacityEffect);

	var fromPos = Spry.Effect.getPosition(element);

	var toPos = new Spry.Effect.Utils.Position;
	toPos.x = startWidth / 2.0 * -1.0;
	toPos.y = startHeight / 2.0 * -1.0;

	options = {duration:durationInMilliseconds, toggle:doToggle, transition:kindOfTransition, from: fromPos, to: toPos, fps: fps};
	var moveEffect = new Spry.Effect.Move(element, fromPos, toPos, options);
	this.addParallelEffect(moveEffect);

	var self = this;
	this.addObserver({
		onPreEffect:function(){if (self.direction == Spry.backwards){self.element.style.display = 'block';}},
		onPostEffect: function(){if (self.direction == Spry.forwards){self.element.style.display = 'none';}}
	});
};
Spry.Effect.Puff.prototype = new Spry.Effect.Cluster;
Spry.Effect.Puff.prototype.constructor = Spry.Effect.Puff;

Spry.Effect.DropOut = function (element, options)
{
	if (!this.notStaticAnimator)
		return Spry.Effect.Utils.showInitError('DropOut');

	Spry.Effect.Cluster.call(this, options);

	var element = Spry.Effect.getElement(element);
	this.element = element;
	if (!this.element)
		return;
	var durationInMilliseconds = 1000;
	var fps = 60;
	var kindOfTransition = Spry.fifthTransition;
	var direction = Spry.forwards;
	var doToggle = false;
	this.name = 'DropOut';

	Spry.Effect.makePositioned(element);

	if (options)
	{
		if (options.duration != null) durationInMilliseconds = options.duration;
		if (options.toggle != null) doToggle = options.toggle;
		if (options.fps != null) fps = options.fps;
		if (options.transition != null) kindOfTransition = options.transition;
		if (options.dropIn != null) direction = -1;
	}

	var startOffsetPosition = new Spry.Effect.Utils.Position();
	startOffsetPosition.x = parseInt(Spry.Effect.getStyleProp(element, "left"), 10);
	startOffsetPosition.y = parseInt(Spry.Effect.getStyleProp(element, "top"), 10);	
	if (!startOffsetPosition.x) startOffsetPosition.x = 0;
	if (!startOffsetPosition.y) startOffsetPosition.y = 0;

	var fromPos = new Spry.Effect.Utils.Position;
	fromPos.x = startOffsetPosition.x + 0;
	fromPos.y = startOffsetPosition.y + 0;

	var toPos = new Spry.Effect.Utils.Position;
	toPos.x = startOffsetPosition.x + 0;
	toPos.y = startOffsetPosition.y + (direction * 160);

	options = {from:fromPos, to:toPos, duration:durationInMilliseconds, toggle:doToggle, transition: kindOfTransition, fps: fps};
	var moveEffect = new Spry.Effect.Move(element, options.from, options.to, options);
	this.addParallelEffect(moveEffect);

	var fromOpacity = 1.0;
	var toOpacity = 0.0;
	options = {duration:durationInMilliseconds, toggle:doToggle, transition: kindOfTransition, fps: fps};
	var opacityEffect = new Spry.Effect.Opacity(element, fromOpacity, toOpacity, options);
	this.addParallelEffect(opacityEffect);

	var self = this;
	this.addObserver({
		onPreEffect:function(){self.element.style.display = 'block';},
		onPostEffect: function(){if (self.direction == Spry.forwards){self.element.style.display = 'none';}}
	});

};
Spry.Effect.DropOut.prototype = new Spry.Effect.Cluster();
Spry.Effect.DropOut.prototype.constructor = Spry.Effect.DropOut;

Spry.Effect.Fold = function (element, options)
{
	if (!this.notStaticAnimator)
		return Spry.Effect.Utils.showInitError('Fold');

	Spry.Effect.Cluster.call(this, options);

	var element = Spry.Effect.getElement(element);
	this.element = element;
	if (!this.element)
		return;
	this.name = 'Fold';
	var durationInMilliseconds = 1000;
	var doToggle = false;
	var doScaleContent = true;
	var fullCSSBox = false;
	var kindOfTransition = Spry.fifthTransition;
	var fps = fps;
	
	Spry.Effect.makeClipping(element);

	var originalRect = Spry.Effect.getDimensionsRegardlessOfDisplayState(element);
	var startWidth = originalRect.width;
	var startHeight = originalRect.height;

	var stopWidth = startWidth;
	var stopHeight = startHeight / 5;

	var fromRect = new Spry.Effect.Utils.Rectangle;
	fromRect.width = startWidth;
	fromRect.height = startHeight;

	var toRect = new Spry.Effect.Utils.Rectangle;
	toRect.width = stopWidth;
	toRect.height = stopHeight;

	if (options)
	{
		if (options.duration != null) durationInMilliseconds = Math.ceil(options.duration/2);
		if (options.toggle != null) doToggle = options.toggle;
		if (options.useCSSBox != null) fullCSSBox = options.useCSSBox; 
		if (options.fps != null) fps = options.fps;
		if (options.transition != null) kindOfTransition = options.transition;
	}

	options = {duration:durationInMilliseconds, toggle:doToggle, scaleContent:doScaleContent, useCSSBox: fullCSSBox, transition: kindOfTransition, fps: fps};
	var sizeEffect = new Spry.Effect.Size(element, fromRect, toRect, options);
	this.addNextEffect(sizeEffect);

	fromRect.width = toRect.width;
	fromRect.height = toRect.height;
	toRect.width = '0%';
	var sizeEffect = new Spry.Effect.Size(element, fromRect, toRect, options);
	this.addNextEffect(sizeEffect);
};

Spry.Effect.Fold.prototype = new Spry.Effect.Cluster();
Spry.Effect.Fold.prototype.constructor = Spry.Effect.Fold;

//
// The names of some of the static effect functions changed in Spry 1.5. These wrappers will insure that we remain compatible with previous versions of Spry.
//

Spry.Effect.DoFade = function (element, options)
{
	return Spry.Effect.Utils.DoEffect('Fade', element, options);
};

Spry.Effect.DoBlind = function (element, options)
{
	return Spry.Effect.Utils.DoEffect('Blind', element, options);
};

Spry.Effect.DoHighlight = function (element, options)
{
	return Spry.Effect.Utils.DoEffect('Highlight', element, options);
};

Spry.Effect.DoSlide = function (element, options)
{
	return Spry.Effect.Utils.DoEffect('Slide', element, options);
};

Spry.Effect.DoGrow = function (element, options)
{
	return Spry.Effect.Utils.DoEffect('Grow', element, options);
};

Spry.Effect.DoShake = function (element, options)
{
	return Spry.Effect.Utils.DoEffect('Shake', element, options);
};

Spry.Effect.DoSquish = function (element, options)
{
	return Spry.Effect.Utils.DoEffect('Squish', element, options);
};

Spry.Effect.DoPulsate = function (element, options)
{
	return Spry.Effect.Utils.DoEffect('Pulsate', element, options);
};

Spry.Effect.DoPuff = function (element, options)
{
	return Spry.Effect.Utils.DoEffect('Puff', element, options);
};

Spry.Effect.DoDropOut = function (element, options)
{
	return Spry.Effect.Utils.DoEffect('DropOut', element, options);
};

Spry.Effect.DoFold = function (element, options)
{
	return Spry.Effect.Utils.DoEffect('Fold', element, options);
};

// 2429                         
// SpryDOMUtils.js - version 0.6
//                              

var Spry; if (!Spry) Spry = {}; if (!Spry.Utils) Spry.Utils = {};

//
// Define Prototype's $() convenience function, but make sure it is namespaced under Spry so that we avoid collisions with other toolkits.
//

Spry.$ = function(element)
{
	if (arguments.length > 1)
	{
		for (var i = 0, elements = [], length = arguments.length; i < length; i++)
			elements.push(Spry.$(arguments[i]));
		return elements;
	}
	if (typeof element == 'string')
		element = document.getElementById(element);
	return element;
};

//
// DOM Utils
//

Spry.Utils.setAttribute = function(ele, name, value)
{
	ele = Spry.$(ele);
	if (!ele || !name)
		return;

	if (name == "class")
		ele.className = value;
	else
		ele.setAttribute(name, value);
};

Spry.Utils.removeAttribute = function(ele, name)
{
	ele = Spry.$(ele);
	if (!ele || !name)
		return;

	try
	{
		ele.removeAttribute(name);

		if (name == "class")
			ele.removeAttribute("className");
	} catch(e) {}
};

Spry.Utils.addClassName = function(ele, className)
{
	ele = Spry.$(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 = Spry.$(ele);
	if (Spry.Utils.hasClassName(ele, className))
		ele.className = ele.className.replace(new RegExp("\\s*\\b" + className + "\\b", "g"), "");
};

Spry.Utils.toggleClassName = function(ele, className)
{
	if (Spry.Utils.hasClassName(ele, className))
		Spry.Utils.removeClassName(ele, className);
	else
		Spry.Utils.addClassName(ele, className);
};

Spry.Utils.hasClassName = function(ele, className)
{
	ele = Spry.$(ele);
	if (!ele || !className || !ele.className || ele.className.search(new RegExp("\\b" + className + "\\b")) == -1)
		return false;
	return true;
};

Spry.Utils.camelizeString = function(str)
{
	var cStr = "";
	var a = str.split("-");
	for (var i = 0; i < a.length; i++)
	{
		var s = a[i];
		if (s)
			cStr = cStr ? (cStr + s.charAt(0).toUpperCase() + s.substring(1)) : s;
	}
	return cStr;
};

Spry.Utils.styleStringToObject = function(styleStr)
{
	var o = {};
	if (styleStr)
	{
		pvA = styleStr.split(";");
		for (var i = 0; i < pvA.length; i++)
		{
			var pv = pvA[i];
			if (pv && pv.indexOf(":") != -1)
			{
				var nvA = pv.split(":");
				var n = nvA[0].replace(/^\s*|\s*$/g, "");			
				var v = nvA[1].replace(/^\s*|\s*$/g, "");
				if (n && v)
					o[Spry.Utils.camelizeString(n)] = v;
			}
		}
	}
	return o;
};

Spry.Utils.addEventListener = function(element, eventType, handler, capture)
{
	try
	{
		if (!Spry.Utils.eventListenerIsBoundToElement(element, eventType, handler, capture))
		{
			element = Spry.$(element);
			handler = Spry.Utils.bindEventListenerToElement(element, eventType, handler, capture);
			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 = Spry.$(element);
			handler = Spry.Utils.unbindEventListenerFromElement(element, eventType, handler, capture);
			if (element.removeEventListener)
				element.removeEventListener(eventType, handler, capture);
			else if (element.detachEvent)
				element.detachEvent("on" + eventType, handler);
	}
	catch (e) {}
};

Spry.Utils.eventListenerHash = {};
Spry.Utils.nextEventListenerID = 1;

Spry.Utils.getHashForElementAndHandler = function(element, eventType, handler, capture)
{
	var hash = null;
	element = Spry.$(element);
	if (element)
	{
		if (typeof element.spryEventListenerID == "undefined")
			element.spryEventListenerID = "e" + (Spry.Utils.nextEventListenerID++);
		if (typeof handler.spryEventHandlerID == "undefined")
			handler.spryEventHandlerID = "h" + (Spry.Utils.nextEventListenerID++);	
		hash = element.spryEventListenerID + "-" + handler.spryEventHandlerID + "-" + eventType + (capture?"-capture":"");
	}
	return hash;
};

Spry.Utils.eventListenerIsBoundToElement = function(element, eventType, handler, capture)
{
	element = Spry.$(element);
	var hash = Spry.Utils.getHashForElementAndHandler(element, eventType, handler, capture);
	return Spry.Utils.eventListenerHash[hash] != undefined;
};

Spry.Utils.bindEventListenerToElement = function(element, eventType, handler, capture)
{
	element = Spry.$(element);
	var hash = Spry.Utils.getHashForElementAndHandler(element, eventType, handler, capture);
	if (Spry.Utils.eventListenerHash[hash])
		return Spry.Utils.eventListenerHash[hash];
	return Spry.Utils.eventListenerHash[hash] = function(e)
	{
		e = e || window.event;

		if (!e.preventDefault) e.preventDefault = function() { this.returnValue = false; };
		if (!e.stopPropagation) e.stopPropagation = function() { this.cancelBubble = true; };

		var result = handler.call(element, e);
		if (result == false)
		{
			e.preventDefault();
			e.stopPropagation();
		}
		return result;
	};
};

Spry.Utils.unbindEventListenerFromElement = function(element, eventType, handler, capture)
{
	element = Spry.$(element);
	var hash = Spry.Utils.getHashForElementAndHandler(element, eventType, handler, capture);
	if (Spry.Utils.eventListenerHash[hash])
	{
		handler = Spry.Utils.eventListenerHash[hash];
		Spry.Utils.eventListenerHash[hash] = undefined;
	}
	return handler;
};

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.getAncestor = function(ele, selector)
{
	ele = Spry.$(ele);
	if (ele)
	{
		var s = Spry.$$.tokenizeSequence(selector ? selector : "*")[0];
		var t = s ? s[0] : null;
		if (t)
		{
			var p = ele.parentNode;
			while (p)
			{
				if (t.match(p))
					return p;
				p = p.parentNode;
			}
		}
	}
	return null;
};

//
// CSS Selector Matching
//

Spry.$$ = function(selectorSequence, rootNode)
{
	if (!rootNode)
		rootNode = document;
	else
		rootNode = Spry.$(rootNode);

	var sequences = Spry.$$.tokenizeSequence(selectorSequence);

	var matches = [];
	Spry.$$.addExtensions(matches);
	++Spry.$$.queryID;

	var nid = 0;
	var ns = sequences.length;
	for (var i = 0; i < ns; i++)
	{
		var m = Spry.$$.processTokens(sequences[i], rootNode);
		var nm = m.length;
		for (var j = 0; j < nm; j++)
		{
			var n = m[j];
			if (!n.spry$$ID)
			{
				n.spry$$ID = ++nid;
				matches.push(n);
			}
		}
	}

	var nm = matches.length;
	for (i = 0; i < nm; i++)
		matches[i].spry$$ID = undefined;

	return matches;
};

Spry.$$.cache = {};
Spry.$$.queryID = 0;

Spry.$$.Token = function()
{
	this.type = Spry.$$.Token.SELECTOR;
	this.name = "*";
	this.id = "";
	this.classes = [];
	this.attrs = [];
	this.pseudos = [];
};

Spry.$$.Token.Attr = function(n, v)
{
	this.name = n;
	this.value = v ? new RegExp(v) : undefined;
};

Spry.$$.Token.PseudoClass = function(pstr)
{
	this.name = pstr.replace(/\(.*/, "");
	this.arg = pstr.replace(/^[^\(\)]*\(?\s*|\)\s*$/g, "");
	this.func = Spry.$$.pseudoFuncs[this.name];
};

Spry.$$.Token.SELECTOR = 0;
Spry.$$.Token.COMBINATOR = 1;

Spry.$$.Token.prototype.match = function(ele, nameAlreadyMatches)
{
	if (this.type == Spry.$$.Token.COMBINATOR)
		return false;
	if (!nameAlreadyMatches && this.name != '*' && this.name != ele.nodeName.toLowerCase())
		return false;
	if (this.id && this.id != ele.id)
		return false;
	var classes = this.classes;
	var len = classes.length;
	for (var i = 0; i < len; i++)
	{
		if (!ele.className || !classes[i].value.test(ele.className))
			return false;
	}

	var attrs = this.attrs;
	len = attrs.length;
	for (var i = 0; i < len; i++)
	{
		var a = attrs[i];
		var an = ele.attributes.getNamedItem(a.name);
		if (!an || (!a.value && an.nodeValue == undefined) || (a.value && !a.value.test(an.nodeValue)))
			return false;
	}

	var ps = this.pseudos;
	var len = ps.length;
	for (var i = 0; i < len; i++)
	{
		var p = ps[i];
		if (p && p.func && !p.func(p.arg, ele, this))
			return false;
	}

	return true;
};

Spry.$$.Token.prototype.getNodeNameIfTypeMatches = function(ele)
{
	var nodeName = ele.nodeName.toLowerCase();
	if (this.name != '*')
	{
		if (this.name != nodeName)
			return null;
		return this.name;
	}
	return nodeName;
};

Spry.$$.escapeRegExpCharsRE = /\/|\.|\*|\+|\(|\)|\[|\]|\{|\}|\\|\|/g;

Spry.$$.tokenizeSequence = function(s)
{
	var cc = Spry.$$.cache[s];
	if (cc) return cc;

	var tokenExpr = /(\[[^\"'~\^\$\*\|\]=]+([~\^\$\*\|]?=\s*('[^']*'|"[^"]*"|[^"'\]]+))?\s*\])|((:[^\.#:\s,>~\+\[\]]+\(([^\(\)]+|\([^\(\)]*\))*\))|[\.#:]?[^\.#:\s,>~\+\[\]]+)|(\s*[\s,>~\+]\s*)/g;

	var tkn = new Spry.$$.Token;
	var sequence = [];
	sequence.push(tkn);
	var tokenSequences = [];
	tokenSequences.push(sequence);

	s = s.replace(/^\s*|\s*$/, "");

	var expMatch = tokenExpr.exec(s);
	while (expMatch)
	{
		var tstr = expMatch[0];
		var c = tstr.charAt(0);
		switch (c)
		{
			case '.':
				tkn.classes.push(new Spry.$$.Token.Attr("class", "\\b" + tstr.substr(1) + "\\b"));
				break;
			case '#':
				tkn.id = tstr.substr(1);
				break;
			case ':':
				tkn.pseudos.push(new Spry.$$.Token.PseudoClass(tstr));
				break;
			case '[':
				var attrComps = tstr.match(/\[([^\"'~\^\$\*\|\]=]+)(([~\^\$\*\|]?=)\s*('[^']*'|"[^"]*"|[^"'\]]+))?\s*\]/);
				var name = attrComps[1];				
				var matchType = attrComps[3];
				var val = attrComps[4];
				if (val)
				{
					val = val.replace(/^['"]|['"]$/g, "");
					val = val.replace(Spry.$$.escapeRegExpCharsRE, '\\$&');
				}

				var matchStr = undefined;

				switch(matchType)
				{
					case "=":
						matchStr = "^" + val + "$";
						break;
					case "^=":
						matchStr = "^" + val;
						break;
					case "$=":
						matchStr = val + "$";
						break;
					case "~=":
					case "|=":
						matchStr = "\\b" + val + "\\b";
						break;
					case "*=":
						matchStr = val;
						break;
				}

				tkn.attrs.push(new Spry.$$.Token.Attr(name, matchStr));
				break;
			default:
				var combiMatch = tstr.match(/^\s*([\s,~>\+])\s*$/);
				if (combiMatch)
				{
					if (combiMatch[1] == ',')
					{
						sequence = new Array;
						tokenSequences.push(sequence);
						tkn = new Spry.$$.Token;
						sequence.push(tkn);
					}
					else
					{
						tkn = new Spry.$$.Token;
						tkn.type = Spry.$$.Token.COMBINATOR;
						tkn.name = combiMatch[1];
						sequence.push(tkn);
						tkn = new Spry.$$.Token();
						sequence.push(tkn);
					}
				}
				else
					tkn.name = tstr.toLowerCase();
				break;
		}
		expMatch = tokenExpr.exec(s);
	}

	Spry.$$.cache[s] = tokenSequences;

	return tokenSequences;
};

Spry.$$.combinatorFuncs = {
	// Element Descendant

	" ": function(nodes, token)
	{
		var uid = ++Spry.$$.uniqueID;
		var results = [];
		var nn = nodes.length;
		for (var i = 0; i < nn; i++)
		{
			var n = nodes[i];
			if (uid != n.spry$$uid)
			{
				// n.spry$$uid = uid;
				var ea = nodes[i].getElementsByTagName(token.name);
				var ne = ea.length;
				for (var j = 0; j < ne; j++)
				{
					var e = ea[j];
					if (token.match(e, true))
						results.push(e);
					e.spry$$uid = uid;
				}
			}
		}
		return results;
	},

	// Element Child

	">": function(nodes, token)
	{
		var results = [];
		var nn = nodes.length;
		for (var i = 0; i < nn; i++)
		{
			var n = nodes[i].firstChild;
			while (n)
			{
				if (n.nodeType == 1 /* Node.ELEMENT_NODE */ && token.match(n))
					results.push(n);
				n = n.nextSibling;
			}
		}
		return results;
	},

	// Element Immediately Preceded By

	"+": function(nodes, token)
	{
		var results = [];
		var nn = nodes.length;
		for (var i = 0; i < nn; i++)
		{
			var n = nodes[i].nextSibling;
			while (n && n.nodeType != 1 /* Node.ELEMENT_NODE */)
				n = n.nextSibling;
			if (n && token.match(n))
				results.push(n);
		}
		return results;
	},

	// Element Preceded By

	"~": function(nodes, token)
	{
		var uid = ++Spry.$$.uniqueID;
		var results = [];
		var nn = nodes.length;
		for (var i = 0; i < nn; i++)
		{
			var n = nodes[i].nextSibling;
			while (n)
			{
				if (n.nodeType == 1 /* Node.ELEMENT_NODE */)
				{
					if (uid == n.spry$$uid)
						break;

					if (token.match(n))
					{
						results.push(n);
						n.spry$$uid = uid;
					}
				}
				n = n.nextSibling;
			}
		}
		return results;
	}
};

Spry.$$.uniqueID = 0;

Spry.$$.pseudoFuncs = {
	":first-child": function(arg, node, token)
	{
		var n = node.previousSibling;
		while (n)
		{
			if (n.nodeType == 1) return false; // Node.ELEMENT_NODE
			n = n.previousSibling;
		}

		return true;
	},

	":last-child": function(arg, node, token)
	{
		var n = node.nextSibling;
		while (n)
		{
			if (n.nodeType == 1) // Node.ELEMENT_NODE
				return false;
			n = n.nextSibling;
		}
		return true;
	},

	":empty": function(arg, node, token)
	{
		var n = node.firstChild;
		while (n)
		{
			switch(n.nodeType)
			{
				case 1: // Node.ELEMENT_NODE
				case 3: // Node.TEXT_NODE
				case 4: // Node.CDATA_NODE
				case 5: // Node.ENTITY_REFERENCE_NODE
					return false;
			}
			n = n.nextSibling;
		}
		return true;
	},

	":nth-child": function(arg, node, token)
	{
		return Spry.$$.nthChild(arg, node, token);
	},

	":nth-last-child": function(arg, node, token)
	{
		return Spry.$$.nthChild(arg, node, token, true);
	},

	":nth-of-type": function(arg, node, token)
	{
		return Spry.$$.nthChild(arg, node, token, false, true);
	},
	
	":nth-last-of-type": function(arg, node, token)
	{
		return Spry.$$.nthChild(arg, node, token, true, true);
	},
	
	":first-of-type": function(arg, node, token)
	{
		var nodeName = token.getNodeNameIfTypeMatches(node);
		if (!nodeName) return false;

		var n = node.previousSibling;
		while (n)
		{
			if (n.nodeType == 1 && nodeName == n.nodeName.toLowerCase()) return false; // Node.ELEMENT_NODE
			n = n.previousSibling;
		}

		return true;
	},

	":last-of-type": function(arg, node, token)
	{
		var nodeName = token.getNodeNameIfTypeMatches(node);
		if (!nodeName) return false;

		var n = node.nextSibling;
		while (n)
		{
			if (n.nodeType == 1 && nodeName == n.nodeName.toLowerCase()) // Node.ELEMENT_NODE
				return false;
			n = n.nextSibling;
		}
		return true;
	},

	":only-child": function(arg, node, token)
	{
		var f = Spry.$$.pseudoFuncs;
		return f[":first-child"](arg, node, token) && f[":last-child"](arg, node, token);
	},

	":only-of-type": function(arg, node, token)
	{
		var f = Spry.$$.pseudoFuncs;
		return f[":first-of-type"](arg, node, token) && f[":last-of-type"](arg, node, token);
	},

	":not": function(arg, node, token)
	{
		var s = Spry.$$.tokenizeSequence(arg)[0];
		var t = s ? s[0] : null;
		return !t || !t.match(node);
	},

	":enabled": function(arg, node, token)
	{
		return !node.disabled;
	},

	":disabled": function(arg, node, token)
	{
		return node.disabled;
	},

	":checked": function(arg, node, token)
	{
		return node.checked;
	},

	":root": function(arg, node, token)
	{
		return node.parentNode && node.ownerDocument && node.parentNode == node.ownerDocument;
	}
};

Spry.$$.nthRegExp = /((-|[0-9]+)?n)?([+-]?[0-9]*)/;

Spry.$$.nthCache = {
	  "even": { a: 2, b: 0, mode: 1, invalid: false }
	, "odd":  { a: 2, b: 1, mode: 1, invalid: false }
	, "2n":   { a: 2, b: 0, mode: 1, invalid: false }
	, "2n+1": { a: 2, b: 1, mode: 1, invalid: false }
};

Spry.$$.parseNthChildString = function(str)
{
	var o = Spry.$$.nthCache[str];
	if (!o)
	{
		var m = str.match(Spry.$$.nthRegExp);
		var n = m[1];
		var a = m[2];
		var b = m[3];

		if (!a)
		{
			// An 'a' value was not specified. Was there an 'n' present?
			// If so, we treat it as an increment of 1, otherwise we're

			a = n ? 1 : 0;
		}
		else if (a == "-")
		{

			a = -1;
		}
		else
		{

			a = parseInt(a, 10);
		}

		b = b ? parseInt(b, 10) : 0;

		var mode = (a == 0) ? 0 : ((a > 0) ? 1 : -1);
		var invalid = false;

		if (a > 0 && b < 0)
		{
			b = b % a;
			b = ((b=(b%a)) < 0) ? a + b : b;
		}
		else if (a < 0)
		{
			if (b < 0)
				invalid = true;
			else
				a = Math.abs(a);
		}

		o = new Object;
		o.a = a;
		o.b = b;
		o.mode = mode;
		o.invalid = invalid;

		Spry.$$.nthCache[str] = o;
	}

	return o;
};

Spry.$$.nthChild = function(arg, node, token, fromLastSib, matchNodeName)
{
	if (matchNodeName)
	{
		var nodeName = token.getNodeNameIfTypeMatches(node);
		if (!nodeName) return false;
	}

	var o = Spry.$$.parseNthChildString(arg);

	if (o.invalid)
		return false;

	var qidProp = "spry$$ncQueryID";
	var posProp = "spry$$ncPos";
	var countProp = "spry$$ncCount";
	if (matchNodeName)
	{
		qidProp += nodeName;
		posProp += nodeName;
		countProp += nodeName;
	}

	var parent = node.parentNode;
	if (parent[qidProp] != Spry.$$.queryID)
	{
		var pos = 0;
		parent[qidProp] = Spry.$$.queryID;
		var c = parent.firstChild;
		while (c)
		{
			if (c.nodeType == 1 && (!matchNodeName || nodeName == c.nodeName.toLowerCase()))
				c[posProp] = ++pos;
			c = c.nextSibling;
		}
		parent[countProp] = pos;
	}

	pos = node[posProp];
	if (fromLastSib)
		pos = parent[countProp] - pos + 1;

/*
	var sib = fromLastSib ? "nextSibling" : "previousSibling";

	var pos = 1;
	var n = node[sib];
	while (n)
	{
		if (n.nodeType == 1 && (!matchNodeName || nodeName == n.nodeName.toLowerCase()))
		{
			if (n == node) break;
			++pos;
		}
		n = n[sib];
	}
*/

	if (o.mode == 0)
		return pos == o.b;
	if (o.mode > 0)
		return (pos < o.b) ? false : (!((pos - o.b) % o.a));
	return (pos > o.b) ? false : (!((o.b - pos) % o.a));
};

Spry.$$.processTokens = function(tokens, root)
{
	var numTokens = tokens.length;
	var nodeSet = [ root ];
	var combiFunc = null;

	for (var i = 0; i < numTokens && nodeSet.length > 0; i++)
	{
		var t = tokens[i];
		if (t.type == Spry.$$.Token.SELECTOR)
		{
			if (combiFunc)
			{
				nodeSet = combiFunc(nodeSet, t);
				combiFunc = null;
			}
			else
				nodeSet = Spry.$$.getMatchingElements(nodeSet, t);
		}
		else
			combiFunc = Spry.$$.combinatorFuncs[t.name];
	}
	return nodeSet;
};

Spry.$$.getMatchingElements = function(nodes, token)
{
	var results = [];
	if (token.id)
	{
		n = nodes[0];
		if (n && n.ownerDocument)
		{
			var e = n.ownerDocument.getElementById(token.id);
			if (e)
			{

				if (token.match(e))
					results.push(e);
			}
			return results;
		}
	}

	var nn = nodes.length;
	for (var i = 0; i < nn; i++)
	{
		var n = nodes[i];
		
		var ea = n.getElementsByTagName(token.name);
		var ne = ea.length;
		for (var j = 0; j < ne; j++)
		{
			var e = ea[j];
			if (token.match(e, true))
				results.push(e);
		}
	}
	return results;
};

/*
Spry.$$.dumpSequences = function(sequences)
{
	Spry.Debug.trace("<hr />Number of Sequences: " + sequences.length);
	for (var i = 0; i < sequences.length; i++)
	{
		var str = "";
		var s = sequences[i];
		Spry.Debug.trace("<hr />Sequence " + i + " -- Tokens: " + s.length);
		for (var j = 0; j < s.length; j++)
		{
			var t = s[j];
			if (t.type == Spry.$$.Token.SELECTOR)
			{
				str += "  SELECTOR:\n    Name: " + t.name + "\n    ID: " + t.id + "\n    Attrs:\n";
				for (var k = 0; k < t.classes.length; k++)
					str += "      " + t.classes[k].name + ": " + t.classes[k].value + "\n";
				for (var k = 0; k < t.attrs.length; k++)
					str += "      " + t.attrs[k].name + ": " + t.attrs[k].value + "\n";
				str += "    Pseudos:\n";
				for (var k = 0; k < t.pseudos.length; k++)
					str += "      " + t.pseudos[k].name + (t.pseudos[k].arg ? "(" + t.pseudos[k].arg + ")" : "") + "\n";
			}
			else
			{
				str += "  COMBINATOR:\n    Name: '" + t.name + "'\n"; 
			}
		}
		Spry.Debug.trace("<pre>" + Spry.Utils.encodeEntities(str) + "</pre>");
	}
};
*/

Spry.$$.addExtensions = function(a)
{
	for (var f in Spry.$$.Results)
		a[f] = Spry.$$.Results[f];
};

Spry.$$.Results = {};

Spry.$$.Results.forEach = function(func)
{
	var n = this.length;
	for (var i = 0; i < n; i++)
		func(this[i]);
	return this;
};

Spry.$$.Results.setAttribute = function(name, value)
{
	return this.forEach(function(n) { Spry.Utils.setAttribute(n, name, value); });
};

Spry.$$.Results.removeAttribute = function(name)
{
	return this.forEach(function(n) { Spry.Utils.removeAttribute(n, name); });
};

Spry.$$.Results.addClassName = function(className)
{
	return this.forEach(function(n) { Spry.Utils.addClassName(n, className); });
};

Spry.$$.Results.removeClassName = function(className)
{
	return this.forEach(function(n) { Spry.Utils.removeClassName(n, className); });
};

Spry.$$.Results.toggleClassName = function(className)
{
	return this.forEach(function(n) { Spry.Utils.toggleClassName(n, className); });
};

Spry.$$.Results.addEventListener = function(eventType, handler, capture, bindHandler)
{
	return this.forEach(function(n) { Spry.Utils.addEventListener(n, eventType, handler, capture, bindHandler); });
};

Spry.$$.Results.removeEventListener = function(eventType, handler, capture)
{
	return this.forEach(function(n) { Spry.Utils.removeEventListener(n, eventType, handler, capture); });
};

Spry.$$.Results.setStyle = function(style)
{
	if (style)
	{
		style = Spry.Utils.styleStringToObject(style);
		this.forEach(function(n)
		{
			for (var p in style)
				try { n.style[p] = style[p]; } catch (e) {}
		});
	}
	return this;
};

Spry.$$.Results.setProperty = function(prop, value)
{
	if (prop)
	{
		if (typeof prop == "string")
		{
			var p = {};
			p[prop] = value;
			prop = p;
		}

		this.forEach(function(n)
		{
			for (var p in prop)
				try { n[p] = prop[p]; } catch (e) {}
		});
	}
	return this;
};

// 3432                            
// SpryImageLoader.js - version 0.2
//                                 

var Spry; if (!Spry) Spry = {}; if (!Spry.Utils) Spry.Utils = {};

Spry.Utils.ImageLoader = function()
{
	this.queue = [];
	this.timerID = 0;
	this.currentEntry = null;
};

Spry.Utils.ImageLoader.prototype.start = function()
{
	if (!this.timerID)
	{
		var self = this;
		this.timerID = setTimeout(function()
		{
			self.timerID = 0;
			self.processQueue();
		}, 0);
	}
};

Spry.Utils.ImageLoader.prototype.stop = function()
{
	if (this.currentEntry)
	{
		var entry = this.currentEntry;
		entry.loader.onload = null;
		entry.loader.src = "";
		entry.loader = null;
		this.currentEntry = null;
		this.queue.unshift(entry);
	}

	if (this.timerID)
		clearTimeout(this.timerID);

	this.timerID = 0;
};

Spry.Utils.ImageLoader.prototype.clearQueue = function()
{
	this.stop();
	this.queue.length = 0;
};

Spry.Utils.ImageLoader.prototype.load = function(url, callback, priority)
{
	if (url)
	{
		if (typeof priority == "undefined")
			priority = 0;
		this.queue.push({ url: url, callback: callback, priority: priority });

		this.queue.sort(function(a,b){ return (a.priority > b.priority) ? -1 : ((a.priority < b.priority) ? 1 : 0); });
		this.start();
	}
};

Spry.Utils.ImageLoader.prototype.processQueue = function()
{
	if (this.queue.length < 1)
		return;

	var entry = this.currentEntry = this.queue.shift();
	var loader = entry.loader = new Image;
	var self = this;

	loader.onload = function()
	{
		self.currentEntry = null;
		if (entry.callback)
			entry.callback(entry.url, entry.loader);
		if (self.queue.length > 0)
			self.start();
	};

	loader.onerror = function()
	{
		self.currentEntry = null;
		if (self.queue.length > 0)
			self.start();
	};

	this.currentLoader = loader;
	loader.src = entry.url;
};

// 3524                         
// SpryNotifier.js - version 0.1
//                              

var Spry; if (!Spry) Spry = {}; if (!Spry.Utils) Spry.Utils = {};

Spry.Utils.Notifier = function()
{
	this.observers = [];
	this.suppressNotifications = 0;
};

Spry.Utils.Notifier.prototype.addObserver = function(observer)
{
	if (!observer)
		return;

	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;
};

// 3601                            
// SpryThumbViewer.js - version 0.1
//                                 

var Spry; if (!Spry) Spry = {}; if (!Spry.Widget) Spry.Widget = {};

Spry.Widget.ThumbViewer = function(ele, options)
{
	Spry.Utils.Notifier.call(this);

	this.element = Spry.$(ele);
	this.loader = new Spry.Utils.ImageLoader();
	this.nextImageID = 0;

	this.maxDimension = 70;
	this.thumbWidth = 24;
	this.thumbHeight = 24;

	this.tnLinkSelector = "a[href]";
	this.tnImageSelector = "a[href] > img";

	this.currentImage = null;

	this.behaviorsArray = [];

	this.attachBehaviors();
	this.select(0);
};

Spry.Widget.ThumbViewer.prototype = new Spry.Utils.Notifier();
Spry.Widget.ThumbViewer.prototype.constructor = Spry.Widget.ThumbViewer;

Spry.Widget.ThumbViewer.setOptions = function(obj, optionsObj, ignoreUndefinedProps)
{
	if (!optionsObj)
		return;
	for (var optionName in optionsObj)
	{
		if (ignoreUndefinedProps && optionsObj[optionName] == undefined)
			continue;
		obj[optionName] = optionsObj[optionName];
	}
};

Spry.Widget.ThumbViewer.prototype.attachBehaviors = function()
{
	var self = this;
	if (this.element)
		this.getThumbImages().forEach(function(img){ if (img.src) self.preloadImage(img); });
};

Spry.Widget.ThumbViewer.prototype.attachHoverBehaviors = function(img)
{
	var a = Spry.Utils.getAncestor(img, "a[href]");
	if (a)
	{
		var self = this;
		Spry.Utils.addEventListener(a, "mouseover", function(e) { self.growThumbnail(img); return false; }, false);
		Spry.Utils.addEventListener(a, "mouseout", function(e) { self.shrinkThumbnail(img); return false; }, false);
		Spry.Utils.addEventListener(a, "click", function(e) { self.select(img); return false; }, false);
	}

};

Spry.Widget.ThumbViewer.prototype.preloadImage = function(img)
{
	if (this.loader && img.src)
	{
		var self = this;
		this.loader.load(img.src, function(url, loaderImage)
		{
			img.spryOrigWidth = loaderImage.width;
			img.spryOrigHeight = loaderImage.height;
			self.attachHoverBehaviors(img);
		}, 10);

	}
};

Spry.Widget.ThumbViewer.prototype.cancelBehavior = function(id)
{
	if (this.behaviorsArray[id])
	{
		this.behaviorsArray[id].cancel();
		this.behaviorsArray[id] = null;
	}
};

Spry.Widget.ThumbViewer.prototype.sizeAndPosition = function(img, toX, toY, toWidth, toHeight, callback)
{
	var id = img.spryID;

	this.cancelBehavior(id);
	var effectCluster = new Spry.Effect.Cluster( { finish: callback } );
	var moveEffect = new Spry.Effect.Move(img, Spry.Effect.getPosition(img), { x: toX, y: toY, units: "px" }, { duration: 400 });
	var sizeEffect = new Spry.Effect.Size(img, Spry.Effect.getDimensions(img), { width: toWidth, height: toHeight, units: "px" }, { duration: 400 });
	
	effectCluster.addParallelEffect(moveEffect);
	effectCluster.addParallelEffect(sizeEffect);
	
	this.behaviorsArray[id] = effectCluster;
	this.behaviorsArray[id].start();
};

Spry.Widget.ThumbViewer.prototype.growThumbnail = function(img)
{
	if (!img.spryOrigWidth || !img.spryOrigHeight)
		return;

	Spry.Utils.addClassName(img, "inFocus");
	img.style.zIndex = 150;
	
	if (!img.spryID)
		img.spryID = ++this.nextImageID;

	var w = img.spryOrigWidth;
	var h = img.spryOrigHeight;

	var ratio = this.maxDimension / (w > h ? w : h);

	w *= ratio;
	h *= ratio;
	var x = (this.thumbWidth - w) / 2;
	var y = (this.thumbHeight - h) / 2;
	
	var self = this;
	this.sizeAndPosition(img, x, y, w, h, function(b){ self.behaviorsArray[img.spryID] = null; });
};

Spry.Widget.ThumbViewer.prototype.shrinkThumbnail = function(img)
{
	var self = this;
	Spry.Utils.addClassName(img, "inFocus");
	img.style.zIndex = 1;
	this.sizeAndPosition(img, 0, 0, this.thumbWidth, this.thumbHeight, function(b){self.behaviorsArray[img.spryID] = null; Spry.Utils.removeClassName(img, "inFocus");});
};

Spry.Widget.ThumbViewer.prototype.select = function(img)
{
	var imgs = this.getThumbImages();

	img = (typeof img == "number") ? imgs[img] : Spry.$(img);
	if (!img) return;

	if (this.currentImage)
		Spry.Utils.removeClassName(this.currentImage, "selectedThumbnail");
	Spry.Utils.addClassName(img, "selectedThumbnail");
	this.currentImage = img;

	var a = Spry.Utils.getAncestor(img, "a[href]");
	if (a)
	{
		this.notifyObservers("onSelect", a.href);
		if (img == imgs[0])
			this.notifyObservers("onFirstSelect", a.href);
		if (img == imgs[imgs.length - 1])
			this.notifyObservers("onLastSelect", a.href);
	}
};

Spry.Widget.ThumbViewer.prototype.previous = function()
{
	var img = this.currentImage;
	var imgs = this.getThumbImages();

	for (var i = 0; i < imgs.length; i++)
	{
		if (imgs[i] == img)
		{
			if (--i < 0) i = imgs.length - 1;
			this.select(imgs[i]);
			return;
		}
		prevImg = imgs[i];
	}
};

Spry.Widget.ThumbViewer.prototype.next = function()
{
	var img = this.currentImage;
	var imgs = this.getThumbImages();

	for (var i = 0; i < imgs.length; i++)
	{
		if (imgs[i] == img)
		{
			if (++i >= imgs.length) i = 0;
			this.select(imgs[i]);
			return;
		}
		prevImg = imgs[i];
	}
};

Spry.Widget.ThumbViewer.prototype.getThumbLinks = function()
{
	return Spry.$$(this.tnLinkSelector, this.element);
};

Spry.Widget.ThumbViewer.prototype.getThumbImages = function()
{
	return Spry.$$(this.tnImageSelector, this.element);
};

Spry.Widget.ThumbViewer.prototype.getCurrentThumbLink = function()
{
	return Spry.Utils.getAncestor(this.currentImage, "a[href]");
};

Spry.Widget.ThumbViewer.prototype.getCurrentThumbImage = function()
{
	return this.currentImage;
};

Spry.Widget.ThumbViewer.prototype.getThumbCount = function()
{
	return Spry.$$(this.tnImageSelector, this.element).length;
};

// 3820                            
// SpryImageViewer.js - version 0.1
//                                 

var Spry; if (!Spry) Spry = {}; if (!Spry.Widget) Spry.Widget = {};

Spry.Widget.ImageViewer = function(ele, options)
{
	Spry.Utils.Notifier.call(this);

	this.element = Spry.$(ele);
	this.imageSelector = "img";
	this.elementToResizeSelector = "*";
	
	this.currentEffect = null;
	this.currentLoader = null;
};

Spry.Widget.ImageViewer.prototype = new Spry.Utils.Notifier();
Spry.Widget.ImageViewer.prototype.constructor = Spry.Widget.ImageViewer;

Spry.Widget.ImageViewer.prototype.killLoader = function()
{
	if (this.currentLoader)
	{
		this.currentLoader.onload = null;
		this.currentLoader = null;
	}
};

Spry.Widget.ImageViewer.prototype.setImage = function(url)
{
	var img = Spry.$$(this.imageSelector, this.element)[0];
	if (!img) return;

	if (this.currentEffect)
	{
		this.currentEffect.stop();
		this.currentEffect = null;
	}

	this.killLoader();
	var loader = this.currentLoader = new Image;
	var self = this;

	this.notifyObservers("onPreUpdate", url);

	this.currentEffect = new Spry.Effect.Opacity(img, Spry.Effect.getOpacity(img), 0, { duration: 400,
		finish: function()
		{
			loader.onload = function()
			{
				var w = loader.width;
				var h = loader.height;

				var eleToResize = img;
				if (self.elementToResizeSelector)
					eleToResize = Spry.Utils.getAncestor(img, self.elementToResizeSelector);

				self.currentEffect = new Spry.Effect.Size(eleToResize, Spry.Effect.getDimensions(eleToResize), { width: w, height: h, units:"px"}, {duration: 400, finish: function()
				{
					img.src = loader.src;
					loader = null;
					self.currentEffect = new Spry.Effect.Opacity(img, 0, 1, { duration: 400,
						finish: function()
						{
							self.currentEffect = null;
							
							img.style.opacity = "";
							img.style.filter = "";

							self.notifyObservers("onPostUpdate", url);
						}});
					self.currentEffect.start();					
				}});
				self.currentEffect.start();
			};
			loader.src = url;
		}
	});
	this.currentEffect.start();
};

// 3903                                 
// SprySlideShowControl.js - version 0.1
//                                      

var Spry; if (!Spry) Spry = {}; if (!Spry.Widget) Spry.Widget = {};

Spry.Widget.SlideShowControl = function(ele, options)
{
	Spry.Utils.Notifier.call(this);

	this.element = Spry.$(ele);
	this.interval = 4000;
	this.timerID = 0;
	this.active = false;

	this.playBtnSelector = ".playBtn";
	this.prevBtnSelector = ".previousBtn";
	this.nextBtnSelector = ".nextBtn";
	this.firstBtnSelector = ".firstBtn";
	this.lastBtnSelector = ".lastBtn";

	this.playClass = "playBtn";
	this.pauseClass = "pauseBtn";

	var self = this;

	this.playBtn = Spry.$$(this.playBtnSelector, this.element)[0];
	if (this.playBtn)
		Spry.Utils.addEventListener(this.playBtn, "click", function(e){ self.toggle(); return false; }, false);

	this.prevBtn = Spry.$$(this.prevBtnSelector, this.element)[0];
	if (this.prevBtn)
		Spry.Utils.addEventListener(this.prevBtn, "click", function(e){ self.previous(); return false; }, false);

	this.nextBtn = Spry.$$(this.nextBtnSelector, this.element)[0];
	if (this.nextBtn)
		Spry.Utils.addEventListener(this.nextBtn, "click", function(e){ self.next(); return false; }, false);

	this.firstBtn = Spry.$$(this.firstBtnSelector, this.element)[0];
	if (this.firstBtn)
		Spry.Utils.addEventListener(this.firstBtn, "click", function(e){ self.first(); return false; }, false);

	this.lastBtn = Spry.$$(this.lastBtnSelector, this.element)[0];
	if (this.lastBtn)
		Spry.Utils.addEventListener(this.lastBtn, "click", function(e){ self.last(); return false; }, false);

};

Spry.Widget.SlideShowControl.prototype = new Spry.Utils.Notifier();
Spry.Widget.SlideShowControl.prototype.constructor = Spry.Widget.SlideShowControl;

Spry.Widget.SlideShowControl.prototype.isActive = function()
{
	return this.slideShowIsActive;
};

Spry.Widget.SlideShowControl.prototype.startTimer = function()
{
	this.killTimer();

	var self = this;
	this.timerID = setInterval(function(){ self.next(); }, this.interval);
};

Spry.Widget.SlideShowControl.prototype.killTimer = function()
{
	if (this.timerID)
		clearInterval(this.timerID);
	this.timerID = 0;
};

Spry.Widget.SlideShowControl.prototype.start = function()
{
	this.slideShowIsActive = true;

	if (this.playBtn)
	{
		Spry.Utils.removeClassName(this.playBtn, this.playClass);
		Spry.Utils.addClassName(this.playBtn, this.pauseClass);
	}

	this.startTimer();
	this.notifyObservers("onStart");
};

Spry.Widget.SlideShowControl.prototype.stop = function()
{
	this.slideShowIsActive = false;

	if (this.playBtn)
	{
		Spry.Utils.addClassName(this.playBtn, this.playClass);
		Spry.Utils.removeClassName(this.playBtn, this.pauseClass);
	}

	this.killTimer();
	this.notifyObservers("onStop");
};

Spry.Widget.SlideShowControl.prototype.toggle = function()
{
	if (this.slideShowIsActive)
		this.stop();
	else
		this.start();
};

Spry.Widget.SlideShowControl.prototype.previous = function(stopSlideShow)
{
	if (stopSlideShow)
		this.killTimer();
	this.notifyObservers("onPreviousSlide");
};

Spry.Widget.SlideShowControl.prototype.next = function(stopSlideShow)
{
	if (stopSlideShow)
		this.killTimer();
	this.notifyObservers("onNextSlide");
};

Spry.Widget.SlideShowControl.prototype.first = function(stopSlideShow)
{
	if (stopSlideShow)
		this.killTimer();
	this.notifyObservers("onFirstSlide");
};


Spry.Widget.SlideShowControl.prototype.last = function(stopSlideShow)
{
	if (stopSlideShow)
		this.killTimer();
	this.notifyObservers("onLastSlide");
};

// 4039                         
// gallery_init.js - version 0.1
//                              

// The values of these globals must correspond to the ids of markup that exist on the page.
var gThumbnailsContainerID = "thumbnails";
var gMainImageContainerID = "mainImageOutline";
var gSlideShowControlsContainerID = "transport";

var gThumbViewer = null;
var gImageViewer = null;
var gSlideShowControl = null;

function InitializeGallery()
{
	gThumbViewer = new Spry.Widget.ThumbViewer(gThumbnailsContainerID);

	gImageViewer = new Spry.Widget.ImageViewer(gMainImageContainerID);

	gSlideShowControl = new Spry.Widget.SlideShowControl(gSlideShowControlsContainerID);

	gThumbViewer.addObserver(function(notificationType, notifier, data)
	{
		if (notificationType == "onSelect")
			gImageViewer.setImage(data);
	});

	gImageViewer.addObserver(function(notificationType, notifier, data)
	{
		if (notificationType == "onPreUpdate")
			gSlideShowControl.killTimer();
		else if (notificationType == "onPostUpdate")
		{
			if (gSlideShowControl.isActive())
				gSlideShowControl.startTimer();
		}
	});

	gSlideShowControl.addObserver(function(notificationType, notifier, data)
	{
		if (notificationType == "onNextSlide")
			gThumbViewer.next();
		else if (notificationType == "onPreviousSlide")
			gThumbViewer.previous(true);
		else if (notificationType == "onFirstSlide")
			gThumbViewer.first(true);
		else if (notificationType == "onLastSlide")
			gThumbViewer.last(true);
	});

	Spry.$$("#thumbnails a, #transport .previousBtn, #transport .nextBtn").addEventListener("click", function(e) { gSlideShowControl.stop(); }, "false");

	var currentLink = gThumbViewer.getCurrentThumbLink();
	if (currentLink)
		gImageViewer.setImage(currentLink.href);
}

// 4096                      
// SpryData.js - version 0.45
//                           

var Spry; if (!Spry) Spry = {};

//
// Spry.Utils
//

if (!Spry.Utils) Spry.Utils = {};

Spry.Utils.msProgIDs = ["MSXML2.XMLHTTP.6.0", "MSXML2.XMLHTTP.3.0"];

Spry.Utils.createXMLHttpRequest = function()
{
	var req = null;
	try
	{

		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);
			}
		}

		if (!req && window.XMLHttpRequest)
			req = new XMLHttpRequest();
	}
	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)
	{
		if (req.errorCallback)
			req.errorCallback(req);
		else
			Spry.Debug.reportError("Exception caught while loading " + url + ": " + e);
		req = null;
	}

	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 = Spry.$(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)
{
	Spry.Utils.loadURL("GET", url, true, function(req)
	{
		Spry.Utils.setInnerHTML(ele, req.xhRequest.responseText);
		if (finishFunc)
			finishFunc(ele, url);
	}, opts);
};

//
// Functions from SpryDOMUtils.js
// - These have been left in for backwards compatibility, but they should only be defined if Spry.$$ (SpryDOMUtils.js) is not already included.
// - If SpryDOMUtils.js is included *after* SpryData.js, these functions will be replaced with the latest versions in SpryDOMUtils.js.
//

if (!Spry.$$)
{
Spry.Utils.addEventListener = function(element, eventType, handler, capture)
{
	try
	{
		element = Spry.$(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 = Spry.$(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.addClassName = function(ele, className)
{
	ele = Spry.$(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 = Spry.$(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.getObjectByName = function(name)
{
	var result = null;
	if (name)
	{
		var lu = window;
		var objPath = name.split(".");
		for (var i = 0; lu && i < objPath.length; i++)
		{
			result = lu[objPath[i]];
			lu = result;
		}
	}
	return result;
};

//
// Define Prototype's $() convenience function, but make sure it is namespaced under Spry so that we avoid collisions with other toolkits.
//

Spry.$ = function(element)
{
	if (arguments.length > 1)
	{
		for (var i = 0, elements = [], length = arguments.length; i < length; i++)
			elements.push(Spry.$(arguments[i]));
		return elements;
	}
	if (typeof element == 'string')
		element = document.getElementById(element);
	return element;
};
} 

Spry.Utils.eval = function(str)
{

	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
	{
		str = str.replace(/&lt;/gi, "<");
		str = str.replace(/&gt;/gi, ">");
		str = str.replace(/&quot;/gi, "\"");
		str = str.replace(/&amp;/gi, "&");
	}
	return str;
};

Spry.Utils.fixupIETagAttributes = function(inStr)
{
	var outStr = "";

	var tagStart = inStr.match(/^<[^\s>]+\s*/)[0];
	var tagEnd = inStr.match(/\s*\/?>$/)[0];
	var tagAttrs = inStr.replace(/^<[^\s>]+\s*|\s*\/?>/g, "");

	outStr += tagStart;

	if (tagAttrs)
	{
		var startIndex = 0;
		var endIndex = 0;

		while (startIndex < tagAttrs.length)
		{
			while (tagAttrs.charAt(endIndex) != '=' && endIndex < tagAttrs.length)
				++endIndex;

			if (endIndex >= tagAttrs.length)
			{
				outStr += tagAttrs.substring(startIndex, endIndex);
				break;
			}

			++endIndex;
			outStr += tagAttrs.substring(startIndex, endIndex);
			startIndex = endIndex;

			if (tagAttrs.charAt(endIndex) == '"' || tagAttrs.charAt(endIndex) == "'")
			{
				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
			{

				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;

	return outStr;
};

Spry.Utils.fixUpIEInnerHTML = function(inStr)
{
	var outStr = "";

	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)
		{
			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
	{

		var xmlDOMObj = new ActiveXObject("Microsoft.XMLDOM");
		xmlDOMObj.async = false;
		xmlDOMObj.loadXML(str);
		xmlDoc = xmlDOMObj;
	}
	catch (e)
	{

		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)
{

	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.getFirstChildWithNodeName = function(node, nodeName)
{
	var child = node.firstChild;

	while (child)
	{
		if (child.nodeName == nodeName)
			return child;
		child = child.nextSibling;
	}

	return null;
};

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)
	{

		this.clearSelection();
	}
	else
	{

		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;
			}
		}
	}

	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();
};

Spry.Utils.Notifier = function()
{
	this.observers = [];
	this.suppressNotifications = 0;
};

Spry.Utils.Notifier.prototype.addObserver = function(observer)
{
	if (!observer)
		return;

	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.onloadDidFire = false;

Spry.Utils.addLoadListener(function() { Spry.Debug.onloadDidFire = true; Spry.Debug.flushQueuedMessages(); });

Spry.Debug.flushQueuedMessages = function()
{
	if (Spry.Debug.flushQueuedMessages.msgs)
	{
		var msgs = Spry.Debug.flushQueuedMessages.msgs;
		for (var i = 0; i < msgs.length; i++)
			Spry.Debug.debugOut(msgs[i].msg, msgs[i].color);
		Spry.Debug.flushQueuedMessages.msgs = null;
	}
};

Spry.Debug.createDebugWindow = function()
{
	if (!Spry.Debug.enableTrace || Spry.Debug.debugWindow || !Spry.Debug.onloadDidFire)
		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)
		{
			if (!Spry.Debug.flushQueuedMessages.msgs)
				Spry.Debug.flushQueuedMessages.msgs = new Array;
			Spry.Debug.flushQueuedMessages.msgs.push({msg: str, color: bgColor});
			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.initRegionsOnLoad = true;

Spry.Data.initRegions = function(rootNode)
{
	rootNode = rootNode ? Spry.$(rootNode) : document.body;

	var lastRegionFound = null;

	var regions = Spry.Utils.getNodesByFunc(rootNode, function(node)
	{
		try
		{
			if (node.nodeType != 1 /* Node.ELEMENT_NODE */)
				return false;

			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)
					{
						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;
	var newRegions = [];

	for (i = 0; i < regions.length; i++)
	{
		var rgn = regions[i];

		var isDetailRegion = false;

		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;
		}

		rgn.attributes.removeNamedItem(attr.nodeName);

		Spry.Utils.removeClassName(rgn, Spry.Data.Region.hiddenRegionClassName);

		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 = {};

		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;
		attr = rgn.attributes.getNamedItem("spry:expiredstate");
		if (attr && attr.value)
			regionStateMap["expired"] = attr.value;

		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 (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;
		});

		dataStr = rgn.innerHTML;

		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)
		{
			rgn.innerHTML = "";
		}

		var region = new Spry.Data.Region(rgn, name, isDetailRegion, dataStr, dataSets, regionStates, regionStateMap, hasBehaviorAttributes);
		Spry.Data.regionsArray[region.name] = region;
		newRegions.push(region);
	}

	for (var i = 0; i < newRegions.length; i++)
		newRegions[i].updateContent();
};

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.getDataSetByName = function(dataSetName)
{

	var ds = window[dataSetName];
	if (typeof ds != "object" || !ds.getData || !ds.filter)
		return null;
	return ds;
};

//
// Spry.Data.DataSet
//

Spry.Data.DataSet = function(options)
{
	Spry.Utils.Notifier.call(this);

	this.name = "";
	this.internalID = Spry.Data.DataSet.nextDataSetID++;
	this.curRowID = 0;
	this.data = [];
	this.unfilteredData = null;
	this.dataHash = {};
	this.columnTypes = {};
	this.filterFunc = null;
	this.filterDataFunc = null;

	this.distinctOnLoad = false;
	this.distinctFieldsOnLoad = null;
	this.sortOnLoad = null;
	this.sortOrderOnLoad = "ascending";
	this.keepSorted = false;

	this.dataWasLoaded = false;
	this.pendingRequest = null;

	this.lastSortColumns = [];
	this.lastSortOrder = "";

	this.loadIntervalID = 0;

	Spry.Utils.setOptions(this, options);
};

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()
{
	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.getValue = function(valueName, rowContext)
{
	var result = undefined;

	if (!rowContext)
		rowContext = this.getCurrentRow();

	switch(valueName)
	{
		case "ds_RowNumber":
			result = this.getRowNumber(rowContext);
			break;
		case "ds_RowNumberPlus1":
			result = this.getRowNumber(rowContext) + 1;
			break;
		case "ds_RowCount":
			result = this.getRowCount();
			break;
		case "ds_UnfilteredRowCount":
			result = this.getRowCount(true);
			break;
		case "ds_CurrentRowNumber":
			result = this.getCurrentRowNumber();
			break;
		case "ds_CurrentRowID":
			result = this.getCurrentRowID();
			break;
		case "ds_EvenOddRow":
			result = (this.getRowNumber(rowContext) % 2) ? Spry.Data.Region.evenRowClassName : Spry.Data.Region.oddRowClassName;
			break;
		case "ds_SortOrder":
			result = this.getSortOrder();
			break;
		case "ds_SortColumn":
			result = this.getSortColumn();
			break;
		default:
			if (rowContext)
				result = rowContext[valueName];
			break;
	}

	return result;
};

Spry.Data.DataSet.prototype.setDataFromArray = function(arr, fireSyncLoad)
{
	this.notifyObservers("onPreLoad");

	this.unfilteredData = null;
	this.filteredData = null;
	this.data = [];
	this.dataHash = {};

	var arrLen = arr.length;

	for (var i = 0; i < arrLen; i++)
	{
		var row = arr[i];
		if (row.ds_RowID == undefined)
			row.ds_RowID = i;
		this.dataHash[row.ds_RowID] = row;
		this.data.push(row);
	}

	this.loadData(fireSyncLoad);
};

Spry.Data.DataSet.prototype.loadData = function(syncLoad)
{

	var self = this;

	this.pendingRequest = new Object;
	this.dataWasLoaded = false;

	var loadCallbackFunc = function()
	{
		self.pendingRequest = null;
		self.dataWasLoaded = true;

		self.applyColumnTypes();

		self.disableNotifications();
		self.filterAndSortData();
		self.enableNotifications();

		self.notifyObservers("onPostLoad");
		self.notifyObservers("onDataChanged");
	};

	if (syncLoad)
		loadCallbackFunc();
	else
		this.pendingRequest.timer = setTimeout(loadCallbackFunc, 0);
};


Spry.Data.DataSet.prototype.filterAndSortData = function()
{

	if (this.filterDataFunc)
		this.filterData(this.filterDataFunc, true);

	if (this.distinctOnLoad)
		this.distinct(this.distinctFieldsOnLoad);

	if (this.keepSorted && this.getSortColumn())
		this.sort(this.lastSortColumns, this.lastSortOrder);
	else if (this.sortOnLoad)
		this.sort(this.sortOnLoad, this.sortOrderOnLoad);

	if (this.filterFunc)
		this.filter(this.filterFunc, true);

	if (this.data && this.data.length > 0)
		this.curRowID = this.data[0]['ds_RowID'];
	else
		this.curRowID = 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, unfiltered)
{
	if (row)
	{
		var rows = this.getData(unfiltered);
		if (rows && rows.length)
		{
			var numRows = rows.length;
			for (var i = 0; i < numRows; i++)
			{
				if (rows[i] == row)
					return i;
			}
		}
	}
	return -1;
};

Spry.Data.DataSet.prototype.getCurrentRowNumber = function()
{
	return this.getRowNumber(this.getCurrentRow());
};

Spry.Data.DataSet.prototype.getCurrentRowID = function()
{
	return this.curRowID;
};

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(columnNames, columnType)
{
	if (columnNames)
	{
		if (typeof columnNames == "string")
			columnNames = [ columnNames ];
		for (var i = 0; i < columnNames.length; i++)
			this.columnTypes[columnNames[i]] = columnType;
	}
};

Spry.Data.DataSet.prototype.getColumnType = function(columnName)
{
	if (this.columnTypes[columnName])
		return this.columnTypes[columnName];
	return "string";
};

Spry.Data.DataSet.prototype.applyColumnTypes = function()
{
	var rows = this.getData(true);
	var numRows = rows.length;
	var colNames = [];

	if (numRows < 1)
		return;

	for (var cname in this.columnTypes)
	{
		var ctype = this.columnTypes[cname];
		if (ctype != "string")
		{
			for (var i = 0; i < numRows; i++)
			{
				var row = rows[i];
				var val = row[cname];
				if (val != undefined)
				{
					if (ctype == "number")
						row[cname] = new Number(val);
					else if (ctype == "html")
						row[cname] = Spry.Utils.decodeEntities(val);
				}
			}
		}
	}
};

Spry.Data.DataSet.prototype.distinct = function(columnNames)
{
	if (this.data)
	{
		var oldData = this.data;
		this.data = [];
		this.dataHash = {};
		var dataChanged = false;

		var alreadySeenHash = {};
		var i = 0;

		var keys = [];

		if (typeof columnNames == "string")
			keys = [columnNames];
		else if (columnNames)
			keys = columnNames;
		else
			for (var recField in oldData[0])
				keys[i++] = recField;

		for (var i = 0; i < oldData.length; i++)
		{
			var rec = oldData[i];
			var hashStr = "";
			for (var j=0; j < keys.length; j++)
			{
				recField = keys[j];
				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;
			}
			else
				dataChanged = true;
		}
		if (dataChanged)
			this.notifyObservers('onDataChanged');
	}
};

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)
{

	if (!columnNames)
		return;

	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);
	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)
			{
				a = a[prop]; b = b[prop];
				if (a == undefined || b == undefined)
					return (a == b) ? 0 : (a ? 1 : -1);
				return a-b;
			};
		else
			sortfunc = function(a, b)
			{
				a = a[prop]; b = b[prop];
				if (a == undefined || b == undefined)
					return (a == b) ? 0 : (a ? -1 : 1);
				return b-a;
			};
	}
	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
			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
	{
		if (order == "ascending")
			sortfunc = function(a, b){
				a = a[prop];
				b = b[prop];
				if (a == undefined || b == undefined)
					return (a == b) ? 0 : (a ? 1 : -1);
				var tA = a.toString();
				var tB = b.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;
				return -1;
			};
		else
			sortfunc = function(a, b){
				a = a[prop];
				b = b[prop];
				if (a == undefined || b == undefined)
					return (a == b) ? 0 : (a ? -1 : 1);
				var tA = a.toString();
				var tB = b.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;
				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)
{

	var dataChanged = false;

	if (!filterFunc)
	{

		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)
{

	var dataChanged = false;

	if (!filterFunc)
	{
		if (this.filterFunc && this.unfilteredData)
		{

			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.HTTPSourceDataSet
// base class for any DataSet that uses external
//

Spry.Data.HTTPSourceDataSet = function(dataSetURL, dataSetOptions)
{

	Spry.Data.DataSet.call(this);

	this.url = dataSetURL;
	this.dataSetsForDataRefStrings = new Array;
	this.hasDataRefStrings = false;
	this.useCache = true;

	this.setRequestInfo(dataSetOptions, true);

	Spry.Utils.setOptions(this, dataSetOptions, true);

	this.recalculateDataSetDependencies();

	if (this.loadInterval > 0)
		this.startLoadInterval(this.loadInterval);
};

Spry.Data.HTTPSourceDataSet.prototype = new Spry.Data.DataSet();
Spry.Data.HTTPSourceDataSet.prototype.constructor = Spry.Data.HTTPSourceDataSet;

Spry.Data.HTTPSourceDataSet.prototype.setRequestInfo = function(requestInfo, undefineRequestProps)
{

	this.requestInfo = new Spry.Utils.loadURL.Request();
	this.requestInfo.extractRequestOptions(requestInfo, undefineRequestProps);

	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.Data.HTTPSourceDataSet.prototype.recalculateDataSetDependencies = function()
{
	this.hasDataRefStrings = false;

	var i = 0;
	for (i = 0; i < this.dataSetsForDataRefStrings.length; i++)
	{
		var ds = this.dataSetsForDataRefStrings[i];
		if (ds)
			ds.removeObserver(this);
	}

	this.dataSetsForDataRefStrings = new Array();

	var regionStrs = this.getDataRefStrings();

	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])
				{
					ds = Spry.Data.getDataSetByName(dsName);
					if (dsName && ds)
					{

						this.dataSetsForDataRefStrings[dsName] = ds;
						this.dataSetsForDataRefStrings[dsCount++] = ds;
						this.hasDataRefStrings = true;
					}
				}
			}
		}
	}

	for (i = 0; i < this.dataSetsForDataRefStrings.length; i++)
	{
		var ds = this.dataSetsForDataRefStrings[i];
		ds.addObserver(this);
	}
};

Spry.Data.HTTPSourceDataSet.prototype.getDataRefStrings = function()
{
	var strArr = [];
	if (this.url) strArr.push(this.url);
	if (this.requestInfo && this.requestInfo.postData) strArr.push(this.requestInfo.postData);
	return strArr;
};

Spry.Data.HTTPSourceDataSet.prototype.attemptLoadData = function()
{
	for (var i = 0; i < this.dataSetsForDataRefStrings.length; i++)
	{
		var ds = this.dataSetsForDataRefStrings[i];
		if (ds.getLoadDataRequestIsPending() || !ds.getDataWasLoaded())
			return;
	}

	this.loadData();
};

Spry.Data.HTTPSourceDataSet.prototype.onCurrentRowChanged = function(ds, data)
{
	this.attemptLoadData();
};

Spry.Data.HTTPSourceDataSet.prototype.onPostSort = function(ds, data)
{
	this.attemptLoadData();
};

Spry.Data.HTTPSourceDataSet.prototype.onDataChanged = function(ds, data)
{
	this.attemptLoadData();
};

Spry.Data.HTTPSourceDataSet.prototype.loadData = function()
{
	if (!this.url)
		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())
			{
				ds.loadData();
				allDataSetsReady = false;
			}
		}

		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;

	var req = this.requestInfo.clone();
	req.url = url;
	req.postData = postData;

	this.pendingRequest = new Object;
	this.pendingRequest.data = Spry.Data.HTTPSourceDataSet.LoadManager.loadData(req, this, this.useCache);
};

Spry.Data.HTTPSourceDataSet.prototype.cancelLoadData = function()
{
	if (this.pendingRequest)
	{
		Spry.Data.HTTPSourceDataSet.LoadManager.cancelLoadData(this.pendingRequest.data, this);
		this.pendingRequest = null;
	}
};

Spry.Data.HTTPSourceDataSet.prototype.getURL = function() { return this.url; };
Spry.Data.HTTPSourceDataSet.prototype.setURL = function(url, requestOptions)
{
	if (this.url == url)
	{

		if (!requestOptions || (this.requestInfo.method == requestOptions.method && (requestOptions.method != "POST" || this.requestInfo.postData == requestOptions.postData)))
			return;
	}

	this.url = url;

	this.setRequestInfo(requestOptions);

	this.cancelLoadData();
	this.recalculateDataSetDependencies();
	this.dataWasLoaded = false;
};

Spry.Data.HTTPSourceDataSet.prototype.setDataFromDoc = function(rawDataDoc)
{
	this.pendingRequest = null;

	this.loadDataIntoDataSet(rawDataDoc);
	this.applyColumnTypes();

	this.disableNotifications();
	this.filterAndSortData();
	this.enableNotifications();

	this.notifyObservers("onPostLoad");
	this.notifyObservers("onDataChanged");
};

Spry.Data.HTTPSourceDataSet.prototype.loadDataIntoDataSet = function(rawDataDoc)
{

	this.dataHash = new Object;
	this.data = new Array;
	this.dataWasLoaded = true;
};

Spry.Data.HTTPSourceDataSet.prototype.xhRequestProcessor = function(xhRequest)
{

	var resp = xhRequest.responseText;

	if (xhRequest.status == 200 || xhRequest.status == 0)
		return resp;
	return null;
};

Spry.Data.HTTPSourceDataSet.prototype.sessionExpiredChecker = function(req)
{
	if (req.xhRequest.responseText == 'session expired')
		return true;
	return false;
};

Spry.Data.HTTPSourceDataSet.prototype.setSessionExpiredChecker = function(checker)
{
	this.sessionExpiredChecker = checker;
};


Spry.Data.HTTPSourceDataSet.prototype.onRequestResponse = function(cachedRequest, req)
{
	this.setDataFromDoc(cachedRequest.rawData);
};

Spry.Data.HTTPSourceDataSet.prototype.onRequestError = function(cachedRequest, req)
{
	this.notifyObservers("onLoadError", req);
};

Spry.Data.HTTPSourceDataSet.prototype.onRequestSessionExpired = function(cachedRequest, req)
{
	this.notifyObservers("onSessionExpired", req);
};


Spry.Data.HTTPSourceDataSet.LoadManager = {};
Spry.Data.HTTPSourceDataSet.LoadManager.cache = [];

Spry.Data.HTTPSourceDataSet.LoadManager.CachedRequest = function(reqInfo, xhRequestProcessor, sessionExpiredChecker)
{
	Spry.Utils.Notifier.call(this);

	this.reqInfo = reqInfo;
	this.rawData = null;
	this.timer = null;
	this.state = Spry.Data.HTTPSourceDataSet.LoadManager.CachedRequest.NOT_LOADED;
	this.xhRequestProcessor = xhRequestProcessor;
	this.sessionExpiredChecker = sessionExpiredChecker;
};

Spry.Data.HTTPSourceDataSet.LoadManager.CachedRequest.prototype = new Spry.Utils.Notifier();
Spry.Data.HTTPSourceDataSet.LoadManager.CachedRequest.prototype.constructor = Spry.Data.HTTPSourceDataSet.LoadManager.CachedRequest;

Spry.Data.HTTPSourceDataSet.LoadManager.CachedRequest.NOT_LOADED      = 1;
Spry.Data.HTTPSourceDataSet.LoadManager.CachedRequest.LOAD_REQUESTED  = 2;
Spry.Data.HTTPSourceDataSet.LoadManager.CachedRequest.LOAD_FAILED     = 3;
Spry.Data.HTTPSourceDataSet.LoadManager.CachedRequest.LOAD_SUCCESSFUL = 4;

Spry.Data.HTTPSourceDataSet.LoadManager.CachedRequest.prototype.loadDataCallback = function(req)
{
	if (req.xhRequest.readyState != 4)
		return;

	var rawData = null;
	if (this.xhRequestProcessor) rawData = this.xhRequestProcessor(req.xhRequest);

	if (this.sessionExpiredChecker)
	{
		Spry.Utils.setOptions(req, {'rawData': rawData}, false);
		if (this.sessionExpiredChecker(req))
		{
			this.state = Spry.Data.HTTPSourceDataSet.LoadManager.CachedRequest.LOAD_FAILED;
			this.notifyObservers("onRequestSessionExpired", req);
			this.observers.length = 0;
			return;
		}
	}

	if (!rawData)
	{
		this.state = Spry.Data.HTTPSourceDataSet.LoadManager.CachedRequest.LOAD_FAILED;
		this.notifyObservers("onRequestError", req);
		this.observers.length = 0;
		return;
	}

	this.rawData = rawData;
	this.state = Spry.Data.HTTPSourceDataSet.LoadManager.CachedRequest.LOAD_SUCCESSFUL;

	this.notifyObservers("onRequestResponse", req);

	this.observers.length = 0;
};

Spry.Data.HTTPSourceDataSet.LoadManager.CachedRequest.prototype.loadData = function()
{

	var self = this;
	this.cancelLoadData();
	this.rawData = null;
	this.state = Spry.Data.HTTPSourceDataSet.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.HTTPSourceDataSet.LoadManager.CachedRequest.prototype.cancelLoadData = function()
{
	if (this.state == Spry.Data.HTTPSourceDataSet.LoadManager.CachedRequest.LOAD_REQUESTED)
	{
		if (this.timer)
		{
			this.timer.clearTimeout();
			this.timer = null;
		}

		this.rawData = null;
		this.state = Spry.Data.HTTPSourceDataSet.LoadManager.CachedRequest.NOT_LOADED;
	}
};

Spry.Data.HTTPSourceDataSet.LoadManager.getCacheKey = function(reqInfo)
{
	return reqInfo.method + "::" + reqInfo.url + "::" + reqInfo.postData + "::" + reqInfo.username;
};

Spry.Data.HTTPSourceDataSet.LoadManager.loadData = function(reqInfo, ds, useCache)
{
	if (!reqInfo)
		return null;

	var cacheObj = null;
	var cacheKey = null;

	if (useCache)
	{
		cacheKey = Spry.Data.HTTPSourceDataSet.LoadManager.getCacheKey(reqInfo);
		cacheObj = Spry.Data.HTTPSourceDataSet.LoadManager.cache[cacheKey];
	}

	if (cacheObj)
	{
		if (cacheObj.state == Spry.Data.HTTPSourceDataSet.LoadManager.CachedRequest.LOAD_REQUESTED)
		{
			if (ds)
				cacheObj.addObserver(ds);
			return cacheObj;
		}
		else if (cacheObj.state == Spry.Data.HTTPSourceDataSet.LoadManager.CachedRequest.LOAD_SUCCESSFUL)
		{
			if (ds)
				setTimeout(function() { ds.setDataFromDoc(cacheObj.rawData); }, 0);
			return cacheObj;
		}
	}

	if (!cacheObj)
	{
		cacheObj = new Spry.Data.HTTPSourceDataSet.LoadManager.CachedRequest(reqInfo, (ds ? ds.xhRequestProcessor : null), (ds ? ds.sessionExpiredChecker : null));

		if (useCache)
		{
			Spry.Data.HTTPSourceDataSet.LoadManager.cache[cacheKey] = cacheObj;

			cacheObj.addObserver({ onRequestError: function() { Spry.Data.HTTPSourceDataSet.LoadManager.cache[cacheKey] = undefined; }});
		}
	}

	if (ds)
		cacheObj.addObserver(ds);

	cacheObj.loadData();

	return cacheObj;
};

Spry.Data.HTTPSourceDataSet.LoadManager.cancelLoadData = function(cacheObj, ds)
{
	if (cacheObj)
	{
		if (ds)
			cacheObj.removeObserver(ds);
		else
			cacheObj.cancelLoadData();
	}
};

//
// Spry.Data.XMLDataSet
//

Spry.Data.XMLDataSet = function(dataSetURL, dataSetPath, dataSetOptions)
{

	this.xpath = dataSetPath;
	this.doc = null;
	this.subPaths = [];
	this.entityEncodeStrings = true;

	Spry.Data.HTTPSourceDataSet.call(this, dataSetURL, dataSetOptions);


	var jwType = typeof this.subPaths;
	if (jwType == "string" || (jwType == "object" && this.subPaths.constructor != Array))
		this.subPaths = [ this.subPaths ];
};

Spry.Data.XMLDataSet.prototype = new Spry.Data.HTTPSourceDataSet();
Spry.Data.XMLDataSet.prototype.constructor = Spry.Data.XMLDataSet;


Spry.Data.XMLDataSet.prototype.getDataRefStrings = function()
{
	var strArr = [];
	if (this.url) strArr.push(this.url);
	if (this.xpath) strArr.push(this.xpath);
	if (this.requestInfo && this.requestInfo.postData) strArr.push(this.requestInfo.postData);
	return strArr;
};

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.notifyObservers("onPreLoad");
			this.setDataFromDoc(this.doc);
		}
	}
};

Spry.Data.XMLDataSet.nodeContainsElementNode = function(node)
{
	if (node)
	{
		node = node.firstChild;

		while (node)
		{
			if (node.nodeType == 1 /* Node.ELEMENT_NODE */)
				return true;

			node = node.nextSibling;
		}
	}
	return false;
};

Spry.Data.XMLDataSet.getNodeText = function(node, encodeText, encodeCData)
{
	var txt = "";

	if (!node)
		return;

	try
	{
		var child = node.firstChild;

		while (child)
		{
			try
			{
				if (child.nodeType == 3 /* TEXT_NODE */)
					txt += encodeText ? Spry.Utils.encodeEntities(child.data) : child.data;
				else if (child.nodeType == 4 /* CDATA_SECTION_NODE */)
					txt += encodeCData ? Spry.Utils.encodeEntities(child.data) : child.data;
			} catch (e) { Spry.Debug.reportError("Spry.Data.XMLDataSet.getNodeText() exception caught: " + e + "\n"); }

			child = child.nextSibling;
		}
	}
	catch (e) { Spry.Debug.reportError("Spry.Data.XMLDataSet.getNodeText() exception caught: " + e + "\n"); }

	return txt;
};

Spry.Data.XMLDataSet.createObjectForNode = function(node, encodeText, encodeCData)
{
	if (!node)
		return null;

	var obj = new Object();
	var i = 0;
	var attr = null;

	try
	{
		for (i = 0; i < node.attributes.length; i++)
		{
			attr = node.attributes[i];
			if (attr && attr.nodeType == 2 /* Node.ATTRIBUTE_NODE */)
				obj["@" + attr.name] = attr.value;
		}
	}
	catch (e)
	{
		Spry.Debug.reportError("Spry.Data.XMLDataSet.createObjectForNode() caught exception while accessing attributes: " + e + "\n");
	}

	var child = node.firstChild;

	if (child && !child.nextSibling && child.nodeType != 1 /* Node.ELEMENT_NODE */)
	{

		obj[node.nodeName] = Spry.Data.XMLDataSet.getNodeText(node, encodeText, encodeCData);
	}

	while (child)
	{

		if (child.nodeType == 1 /* Node.ELEMENT_NODE */)
		{
			if (!Spry.Data.XMLDataSet.nodeContainsElementNode(child))
			{
				obj[child.nodeName] = Spry.Data.XMLDataSet.getNodeText(child, encodeText, encodeCData);

				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.Data.XMLDataSet.createObjectForNode() caught exception while accessing attributes: " + e + "\n");
				}
			}
		}

		child = child.nextSibling;
	}

	return obj;
};

Spry.Data.XMLDataSet.getRecordSetFromXMLDoc = function(xmlDoc, path, suppressColumns, entityEncodeStrings)
{
	if (!xmlDoc || !path)
		return null;

	var recordSet = new Object();
	recordSet.xmlDoc = xmlDoc;
	recordSet.xmlPath = path;
	recordSet.dataHash = new Object;
	recordSet.data = new Array;
	recordSet.getData = function() { return this.data; };

	var ctx = new ExprContext(xmlDoc);
	var pathExpr = xpathParse(path);
	var e = pathExpr.evaluate(ctx);

	var nodeArray = e.nodeSetValue();

	var isDOMNodeArray = true;

	if (nodeArray && nodeArray.length > 0)
		isDOMNodeArray = nodeArray[0].nodeType != 2 /* Node.ATTRIBUTE_NODE */;

	var nextID = 0;

	var encodeText = true;
	var encodeCData = false;
	if (typeof entityEncodeStrings == "boolean")
		encodeText = encodeCData = entityEncodeStrings;

	for (var i = 0; i < nodeArray.length; i++)
	{
		var rowObj = null;

		if (suppressColumns)
			rowObj = new Object;
		else
		{
			if (isDOMNodeArray)
				rowObj = Spry.Data.XMLDataSet.createObjectForNode(nodeArray[i], encodeText, encodeCData);
			else
			{
				rowObj = new Object;
				rowObj["@" + nodeArray[i].name] = nodeArray[i].value;
			}
		}

		if (rowObj)
		{

			rowObj['ds_RowID'] = nextID++;
			rowObj['ds_XMLNode'] = nodeArray[i];
			recordSet.dataHash[rowObj['ds_RowID']] = rowObj;
			recordSet.data.push(rowObj);
		}
	}

	return recordSet;
};

Spry.Data.XMLDataSet.PathNode = function(path)
{
	this.path = path;
	this.subPaths = [];
	this.xpath = "";
};

Spry.Data.XMLDataSet.PathNode.prototype.addSubPath = function(path)
{
	var node = this.findSubPath(path);
	if (!node)
	{
		node = new Spry.Data.XMLDataSet.PathNode(path);
		this.subPaths.push(node);
	}
	return node;
};

Spry.Data.XMLDataSet.PathNode.prototype.findSubPath = function(path)
{
	var numSubPaths = this.subPaths.length;
	for (var i = 0; i < numSubPaths; i++)
	{

		var subPath = this.subPaths[i];
		if (path == subPath.path)
			return subPath;
	}
	return null;
};

Spry.Data.XMLDataSet.PathNode.prototype.consolidate = function()
{

	var numSubPaths = this.subPaths.length;
	if (!this.xpath && numSubPaths == 1)
	{
		var subPath = this.subPaths[0];
		this.path += ((subPath[0] != "/") ? "/" : "") + subPath.path;
		this.xpath = subPath.xpath;
		this.subPaths = subPath.subPaths;
		this.consolidate();
		return;
	}

	for (var i = 0; i < numSubPaths; i++)
		this.subPaths[i].consolidate();
};

/* This method is commented out so that it gets stripped when the file is minimized. Please do not remove this from the full version of the file! It is needed for debugging.

Spry.Data.XMLDataSet.PathNode.prototype.dump = function(indentStr)
{
	var didPre = false;
	var result = "";
	if (!indentStr)
	{
		indentStr = "";
		didPre = true;
		result = "<pre>";
	}
	result += indentStr + "<strong>" + this.path + "</strong>" + (this.xpath ? " <em>-- xpath(" + Spry.Utils.encodeEntities(this.xpath) + ")</em>" : "") + "\n";
	var numSubPaths = this.subPaths.length;
	indentStr += "    ";
	for (var i = 0; i < numSubPaths; i++)
		result += this.subPaths[i].dump(indentStr);
	if (didPre)
		result += "</pre>";
	return result;
};
*/

Spry.Data.XMLDataSet.prototype.convertXPathsToPathTree = function(xpathArray)
{
	var xpaLen = xpathArray.length;
	var root = new Spry.Data.XMLDataSet.PathNode("");

	for (var i = 0; i < xpaLen; i++)
	{

		var xpath = xpathArray[i];
		var cleanXPath = xpath.replace(/\/\//g, "/__SPRYDS__");
		cleanXPath = cleanXPath.replace(/^\//, "");
		var pathItems = cleanXPath.split(/\//);
		var pathItemsLen = pathItems.length;

		var node = root;
		for (var j = 0; j < pathItemsLen; j++)
		{

			var path = pathItems[j].replace(/__SPRYDS__/, "//");
			node = node.addSubPath(path);
		}

		node.xpath = xpath;
	}

	root.consolidate();
	return root;
};

Spry.Data.XMLDataSet.prototype.flattenSubPaths = function(rs, subPaths)
{
	if (!rs || !subPaths)
		return;

	var numSubPaths = subPaths.length;
	if (numSubPaths < 1)
		return;

	var data = rs.data;
	var dataHash = {};

	var xpathArray = [];
	var cleanedXPathArray = [];

	for (var i = 0; i < numSubPaths; i++)
	{

		var subPath = subPaths[i];
		if (typeof subPath == "object")
			subPath = subPath.path;
		if (!subPath)
			subPath = "";

		xpathArray[i] = Spry.Data.Region.processDataRefString(null, subPath, this.dataSetsForDataRefStrings);

		cleanedXPathArray[i] = xpathArray[i].replace(/\[.*\]/g, "");
	}

	var row;
	var numRows = data.length;
	var newData = [];

	for (var i = 0; i < numRows; i++)
	{
		row = data[i];
		var newRows = [ row ];

		for (var j = 0; j < numSubPaths; j++)
		{
			var newRS = Spry.Data.XMLDataSet.getRecordSetFromXMLDoc(row.ds_XMLNode, xpathArray[j], (subPaths[j].xpath ? false : true), this.entityEncodeStrings);

			if (newRS && newRS.data && newRS.data.length)
			{
				if (typeof subPaths[j] == "object" && subPaths[j].subPaths)
				{

					var sp = subPaths[j].subPaths;
					spType = typeof sp;
					if (spType == "string")
						sp = [ sp ];
					else if (spType == "object" && spType.constructor == Object)
						sp = [ sp ];

					this.flattenSubPaths(newRS, sp);
				}

				var newRSData = newRS.data;
				var numRSRows = newRSData.length;

				var cleanedXPath = cleanedXPathArray[j] + "/";

				var numNewRows = newRows.length;
				var joinedRows = [];

				for (var k = 0; k < numNewRows; k++)
				{
					var newRow = newRows[k];

					for (var l = 0; l < numRSRows; l++)
					{

						var newRowObj = new Object;
						var newRSRow = newRSData[l];

						for (prop in newRow)
							newRowObj[prop] = newRow[prop];

						for (var prop in newRSRow)
						{
							var newPropName = cleanedXPath + prop;

							if (cleanedXPath == (prop + "/") || cleanedXPath.search(new RegExp("\\/" + prop + "\\/$")) != -1)
								newPropName = cleanedXPathArray[j];

							newRowObj[newPropName] = newRSRow[prop];
						}

						joinedRows.push(newRowObj);
					}
				}

				newRows = joinedRows;
			}
		}

		newData = newData.concat(newRows);
	}

	data = newData;
	numRows = data.length;

	for (i = 0; i < numRows; i++)
	{
		row = data[i];
		row.ds_RowID = i;
		dataHash[row.ds_RowID] = row;
	}

	rs.data = data;
	rs.dataHash = dataHash;
};

Spry.Data.XMLDataSet.prototype.loadDataIntoDataSet = function(rawDataDoc)
{
	var rs = null;
	var mainXPath = Spry.Data.Region.processDataRefString(null, this.xpath, this.dataSetsForDataRefStrings);
	var subPaths = this.subPaths;
	var suppressColumns = false;

	if (this.subPaths && this.subPaths.length > 0)
	{

		var processedSubPaths = [];
		var numSubPaths = subPaths.length;
		for (var i = 0; i < numSubPaths; i++)
		{
			var subPathStr = Spry.Data.Region.processDataRefString(null, subPaths[i], this.dataSetsForDataRefStrings);
			if (subPathStr.charAt(0) != '/')
				subPathStr = mainXPath + "/" + subPathStr;
			processedSubPaths.push(subPathStr);
		}

		processedSubPaths.unshift(mainXPath);
		var commonParent = this.convertXPathsToPathTree(processedSubPaths);

		mainXPath = commonParent.path;
		subPaths = commonParent.subPaths;

		suppressColumns = commonParent.xpath ? false : true;
	}

	rs = Spry.Data.XMLDataSet.getRecordSetFromXMLDoc(rawDataDoc, mainXPath, suppressColumns, this.entityEncodeStrings);

	if (!rs)
	{
		Spry.Debug.reportError("Spry.Data.XMLDataSet.loadDataIntoDataSet() failed to create dataSet '" + this.name + "'for '" + this.xpath + "' - " + this.url + "\n");
		return;
	}

	this.flattenSubPaths(rs, subPaths);

	this.doc = rs.xmlDoc;
	this.data = rs.data;
	this.dataHash = rs.dataHash;
	this.dataWasLoaded = (this.doc != null);
};

Spry.Data.XMLDataSet.prototype.xhRequestProcessor = function(xhRequest)
{

	var resp = xhRequest.responseXML;
	var manualParseRequired = false;

	if (xhRequest.status != 200)
	{
		if (xhRequest.status == 0)
		{

			if (xhRequest.responseText && (!resp || !resp.firstChild))
				manualParseRequired = true;
		}
	}
	else if (!resp)
	{

		manualParseRequired = true;
	}

	if (manualParseRequired)
		resp = Spry.Utils.stringToXMLDoc(xhRequest.responseText);

	if (!resp  || !resp.firstChild || resp.firstChild.nodeName == "parsererror")
		return null;

	return resp;
};

Spry.Data.XMLDataSet.prototype.sessionExpiredChecker = function(req)
{
	if (req.xhRequest.responseText == 'session expired')
		return true;
	else
	{
		if (req.rawData)
		{
			var firstChild = req.rawData.documentElement.firstChild;
			if (firstChild && firstChild.nodeValue == "session expired")
				return true;
		}
	}
	return false;
};

//
// 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);

	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"); }
	}
};

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 (this.states[stateName])
	{
		var notificationData = { state: this.currentState };
		if (!suppressNotfications)
			Spry.Data.Region.notifyObservers("onPreUpdate", this, notificationData);

		var str = this.transform();

		if (Spry.Data.Region.debug)
			Spry.Debug.trace("<hr />Generated region markup for '" + this.name + "':<br /><br />" + Spry.Utils.encodeEntities(str));

		Spry.Utils.setInnerHTML(this.regionNode, str, !Spry.Data.Region.evalScripts);

		if (this.hasBehaviorAttributes)
			this.attachBehaviors();

		if (!suppressNotfications)
			Spry.Data.Region.notifyObservers("onPostUpdate", this, notificationData);
	}

	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;

	for (var i = 0; i < this.dataSets.length; i++)
	{
		if (this.dataSets[i] == aDataSet)
			return; 
	}

	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.onSessionExpired = function(dataSet)
{
	if (this.currentState != "expired")
		this.setState("expired");
	Spry.Data.Region.notifyObservers("onExpired", 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)
{

	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)
	{

		dsName = valArr[0];
		node.setAttribute(attr, valArr[1]);
	}

	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 = Spry.Data.getDataSetByName(dsName);
			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;
		}

		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;
		}

		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;

		var ds = rgn.getDataSets()[0];
		var sortOrder = "toggle";

		var colArray = value.split(/\s/);
		if (colArray.length > 1)
		{

			var specifiedDS = Spry.Data.getDataSetByName(colArray[0]);
			if (specifiedDS)
			{
				ds = specifiedDS;
				colArray.shift();
			}

			if (colArray.length > 1)
			{
				var str = colArray[colArray.length - 1];
				if (str == "ascending" || str == "descending" || str == "toggle")
				{
					sortOrder = str;
					colArray.pop();
				}
			}
		}

		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())
			{
				ds.loadData();
				allDataSetsReady = false;
			}
		}
	}

	if (!allDataSetsReady)
	{
		Spry.Data.Region.notifyObservers("onLoadingData", this);

		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)
		{
			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 (!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);

	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])
		{
			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)
		{
			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);
		}

		if (results[0].search(/^({|%7[bB])/) != -1 /* results[0].charAt(0) == '{' */)
		{
			var valueName = results[0];
			var regionStr = results[0];

			valueName = valueName.replace(/^({|%7[bB])/, "");
			valueName = valueName.replace(/(}|%7[dD])$/, "");

			var dataSetName = null;
			var splitArray = valueName.split(/::/);

			if (splitArray.length > 1)
			{
				dataSetName = splitArray[0];
				valueName = splitArray[1];
			}

			regionStr = regionStr.replace(/^%7[bB]/, "{");
			regionStr = regionStr.replace(/%7[dD]$/, "}");

			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) == '<')
		{
			var piName = results[0].replace(/^(<!--\s*){0,1}<\/?/, "");
			piName = piName.replace(/>(\s*-->){0,1}|\s.*$/, "");

			if (results[0].search(/<\//) != -1 /* results[0].charAt(1) == '/' */)
			{
				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
			{

				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)
						{
							dataSet = Spry.Data.getDataSetByName(selectedDataSetName);
							if (!dataSet)
							{
								Spry.Debug.reportError("Failed to retrieve data set (" + selectedDataSetName + ") for " + piName + "\n");
								selectedDataSetName = "";
							}
						}
					}

					var jsExpr = null;
					if (results[0].search(/^.*\btest=\"/) != -1)
					{
						jsExpr = results[0].replace(/^.*\btest=\"/, "");
						jsExpr = jsExpr.replace(/".*$/, "");
						jsExpr = Spry.Utils.decodeEntities(jsExpr);
					}

					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.callScriptFunction = function(funcName, processContext)
{
	var result = undefined;

	funcName = funcName.replace(/^\s*\{?\s*function::\s*|\s*\}?\s*$/g, "");
	var func = Spry.Utils.getObjectByName(funcName);
	if (func)
		result = func(this.name, function() { return processContext.getValueFromDataSet.apply(processContext, arguments); });

	return result;
};

Spry.Data.Region.prototype.evaluateExpression = function(exprStr, processContext)
{
	var result = undefined;

	try
	{
		if (exprStr.search(/^\s*function::/) != -1)
			result = this.callScriptFunction(exprStr, processContext);
		else
			result = Spry.Utils.eval(Spry.Data.Region.processDataRefString(processContext, exprStr, null, true));
	}
	catch(e)
	{
		Spry.Debug.trace("Caught exception in Spry.Data.Region.prototype.evaluateExpression() while evaluating: " + Spry.Utils.encodeEntities(exprStr) + "\n    Exception:" + e + "\n");
	}

	return result;
};

Spry.Data.Region.prototype.processTokenChildren = function(outputArr, token, processContext)
{
	var children = token.children;
	var len = children.length;

	for (var i = 0; i < len; i++)
		this.processTokens(outputArr, children[i], processContext);
};

Spry.Data.Region.prototype.processTokens = function(outputArr, token, processContext)
{
	var i = 0;

	switch(token.tokenType)
	{
		case Spry.Data.Region.Token.LIST_TOKEN:
			this.processTokenChildren(outputArr, token, processContext);
			break;
		case Spry.Data.Region.Token.STRING_TOKEN:
			outputArr.push(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;
					}

					dsContext.pushState();

					var dataSetRows = dsContext.getData();
					var numRows = dataSetRows.length;
					for (i = 0; i < numRows; i++)
					{
						dsContext.setRowIndex(i);
						var testVal = true;

						if (token.data.jsExpr)
							testVal = this.evaluateExpression(token.data.jsExpr, processContext);

						if (testVal)
							this.processTokenChildren(outputArr, token, processContext);
					}
					dsContext.popState();
				}
			}
			else if (token.data.name == "spry:if")
			{
				var testVal = true;

				if (token.data.jsExpr)
					testVal = this.evaluateExpression(token.data.jsExpr, processContext);

				if (testVal)
					this.processTokenChildren(outputArr, token, processContext);
			}
			else if (token.data.name == "spry:choose")
			{
				var defaultChild = null;
				var childToProcess = null;
				var testVal = false;
				var j = 0;

				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)
							{
								testVal = this.evaluateExpression(child.data.jsExpr, processContext);

								if (testVal)
								{
									childToProcess = child;
									break;
								}
							}
						}
						else if (child.data.name == "spry:default")
							defaultChild = child;
					}
				}

				if (!childToProcess && defaultChild)
					childToProcess = defaultChild;

				if (childToProcess)
					this.processTokenChildren(outputArr, childToProcess, processContext);
			}
			else if (token.data.name == "spry:state")
			{
				var testVal = true;

				if (!token.data.regionState || token.data.regionState == this.currentState)
					this.processTokenChildren(outputArr, 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;
			var val = undefined;

			if (dataSet && dataSet == "function")
			{

				val = this.callScriptFunction(token.data, processContext);
			}
			else
			{
				if (!dataSet && this.dataSets && this.dataSets.length > 0 && this.dataSets[0])
				{
	
					dataSet = this.dataSets[0];
				}
				if (!dataSet)
				{
					Spry.Debug.reportError("processTokens(): Value reference has no data set specified: " + token.regionStr + "\n");
					return "";
				}
	
				val = processContext.getValueFromDataSet(dataSet, token.data);
			}

			if (typeof val != "undefined")
				outputArr.push(val + "");

			break;
		default:
			Spry.Debug.reportError("processTokens(): Invalid token type: " + token.regionStr + "\n");
			break;
	}
};

Spry.Data.Region.prototype.transform = function()
{
	if (this.data && !this.tokens)
		this.tokens = this.tokenizeData(this.data);

	if (!this.tokens)
		return "";

	processContext = new Spry.Data.Region.ProcessingContext(this);
	if (!processContext)
		return "";

	var outputArr = [ "" ];
	this.processTokens(outputArr, this.tokens, processContext);
	return outputArr.join("");
};

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)
{
	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;

		var val = "";

		if (processingContext)
			val = processingContext.getValueFromDataSet(dsName, fieldName);
		else
		{
			var ds = dsName ? dataSetsToUse[dsName] : dataSetsToUse[0];
			if (ds)
				val = ds.getValue(fieldName);
		}

		if (typeof val != "undefined")
		{
			val += ""; 
			resultStr += isJSExpr ? Spry.Utils.escapeQuotesAndLineBreaks(val) : val;
		}

		if (startSearchIndex == re.lastIndex)
		{

			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 = Spry.Data.getDataSetByName(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, processingContext)
{
	var m_dataSet = dataSet;
	var m_processingContext = processingContext;
	var m_curRowIndexArray = [ { rowIndex: -1 } ]; 
	var m_parent = null;
	var m_children = [];

	var getInternalRowIndex = function() { return m_curRowIndexArray[m_curRowIndexArray.length - 1].rowIndex; };

	this.resetAll = function() { m_curRowIndexArray = [ { rowIndex: m_dataSet.getCurrentRow() } ] };
	this.getDataSet = function() { return m_dataSet; };
	this.getNumRows = function(unfiltered)
	{
		var data = this.getCurrentState().data;
		return data ? data.length : m_dataSet.getRowCount(unfiltered);
	};
	this.getData = function()
	{
		var data = this.getCurrentState().data;
		return data ? data : m_dataSet.getData();
	};
	this.setData = function(data)
	{
		this.getCurrentState().data = data;
	};
	this.getValue = function(valueName, rowContext)
	{
		var result = "";
		var curState = this.getCurrentState();
		var ds = curState.nestedDS ? curState.nestedDS : this.getDataSet();
		if (ds)
			result = ds.getValue(valueName, rowContext);
		return result;
	};
	this.getCurrentRow = function()
	{
		if (m_curRowIndexArray.length < 2 || getInternalRowIndex() < 0)
			return m_dataSet.getCurrentRow();

		var data = this.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)
	{
		this.getCurrentState().rowIndex = rowIndex;

		var data = this.getData();
		var numChildren = m_children.length;
		for (var i = 0; i < numChildren; i++)
			m_children[i].syncDataWithParentRow(this, rowIndex, data);
	};
	this.syncDataWithParentRow = function(parentDSContext, rowIndex, parentData)
	{
		var row = parentData[rowIndex];
		if (row)
		{
			nestedDS = m_dataSet.getNestedDataSetForParentRow(row);
			if (nestedDS)
			{
				var currentState = this.getCurrentState();
				currentState.nestedDS = nestedDS;
				currentState.data = nestedDS.getData();
				currentState.rowIndex = nestedDS.getCurrentRowNumber();

				currentState.rowIndex = currentState.rowIndex < 0 ? 0 : currentState.rowIndex;

				var numChildren = m_children.length;
				for (var i = 0; i < numChildren; i++)
					m_children[i].syncDataWithParentRow(this, currentState.rowIndex, currentState.data);
			}
		}
	};
	this.pushState = function()
	{
		var curState = this.getCurrentState();
		var newState = new Object;
		newState.rowIndex = curState.rowIndex;
		newState.data = curState.data;
		newState.nestedDS = curState.nestedDS;

		m_curRowIndexArray.push(newState);

		var numChildren = m_children.length;
		for (var i = 0; i < numChildren; i++)
			m_children[i].pushState();
	};
	this.popState = function()
	{
		if (m_curRowIndexArray.length < 2)
		{
			Spry.Debug.reportError("Stack underflow in Spry.Data.Region.DSContext.popState()!\n");
			return;
		}

		var numChildren = m_children.length;
		for (var i = 0; i < numChildren; i++)
			m_children[i].popState();

		m_curRowIndexArray.pop();
	};
	this.getCurrentState = function()
	{
		return m_curRowIndexArray[m_curRowIndexArray.length - 1];
	};
	this.addChild = function(childDSContext)
	{
		var numChildren = m_children.length;
		for (var i = 0; i < numChildren; i++)
		{
			if (m_children[i] == childDSContext)
				return;
		}
		m_children.push(childDSContext);
	};
};

Spry.Data.Region.ProcessingContext = function(region)
{
	this.region = region;
	this.dataSetContexts = [];

	if (region && region.dataSets)
	{
		var dsArray = region.dataSets.slice(0);
		var dsArrayLen = dsArray.length;
		for (var i = 0; i < dsArrayLen; i++)
		{
			var ds = region.dataSets[i];
			while (ds && ds.getParentDataSet)
			{
				var doesExist = false;
				ds = ds.getParentDataSet();
				if (ds && this.indexOf(dsArray, ds) == -1)
					dsArray.push(ds);
			}
		}

		for (i = 0; i < dsArray.length; i++)
			this.dataSetContexts.push(new Spry.Data.Region.DSContext(dsArray[i], this));

		var dsContexts = this.dataSetContexts;
		var numDSContexts = dsContexts.length;

		for (i = 0; i < numDSContexts; i++)
		{
			var dsc = dsContexts[i];
			var ds = dsc.getDataSet();
			if (ds.getParentDataSet)
			{
				var parentDS = ds.getParentDataSet();
				if (parentDS)
				{
					var pdsc = this.getDataSetContext(parentDS);
					if (pdsc) pdsc.addChild(dsc);
				}
			}
		}
	}
};

Spry.Data.Region.ProcessingContext.prototype.indexOf = function(arr, item)
{

	if (arr)
	{
		var arrLen = arr.length;
		for (var i = 0; i < arrLen; i++)
			if (arr[i] == item)
				return i;
	}
	return -1;
};

Spry.Data.Region.ProcessingContext.prototype.getDataSetContext = function(dataSet)
{
	if (!dataSet)
	{
		if (this.dataSetContexts.length > 0)
			return this.dataSetContexts[0];
		return null;
	}

	if (typeof dataSet == 'string')
	{
		dataSet = Spry.Data.getDataSetByName(dataSet);
		if (!dataSet)
			return null;
	}

	for (var i = 0; i < this.dataSetContexts.length; i++)
	{
		var dsc = this.dataSetContexts[i];
		if (dsc.getDataSet() == dataSet)
			return dsc;
	}

	return null;
};

Spry.Data.Region.ProcessingContext.prototype.getValueFromDataSet = function()
{
	var dsName = "";
	var columnName = "";

	if (arguments.length > 1)
	{

		dsName = arguments[0];
		columnName = arguments[1];
	}
	else
	{

		var dataRef = arguments[0].replace(/\s*{\s*|\s*}\s*/g, "");
		if (dataRef.search("::") != -1)
		{
			dsName = dataRef.replace(/::.*/, "");
			columnName = dataRef.replace(/.*::/, "");
		}
		else
			columnName = dataRef;
	}

	var result = "";
	var dsContext = this.getDataSetContext(dsName);
	if (dsContext)
		result = dsContext.getValue(columnName, dsContext.getCurrentRow());
	else
		Spry.Debug.reportError("getValueFromDataSet: Failed to get " + dsName + " context for the " + this.region.regionNode.id + " region.\n");

	return result;
};

Spry.Data.Region.ProcessingContext.prototype.$v = Spry.Data.Region.ProcessingContext.prototype.getValueFromDataSet;

Spry.Data.Region.ProcessingContext.prototype.getCurrentRowForDataSet = function(dataSet)
{
	var dsc = this.getDataSetContext(dataSet);
	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() { if (Spry.Data.initRegionsOnLoad) Spry.Data.initRegions(); }, 0); });


// 8238                             
// SpryHTMLDataSet.js - version 0.20
//                                  

//
// Spry.Data.HTMLDataSet
//

Spry.Data.HTMLDataSet = function(dataSetURL, sourceElementID, dataSetOptions)
{
	this.sourceElementID = sourceElementID;
	this.sourceElement = null;

	this.sourceWasInitialized = false;
	this.usesExternalFile = (dataSetURL != null) ? true : false;
	
	this.firstRowAsHeaders = true;
	this.useColumnsAsRows = false;
	this.columnNames = null;
	this.hideDataSourceElement = true;
	
	this.rowSelector = null;
	this.dataSelector = null;

	this.tableModeEnabled = true;

	Spry.Data.HTTPSourceDataSet.call(this, dataSetURL, dataSetOptions);
};

Spry.Data.HTMLDataSet.prototype = new Spry.Data.HTTPSourceDataSet();
Spry.Data.HTMLDataSet.prototype.constructor = Spry.Data.HTMLDataSet;

Spry.Data.HTMLDataSet.prototype.getDataRefStrings = function() 
{
	var dep = [];
	if (this.url) 
		dep.push(this.url);
	if (typeof this.sourceElementID == "string") 
		dep.push(this.sourceElementID);
	
	return dep;
};

Spry.Data.HTMLDataSet.prototype.setDisplay = function(ele, display)
{
	if( ele )
		ele.style.display = display;
};

Spry.Data.HTMLDataSet.prototype.initDataSource = function(callLoadData)
{
	if (!this.loadDependentDataSets())
		return;
	if (!this.usesExternalFile)
	{
		this.setSourceElement();
		if (this.hideDataSourceElement)
			this.setDisplay(this.sourceElement, "none");
	}
};

Spry.Data.HTMLDataSet.prototype.setSourceElement = function (externalDataElement)
{
	this.sourceElement = null;
	if (!this.sourceElementID) 
	{
	  if (externalDataElement)
  	  this.sourceElement = externalDataElement;
  	else
  	{
  	  this.hideDataSourceElement = false;
  	  this.sourceElement = document.body;
  	}
	  return; 
	}
	
	var sourceElementID = Spry.Data.Region.processDataRefString(null, this.sourceElementID, this.dataSetsForDataRefStrings);
	if (!this.usesExternalFile)
	   this.sourceElement = Spry.$(sourceElementID);
	else
    if (externalDataElement) 
    {
      var foundElement = false;
      var sources = Spry.Utils.getNodesByFunc(externalDataElement, function(node)
    	{
    	    if (foundElement) 
    	      return false;
    			if (node.nodeType != 1)
    				return false;
    			if (node.id && node.id.toLowerCase() == sourceElementID.toLowerCase())
    			{
    			  foundElement = true;
    			  return true;
    			}
      });
      this.sourceElement = sources[0];
    }
    
	if (!this.sourceElement) 
		Spry.Debug.reportError("Spry.Data.HTMLDataSet: '" + sourceElementID + "' is not a valid element ID");
};

Spry.Data.HTMLDataSet.prototype.getSourceElement = function() { return this.sourceElement; };
Spry.Data.HTMLDataSet.prototype.getSourceElementID = function() { return this.sourceElementID; };
Spry.Data.HTMLDataSet.prototype.setSourceElementID = function(sourceElementID)
{
	if (this.sourceElementID != sourceElementID)
	{
		this.sourceElementID = sourceElementID;
		this.recalculateDataSetDependencies();
		this.dataWasLoaded = false;
	}
};

Spry.Data.HTMLDataSet.prototype.getDataSelector = function() { return this.dataSelector; };
Spry.Data.HTMLDataSet.prototype.setDataSelector = function(dataSelector)
{ 
  if (this.dataSelector != dataSelector)
  {
     this.dataSelector = dataSelector;
  	 this.dataWasLoaded = false;
  }
};

Spry.Data.HTMLDataSet.prototype.getRowSelector = function() { return this.rowSelector; };
Spry.Data.HTMLDataSet.prototype.setRowSelector = function(rowSelector)
{ 
  if (this.rowSelector != rowSelector)
  {
     this.rowSelector = rowSelector;
  	 this.dataWasLoaded = false;
  }
};

Spry.Data.HTMLDataSet.prototype.loadDataIntoDataSet = function(rawDataDoc)
{
	var responseText = rawDataDoc;
	responseText = Spry.Data.HTMLDataSet.cleanupSource(responseText);

	var div = document.createElement("div");
	div.id = "htmlsource" + this.internalID;
	div.innerHTML = responseText;

	this.setSourceElement(div);
	if (this.sourceElement)
	{
		var parsedStructure = this.getDataFromSourceElement();
		if (parsedStructure) 
		{
			this.dataHash = parsedStructure.dataHash;
			this.data = parsedStructure.data;
		}		
	}
	this.dataWasLoaded = true;
	div = null;
};

Spry.Data.HTMLDataSet.prototype.loadDependentDataSets = function() 
{
	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())
			{
				ds.loadData();
				allDataSetsReady = false;
			}
		}

		if (!allDataSetsReady)
			return false;
	}
	return true;
};

Spry.Data.HTMLDataSet.prototype.loadData = function()
{
	this.cancelLoadData();
	this.initDataSource();
	
	var self = this;
	if (!this.usesExternalFile) 
	{
		this.notifyObservers("onPreLoad");
		
		this.dataHash = new Object;
		this.data = new Array;
		this.dataWasLoaded = false;
		this.unfilteredData = null;
		this.curRowID = 0;
		
		this.pendingRequest = new Object;
		this.pendingRequest.timer = setTimeout(function()
		{
			self.pendingRequest = null;
			var parsedStructure = self.getDataFromSourceElement();
			if (parsedStructure) 
			{
				self.dataHash = parsedStructure.dataHash;
				self.data = parsedStructure.data;
			}
			self.dataWasLoaded = true;
			
			self.disableNotifications();
			self.filterAndSortData();
			self.enableNotifications();
			
			self.notifyObservers("onPostLoad");
			self.notifyObservers("onDataChanged");	
		}, 0); 
	}
	else 
	{
		var url = Spry.Data.Region.processDataRefString(null, this.url, this.dataSetsForDataRefStrings);

		var postData = this.requestInfo.postData;
		if (postData && (typeof postData) == "string") 
			postData = Spry.Data.Region.processDataRefString(null, postData, this.dataSetsForDataRefStrings);
		this.notifyObservers("onPreLoad");
		
	
		this.dataHash = new Object;
		this.data = new Array;
		this.dataWasLoaded = false;
		this.unfilteredData = null;
		this.curRowID = 0;

		var req = this.requestInfo.clone();
		req.url = url;
		req.postData = postData;
	
		this.pendingRequest = new Object;
		this.pendingRequest.data = Spry.Data.HTTPSourceDataSet.LoadManager.loadData(req, this, this.useCache);
	}
};

Spry.Data.HTMLDataSet.cleanupSource = function (source)
{
  source = source.replace(/<(img|script|link|frame|iframe|input)([^>]+)>/gi, function(a,b,c) {
			return '<' + b + c.replace(/\b(src|href)\s*=/gi, function(a, b) {
				return 'spry_'+ b + '=';
			}) + '>';
		});
	return source;
};

Spry.Data.HTMLDataSet.undoCleanupSource = function (source)
{
	source = source.replace(/<(img|script|link|frame|iframe|input)([^>]+)>/gi, function(a,b,c) {
			return '<' + b + c.replace(/\bspry_(src|href)\s*=/gi, function(a, b) {
				return b + '=';
			}) + '>';
		});
	return source;
};

Spry.Data.HTMLDataSet.normalizeColumnName = function(colName) 
{
	colName = colName.replace(/(?:^[\s\t]+|[\s\t]+$)/gi, "");
	colName = colName.replace(/<\/?([a-z]+)([^>]+)>/gi, "");
	colName = colName.replace(/[\s\t]+/gi, "_");
	return colName;
};

Spry.Data.HTMLDataSet.prototype.getDataFromSourceElement = function() 
{
	if (!this.sourceElement) 
    return null;

	var extractedData;
	var usesTable = (this.tableModeEnabled && this.sourceElement.nodeName.toLowerCase() == "table");
	if (usesTable)
		extractedData = this.getDataFromHTMLTable();
	else
		extractedData = this.getDataFromNestedStructure();

	if (!extractedData)
     return null;

	if (this.useColumnsAsRows) 
	{
	   var flipedData = new Array;
	   for (var rowIdx = 0; rowIdx < extractedData.length; rowIdx++)
	   {
	     var row = extractedData[rowIdx];
	     for (var cellIdx = 0; cellIdx < row.length; cellIdx++) 
	     {
	       if (!flipedData[cellIdx]) flipedData[cellIdx] = new Array;
	       flipedData[cellIdx][rowIdx]= row[cellIdx];
	     }
	   }
	   extractedData = flipedData;
	}

	var parsedStructure = new Object();
	parsedStructure.dataHash = new Object;
	parsedStructure.data = new Array;
	
	if (extractedData.length == 0) 
	   return parsedStructure;
	 
	var columnNames = new Array;
	var firstRowOfData = extractedData[0];
	for (var cellIdx=0; cellIdx < firstRowOfData.length; cellIdx++)
	{
	  if (usesTable && this.firstRowAsHeaders) columnNames[cellIdx] = Spry.Data.HTMLDataSet.normalizeColumnName(firstRowOfData[cellIdx]);
	  else columnNames[cellIdx] = "column" + cellIdx;
	}
  
  if (this.columnNames && this.columnNames.length) 
  {
    if (this.columnNames.length < columnNames.length) 
        Spry.Debug.reportError("Too few elements in the columnNames array. The columNames length must match the actual number of columns." );
    else
       for (var i=0; i < columnNames.length; i++) {
         if (this.columnNames[i]) columnNames[i] = this.columnNames[i];
    }
  }
  
	var nextID = 0;
	var firstDataRowIndex = (usesTable && this.firstRowAsHeaders) ? 1: 0;

	for (var rowIdx = firstDataRowIndex; rowIdx < extractedData.length; rowIdx++)
	{
		var row = extractedData[rowIdx];
		if (columnNames.length != row.length)
		{
			Spry.Debug.reportError("Unbalanced column names for row #" + (rowIdx+1) + ". Skipping row." );
			continue;
		}

		var rowObj = {};
		for (var cellIdx = 0; cellIdx < row.length; cellIdx++)
       rowObj[columnNames[cellIdx]] = row[cellIdx];

    rowObj['ds_RowID'] = nextID++;
    parsedStructure.dataHash[rowObj['ds_RowID']] = rowObj;
    parsedStructure.data.push(rowObj);
	}
	return parsedStructure;
};

Spry.Data.HTMLDataSet.getElementChildren = function(element)
{
	var children = [];
	var child = element.firstChild;
	while (child)
	{
		if (child.nodeType == 1)
			children.push(child);
		child = child.nextSibling;
	}
	return children;
};

Spry.Data.HTMLDataSet.prototype.getDataFromHTMLTable = function()
{
  var tHead = this.sourceElement.tHead;
  var tBody = this.sourceElement.tBodies[0];
  
  var rowsHead = [];
  var rowsBody = [];
  if (tHead) rowsHead = Spry.Data.HTMLDataSet.getElementChildren(tHead);
  if (tBody) rowsBody = Spry.Data.HTMLDataSet.getElementChildren(tBody);
  
  var extractedData = new Array;
  var rows = rowsHead.concat(rowsBody);
  if (this.rowSelector) rows = Spry.Data.HTMLDataSet.applySelector(rows, this.rowSelector);
  for (var rowIdx = 0; rowIdx < rows.length; rowIdx++)
  {
     var row = rows[rowIdx];
     
     var dataRow;
     if (extractedData[rowIdx]) dataRow = extractedData[rowIdx];
     else dataRow = new Array;
     
     var offset = 0;
     var cells = row.cells;
     if (this.dataSelector) cells = Spry.Data.HTMLDataSet.applySelector(cells, this.dataSelector);
     for (var cellIdx=0; cellIdx < cells.length; cellIdx++)
     {
       var cell = cells[cellIdx];
       var nextCellIndex = cellIdx + offset;
       
       while (dataRow[nextCellIndex])
       {
          offset ++;
          nextCellIndex ++;
       }
       var cellValue = Spry.Data.HTMLDataSet.undoCleanupSource(cell.innerHTML);
       dataRow[nextCellIndex] = cellValue;
       
       var colspan = cell.colSpan;
       if (colspan == 0) colspan = 1;
       var startOffset = offset;
       for (var offIdx = 1; offIdx < colspan; offIdx++)
       {
         offset ++;
         nextCellIndex = cellIdx + offset;
         dataRow[nextCellIndex] = cellValue;
       }
       
       var rowspan = cell.rowSpan;
       if (rowspan == 0) rowspan = 1;
       for (var rowOffIdx = 1; rowOffIdx < rowspan; rowOffIdx++)
       {
         nextRowIndex = rowIdx + rowOffIdx;
         var nextDataRow;
         if (extractedData[nextRowIndex]) nextDataRow = extractedData[nextRowIndex];
         else nextDataRow = new Array;
         
         var rowSpanCellOffset = startOffset;
         for (var offIdx = 0; offIdx < colspan; offIdx++)
         {
           nextCellIndex = cellIdx + rowSpanCellOffset;
           nextDataRow[nextCellIndex] = cellValue;
           rowSpanCellOffset ++;
         }
         extractedData[nextRowIndex] = nextDataRow;
       }
      }
     extractedData[rowIdx] = dataRow;
  }
  return extractedData;
};

Spry.Data.HTMLDataSet.prototype.getDataFromNestedStructure = function()
{
  var extractedData = new Array;
  
  if (this.sourceElementID && !this.rowSelector && !this.dataSelector) 
  {
     extractedData[0] = [Spry.Data.HTMLDataSet.undoCleanupSource(this.sourceElement.innerHTML)];
     return extractedData;
  }
  
  var self = this;
  var rows = [];
  if (!this.rowSelector)
     rows = [this.sourceElement];
  else
     rows = Spry.Utils.getNodesByFunc(this.sourceElement, function(node) { 
            return Spry.Data.HTMLDataSet.evalSelector(node, self.sourceElement, self.rowSelector); 
           }); 
           
  for (var rowIdx = 0; rowIdx < rows.length; rowIdx++)
  {
    var row = rows[rowIdx];
    var cells = [];
    if (!this.dataSelector)
      cells = [row];
    else
      cells = Spry.Utils.getNodesByFunc(row, function(node) { 
               return Spry.Data.HTMLDataSet.evalSelector(node, row, self.dataSelector); 
              });
              
    extractedData[rowIdx] = new Array;
    for (var cellIdx = 0; cellIdx < cells.length; cellIdx ++)
       extractedData[rowIdx][cellIdx] = Spry.Data.HTMLDataSet.undoCleanupSource(cells[cellIdx].innerHTML);
  }
  return extractedData;
};

Spry.Data.HTMLDataSet.applySelector = function(collection, selector, root)
{
   var newCollection = [];
   for (var idx = 0; idx < collection.length; idx++)
   {
     var node = collection[idx];
     if (Spry.Data.HTMLDataSet.evalSelector(node, root?root:node.parentNode, selector))
        newCollection.push(node);
   }
   return newCollection;
};

Spry.Data.HTMLDataSet.evalSelector = function (node, root, selector)
{
  if (node.nodeType != 1)
 		return false;
 	if (node == root)
 	  return false;
 	  
  var selectors = selector.split(",");
  for (var idx = 0; idx < selectors.length; idx ++)
  {
    var currentSelector = selectors[idx].replace(/^\s+/, "").replace(/\s+$/, "");
   	var tagName = null;
   	var className = null;
   	var id = null;
   	
   	var selected = true;
   	if (currentSelector.substring(0,1) == ">") 
   	{
   	    if (node.parentNode != root) 
   	      selected = false;
   	    else
   	      currentSelector = currentSelector.substring(1).replace(/^\s+/, "");
   	}
   	if (selected) 
   	{
     	tagName = currentSelector.toLowerCase();
     	if (currentSelector.indexOf(".") != -1)
     	{
     	  var parts = currentSelector.split(".");
     	  tagName = parts[0];
     	  className = parts[1];
     	}
     	else if (currentSelector.indexOf("#") != -1)
     	{
     	  var parts = currentSelector.split("#");
     	  tagName = parts[0];
     	  id = parts[1];
     	}
   	}
   	if (selected && tagName != '' && tagName != '*')
   	    if (node.nodeName.toLowerCase() != tagName) 
   	       selected = false;
   	if (selected && id && node.id != id)
   	    selected = false;
    	if (selected && className && node.className.search(new RegExp('\\b' + className + '\\b', 'i')) ==-1) 
   	    selected = false;
   	if (selected)
   	 return true;
  }
  return false;
};

// 8771                           
// DynamicGallery.js - version 0.1
//                                

var dsPhotos = new Spry.Data.HTMLDataSet(null, "gallery", { firstRowAsHeaders:false, rowSelector:".thumbnail", columnNames:["thumbimg"], filterDataFunc: PhotosFilter });

function PhotosFilter(ds, row, rowIndex)
{
	var tnStr = row.thumbimg;
	if (tnStr)
	{
		row.path = tnStr.replace(/.*<a[^>]*href="?([^"]*)"?.*/i, "$1");
		row.thumbpath = tnStr.replace(/.*<img[^>]*src="?([^"]*)"?.*/i, "$1");
	}
	return row;
}

dsPhotos.addObserver({ onPostLoad: function(notifier, data)
{

	Spry.Utils.updateContent("gallery", "photogallery.html", function()
	{
		Spry.Data.initRegions();
		Spry.$('gallery').style.display = "block";
	});
}});

Spry.Data.initRegionsOnLoad = false;

Spry.Data.Region.addObserver("thumbnails", { onPostUpdate: function(){ InitializeGallery(); }});

Spry.Utils.addLoadListener(function()
{
	Spry.$$("body.staticVersion").removeClassName("staticVersion");
	dsPhotos.loadData();
});
