
/* tabs */

/****************************************************************************
* Accessible Tabs 1.0
* Written by Greg Burghardt
* greg_burghardt@yahoo.com
*
* This was created as open source software and is free to download and
* distribute. Please include this JavaScript code in its entirety. 
****************************************************************************/

// Determines whether any occurence of the TAB class can parse the window
// location to detect if a tab box should be switched to active. When false,
// the TAB.detectTabByURL() function will search the window location for a
// page anchor and switch the proper tab and tab box on if that ID is found
// in the this.tabBoxIDs[] array.
var TAB_Found_In_URL = false;



/****************************************************************************
* Declare the TAB class.
****************************************************************************/
function TAB() {
 // Version string
 this.version = "Accessible Tabs: Version 1.0";
 
 // Detect standard DOM
 this.DOM     = document.getElementById;
 
 this.name              = "";          // ID of tab wrapper
 this.isInitialized     = false;       // Whether or not the initialization function has been called
 this.tabIDs            = new Array(); // Array of IDs for individual tabs
 this.tabNodes          = new Array(); // Node references to tab links
 this.activeTabID       = "";          // ID of the active tab
 this.activeTabIndex    = 0;           // Array indice of the active tab
 this.boxIDs            = new Array(); // Array of tab box IDs
 this.boxNodes          = new Array(); // Array of node references to tab boxes
 this.makeUniformHeight = false;       // Flag to make all tab boxes a uniform height
 this.size              = 0;           // Number of tab boxes in tab structure
 this.tabSpacerID       = "";          // ID of the spacer DIV under the tabs
 this.tabRootID         = "";          // ID of the OL or UL tag which directly holds the tabs
 this.tabOptionsID      = "";          // ID of the element containing the tab options link at the bottom
 
 
 
 /***************************************************************************
 * Declare the prototypes for the member functions.
 ***************************************************************************/
 if (typeof(_tab_prototype_called) == 'undefined') {
  _tab_prototype_called = true;
  TAB.prototype.initialize         = initialize;
  TAB.prototype.initializeWithIDs  = initializeWithIDs;
  TAB.prototype.uninitialize       = uninitialize;
  TAB.prototype.switchTab          = switchTab;
  TAB.prototype.detectTabByURL     = detectTabByURL;
  TAB.prototype.getVersion         = getVersion;
  TAB.prototype.getTabIndexByID    = getTabIndexByID;
  TAB.prototype.getTabBoxIndexByID = getTabBoxIndexByID;
 }
 
 
 
 /***************************************************************************
 * Initializes the tab box. It does not take any parameters and assumes
 * you've given the data structure a name, at least one ID for a tab, the
 * active tab's ID and at least one tab box ID. This function activates the
 * default tab (as denoted by this.activeTabID).
 ***************************************************************************/
 function initialize() {
  if (this.DOM &&
      this.name         != "" &&
      this.tabIDs[0]    != "" &&
      this.boxIDs[0]    != "" &&
      this.tabSpacerID  != "" &&
      this.tabOptionsID != "" &&
      this.tabRootID    != "") {
   // Get a node reference to the tabs' root element and activate it
   var TabRoot = document.getElementById(this.tabRootID);
   TabRoot.className = "tabbedNavOn";
   
   this.size = this.tabIDs.length;
   
   this.activeTabID = this.tabIDs[0];
   
   for (var i = 0; i < this.size; i++) {
    this.tabNodes[i] = document.getElementById(this.tabIDs[i]);
    this.boxNodes[i] = document.getElementById(this.boxIDs[i]);
    
    if (this.tabNodes[i].id == this.activeTabID) {
     this.boxNodes[i].className = "tabBoxOn";
     this.tabNodes[i].className = "tabOn";
     this.activeTabIndex = i;
    } else {
     this.boxNodes[i].className = "tabBoxOff";
     this.tabNodes[i].className = "tabOff";
    }
   }
   
   TabRoot = document.getElementById(this.tabSpacerID);
   if (TabRoot != "undefined")
    TabRoot.className = "tabSpacerOn";
   
   TabRoot = document.getElementById(this.tabOptionsID);
   if (TabRoot != "undefined")
    TabRoot.className = "tabOptionsOn";
   
   TabRoot = null;
   this.isInitialized = true;
   // If a page anchor pointing to a tab box exists in the window location,
   // find it and switch to the proper tab.
   this.detectTabByURL();
  }
 }
 
 
 /***************************************************************************
 * Initializes the entire tab structure by passing variables to this function
 * for all needed IDs
 ***************************************************************************/
 function initializeWithIDs(Name, RootID, SpacerID, OptionsID) {
  if (this.DOM) {
   this.name         = Name;
   this.tabRootID    = RootID;
   this.tabSpacerID  = SpacerID;
   this.tabOptionsID = OptionsID;
   
   this.initialize();
  }
 }
 
 
 
 /***************************************************************************
 * Removes all node references for this instance of the TAB class. This
 * should occur when the window.onunload event is fired to clean up any node
 * references that might be double-referenced, which should prevent memory
 * leaks in Internet Explorer 5.01 - 6.0.
 ***************************************************************************/
 function uninitialize(override) {
  if (this.isInitialized || override) {
   for (var i = 0; i < this.size; i++) {
    this.tabNodes[i] = null;
    this.boxNodes[i] = null;
   }
   this.isInitialized = false;
  }
 }
 
 
 
 /***************************************************************************
 * Switches to a new tab. Gets the ID of the active Tab and whether or not a
 * null value should be returned to nullify a tab link's click (and
 * consequent browser location change).
 ***************************************************************************/
 function switchTab(ActiveID, ReturnValue) {
  if (this.isInitialized) {
   this.activeTabID = ActiveID;
   this.tabNodes[this.activeTabIndex].className = "tabOff";
   this.boxNodes[this.activeTabIndex].className = "tabBoxOff";
   
   for (var i = 0; i < this.size; i++) {
    if (this.tabNodes[i].id == this.activeTabID) {
     this.tabNodes[i].className = "tabOn";
     this.boxNodes[i].className = "tabBoxOn";
     this.activeTabIndex = i;
     if (ReturnValue != null)
      return ReturnValue;
    }
   }
   return true;
  }
 }
 
 
 
 /***************************************************************************
 * Detects if a tab box has been referenced in the window location and
 * switches to the correct tab. This makes it so you can bookmark the page
 * under a given tab, and return to the page via the bookmark or link and
 * have the appropriate tab active.
 ***************************************************************************/
 function detectTabByURL() {
  if (!TAB_Found_In_URL) {
   var fullurl = "" + window.location;
   var hashPos = 0;
   var activeTabBoxID = "";
   var newActiveTabIndex = 0;
   
   hashPos = fullurl.indexOf("#");
   
   if (hashPos > 0) {
    // Get the page anchor, which is the ID of the soon-to-be active tab box.
    activeTabBoxID = fullurl.substring(hashPos + 1, fullurl.length);
    newActiveTabIndex = this.getTabBoxIndexByID(activeTabBoxID);
    
    if (newActiveTabIndex > -1) {
     // Switch to the appropriate tab.
     this.switchTab(this.tabIDs[this.getTabBoxIndexByID(activeTabBoxID)], null);
     
     // Prevent other occurences of the TAB class from trying to detect the
     // active tab if a URL has a page anchor in it.
     TAB_Found_In_URL = true;
    }
   }
  }
 }
 
 
 
 /***************************************************************************
 * Returns the current version of Accessible Tabs.
 ***************************************************************************/
 function getVersion() {
  return this.version;
 }
 
 
 
 /***************************************************************************
 * Given a tab's ID, this will return it's indice in the TAB.tabIDs array.
 ***************************************************************************/
 function getTabIndexByID(id) {
  for (var i = 0; i < this.size; i++) {
   if (this.tabIDs[i] == id)
    return i;
  }
  return -1;
 }
 
 
 
 /***************************************************************************
 * Given a tab boxe's ID, this will return it's indice in the TAB.tabBoxIDs
 * array.
 ***************************************************************************/
 function getTabBoxIndexByID(id) {
  for (var i = 0; i < this.size; i++) {
   if (this.boxIDs[i] == id)
    return i;
  }
  return -1;
 }
}



/****************************************************************************
* Global tab function to uninitialize an array containing instances of the
* TAB class. This is mainly to prevent memory leaks in Internet Explorer.
* This function should always be used when the page unloads.
****************************************************************************/
function TAB_GLOBAL_UNINITIALIZE(Tabs) {
 for (i = 0; i < Tabs.length; i++) {
  Tabs[i].uninitialize(true);
 }
}




/* ENQYERY FORM */

/* SpryValidationTextField.js - Revision: Spry Preview Release 1.4 */

// Copyright (c) 2006. Adobe Systems Incorporated.
// All rights reserved.
//
// Redistribution and use in source and binary forms, with or without
// modification, are permitted provided that the following conditions are met:
//
//   * Redistributions of source code must retain the above copyright notice,
//     this list of conditions and the following disclaimer.
//   * Redistributions in binary form must reproduce the above copyright notice,
//     this list of conditions and the following disclaimer in the documentation
//     and/or other materials provided with the distribution.
//   * Neither the name of Adobe Systems Incorporated nor the names of its
//     contributors may be used to endorse or promote products derived from this
//     software without specific prior written permission.
//
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
// AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
// ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
// LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
// CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
// SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
// INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
// CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
// ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
// POSSIBILITY OF SUCH DAMAGE.

var Spry;
if (!Spry) Spry = {};
if (!Spry.Widget) Spry.Widget = {};

Spry.Widget.BrowserSniff = function() {
	var b = navigator.appName.toString();
	var up = navigator.platform.toString();
	var ua = navigator.userAgent.toString();

	this.mozilla = this.ie = this.opera = r = false;
	var re_opera = /Opera.([0-9\.]*)/i;
	var re_msie = /MSIE.([0-9\.]*)/i;
	var re_gecko = /gecko/i;
	var re_safari = /safari\/([\d\.]*)/i;
	
	if (ua.match(re_opera)) {
		r = ua.match(re_opera);
		this.opera = true;
		this.version = parseFloat(r[1]);
	} else if (ua.match(re_msie)) {
		r = ua.match(re_msie);
		this.ie = true;
		this.version = parseFloat(r[1]);
	} else if (ua.match(re_safari)) {
		this.safari = true;
		this.version = 1.4;
	} else if (ua.match(re_gecko)) {
		var re_gecko_version = /rv:\s*([0-9\.]+)/i;
		r = ua.match(re_gecko_version);
		this.mozilla = true;
		this.version = parseFloat(r[1]);
	}
	this.windows = this.mac = this.linux = false;

	this.Platform = ua.match(/windows/i) ? "windows" :
					(ua.match(/linux/i) ? "linux" :
					(ua.match(/mac/i) ? "mac" :
					ua.match(/unix/i)? "unix" : "unknown"));
	this[this.Platform] = true;
	this.v = this.version;

	if (this.safari && this.mac && this.mozilla) {
		this.mozilla = false;
	}
};

Spry.is = new Spry.Widget.BrowserSniff();

Spry.Widget.ValidationTextField = function(element, type, options)
{
	type = Spry.Widget.Utils.firstValid(type, "none");
	if (typeof type != 'string') {
		return;
	}
	if (typeof Spry.Widget.ValidationTextField.ValidationDescriptors[type] == 'undefined') {
		return;
	}
	options = Spry.Widget.Utils.firstValid(options, {});
	this.type = type;
	if (!this.isBrowserSupported()) {
		//disable character masking and pattern behaviors for low level browsers
		options.useCharacterMasking = false;
	}
	this.init(element, options);

	//make sure we validate at least on submit
	var validateOn = ['submit'].concat(Spry.Widget.Utils.firstValid(this.options.validateOn, []));
	validateOn = validateOn.join(",");

	this.validateOn = 0;
	this.validateOn = this.validateOn | (validateOn.indexOf('submit') != -1 ? Spry.Widget.ValidationTextField.ONSUBMIT : 0);
	this.validateOn = this.validateOn | (validateOn.indexOf('blur') != -1 ? Spry.Widget.ValidationTextField.ONBLUR : 0);
	this.validateOn = this.validateOn | (validateOn.indexOf('change') != -1 ? Spry.Widget.ValidationTextField.ONCHANGE : 0);

	if (Spry.Widget.ValidationTextField.onloadDidFire)
		this.attachBehaviors();
	else
		Spry.Widget.ValidationTextField.loadQueue.push(this);
};

Spry.Widget.ValidationTextField.ONCHANGE = 1;
Spry.Widget.ValidationTextField.ONBLUR = 2;
Spry.Widget.ValidationTextField.ONSUBMIT = 4;

Spry.Widget.ValidationTextField.ERROR_REQUIRED = 1;
Spry.Widget.ValidationTextField.ERROR_FORMAT = 2;
Spry.Widget.ValidationTextField.ERROR_RANGE_MIN = 4;
Spry.Widget.ValidationTextField.ERROR_RANGE_MAX = 8;
Spry.Widget.ValidationTextField.ERROR_CHARS_MIN = 16;
Spry.Widget.ValidationTextField.ERROR_CHARS_MAX = 32;

/* validation parameters:
 *  - characterMasking : prevent typing of characters not matching an regular expression
 *  - regExpFilter : additional regular expression to disalow typing of characters 
 *		(like the "-" sign in the middle of the value); use for partial matching of the currently typed value;
 * 		the typed value must match regExpFilter at any moment
 *  - pattern : enforce character on each position inside a pattern (AX0?)
 *  - validation : function performing logic validation; return false if failed and the typedValue value on success
 *  - minValue, maxValue : range validation; check if typedValue inside the specified range
 *  - minChars, maxChars : value length validation; at least/at most number of characters
 * */
Spry.Widget.ValidationTextField.ValidationDescriptors = {
	'none': {
	},
	'custom': {
	},
	'integer': {
		characterMasking: /[\-\+\d]/,
		regExpFilter: /^[\-\+]?\d*$/,
		validation: function(value, options) {
			if (value == '' || value == '-' || value == '+') {
				return false;
			}
			var regExp = /^[\-\+]?\d*$/;
			if (!regExp.test(value)) {
				return false;
			}
			options = options || {allowNegative:false};
			var ret = parseInt(value, 10);
			if (!isNaN(ret)) {
				var allowNegative = true;
				if (typeof options.allowNegative != 'undefined' && options.allowNegative == false) {
					allowNegative = false;
				}
				if (!allowNegative && value < 0) {
					ret = false;
				}
			} else {
				ret = false;
			}
			return ret;
		}
	},
	'real': {
		characterMasking: /[\d\.,\-\+e]/i,
		regExpFilter: /^[\-\+]?\d(?:|\.,\d{0,2})|(?:|e{0,1}[\-\+]?\d{0,})$/i,
		validation: function (value, options) {
			var regExp = /^[\+\-]?[0-9]+([\.,][0-9]+)?([eE]{0,1}[\-\+]?[0-9]+)?$/;
			if (!regExp.test(value)) {
				return false;
			}
			var ret = parseFloat(value);
			if (isNaN(ret)) {
				ret = false;
			}
			return ret;
		}
	},
	'currency': {
		formats: {
			'dot_comma': {
				characterMasking: /[\d\.\,\-\+\$]/,
				regExpFilter: /^[\-\+]?(?:[\d\.]*)+(|\,\d{0,2})$/,
				validation: function(value, options) {
					var ret = false;
					//2 or no digits after the comma
					if (/^(\-|\+)?\d{1,3}(?:\.\d{3})*(?:\,\d{2}|)$/.test(value) || /^(\-|\+)?\d+(?:\,\d{2}|)$/.test(value)) {
						value = value.toString().replace(/\./gi, '').replace(/\,/, '.');
						ret = parseFloat(value);
					}
					return ret;
				}
			},
			'comma_dot': {
				characterMasking: /[\d\.\,\-\+\$]/,
				regExpFilter: /^[\-\+]?(?:[\d\,]*)+(|\.\d{0,2})$/,
				validation: function(value, options) {
					var ret = false;
					//2 or no digits after the comma
					if (/^(\-|\+)?\d{1,3}(?:\,\d{3})*(?:\.\d{2}|)$/.test(value) || /^(\-|\+)?\d+(?:\.\d{2}|)$/.test(value)) {
						value = value.toString().replace(/\,/gi, '');
						ret = parseFloat(value);
					}
					return ret;
				}
			}
		}
	},
	'email': {
		characterMasking: /[^\s]/,
		validation: function(value, options) {
			var rx = /^[\w\.-]+@[\w\.-]+\.\w+$/i;
			return rx.test(value);
		}
	},
	'date': {
		validation: function(value, options) {
			var formatRegExp = /^([mdy]+)[\.\-\/\\\s]+([mdy]+)[\.\-\/\\\s]+([mdy]+)$/i;
			var valueRegExp = this.dateValidationPattern;
			var formatGroups = options.format.match(formatRegExp);
			var valueGroups = value.match(valueRegExp);
			if (formatGroups !== null && valueGroups !== null) {
				var dayIndex = -1;
				var monthIndex = -1;
				var yearIndex = -1;
				for (var i=1; i<formatGroups.length; i++) {
					switch (formatGroups[i].toLowerCase()) {
						case "dd":
							dayIndex = i;
							break;
						case "mm":
							monthIndex = i;
							break;
						case "yy":
						case "yyyy":
							yearIndex = i;
							break;
					}
				}
				if (dayIndex != -1 && monthIndex != -1 && yearIndex != -1) {
					var maxDay = -1;
					var theDay = parseInt(valueGroups[dayIndex], 10);
					var theMonth = parseInt(valueGroups[monthIndex], 10);
					var theYear = parseInt(valueGroups[yearIndex], 10);

					// Check month value to be between 1..12
					if (theMonth < 1 || theMonth > 12) {
						return false;
					}
					
					// Calculate the maxDay according to the current month
					switch (theMonth) {
						case 1:	// January
						case 3: // March
						case 5: // May
						case 7: // July
						case 8: // August
						case 10: // October
						case 12: // December
							maxDay = 31;
							break;
						case 4:	// April
						case 6: // Juna
						case 9: // September
						case 11: // November
							maxDay = 30;
							break;
						case 2: // February
							if ((parseInt(theYear/4, 10) * 4 == theYear) && (parseInt(theYear/100, 10) * 100 != theYear)) {
								maxDay = 29;
							} else {
								maxDay = 28;
							}
							break;
					}

					// Check day value to be between 1..maxDay
					if (theDay < 1 || theDay > maxDay) {
						return false;
					}
					
					// If successfull we'll return the date object
					return (new Date(theYear, theMonth, theDay));
				}
			} else {
				return false;
			}
		}
	},
	'time': {
		validation: function(value, options) {
			//	HH:MM:SS T
			var formatRegExp = /([hmst]+)/gi;
			var valueRegExp = /(\d+|AM?|PM?)/gi;
			var formatGroups = options.format.match(formatRegExp);
			var valueGroups = value.match(valueRegExp);
			//mast match and have same length
			if (formatGroups !== null && valueGroups !== null) {
				if (formatGroups.length != valueGroups.length) {
					return false;
				}

				var hourIndex = -1;
				var minuteIndex = -1;
				var secondIndex = -1;
				//T is AM or PM
				var tIndex = -1;
				var theHour = 0, theMinute = 0, theSecond = 0, theT = 'AM';
				for (var i=0; i<formatGroups.length; i++) {
					switch (formatGroups[i].toLowerCase()) {
						case "hh":
							hourIndex = i;
							break;
						case "mm":
							minuteIndex = i;
							break;
						case "ss":
							secondIndex = i;
							break;
						case "t":
						case "tt":
							tIndex = i;
							break;
					}
				}
				if (hourIndex != -1) {
					var theHour = parseInt(valueGroups[hourIndex], 10);
					if (isNaN(theHour) || theHour > (formatGroups[hourIndex] == 'HH' ? 23 : 12 )) {
						return false;
					}
				}
				if (minuteIndex != -1) {
					var theMinute = parseInt(valueGroups[minuteIndex], 10);
					if (isNaN(theMinute) || theMinute > 59) {
						return false;
					}
				}
				if (secondIndex != -1) {
					var theSecond = parseInt(valueGroups[secondIndex], 10);
					if (isNaN(theSecond) || theSecond > 59) {
						return false;
					}
				}
				if (tIndex != -1) {
					var theT = valueGroups[tIndex].toUpperCase();
					if (
						formatGroups[tIndex].toUpperCase() == 'TT' && !/^a|pm$/i.test(theT) || 
						formatGroups[tIndex].toUpperCase() == 'T' && !/^a|p$/i.test(theT)
					) {
						return false;
					}
				}
				var date = new Date(2000, 0, 1, theHour + (theT.charAt(0) == 'P'?12:0), theMinute, theSecond);
				return date;
			} else {
				return false;
			}
		}
	},
	'credit_card': {
		characterMasking: /\d/,
		validation: function(value, options) {
			var regExp = null;
			options.format = options.format || 'ALL';
			switch (options.format.toUpperCase()) {
				case 'ALL': regExp = /^[3-6]{1}[0-9]{12,15}$/; break;
				case 'VISA': regExp = /^4[0-9]{12,15}$/; break;
				case 'MASTERCARD': regExp = /^5[1-5]{1}[0-9]{14}$/; break;
				case 'AMEX': regExp = /^3(4|7){1}[0-9]{13}$/; break;
				case 'DISCOVER': regExp = /^6011[0-9]{12}$/; break;
				case 'DINERSCLUB': regExp = /^3((0[0-5]{1}[0-9]{11})|(6[0-9]{12})|(8[0-9]{12}))$/; break;
			}
			if (!regExp.test(value)) {
				return false;
			}
			var digits = [];
			var j = 1, digit = '';
			for (var i = value.length - 1; i >= 0; i--) {
				if ((j%2) == 0) {
					digit = parseInt(value.charAt(i), 10) * 2;
					digits[digits.length] = digit.toString().charAt(0);
					if (digit.toString().length == 2) {
						digits[digits.length] = digit.toString().charAt(1);
					}
				} else {
					digit = value.charAt(i);
					digits[digits.length] = digit;
				}
				j++;
			}
			var sum = 0;
			for(i=0; i < digits.length; i++ ) {
				sum += parseInt(digits[i], 10);
			}
			if ((sum%10) == 0) {
				return true;
			}
			return false;
		}
	},
	'zip_code': {
		formats: {
			'zip_us9': {
				pattern:'00000-0000'
			},
			'zip_us5': {
				pattern:'00000'
			},
			'zip_uk': {
				characterMasking: /[\dA-Z\s]/,
				validation: function(value, options) {
					//check one of the following masks
					// AN NAA, ANA NAA, ANN NAA, AAN NAA, AANA NAA, AANN NAA
					return /^[A-Z]{1,2}\d[\dA-Z]?\s?\d[A-Z]{2}$/.test(value);
				}
			},
			'zip_canada': {
				characterMasking: /[\dA-Z\s]/,
				pattern: 'A0A 0A0'
			},
			'zip_custom': {}
		}
	},
	'phone_number': {
		formats: {
			//US phone number; 10 digits
			'phone_us': {
				pattern:'(000) 000-0000'
			},
			'phone_custom': {}
		}
	},
	'social_security_number': {
		pattern:'000-00-0000'
	},
	'ip': {
		characterMaskingFormats: {
			'ipv4': /[\d\.]/i,
			'ipv6_ipv4': /[\d\.\:A-F\/]/i,
			'ipv6': /[\d\.\:A-F\/]/i
		},
		validation: function (value, options) {
			return Spry.Widget.ValidationTextField.validateIP(value, options.format);
		}
	},

	'url': {
		characterMasking: /[^\s]/,
		validation: function(value, options) {
			//fix for ?ID=223429 and ?ID=223387
			/* the following regexp matches components of an URI as specified in http://tools.ietf.org/html/rfc3986#page-51 page 51, Appendix B.
				scheme    = $2
				authority = $4
				path      = $5
				query     = $7
				fragment  = $9
			*/
			var URI_spliter = /^(([^:\/?#]+):)?(\/\/([^\/?#]*))?([^?#]*)(\?([^#]*))?(#(.*))?/;
			var parts = value.match(URI_spliter);
			if (parts && parts[4]) {
				//encode each component of the domain name using Punycode encoding scheme: http://tools.ietf.org/html/rfc3492
				var host  = parts[4].split(".");
				var punyencoded = '';
				for (var i=0; i<host.length; i++) {
					punyencoded = Spry.Widget.Utils.punycode_encode(host[i], 64);
					if (!punyencoded) {
						return false;
					} else {
						if (punyencoded != (host[i] + "-")) {
							host[i] = 'xn--' + punyencoded;
						}
					}
				}
				host = host .join(".");
				//the encoded domain name is replaced into the original URL to be validated again later as URL
				value = value.replace(URI_spliter, "$1//" + host + "$5$6$8");
			}

			//fix for ?ID=223358 and ?ID=223594
			//the following validates an URL using ABNF rules as defined in http://tools.ietf.org/html/rfc3986 , Appendix A., page 49
			//except host which is extracted by match[1] and validated separately
			/*
			 * userinfo=	(?:(?:[a-z0-9\-\._~\!\$\&\'\(\)\*\+\,\;\=:]|%[0-9a-f]{2,2})*\@)?
			 * host=			(?:((?:(?:[a-z0-9][a-z0-9\-]*[a-z0-9]|[a-z0-9])\.)*(?:[a-z][a-z0-9\-]*[a-z0-9]|[a-z])|(?:\[[^\]]*\]))
			 * pathname=	(?:\/(?:[a-z0-9\-\._~\!\$\&\'\(\)\*\+\,\;\=\:\@]|%[0-9a-f]{2,2})*)*
			 * query=			(?:\?(?:[a-z0-9\-\._~\!\$\&\'\(\)\*\+\,\;\=\:\@\/\?]|%[0-9a-f]{2,2})*)?
			 * anchor=		(?:\#(?:[a-z0-9\-\._~\!\$\&\'\(\)\*\+\,\;\=\:\@\/\?]|%[0-9a-f]{2,2})*)?
			 */
			var regExp = /^(?:https?|ftp)\:\/\/(?:(?:[a-z0-9\-\._~\!\$\&\'\(\)\*\+\,\;\=:]|%[0-9a-f]{2,2})*\@)?(?:((?:(?:[a-z0-9][a-z0-9\-]*[a-z0-9]|[a-z0-9])\.)*(?:[a-z][a-z0-9\-]*[a-z0-9]|[a-z])|(?:\[[^\]]*\]))(?:\:[0-9]*)?)(?:\/(?:[a-z0-9\-\._~\!\$\&\'\(\)\*\+\,\;\=\:\@]|%[0-9a-f]{2,2})*)*(?:\?(?:[a-z0-9\-\._~\!\$\&\'\(\)\*\+\,\;\=\:\@\/\?]|%[0-9a-f]{2,2})*)?(?:\#(?:[a-z0-9\-\._~\!\$\&\'\(\)\*\+\,\;\=\:\@\/\?]|%[0-9a-f]{2,2})*)?$/i;

			var valid = value.match(regExp);
			if (valid) {
				//extract the  address from URL
				var address = valid[1];

				if (address) {
					if (address == '[]') {
						return false;
					}
					var first = address.charAt(0);
					var last = address.charAt(address.length - 1);
					if (first == '[' && last != ']' || first != '[' && last == ']') {
						return false;
					} else if (first == '[' && last == ']') {
						//IPv6 address or IPv4 enclosed in square brackets
						address = address.replace(/^\[|\]$/gi, '');
						return Spry.Widget.ValidationTextField.validateIP(address, 'ipv6_ipv4');
					} else {
						if (/[^0-9\.]/.test(address)) {
							return true;
						} else {
							//check if hostname is all digits and dots and then check for IPv4
							return Spry.Widget.ValidationTextField.validateIP(address, 'ipv4');
						}
					}
				} else {
					return true;
				}
			} else {
				return false;
			}
		}
	}
};

/*
2.2.1. Preferred
x:x:x:x:x:x:x:x, where the 'x's are the hexadecimal values of the eight 16-bit pieces of the address.
Examples:
	FEDC:BA98:7654:3210:FEDC:BA98:7654:3210
	1080:0:0:0:8:800:200C:417A
Note that it is not necessary to write the leading zeros in an
individual field, but there must be at least one numeral in every
field (except for the case described in 2.2.2.).

2.2.2. Compressed
The use of "::" indicates multiple groups of 16-bits of zeros.
The "::" can only appear once in an address.  The "::" can also be
used to compress the leading and/or trailing zeros in an address.
	1080:0:0:0:8:800:200C:417A --> 1080::8:800:200C:417A
	FF01:0:0:0:0:0:0:101 --> FF01::101
	0:0:0:0:0:0:0:1 --> ::1
	0:0:0:0:0:0:0:0 --> ::

2.5.4 IPv6 Addresses with Embedded IPv4 Addresses
	IPv4-compatible IPv6 address (tunnel IPv6 packets over IPv4 routing infrastructures)
	::0:129.144.52.38
	IPv4-mapped IPv6 address (represent the addresses of IPv4-only nodes as IPv6 addresses)
	::ffff:129.144.52.38

The text representation of IPv6 addresses and prefixes in Augmented BNF (Backus-Naur Form) [ABNF] for reference purposes.
[ABNF http://tools.ietf.org/html/rfc2234]
      IPv6address = hexpart [ ":" IPv4address ]
      IPv4address = 1*3DIGIT "." 1*3DIGIT "." 1*3DIGIT "." 1*3DIGIT

      IPv6prefix  = hexpart "/" 1*2DIGIT

      hexpart = hexseq | hexseq "::" [ hexseq ] | "::" [ hexseq ]
      hexseq  = hex4 *( ":" hex4)
      hex4    = 1*4HEXDIG
*/
Spry.Widget.ValidationTextField.validateIP = function (value, format)
{
	var validIPv6Addresses = [
		//preferred
		/^(?:[a-f0-9]{1,4}:){7}[a-f0-9]{1,4}(?:\/\d{1,3})?$/i,

		//various compressed
		/^[a-f0-9]{0,4}::(?:\/\d{1,3})?$/i,
		/^:(?::[a-f0-9]{1,4}){1,6}(?:\/\d{1,3})?$/i,
		/^(?:[a-f0-9]{1,4}:){1,6}:(?:\/\d{1,3})?$/i,
		/^(?:[a-f0-9]{1,4}:)(?::[a-f0-9]{1,4}){1,6}(?:\/\d{1,3})?$/i,
		/^(?:[a-f0-9]{1,4}:){2}(?::[a-f0-9]{1,4}){1,5}(?:\/\d{1,3})?$/i,
		/^(?:[a-f0-9]{1,4}:){3}(?::[a-f0-9]{1,4}){1,4}(?:\/\d{1,3})?$/i,
		/^(?:[a-f0-9]{1,4}:){4}(?::[a-f0-9]{1,4}){1,3}(?:\/\d{1,3})?$/i,
		/^(?:[a-f0-9]{1,4}:){5}(?::[a-f0-9]{1,4}){1,2}(?:\/\d{1,3})?$/i,
		/^(?:[a-f0-9]{1,4}:){6}(?::[a-f0-9]{1,4})(?:\/\d{1,3})?$/i,


		//IPv6 mixes with IPv4
		/^(?:[a-f0-9]{1,4}:){6}(?:\d{1,3}\.){3}\d{1,3}(?:\/\d{1,3})?$/i,
		/^:(?::[a-f0-9]{1,4}){0,4}:(?:\d{1,3}\.){3}\d{1,3}(?:\/\d{1,3})?$/i,
		/^(?:[a-f0-9]{1,4}:){1,5}:(?:\d{1,3}\.){3}\d{1,3}(?:\/\d{1,3})?$/i,
		/^(?:[a-f0-9]{1,4}:)(?::[a-f0-9]{1,4}){1,4}:(?:\d{1,3}\.){3}\d{1,3}(?:\/\d{1,3})?$/i,
		/^(?:[a-f0-9]{1,4}:){2}(?::[a-f0-9]{1,4}){1,3}:(?:\d{1,3}\.){3}\d{1,3}(?:\/\d{1,3})?$/i,	
		/^(?:[a-f0-9]{1,4}:){3}(?::[a-f0-9]{1,4}){1,2}:(?:\d{1,3}\.){3}\d{1,3}(?:\/\d{1,3})?$/i,
		/^(?:[a-f0-9]{1,4}:){4}(?::[a-f0-9]{1,4}):(?:\d{1,3}\.){3}\d{1,3}(?:\/\d{1,3})?$/i
	];
	var validIPv4Addresses = [
		//IPv4
		/^(\d{1,3}\.){3}\d{1,3}$/i
	];
	var validAddresses = [];
	if (format == 'ipv6' || format == 'ipv6_ipv4') {
		validAddresses = validAddresses.concat(validIPv6Addresses);
	}
	if (format == 'ipv4' || format == 'ipv6_ipv4') {
		validAddresses = validAddresses.concat(validIPv4Addresses);
	}

	var ret = false;
	for (var i=0; i<validAddresses.length; i++) {
		if (validAddresses[i].test(value)) {
			ret = true;
			break;
		}
	}

	if (ret && value.indexOf(".") != -1) {
		//if address contains IPv4 fragment, it must be valid; all 4 groups must be less than 256
		var ipv4 = value.match(/:?(?:\d{1,3}\.){3}\d{1,3}/i);
		if(!ipv4) {
			return false;
		}
		ipv4 = ipv4[0].replace(/^:/, '');
		var pieces = ipv4.split('.');
		if (pieces.length != 4) {
			return false;
		}
		var regExp = /^[\-\+]?\d*$/;
		for (var i=0; i< pieces.length; i++) {
			if (pieces[i] == '') {
				return false;
			}
			var piece = parseInt(pieces[i], 10);
			if (isNaN(piece) || piece > 255 || !regExp.test(pieces[i]) || pieces[i].length>3 || /^0{2,3}$/.test(pieces[i])) {
				return false;
			}
		}
	}
	if (ret && value.indexOf("/") != -1) {
		// if prefix-length is specified must be in [1-128]
		var prefLen = value.match(/\/\d{1,3}$/);
		if (!prefLen) return false;
		var prefLenVal = parseInt(prefLen[0].replace(/^\//,''), 10);
		if (isNaN(prefLenVal) || prefLenVal > 128 || prefLenVal < 1) {
			return false;
		}
	}
	return ret;
};

Spry.Widget.ValidationTextField.onloadDidFire = false;
Spry.Widget.ValidationTextField.loadQueue = [];

Spry.Widget.ValidationTextField.prototype.isBrowserSupported = function()
{
	return Spry.is.ie && Spry.is.v >= 5 && Spry.is.windows
		||
	Spry.is.mozilla && Spry.is.v >= 1.4
		||
	Spry.is.safari
		||
	Spry.is.opera && Spry.is.v >= 9;
};

Spry.Widget.ValidationTextField.prototype.init = function(element, options)
{
	this.element = this.getElement(element);
	this.errors = 0;
	this.flags = {locked: false};
	this.options = {};
	this.event_handlers = [];

	this.validClass = "textfieldValidState";
	this.focusClass = "textfieldFocusState";
	this.requiredClass = "textfieldRequiredState";
	this.invalidFormatClass = "textfieldInvalidFormatState";
	this.invalidRangeMinClass = "textfieldMinValueState";
	this.invalidRangeMaxClass = "textfieldMaxValueState";
	this.invalidCharsMinClass = "textfieldMinCharsState";
	this.invalidCharsMaxClass = "textfieldMaxCharsState";
	this.textfieldFlashTextClass = "textfieldFlashText";
	if (Spry.is.safari) {
		this.flags.lastKeyPressedTimeStamp = 0;
	}

	switch (this.type) {
		case 'phone_number':options.format = Spry.Widget.Utils.firstValid(options.format, 'phone_us');break;
		case 'currency':options.format = Spry.Widget.Utils.firstValid(options.format, 'comma_dot');break;
		case 'zip_code':options.format = Spry.Widget.Utils.firstValid(options.format, 'zip_us5');break;
		case 'date':
			options.format = Spry.Widget.Utils.firstValid(options.format, 'mm/dd/yy');
			break;
		case 'time':
			options.format = Spry.Widget.Utils.firstValid(options.format, 'HH:mm');
			options.pattern = options.format.replace(/[hms]/gi, "0").replace(/TT/gi, 'AM').replace(/T/gi, 'A');
			break;
		case 'ip':
			options.format = Spry.Widget.Utils.firstValid(options.format, 'ipv4');
			options.characterMasking = Spry.Widget.ValidationTextField.ValidationDescriptors[this.type].characterMaskingFormats[options.format]; 
			break;
	}

	//retrieve the validation type descriptor to be used with this instance (base on type and format)
	//widgets may have different validations depending on format (like zip_code with formats)
	var validationDescriptor = {};
	if (options.format && Spry.Widget.ValidationTextField.ValidationDescriptors[this.type].formats) {
		if (Spry.Widget.ValidationTextField.ValidationDescriptors[this.type].formats[options.format]) {
			Spry.Widget.Utils.setOptions(validationDescriptor, Spry.Widget.ValidationTextField.ValidationDescriptors[this.type].formats[options.format]);
		}
	} else {
		Spry.Widget.Utils.setOptions(validationDescriptor, Spry.Widget.ValidationTextField.ValidationDescriptors[this.type]);
	}

	//set default values for some parameters which were not aspecified
	options.useCharacterMasking = Spry.Widget.Utils.firstValid(options.useCharacterMasking, false);
	options.hint = Spry.Widget.Utils.firstValid(options.hint, '');
	options.isRequired = Spry.Widget.Utils.firstValid(options.isRequired, true);

	//set widget validation parameters
	//get values from validation type descriptor
	//use the user specified values, if defined
	options.characterMasking = Spry.Widget.Utils.firstValid(options.characterMasking, validationDescriptor.characterMasking);
	options.regExpFilter = Spry.Widget.Utils.firstValid(options.regExpFilter, validationDescriptor.regExpFilter);
	options.pattern = Spry.Widget.Utils.firstValid(options.pattern, validationDescriptor.pattern);
	options.validation = Spry.Widget.Utils.firstValid(options.validation, validationDescriptor.validation);
	if (typeof options.validation == 'string') {
		options.validation = eval(options.validation);
	}

	options.minValue = Spry.Widget.Utils.firstValid(options.minValue, validationDescriptor.minValue);
	options.maxValue = Spry.Widget.Utils.firstValid(options.maxValue, validationDescriptor.maxValue);

	options.minChars = Spry.Widget.Utils.firstValid(options.minChars, validationDescriptor.minChars);
	options.maxChars = Spry.Widget.Utils.firstValid(options.maxChars, validationDescriptor.maxChars);

	Spry.Widget.Utils.setOptions(this, options);
	Spry.Widget.Utils.setOptions(this.options, options);
};

Spry.Widget.ValidationTextField.prototype.destroy = function() {
	for (var i=0; i<this.event_handlers.length; i++) {
		Spry.Widget.Utils.removeEventListener(this.event_handlers[i][0], this.event_handlers[i][1], this.event_handlers[i][2], false);
	}
	try { delete this.element; } catch(err) {}
	try { delete this.input; } catch(err) {}
	try { delete this.form; } catch(err) {}
	try { delete this.event_handlers; } catch(err) {}
	try { this.selection.destroy(); } catch(err) {}
	try { delete this.selection; } catch(err) {}

	var q = Spry.Widget.Form.onSubmitWidgetQueue;
	var qlen = q.length;
	for (var i = 0; i < qlen; i++) {
		if (q[i] == this) {
			q.splice(i, 1);
			break;
		}
	}
};

Spry.Widget.ValidationTextField.prototype.attachBehaviors = function()
{
	if (this.element) {
		if (this.element.nodeName == "INPUT") {
			this.input = this.element;
		} else {
			this.input = Spry.Widget.Utils.getFirstChildWithNodeNameAtAnyLevel(this.element, "INPUT");
		}
	}

	if (this.input) {
		if (this.maxChars) {
			this.input.removeAttribute("maxLength");
		}
		this.putHint();
		this.compilePattern();
		if (this.type == 'date') {
			this.compileDatePattern();
		}
		this.input.setAttribute("AutoComplete", "off");
		this.selection = new Spry.Widget.SelectionDescriptor(this.input);
		this.oldValue = this.input.value;

		var self = this;
		this.event_handlers = [];

		this.event_handlers.push([this.input, "keydown", function(e) { if (self.isDisabled()) return true; return self.onKeyDown(e || event); }]);
		this.event_handlers.push([this.input, "keypress", function(e) { if (self.isDisabled()) return true; return self.onKeyPress(e || event); }]);
		if (Spry.is.opera) {
			this.event_handlers.push([this.input, "keyup", function(e) { if (self.isDisabled()) return true; return self.onKeyUp(e || event); }]);
		}

		this.event_handlers.push([this.input, "focus", function(e) { if (self.isDisabled()) return true; return self.onFocus(e || event); }]);
		this.event_handlers.push([this.input, "blur", function(e) { if (self.isDisabled()) return true; return self.onBlur(e || event); }]);

		this.event_handlers.push([this.input, "mousedown", function(e) { if (self.isDisabled()) return true; return self.onMouseDown(e || event); }]);

		var changeEvent = 
			Spry.is.mozilla || Spry.is.opera || Spry.is.safari?"input":
			Spry.is.ie?"propertychange":
			"change";
		this.event_handlers.push([this.input, changeEvent, function(e) { if (self.isDisabled()) return true; return self.onChange(e || event); }]);

		if (Spry.is.mozilla || Spry.is.safari) {
			//oninput event on mozilla does not fire ondragdrop
			this.event_handlers.push([this.input, "dragdrop", function(e) { if (self.isDisabled()) return true; self.removeHint();return self.onChange(e || event); }]);
		} else if (Spry.is.ie){
			//ondrop&onpropertychange crash on IE 
			this.event_handlers.push([this.input, "drop", function(e) { if (self.isDisabled()) return true; return self.onDrop(e || event); }]);
		}

		for (var i=0; i<this.event_handlers.length; i++) {
			Spry.Widget.Utils.addEventListener(this.event_handlers[i][0], this.event_handlers[i][1], this.event_handlers[i][2], false);
		}

		// submit
		this.form = Spry.Widget.Utils.getFirstParentWithNodeName(this.input, "FORM");
		if (this.form) {
			// if no "onSubmit" handler has been attached to the current form, attach one
			if (!this.form.attachedSubmitHandler && !this.form.onsubmit) {
				this.form.onsubmit = function(e) { e = e || event; return Spry.Widget.Form.onSubmit(e, e.srcElement || e.currentTarget) };
				this.form.attachedSubmitHandler = true;                 
			}
			if (!this.form.attachedResetHandler) {
				Spry.Widget.Utils.addEventListener(this.form, "reset", function(e) { e = e || event; return Spry.Widget.Form.onReset(e, e.srcElement || e.currentTarget) }, false);
				this.form.attachedResetHandler = true;                 
			}
			// add the currrent widget to the "onSubmit" check queue;
			Spry.Widget.Form.onSubmitWidgetQueue.push(this);
		}
	}	
};

Spry.Widget.ValidationTextField.prototype.isDisabled = function() {
	return this.input && (this.input.disabled || this.input.readOnly) || !this.input;
};

Spry.Widget.ValidationTextField.prototype.getElement = function(ele)
{
	if (ele && typeof ele == "string")
		return document.getElementById(ele);
	return ele;
};

Spry.Widget.ValidationTextField.addLoadListener = function(handler)
{
	if (typeof window.addEventListener != 'undefined')
		window.addEventListener('load', handler, false);
	else if (typeof document.addEventListener != 'undefined')
		document.addEventListener('load', handler, false);
	else if (typeof window.attachEvent != 'undefined')
		window.attachEvent('onload', handler);
};

Spry.Widget.ValidationTextField.processLoadQueue = function(handler)
{
	Spry.Widget.ValidationTextField.onloadDidFire = true;
	var q = Spry.Widget.ValidationTextField.loadQueue;
	var qlen = q.length;
	for (var i = 0; i < qlen; i++)
		q[i].attachBehaviors();
};

Spry.Widget.ValidationTextField.addLoadListener(Spry.Widget.ValidationTextField.processLoadQueue);
Spry.Widget.ValidationTextField.addLoadListener(function(){
	Spry.Widget.Utils.addEventListener(window, "unload", Spry.Widget.Form.destroyAll, false);
});

Spry.Widget.ValidationTextField.prototype.setValue = function(newValue) {
	this.flags.locked = true;
	this.input.value = newValue;
	this.flags.locked = false;
	this.oldValue = newValue;
	if (!Spry.is.ie) {
		this.onChange();
	}
};

/**
 * save the state of the input (selection and value) so we can revert to it
 * should call this just before modifying the input value
 */
Spry.Widget.ValidationTextField.prototype.saveState = function()
{
	this.oldValue = this.input.value;
	this.selection.update();
};

Spry.Widget.ValidationTextField.prototype.revertState = function(revertValue)
{
	if (revertValue != this.input.value) {
		this.input.readOnly = true;
		this.input.value = revertValue;
		this.input.readOnly = false;
		if (Spry.is.safari && this.flags.active) {
			this.input.focus();
		}
	}
	this.selection.moveTo(this.selection.start, this.selection.end);

	this.redTextFlash();
};

Spry.Widget.ValidationTextField.prototype.removeHint = function()
{
	if (this.flags.hintOn) {
		this.input.value = "";
		this.flags.hintOn = false;
	}
};

Spry.Widget.ValidationTextField.prototype.putHint = function()
{
	if(this.hint && this.input && this.input.type == "text" && this.input.value == "") {
		this.flags.hintOn = true;
		this.input.value = this.hint;
	}
};

Spry.Widget.ValidationTextField.prototype.redTextFlash = function()
{
	var self = this;
	this.addClassName(this.element, this.textfieldFlashTextClass);
	setTimeout(function() {
		self.removeClassName(self.element, self.textfieldFlashTextClass)
	}, 100);
};

Spry.Widget.ValidationTextField.prototype.doValidations = function(testValue, revertValue)
{
	if (this.isDisabled()) return false;

	if (this.flags.locked) {
		return false;
	}

	if (testValue.length == 0 && !this.isRequired) {
		this.errors = 0;
		return false;
	}
	this.flags.locked = true;

	var mustRevert = false;
	var continueValidations = true;
	if (!this.options.isRequired && testValue.length == 0) {
		continueValidations = false;
	}

	var errors = 0;
	var fixedValue = testValue;

	//characterMasking - test if all characters are valid with the characterMasking (keyboard filter)
	if (this.useCharacterMasking && this.characterMasking) {
		for(var i=0; i<testValue.length; i++) {
			if (!this.characterMasking.test(testValue.charAt(i))) {
				errors = errors | Spry.Widget.ValidationTextField.ERROR_FORMAT;
				fixedValue = revertValue;
				mustRevert = true;
				break;
			}
		}
	}

	//regExpFilter - character mask positioning (additional mask to restrict some characters only in some position)
	if (!mustRevert && this.useCharacterMasking && this.regExpFilter) {
		if (!this.regExpFilter.test(fixedValue)) {
			errors = errors | Spry.Widget.ValidationTextField.ERROR_FORMAT;
			mustRevert = true;
		}
	}

	//pattern - testValue matches the pattern so far
	if (!mustRevert && this.pattern) {
		var currentRegExp = this.patternToRegExp(testValue.length);
		if (!currentRegExp.test(testValue)) {
			errors = errors | Spry.Widget.ValidationTextField.ERROR_FORMAT;
			mustRevert = true;
		} else if (this.patternLength != testValue.length) {
			//testValue matches pattern so far, but it's not ok if it does not have the proper length
			//do not revert, but should show the error
			errors = errors | Spry.Widget.ValidationTextField.ERROR_FORMAT;
		}
	}

	if (fixedValue == '') {
		errors = errors | Spry.Widget.ValidationTextField.ERROR_REQUIRED;
	}

	if (!mustRevert && this.pattern && this.useCharacterMasking) {
		var n = this.getAutoComplete(testValue.length);
		if (n) {
			fixedValue += n;
		}
	}

	if(!mustRevert && this.minChars !== null  && continueValidations) {
		if (testValue.length < this.minChars) {
			errors = errors | Spry.Widget.ValidationTextField.ERROR_CHARS_MIN;
			continueValidations = false;
		}
	}

	if(!mustRevert && this.maxChars !== null && continueValidations) {
		if (testValue.length > this.maxChars) {
			errors = errors | Spry.Widget.ValidationTextField.ERROR_CHARS_MAX;
			continueValidations = false;
		}
	}

	//validation - testValue passes widget validation function
	if (!mustRevert && this.validation && continueValidations) {
		var value = this.validation(fixedValue, this.options);
		if (false === value) {
			errors = errors | Spry.Widget.ValidationTextField.ERROR_FORMAT;
			continueValidations = false;
		} else {
			this.typedValue = value;
		}
	}

	if(!mustRevert && this.validation && this.minValue !== null && continueValidations) {
		var minValue = this.validation(this.minValue, this.options);
		if (minValue !== false) {
			if (this.typedValue < minValue) {
				errors = errors | Spry.Widget.ValidationTextField.ERROR_RANGE_MIN;
				continueValidations = false;
			}
		}
	}

	if(!mustRevert && this.validation && this.maxValue !== null && continueValidations) {
		var maxValue = this.validation(this.maxValue, this.options);
		if (maxValue !== false) {
			if( this.typedValue > maxValue) {
				errors = errors | Spry.Widget.ValidationTextField.ERROR_RANGE_MAX;
				continueValidations = false;
			}
		}
	}

	//an invalid value was tested; must make sure it does not get inside the input
	if (this.useCharacterMasking && mustRevert) {
		this.revertState(revertValue);
	}

	this.errors = errors;
	this.fixedValue = fixedValue;

	this.flags.locked = false;

	return mustRevert;
};

Spry.Widget.ValidationTextField.prototype.onChange = function(e)
{
	if (Spry.is.opera && this.flags.operaRevertOnKeyUp) {
		return true;
	}
	if (Spry.is.ie && e && e.propertyName != 'value') {
		return true;
	}

	if (this.flags.drop) {
		//delay this if it's a drop operation
		var self = this;
		setTimeout(function() {
			self.flags.drop = false;
			self.onChange(null);
		}, 0);
		return;
	}

	if (this.flags.hintOn) {
		return true;
	}

	if (this.keyCode == 8 || this.keyCode == 46 ) {
		var mustRevert = this.doValidations(this.input.value, this.input.value);
		this.oldValue = this.input.value;
		if ((mustRevert || this.errors) && this.validateOn & Spry.Widget.ValidationTextField.ONCHANGE) {
			var self = this;
			setTimeout(function() {self.validate();}, 0);
			return true;
		}
	}

	var mustRevert = this.doValidations(this.input.value, this.oldValue);
	if ((!mustRevert || this.errors) && this.validateOn & Spry.Widget.ValidationTextField.ONCHANGE) {
		var self = this;
		setTimeout(function() {self.validate();}, 0);
	}
	return true;
};

Spry.Widget.ValidationTextField.prototype.onKeyUp = function(e) {
	if (this.flags.operaRevertOnKeyUp) {
		this.setValue(this.oldValue);
		Spry.Widget.Utils.stopEvent(e);
		this.selection.moveTo(this.selection.start, this.selection.start);
		this.flags.operaRevertOnKeyUp = false;
		return false;
	}
	if (this.flags.operaPasteOperation) {
		window.clearInterval(this.flags.operaPasteOperation);
		this.flags.operaPasteOperation = null;
	}
};

Spry.Widget.ValidationTextField.prototype.operaPasteMonitor = function() {
	if (this.input.value != this.oldValue) {
		var mustRevert = this.doValidations(this.input.value, this.input.value);
		if (mustRevert) {
			this.setValue(this.oldValue);
			this.selection.moveTo(this.selection.start, this.selection.start);
		} else {
			this.onChange();
		}
	}
};


Spry.Widget.ValidationTextField.prototype.compileDatePattern = function () 
{
	var dateValidationPatternString = "";
	var groupPatterns = [];
	var fullGroupPatterns = [];
	var autocompleteCharacters = [];
	
	
	var formatRegExp = /^([mdy]+)([\.\-\/\\\s]+)([mdy]+)([\.\-\/\\\s]+)([mdy]+)$/i;
	var formatGroups = this.options.format.match(formatRegExp);
	if (formatGroups !== null) {
		for (var i=1; i<formatGroups.length; i++) {
			switch (formatGroups[i].toLowerCase()) {
				case "dd":
					groupPatterns[i-1] = "\\d{1,2}";
					fullGroupPatterns[i-1] = "\\d\\d";
					dateValidationPatternString += "(" + groupPatterns[i-1] + ")";
					autocompleteCharacters[i-1] = null;
					break;
				case "mm":
					groupPatterns[i-1] = "\\d{1,2}";
					fullGroupPatterns[i-1] = "\\d\\d";
					dateValidationPatternString += "(" + groupPatterns[i-1] + ")";
					autocompleteCharacters[i-1] = null;
					break;
				case "yy":
					groupPatterns[i-1] = "\\d{1,2}";
					fullGroupPatterns[i-1] = "\\d\\d";
					dateValidationPatternString += "(\\d\\d)";
					autocompleteCharacters[i-1] = null;
					break;
				case "yyyy":
					groupPatterns[i-1] = "\\d{1,4}";
					fullGroupPatterns[i-1] = "\\d\\d\\d\\d";
					dateValidationPatternString += "(\\d\\d\\d\\d)";
					autocompleteCharacters[i-1] = null;
					break;
				default:
					groupPatterns[i-1] = fullGroupPatterns[i-1] = Spry.Widget.ValidationTextField.regExpFromChars(formatGroups[i]);
					dateValidationPatternString += "["+ groupPatterns[i-1] + "]";
					autocompleteCharacters[i-1] = formatGroups[i];
			}
		}
	}
	this.dateValidationPattern = new RegExp("^" + dateValidationPatternString + "$" , "")
	this.dateAutocompleteCharacters = autocompleteCharacters;
	this.dateGroupPatterns = groupPatterns;
	this.dateFullGroupPatterns = fullGroupPatterns;
	this.lastDateGroup = formatGroups.length-2;
}

Spry.Widget.ValidationTextField.prototype.getRegExpForGroup = function (group) 
{
	var ret = '^';
	for (var j = 0; j <= group; j++) ret += this.dateGroupPatterns[j];
	ret += '$';
	return new RegExp(ret, "");	
}

Spry.Widget.ValidationTextField.prototype.getRegExpForFullGroup = function (group) 
{
	var ret = '^';
	for (var j = 0; j < group; j++) ret += this.dateGroupPatterns[j];
	ret += this.dateFullGroupPatterns[group];
	return new RegExp(ret, "");	
}

Spry.Widget.ValidationTextField.prototype.getDateGroup = function(value, pos) 
{
	if (pos == 0) return 0;
	var test_value = value.substring(0, pos);
	for (var i=0; i <= this.lastDateGroup; i++) 
		if (this.getRegExpForGroup(i).test(test_value)) return i;
	return -1;
};


Spry.Widget.ValidationTextField.prototype.isDateGroupFull = function(value, group) 
{
	return this.getRegExpForFullGroup(group).test(value);
}

Spry.Widget.ValidationTextField.prototype.isValueValid = function(value, pos, group) 
{
	var test_value = value.substring(0, pos);
	return this.getRegExpForGroup(group).test(test_value);
	}


Spry.Widget.ValidationTextField.prototype.isPositionAtEndOfGroup = function (value, pos, group)
{
	var test_value = value.substring(0, pos);
	return this.getRegExpForFullGroup(group).test(test_value);
}

Spry.Widget.ValidationTextField.prototype.nextDateDelimiterExists = function (value, pos, group)
{
	var autocomplete = this.dateAutocompleteCharacters[group+1];
	if (value.length < pos  + autocomplete.length) 
		return false;
	else 
	{
		var test_value = value.substring(pos, pos+autocomplete.length);
		if (test_value == autocomplete) 
			return true;
	}
	return false;
}



Spry.Widget.ValidationTextField.prototype.onKeyPress = function(e)
{
	if (this.flags.skp) {
		this.flags.skp = false;
		Spry.Widget.Utils.stopEvent(e);
		return false;
	}

	if (e.ctrlKey || e.metaKey || !this.useCharacterMasking) {
		return true;
	}
/*
	if (Spry.is.safari) {
		if ( (e.timeStamp - this.flags.lastKeyPressedTimeStamp)<10 ) {
			return true;
		}
		this.flags.lastKeyPressedTimeStamp = e.timeStamp;
	}
*/
	if (Spry.is.opera && this.flags.operaRevertOnKeyUp) {
		Spry.Widget.Utils.stopEvent(e);
		return false;
	}

	if (this.keyCode == 8 || this.keyCode == 46) {
		var mr = this.doValidations(this.input.value, this.input.value);
		if (mr) {
			return true;
		}
	}

	var pressed = Spry.Widget.Utils.getCharacterFromEvent(e);

	if (pressed && this.characterMasking) {
		if (!this.characterMasking.test(pressed)) {
			Spry.Widget.Utils.stopEvent(e);
			this.redTextFlash();
			return false;
		}
	}

	if(pressed && this.pattern) {
		var currentPatternChar = this.patternCharacters[this.selection.start];
		if (/[ax]/i.test(currentPatternChar)) {
			//convert the entered character to the pattern character case
			if (currentPatternChar.toLowerCase() == currentPatternChar) {
				pressed = pressed.toLowerCase();
			} else {
				pressed = pressed.toUpperCase();
			}
		}

		var autocomplete = this.getAutoComplete(this.selection.start);
		if (this.selection.start == this.oldValue.length) {
			if (this.oldValue.length < this.patternLength) {
				if (autocomplete) {
					Spry.Widget.Utils.stopEvent(e);
					var futureValue = this.oldValue.substring(0, this.selection.start) + autocomplete + pressed;
					var mustRevert = this.doValidations(futureValue, this.oldValue);
					if (!mustRevert) {
						this.setValue(this.fixedValue);
						this.selection.moveTo(this.fixedValue.length, this.fixedValue.length);
					} else {
						this.setValue(this.oldValue.substring(0, this.selection.start) + autocomplete);
						this.selection.moveTo(this.selection.start + autocomplete.length, this.selection.start + autocomplete.length);
					}
					return false;
				}
			} else {
				Spry.Widget.Utils.stopEvent(e);
				this.setValue(this.input.value);
				return false;
			}
		} else if (autocomplete) {
			Spry.Widget.Utils.stopEvent(e);
			this.selection.moveTo(this.selection.start + autocomplete.length, this.selection.start + autocomplete.length);
			return false;
		}

		Spry.Widget.Utils.stopEvent(e);

		var futureValue = this.oldValue.substring(0, this.selection.start) + pressed + this.oldValue.substring(this.selection.start + 1);
		var mustRevert = this.doValidations(futureValue, this.oldValue);

		if (!mustRevert) {
			autocomplete = this.getAutoComplete(this.selection.start + 1);
			this.setValue(this.fixedValue);
			this.selection.moveTo(this.selection.start + 1 + autocomplete.length, this.selection.start + 1 + autocomplete.length);
		} else {
			this.selection.moveTo(this.selection.start, this.selection.start);
		}

		return false;
	}
	
	
	if (pressed && this.type == 'date' && this.useCharacterMasking) 
	{
		var group = this.getDateGroup(this.oldValue, this.selection.start);
		if (group != -1) {
			Spry.Widget.Utils.stopEvent(e);
			if ( (group % 2) !=0 ) 
				group ++;
			
			if (this.isDateGroupFull(this.oldValue, group)) 
			{
				if(this.isPositionAtEndOfGroup(this.oldValue, this.selection.start, group))
				{
					if(group == this.lastDateGroup) 
					{
						this.redTextFlash(); return false;
					}
					else 
					{
						// add or jump over autocomplete delimiter
						var autocomplete = this.dateAutocompleteCharacters[group+1];
						
						if (this.nextDateDelimiterExists(this.oldValue, this.selection.start, group))
						{
							var autocomplete = this.dateAutocompleteCharacters[group+1];
							
							this.selection.moveTo(this.selection.start + autocomplete.length, this.selection.start + autocomplete.length);
							if (pressed == autocomplete) 
								return false;
							
							if (this.isDateGroupFull(this.oldValue, group+2)) 
								// need to overwrite first char in the next digit group
								futureValue = this.oldValue.substring(0, this.selection.start) + pressed + this.oldValue.substring(this.selection.start + 1);
							else
								futureValue = this.oldValue.substring(0, this.selection.start) + pressed + this.oldValue.substring(this.selection.start);
								
							if (!this.isValueValid(futureValue, this.selection.start + 1, group +2 )) 
							{
								this.redTextFlash(); return false;						
							}
							else
							{
								this.setValue (futureValue);
								this.selection.moveTo(this.selection.start + 1, this.selection.start + 1);									
							}
							return false;					
						}
						else 
						{
							var autocomplete = this.dateAutocompleteCharacters[group+1];
							
							var insertedValue = autocomplete + pressed;
							futureValue = this.oldValue.substring(0, this.selection.start) + insertedValue + this.oldValue.substring(this.selection.start);
							if (!this.isValueValid(futureValue, this.selection.start + insertedValue.length, group +2 )) 
							{
								// block this type
								insertedValue = autocomplete;
								futureValue = this.oldValue.substring(0, this.selection.start) + insertedValue + this.oldValue.substring(this.selection.start);
								this.setValue (futureValue);
								this.selection.moveTo(this.selection.start + insertedValue.length, this.selection.start + insertedValue.length);									
								this.redTextFlash(); return false;
							}
							else 
							{
								this.setValue (futureValue);
								this.selection.moveTo(this.selection.start + insertedValue.length, this.selection.start + insertedValue.length);									
								return false;
							}
						}
						
					}
				}
				else
				{
					// it's not the end of the full digits group
					
					// overwrite
					var movePosition = 1;
					futureValue = this.oldValue.substring(0, this.selection.start) + pressed + this.oldValue.substring(this.selection.start + 1);
					if (!this.isValueValid(futureValue, this.selection.start + 1, group)) 
					{
						this.redTextFlash(); return false;
					}
					else 
					{
						if(this.isPositionAtEndOfGroup(futureValue, this.selection.start+1, group)) 
						{
							if (group != this.lastDateGroup)
							{
								if (this.nextDateDelimiterExists(futureValue, this.selection.start + 1, group))
								{
									var autocomplete = this.dateAutocompleteCharacters[group+1];
									movePosition = 1 + autocomplete.length;
								}
								else
								{
									var autocomplete = this.dateAutocompleteCharacters[group+1];
									futureValue = this.oldValue.substring(0, this.selection.start) + pressed + autocomplete + this.oldValue.substring(this.selection.start + 1);
									movePosition = 1 + autocomplete.length;
								}
							}
						}
						this.setValue (futureValue);
						this.selection.moveTo(this.selection.start + movePosition, this.selection.start + movePosition);									
						return false;							
					}			
				}
			}
			else
			{
				// date group is not full
				// insert
				futureValue = this.oldValue.substring(0, this.selection.start) + pressed + this.oldValue.substring(this.selection.start);
				var movePosition = 1;
				if (!this.isValueValid(futureValue, this.selection.start + 1, group) && !this.isValueValid(futureValue, this.selection.start + 1, group+1)) 
				{
					this.redTextFlash(); return false;
				}
				else 
				{
					var autocomplete = this.dateAutocompleteCharacters[group+1];
					if (pressed == autocomplete) 
					{
						if (this.nextDateDelimiterExists(this.oldValue, this.selection.start, group))
						{
							futureValue = this.oldValue;
							movePosition = 1;
						}
					}
					else
					{
						if(this.isPositionAtEndOfGroup(futureValue, this.selection.start+1, group)) 
						{
							if (group != this.lastDateGroup)
							{
								if (this.nextDateDelimiterExists(futureValue, this.selection.start + 1, group))
								{
									var autocomplete = this.dateAutocompleteCharacters[group+1];
									movePosition = 1 + autocomplete.length;
								}
								else
								{
									var autocomplete = this.dateAutocompleteCharacters[group+1];
									futureValue = this.oldValue.substring(0, this.selection.start) + pressed + autocomplete + this.oldValue.substring(this.selection.start + 1);
									movePosition = 1 + autocomplete.length;
								}
							}
						}
					}
					this.setValue (futureValue);
					this.selection.moveTo(this.selection.start + movePosition, this.selection.start + movePosition);									
					return false;						
				}	
			}
		}
		return false;
	}
	
};

Spry.Widget.ValidationTextField.prototype.onKeyDown = function(e)
{
	this.saveState();
	this.keyCode = e.keyCode;

	if (Spry.is.opera) {
		if (this.flags.operaPasteOperation) {
			window.clearInterval(this.flags.operaPasteOperation);
			this.flags.operaPasteOperation = null;
		}
		if (e.ctrlKey) {
			var pressed = Spry.Widget.Utils.getCharacterFromEvent(e);
			if (pressed && 'vx'.indexOf(pressed.toLowerCase()) != -1) {
				var self = this;
				this.flags.operaPasteOperation = window.setInterval(function() { self.operaPasteMonitor();}, 1);
				return true;
			}
		}
	}

	if (this.keyCode != 8 && this.keyCode != 46 && Spry.Widget.Utils.isSpecialKey(e)) {
		return true;
	}
	if (this.keyCode == 8 || this.keyCode == 46 ) {
		var mr = this.doValidations(this.input.value, this.input.value);
		if (mr) {
			return true;
		}
	}

	//DELETE
	if (this.useCharacterMasking && this.pattern && this.keyCode == 46) {
		if (e.ctrlKey) {
			//delete from selection until end
			this.setValue(this.input.value.substring(0, this.selection.start));
		} else if (this.selection.end == this.input.value.length || this.selection.start == this.input.value.length-1){
			//allow key if selection is at end (will delete selection)
			return true;
		} else {
			this.flags.operaRevertOnKeyUp = true;
		}
		if (Spry.is.mozilla && Spry.is.mac) {
			this.flags.skp = true;
		}
		Spry.Widget.Utils.stopEvent(e);
		return false;
	}

	//BACKSPACE
	if (this.useCharacterMasking && this.pattern && !e.ctrlKey && this.keyCode == 8) {
		if (this.selection.start == this.input.value.length) {
			//delete with BACKSPACE from the end of the input value only
			var n = this.getAutoComplete(this.selection.start, -1);
			this.setValue(this.input.value.substring(0, this.input.value.length - (Spry.is.opera?0:1) - n.length));
			if (Spry.is.opera) {
				//cant stop the event on Opera, we'll just preserve the selection so delete will act on it
				this.selection.start = this.selection.start - 1 - n.length;
				this.selection.end = this.selection.end - 1 - n.length;
			}
		} else if (this.selection.end == this.input.value.length){
			//allow BACKSPACE if selection is at end (will delete selection)
			return true;
		} else {
			this.flags.operaRevertOnKeyUp = true;
		}
		if (Spry.is.mozilla && Spry.is.mac) {
			this.flags.skp = true;
		} 
		Spry.Widget.Utils.stopEvent(e);
		return false;
	}

	return true;
};

Spry.Widget.ValidationTextField.prototype.onMouseDown = function(e)
{
	if (this.flags.active) {
		//mousedown fires before focus
		//avoid double saveState on first focus by mousedown by checking if the control has focus
		//do nothing if it's not focused because saveState will be called onfocus
		this.saveState();
	}
};

Spry.Widget.ValidationTextField.prototype.onDrop = function(e)
{
	//mark that a drop operation is in progress to avoid race conditions with event handlers for other events
	//especially onchange and onfocus
	this.flags.drop = true;
	this.removeHint();
	this.saveState();
	this.flags.active = true;
	this.addClassName(this.element, this.focusClass);
};

Spry.Widget.ValidationTextField.prototype.onFocus = function(e)
{
	if (this.flags.drop) {
		return;
	}
	this.removeHint();

	if (this.pattern && this.useCharacterMasking) {
		var autocomplete = this.getAutoComplete(this.selection.start);
		this.setValue(this.input.value + autocomplete);
		this.selection.moveTo(this.input.value.length, this.input.value.length);
	}
	
	this.saveState();
	this.flags.active = true;
	this.addClassName(this.element, this.focusClass);
};
	
Spry.Widget.ValidationTextField.prototype.onBlur = function(e)
{
	this.flags.active = false;
	this.removeClassName(this.element, this.focusClass);
	var mustRevert = this.doValidations(this.input.value, this.input.value);

	if (this.validateOn & Spry.Widget.ValidationTextField.ONBLUR) {
		this.validate();
	}
	var self = this;
	setTimeout(function() {self.putHint();}, 10);
	return true;
};

Spry.Widget.ValidationTextField.prototype.compilePattern = function() {
	if (!this.pattern) {
		return;
	}
	var compiled = [];
	var regexps = [];
	var patternCharacters = [];
	var idx = 0;
	var c = '', p = '';
	for (var i=0; i<this.pattern.length; i++) {
		c = this.pattern.charAt(i);
		if (p == '\\') {
			if (/[0ABXY\?]/i.test(c)) {
				regexps[idx - 1] = c;
			} else {
				regexps[idx - 1] = Spry.Widget.ValidationTextField.regExpFromChars(c);
			}
			compiled[idx - 1] = c;
			patternCharacters[idx - 1] = null;
			p = '';
			continue;
		}
		regexps[idx] = Spry.Widget.ValidationTextField.regExpFromChars(c);
		if (/[0ABXY\?]/i.test(c)) {
			compiled[idx] = null;
			patternCharacters[idx] = c;
		} else if (c == '\\') {
			compiled[idx] = c;
			patternCharacters[idx] = '\\';
		} else {
			compiled[idx] = c;
			patternCharacters[idx] = null;
		}
		idx++;
		p = c;
	}

	this.autoCompleteCharacters = compiled;
	this.compiledPattern = regexps;
	this.patternCharacters = patternCharacters;
	this.patternLength = compiled.length;
};

Spry.Widget.ValidationTextField.prototype.getAutoComplete = function(from, direction) {
	if (direction == -1) {
		var n = '', m = '';
		while(from && (n = this.getAutoComplete(--from) )) {
			m = n;
		}
		return m;
	}
	var ret = '', c = '';
	for (var i=from; i<this.autoCompleteCharacters.length; i++) {
		c = this.autoCompleteCharacters[i];
		if (c) {
			ret += c;
		} else {
			break;
		}
	}
	return ret;
};

Spry.Widget.ValidationTextField.regExpFromChars = function (string) {
	//string contains pattern characters
	var ret = '', character = '';
	for (var i = 0; i<string.length; i++) {
		character = string.charAt(i);
		switch (character) {
			case '0': ret += '\\d';break;
			case 'A': ret += '[A-Z]';break;
//			case 'A': ret += '[\u0041-\u005A\u0061-\u007A\u0100-\u017E\u0180-\u0233\u0391-\u03CE\u0410-\u044F\u05D0-\u05EA\u0621-\u063A\u0641-\u064A\u0661-\u06D3\u06F1-\u06FE]';break;
			case 'a': ret += '[a-z]';break;
//			case 'a': ret += '[\u0080-\u00FF]';break;
			case 'B': case 'b': ret += '[a-zA-Z]';break;
			case 'x': ret += '[0-9a-z]';break;
			case 'X': ret += '[0-9A-Z]';break;
			case 'Y': case 'y': ret += '[0-9a-zA-Z]';break;
			case '?': ret += '.';break;
			case '1':case '2':case '3':case '4':case '5':case '6':case '7':case '8':case '9':
				ret += character;
				break;
			case 'c': case 'C': case 'e': case 'E': case 'f': case 'F':case 'r':case 'd': case 'D':case 'n':case 's':case 'S':case 'w':case 'W':case 't':case 'v':
				ret += character;
				break;
			default: ret += '\\' + character;
		}
	}
	return ret;
};

Spry.Widget.ValidationTextField.prototype.patternToRegExp = function(len) {
	var ret = '^';
	var end = Math.min(this.compiledPattern.length, len);
	for (var i=0; i < end; i++) {
		ret += this.compiledPattern[i];
	}
	ret += '$';
	ret = new RegExp(ret, "");
	return ret;
};

Spry.Widget.ValidationTextField.prototype.reset = function() {
	this.removeHint();
	this.oldValue = this.input.defaultValue;
	this.removeClassName(this.element, this.requiredClass);
	this.removeClassName(this.element, this.invalidFormatClass);
	this.removeClassName(this.element, this.invalidRangeMinClass);
	this.removeClassName(this.element, this.invalidRangeMaxClass);
	this.removeClassName(this.element, this.invalidCharsMinClass);
	this.removeClassName(this.element, this.invalidCharsMaxClass);
	this.removeClassName(this.element, this.validClass);
	var self = this;
	setTimeout(function() {self.putHint();}, 10);
};

Spry.Widget.ValidationTextField.prototype.validate = function() {
	this.removeClassName(this.element, this.requiredClass);
	this.removeClassName(this.element, this.invalidFormatClass);
	this.removeClassName(this.element, this.invalidRangeMinClass);
	this.removeClassName(this.element, this.invalidRangeMaxClass);
	this.removeClassName(this.element, this.invalidCharsMinClass);
	this.removeClassName(this.element, this.invalidCharsMaxClass);
	this.removeClassName(this.element, this.validClass);

	//possible states: required, format, rangeMin, rangeMax, charsMin, charsMax
	if (this.validateOn & Spry.Widget.ValidationTextField.ONSUBMIT) {

		this.removeHint();
		this.doValidations(this.input.value, this.input.value);

		if(!this.flags.active) {
			var self = this;
			setTimeout(function() {self.putHint();}, 10);
		}
	}

	if (this.isRequired && this.errors & Spry.Widget.ValidationTextField.ERROR_REQUIRED) {
		this.addClassName(this.element, this.requiredClass);
		return false;
	}

	if (this.errors & Spry.Widget.ValidationTextField.ERROR_FORMAT) {
		this.addClassName(this.element, this.invalidFormatClass);
		return false;
	}

	if (this.errors & Spry.Widget.ValidationTextField.ERROR_RANGE_MIN) {
		this.addClassName(this.element, this.invalidRangeMinClass);
		return false;
	}

	if (this.errors & Spry.Widget.ValidationTextField.ERROR_RANGE_MAX) {
		this.addClassName(this.element, this.invalidRangeMaxClass);
		return false;
	}

	if (this.errors & Spry.Widget.ValidationTextField.ERROR_CHARS_MIN) {
		this.addClassName(this.element, this.invalidCharsMinClass);
		return false;
	}

	if (this.errors & Spry.Widget.ValidationTextField.ERROR_CHARS_MAX) {
		this.addClassName(this.element, this.invalidCharsMaxClass);
		return false;
	}

	this.addClassName(this.element, this.validClass);
	return true;
}

Spry.Widget.ValidationTextField.prototype.addClassName = function(ele, className)
{
	if (!ele || !className || (ele.className && ele.className.search(new RegExp("\\b" + className + "\\b")) != -1))
		return;
	ele.className += (ele.className ? " " : "") + className;
};

Spry.Widget.ValidationTextField.prototype.removeClassName = function(ele, className)
{
	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"), "");
};

/**
 * SelectionDescriptor is a wrapper for input type text selection methods and properties 
 * as implemented by various  browsers
 */
Spry.Widget.SelectionDescriptor = function (element)
{
	this.element = element;
	this.update();
};

Spry.Widget.SelectionDescriptor.prototype.update = function()
{
	if (Spry.is.ie && Spry.is.windows) {
		if (this.element.nodeName == "TEXTAREA") {
		var range = this.element.ownerDocument.selection.createRange();
		if (range.parentElement() == this.element){
			var range_all = this.element.ownerDocument.body.createTextRange();
			range_all.moveToElementText(this.element);
			for (var sel_start = 0; range_all.compareEndPoints('StartToStart', range) < 0; sel_start ++){
			 	range_all.moveStart('character', 1);
			}
			this.start = sel_start;
			// create a selection of the whole this.element
			range_all = this.element.ownerDocument.body.createTextRange();
			range_all.moveToElementText(this.element);
			for (var sel_end = 0; range_all.compareEndPoints('StartToEnd', range) < 0; sel_end++){
				range_all.moveStart('character', 1);
			}
			this.end = sel_end;
			this.length = this.end - this.start;
			// get selected and surrounding text
			this.text = range.text;
		 }
		} else if (this.element.nodeName == "INPUT"){
			this.range = this.element.ownerDocument.selection.createRange();
			this.length = this.range.text.length;
			var clone = this.range.duplicate();
			this.start = -clone.moveStart("character", -10000);
			clone = this.range.duplicate();
			clone.collapse(false);
			this.end = -clone.moveStart("character", -10000);
			this.text = this.range.text;
		}
	} else {
		var tmp = this.element;
		var selectionStart = 0;
		var selectionEnd = 0;
		try { selectionStart = tmp.selectionStart;} catch(err) {}
		try { selectionEnd = tmp.selectionEnd;} catch(err) {}

		if (Spry.is.safari) {
			if (selectionStart == 2147483647) {
				selectionStart = 0;
			}
			if (selectionEnd == 2147483647) {
				selectionEnd = 0;
			}
		}
		this.start = selectionStart;
		this.end = selectionEnd;
		this.length = selectionEnd - selectionStart;
		this.text = this.element.value.substring(selectionStart, selectionEnd);
	}
};

Spry.Widget.SelectionDescriptor.prototype.destroy = function() {
	try { delete this.range} catch(err) {}
	try { delete this.element} catch(err) {}
};

Spry.Widget.SelectionDescriptor.prototype.move = function(amount)
{
	if (Spry.is.ie && Spry.is.windows) {
		this.range.move("character", amount);
		this.range.select();
	} else {
		try { this.element.selectionStart++;}catch(err) {}
	}
	this.update();
};

Spry.Widget.SelectionDescriptor.prototype.moveTo = function(start, end)
{
	if (Spry.is.ie && Spry.is.windows) {
		if (this.element.nodeName == "TEXTAREA") {
			var ta_range = this.element.createTextRange();
			this.range = this.element.createTextRange();
			this.range.move("character", start);
			this.range.moveEnd("character", end - start);
			
			var c1 = this.range.compareEndPoints("StartToStart", ta_range);
			if (c1 < 0) {
				this.range.setEndPoint("StartToStart", ta_range);
			}

			var c2 = this.range.compareEndPoints("EndToEnd", ta_range);
			if (c2 > 0) {
				this.range.setEndPoint("EndToEnd", ta_range);
			}
		} else if (this.element.nodeName == "INPUT"){
			this.range = this.element.ownerDocument.selection.createRange();
			this.range.move("character", -10000);
			this.start = this.range.moveStart("character", start);
			this.end = this.start + this.range.moveEnd("character", end - start);
		}
		this.range.select();
	} else {
		this.start = start;
		try { this.element.selectionStart = start;} catch(err) {}
		this.end = end;
		try { this.element.selectionEnd = end;} catch(err) {}
	}
	this.ignore = true;
	this.update();
};

Spry.Widget.SelectionDescriptor.prototype.moveEnd = function(amount)
{
	if (Spry.is.ie && Spry.is.windows) {
		this.range.moveEnd("character", amount);
		this.range.select();
	} else {
		try { this.element.selectionEnd++;} catch(err) {}
	}
	this.update();
};

Spry.Widget.SelectionDescriptor.prototype.collapse = function(begin)
{
	if (Spry.is.ie && Spry.is.windows) {
		this.range = this.element.ownerDocument.selection.createRange();
		this.range.collapse(begin);
		this.range.select();
	} else {
		if (begin) {
			try { this.element.selectionEnd = this.element.selectionStart;} catch(err) {}
		} else {
			try { this.element.selectionStart = this.element.selectionEnd;} catch(err) {}
		}
	}

	this.update();
};

//////////////////////////////////////////////////////////////////////
//
// Spry.Widget.Form - common for all widgets
//
//////////////////////////////////////////////////////////////////////

if (!Spry.Widget.Form) Spry.Widget.Form = {};
if (!Spry.Widget.Form.onSubmitWidgetQueue) Spry.Widget.Form.onSubmitWidgetQueue = [];

if (!Spry.Widget.Form.validate) {
	Spry.Widget.Form.validate = function(vform) {
		var isValid = true;
		var isElementValid = true;
		var q = Spry.Widget.Form.onSubmitWidgetQueue;
		var qlen = q.length;
		for (var i = 0; i < qlen; i++) {
			if (!q[i].isDisabled() && q[i].form == vform) {
				isElementValid = q[i].validate();
				isValid = isElementValid && isValid;
			}
		}
		return isValid;
	}
};

if (!Spry.Widget.Form.onSubmit) {
	Spry.Widget.Form.onSubmit = function(e, form)
	{
		if (Spry.Widget.Form.validate(form) == false) {
			return false;
		}
		return true;
	};
};

if (!Spry.Widget.Form.onReset) {
	Spry.Widget.Form.onReset = function(e, vform)
	{
		var q = Spry.Widget.Form.onSubmitWidgetQueue;
		var qlen = q.length;
		for (var i = 0; i < qlen; i++) {
			if (!q[i].isDisabled() && q[i].form == vform && typeof(q[i].reset) == 'function') {
				q[i].reset();
			}
		}
		return true;
	};
};

if (!Spry.Widget.Form.destroy) {
	Spry.Widget.Form.destroy = function(form)
	{
		var q = Spry.Widget.Form.onSubmitWidgetQueue;
		for (var i = 0; i < Spry.Widget.Form.onSubmitWidgetQueue.length; i++) {
			if (q[i].form == form && typeof(q[i].destroy) == 'function') {
				q[i].destroy();
				i--;
			}
		}
	}
};

if (!Spry.Widget.Form.destroyAll) {
	Spry.Widget.Form.destroyAll = function()
	{
		var q = Spry.Widget.Form.onSubmitWidgetQueue;
		for (var i = 0; i < Spry.Widget.Form.onSubmitWidgetQueue.length; i++) {
			if (typeof(q[i].destroy) == 'function') {
				q[i].destroy();
				i--;
			}
		}
	}
};

//////////////////////////////////////////////////////////////////////
//
// Spry.Widget.Utils
//
//////////////////////////////////////////////////////////////////////

if (!Spry.Widget.Utils)	Spry.Widget.Utils = {};

Spry.Widget.Utils.punycode_constants = {
	base : 36, tmin : 1, tmax : 26, skew : 38, damp : 700,
  initial_bias : 72, initial_n : 0x80, delimiter : 0x2D,
  maxint : 2<<26-1
};

Spry.Widget.Utils.punycode_encode_digit = function (d) {
  return String.fromCharCode(d + 22 + 75 * (d < 26));
};

Spry.Widget.Utils.punycode_adapt = function (delta, numpoints, firsttime) {
	delta = firsttime ? delta / this.punycode_constants.damp : delta >> 1;
	delta += delta / numpoints;
	
	for (var k = 0; delta > ((this.punycode_constants.base - this.punycode_constants.tmin) * this.punycode_constants.tmax) / 2; k += this.punycode_constants.base) {
		delta /= this.punycode_constants.base - this.punycode_constants.tmin;
	}
	return k + (this.punycode_constants.base - this.punycode_constants.tmin + 1) * delta / (delta + this.punycode_constants.skew);
};

/**
 * returns a 	Punicode representation of a UTF-8 string
 * adapted from http://tools.ietf.org/html/rfc3492
 */
Spry.Widget.Utils.punycode_encode = function (input, max_out) {
	var inputc = input.split("");
	input = [];
	for(var i=0; i<inputc.length; i++) {
		input.push(inputc[i].charCodeAt(0));
	}
	var output = '';

  var h, b, j, m, q, k, t;
	var input_len = input.length;
  var n = this.punycode_constants.initial_n;
  var delta = 0;
  var bias = this.punycode_constants.initial_bias;
  var out = 0;

  for (j = 0; j < input_len; j++) {
		if (input[j] < 128) {
			if (max_out - out < 2) {
				return false;
			}
			output += String.fromCharCode(input[j]);
			out++;
		}
	}

	h = b = out;
	if (b > 0) {
		output += String.fromCharCode(this.punycode_constants.delimiter);
		out++;
	}

  while (h < input_len)	{
		for (m = this.punycode_constants.maxint, j = 0; j < input_len; j++) {
			if (input[j] >= n && input[j] < m) {
				m = input[j];
			}
		}
		if (m - n > (this.punycode_constants.maxint - delta) / (h + 1)) {
			return false;
		}
		
		delta += (m - n) * (h + 1);
		n = m;

		for (j = 0; j < input_len; j++) {
			if (input[j] < n ) {
				if (++delta == 0) {
					return false;
				}
			}

			if (input[j] == n) {
				for (q = delta, k = this.punycode_constants.base;; k += this.punycode_constants.base) {
					if (out >= max_out) {
						return false;
					}

					t = k <= bias ? this.punycode_constants.tmin : k >= bias + this.punycode_constants.tmax ? this.punycode_constants.tmax : k - bias;
					if (q < t) {
						break;
					}

					output += this.punycode_encode_digit(t + (q - t) % (this.punycode_constants.base - t));
					out++;
					q = (q - t) / (this.punycode_constants.base - t);
				}

				output += this.punycode_encode_digit(q);
				out++;
				bias = this.punycode_adapt(delta, h + 1, h == b);
				delta = 0;
				h++;
			}
		}
		delta++, n++;
	}

  return output;
};

Spry.Widget.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.Widget.Utils.firstValid = function() {
	var ret = null;
	for(var i=0; i<Spry.Widget.Utils.firstValid.arguments.length; i++) {
		if (typeof(Spry.Widget.Utils.firstValid.arguments[i]) != 'undefined') {
			ret = Spry.Widget.Utils.firstValid.arguments[i];
			break;
		}
	}
	return ret;
};


Spry.Widget.Utils.specialCharacters = ",8,9,16,17,18,20,27,33,34,35,36,37,38,40,45,144,192,63232,";
Spry.Widget.Utils.specialSafariNavKeys = "63232,63233,63234,63235,63272,63273,63275,63276,63277,63289,";
Spry.Widget.Utils.specialNotSafariCharacters = "39,46,91,92,93,";

Spry.Widget.Utils.specialCharacters += Spry.Widget.Utils.specialSafariNavKeys;

if (!Spry.is.safari) {
	Spry.Widget.Utils.specialCharacters += Spry.Widget.Utils.specialNotSafariCharacters;
}

Spry.Widget.Utils.isSpecialKey = function (ev) {
	return Spry.Widget.Utils.specialCharacters.indexOf("," + ev.keyCode + ",") != -1;
};

Spry.Widget.Utils.getCharacterFromEvent = function(e){
	var keyDown = e.type == "keydown";

	var code = null;
	var character = null;
	if(Spry.is.mozilla && !keyDown){
		if(e.charCode){
			character = String.fromCharCode(e.charCode);
		} else {
			code = e.keyCode;
		}
	} else {
		code = e.keyCode || e.which;
		if (code != 13) {
			character = String.fromCharCode(code);
		}
	}

	if (Spry.is.safari) {
		if (keyDown) {
			code = e.keyCode || e.which;
			character = String.fromCharCode(code);
		} else {
			code = e.keyCode || e.which;
			if (Spry.Widget.Utils.specialCharacters.indexOf("," + code + ",") != -1) {
				character = null;
			} else {
				character = String.fromCharCode(code);
			}
		}
	}

	if(Spry.is.opera) {
		if (Spry.Widget.Utils.specialCharacters.indexOf("," + code + ",") != -1) {
			character = null;
		} else {
			character = String.fromCharCode(code);
		}
	}

	return character;
};

Spry.Widget.Utils.getFirstChildWithNodeNameAtAnyLevel = function(node, nodeName)
{
	var elements  = node.getElementsByTagName(nodeName);
	if (elements) {
		return elements[0];
	}
	return null;
};

Spry.Widget.Utils.getFirstParentWithNodeName = function(node, nodeName)
{
	while (node.parentNode
			&& node.parentNode.nodeName.toLowerCase() != nodeName.toLowerCase()
			&& node.parentNode.nodeName != 'BODY') {
		node = node.parentNode;
	}

	if (node.parentNode && node.parentNode.nodeName.toLowerCase() == nodeName.toLowerCase()) {
		return node.parentNode;
	} else {
		return null;
	}
};

Spry.Widget.Utils.destroyWidgets = function (container)
{
	if (typeof container == 'string') {
		container = document.getElementById(container);
	}

	var q = Spry.Widget.Form.onSubmitWidgetQueue;
	for (var i = 0; i < Spry.Widget.Form.onSubmitWidgetQueue.length; i++) {
		if (typeof(q[i].destroy) == 'function' && Spry.Widget.Utils.contains(container, q[i].element)) {
			q[i].destroy();
			i--;
		}
	}
};

Spry.Widget.Utils.contains = function (who, what)
{
	if (typeof who.contains == 'object') {
		return what && who && (who == what || who.contains(what));
	} else {
		var el = what;
		while(el) {
			if (el == who) {
				return true;
			}
			el = el.parentNode;
		}
		return false;
	}
};

Spry.Widget.Utils.addEventListener = function(element, eventType, handler, capture)
{
	try
	{
		if (element.addEventListener)
			element.addEventListener(eventType, handler, capture);
		else if (element.attachEvent)
			element.attachEvent("on" + eventType, handler, capture);
	}
	catch (e) {}
};

Spry.Widget.Utils.removeEventListener = function(element, eventType, handler, capture)
{
	try
	{
		if (element.removeEventListener)
			element.removeEventListener(eventType, handler, capture);
		else if (element.detachEvent)
			element.detachEvent("on" + eventType, handler, capture);
	}
	catch (e) {}
};

Spry.Widget.Utils.stopEvent = function(ev)
{
	try
	{
		this.stopPropagation(ev);
		this.preventDefault(ev);
	}
	catch (e) {}
};

/**
 * Stops event propagation
 * @param {Event} ev the event
 */
Spry.Widget.Utils.stopPropagation = function(ev)
{
	if (ev.stopPropagation)
	{
		ev.stopPropagation();
	}
	else
	{
		ev.cancelBubble = true;
	}
};

/**
 * Prevents the default behavior of the event
 * @param {Event} ev the event
 */
Spry.Widget.Utils.preventDefault = function(ev)
{
	if (ev.preventDefault)
	{
		ev.preventDefault();
	}
	else
	{
		ev.returnValue = false;
	}
};




/* ENQYERY FORM */

/* SpryValidationSelect.js - Revision: Spry Preview Release 1.4 */

// Copyright (c) 2006. Adobe Systems Incorporated.
// All rights reserved.
//
// Redistribution and use in source and binary forms, with or without
// modification, are permitted provided that the following conditions are met:
//
//   * Redistributions of source code must retain the above copyright notice,
//     this list of conditions and the following disclaimer.
//   * Redistributions in binary form must reproduce the above copyright notice,
//     this list of conditions and the following disclaimer in the documentation
//     and/or other materials provided with the distribution.
//   * Neither the name of Adobe Systems Incorporated nor the names of its
//     contributors may be used to endorse or promote products derived from this
//     software without specific prior written permission.
//
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
// AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
// ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
// LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
// CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
// SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
// INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
// CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
// ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
// POSSIBILITY OF SUCH DAMAGE.

var Spry;
if (!Spry) Spry = {};
if (!Spry.Widget) Spry.Widget = {};

Spry.Widget.ValidationSelect = function(element, opts)
{
	this.init(element);

	Spry.Widget.Utils.setOptions(this, opts);

	// set validateOn flags
	var validateOn = ['submit'].concat(this.validateOn || []);
	validateOn = validateOn.join(",");
	this.validateOn = 0 | (validateOn.indexOf('submit') != -1 ? Spry.Widget.ValidationSelect.ONSUBMIT : 0);
	this.validateOn = this.validateOn | (validateOn.indexOf('blur') != -1 ? Spry.Widget.ValidationSelect.ONBLUR : 0);
	this.validateOn = this.validateOn | (validateOn.indexOf('change') != -1 ? Spry.Widget.ValidationSelect.ONCHANGE : 0);


	// Unfortunately in some browsers like Safari, the Stylesheets our
	// page depends on may not have been loaded at the time we are called.
	// This means we have to defer attaching our behaviors until after the
	// onload event fires, since some of our behaviors rely on dimensions
	// specified in the CSS.

	if (Spry.Widget.ValidationSelect.onloadDidFire)
		this.attachBehaviors();
	else 
		Spry.Widget.ValidationSelect.loadQueue.push(this);
};

Spry.Widget.ValidationSelect.ONCHANGE = 1;
Spry.Widget.ValidationSelect.ONBLUR = 2;
Spry.Widget.ValidationSelect.ONSUBMIT = 4;

Spry.Widget.ValidationSelect.prototype.init = function(element)
{
	this.element = this.getElement(element);
	this.selectElement = null;
	this.form = null;
	this.event_handlers = [];
	
	 // this.element can be either the container (<span>)
	 // or the <select> element, when no error messages are used.
	
	this.requiredClass = "selectRequiredState";
	this.invalidClass = "selectInvalidState";
	this.focusClass = "selectFocusState";
	this.validClass = "selectValidState";
	
	this.emptyValue = "";
	this.invalidValue = null;
	this.isRequired = true;
	
	this.validateOn = ["submit"];  // change, blur, submit
	// flag used to avoid cascade validation when both 
	// onChange and onBlur events are used to trigger validation
	this.validatedByOnChangeEvent = false;
};

Spry.Widget.ValidationSelect.prototype.destroy = function() {
	for (var i=0; i<this.event_handlers.length; i++) {
		Spry.Widget.Utils.removeEventListener(this.event_handlers[i][0], this.event_handlers[i][1], this.event_handlers[i][2], false);
	}
	try { delete this.element; } catch(err) {}
	try { delete this.selectElement; } catch(err) {}
	try { delete this.form; } catch(err) {}
	try { delete this.event_handlers; } catch(err) {}

	var q = Spry.Widget.Form.onSubmitWidgetQueue;
	var qlen = q.length;
	for (var i = 0; i < qlen; i++) {
		if (q[i] == this) {
			q.splice(i, 1);
			break;
		}
	}
};

Spry.Widget.ValidationSelect.onloadDidFire = false;
Spry.Widget.ValidationSelect.loadQueue = [];

Spry.Widget.ValidationSelect.prototype.getElement = function(ele)
{
	if (ele && typeof ele == "string")
		return document.getElementById(ele);
	return ele;
};

Spry.Widget.ValidationSelect.processLoadQueue = function(handler)
{
	Spry.Widget.ValidationSelect.onloadDidFire = true;
	var q = Spry.Widget.ValidationSelect.loadQueue;
	var qlen = q.length;
	for (var i = 0; i < qlen; i++)
		q[i].attachBehaviors();
};

Spry.Widget.ValidationSelect.addLoadListener = function(handler)
{
	if (typeof window.addEventListener != 'undefined')
		window.addEventListener('load', handler, false);
	else if (typeof document.addEventListener != 'undefined')
		document.addEventListener('load', handler, false);
	else if (typeof window.attachEvent != 'undefined')
		window.attachEvent('onload', handler);
};

Spry.Widget.ValidationSelect.addLoadListener(Spry.Widget.ValidationSelect.processLoadQueue);
Spry.Widget.ValidationSelect.addLoadListener(function(){
	Spry.Widget.Utils.addEventListener(window, "unload", Spry.Widget.Form.destroyAll, false);
});

Spry.Widget.ValidationSelect.prototype.attachBehaviors = function()
{
	// find the SELECT element inside current container
	if (this.element.nodeName == "SELECT") {
		this.selectElement = this.element;
	} else {
		this.selectElement = Spry.Widget.Utils.getFirstChildWithNodeNameAtAnyLevel(this.element, "SELECT");
	}

	if (this.selectElement) {
		var self = this;
		this.event_handlers = [];
		// focus
		// attach on beforeactivate instead of focus for IE 7 (to overcome this bug: setting a class name, closes the select)
		var focusEventName = "focus";
		if (navigator.userAgent.toLowerCase().indexOf("msie 7.") != -1) {
			focusEventName = "beforeactivate";
		}
		this.event_handlers.push([this.selectElement, focusEventName, function(e) { if (self.isDisabled()) return true; return self.onFocus(e); }]);
		// blur
		this.event_handlers.push([this.selectElement, "blur", function(e) { if (self.isDisabled()) return true; return self.onBlur(e); }]);
		// change
		if (this.validateOn & Spry.Widget.ValidationSelect.ONCHANGE) {
			this.event_handlers.push([this.selectElement, "change", function(e) { if (self.isDisabled()) return true; return self.onChange(e); }]);
			this.event_handlers.push([this.selectElement, "keypress", function(e) { if (self.isDisabled()) return true; return self.onChange(e); }]);
		}

		for (var i=0; i<this.event_handlers.length; i++) {
			Spry.Widget.Utils.addEventListener(this.event_handlers[i][0], this.event_handlers[i][1], this.event_handlers[i][2], false);
		}

		// submit
		this.form = Spry.Widget.Utils.getFirstParentWithNodeName(this.selectElement, "FORM");
		if (this.form) {
			// if no "onSubmit" handler has been attached to the current form, attach one
			if (!this.form.attachedSubmitHandler && !this.form.onsubmit) {
				this.form.onsubmit = function(e) { e = e || event; return Spry.Widget.Form.onSubmit(e, e.srcElement || e.currentTarget) };
				this.form.attachedSubmitHandler = true;                 
			}
			if (!this.form.attachedResetHandler) {
				Spry.Widget.Utils.addEventListener(this.form, "reset", function(e) { e = e || event; return Spry.Widget.Form.onReset(e, e.srcElement || e.currentTarget) }, false);
				this.form.attachedResetHandler = true;                 
			}
			// add the currrent widget to the "onSubmit" check queue;
			Spry.Widget.Form.onSubmitWidgetQueue.push(this);
		}
	}
};


Spry.Widget.ValidationSelect.prototype.addClassName = function(ele, className)
{
	if (!ele || !className || (ele.className && ele.className.search(new RegExp("\\b" + className + "\\b")) != -1))
		return;
	ele.className += (ele.className ? " " : "") + className;
};

Spry.Widget.ValidationSelect.prototype.removeClassName = function(ele, className)
{
	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.Widget.ValidationSelect.prototype.onFocus = function(e)
{
	this.hasFocus = true;
	this.validatedByOnChangeEvent = false;
	this.addClassName(this.element, this.focusClass);
};

Spry.Widget.ValidationSelect.prototype.onBlur = function(e)
{
	this.hasFocus = false;
	var doValidation = false;
	if (this.validateOn & Spry.Widget.ValidationSelect.ONBLUR)
		doValidation = true;
	if (doValidation && !this.validatedByOnChangeEvent)
		this.validate();
	this.removeClassName(this.element, this.focusClass);
};

Spry.Widget.ValidationSelect.prototype.onChange = function(e)
{
	this.hasFocus = false;
	this.validate();
	this.validatedByOnChangeEvent = true;
};

Spry.Widget.ValidationSelect.prototype.reset = function() {
	this.removeClassName(this.element, this.requiredClass);
	this.removeClassName(this.element, this.invalidClass);
	this.removeClassName(this.element, this.validClass);
};

Spry.Widget.ValidationSelect.prototype.validate = function() {
	this.removeClassName(this.element, this.requiredClass);
	this.removeClassName(this.element, this.invalidClass);
	this.removeClassName(this.element, this.validClass);
	// check isRequired
	if (this.isRequired) {
		// there are no options, or no option has been selected
		if (this.selectElement.options.length == 0 || this.selectElement.selectedIndex == -1) {
			this.addClassName(this.element, this.requiredClass);
			return false;
		}
		// the current selected option has no "value" attribute
		// when no value is set, browsers implement different behaviour for the value property
		// IE: value = blank string ("")
		// FF, Opera: value = option text
		if (this.selectElement.options[this.selectElement.selectedIndex].getAttribute("value") == null) {
			this.addClassName(this.element, this.requiredClass);
			return false;
		}
		// the current selected option has blank string ("") value
		if (this.selectElement.options[this.selectElement.selectedIndex].value == this.emptyValue) {
			this.addClassName(this.element, this.requiredClass);
			return false;
		}
		// the current selected option has "disabled" attribute
		// IE 6 allows to select such options
		if (this.selectElement.options[this.selectElement.selectedIndex].disabled) {
			this.addClassName(this.element, this.requiredClass);
			return false;
		}
	}
	if (this.invalidValue) {
		if (this.selectElement.options.length > 0 && 
			this.selectElement.selectedIndex != -1 &&
			this.selectElement.options[this.selectElement.selectedIndex].value == this.invalidValue) {
			this.addClassName(this.element, this.invalidClass);
			return false;
		}
	}
	this.addClassName(this.element, this.validClass);
	return true;
}

Spry.Widget.ValidationSelect.prototype.isDisabled = function() {
	return this.selectElement.disabled;	
}

//////////////////////////////////////////////////////////////////////
//
// Spry.Widget.Form - common for all widgets
//
//////////////////////////////////////////////////////////////////////

if (!Spry.Widget.Form) Spry.Widget.Form = {};
if (!Spry.Widget.Form.onSubmitWidgetQueue) Spry.Widget.Form.onSubmitWidgetQueue = [];

if (!Spry.Widget.Form.validate) {
	Spry.Widget.Form.validate = function(vform) {
		var isValid = true;
		var isElementValid = true;
		var q = Spry.Widget.Form.onSubmitWidgetQueue;
		var qlen = q.length;
		for (var i = 0; i < qlen; i++) {
			if (!q[i].isDisabled() && q[i].form == vform) {
				isElementValid = q[i].validate();
				isValid = isElementValid && isValid;
			}
		}
		return isValid;
	}
};

if (!Spry.Widget.Form.onSubmit) {
	Spry.Widget.Form.onSubmit = function(e, form)
	{
		if (Spry.Widget.Form.validate(form) == false) {
			return false;
		}
		return true;
	};
};

if (!Spry.Widget.Form.onReset) {
	Spry.Widget.Form.onReset = function(e, vform)
	{
		var q = Spry.Widget.Form.onSubmitWidgetQueue;
		var qlen = q.length;
		for (var i = 0; i < qlen; i++) {
			if (!q[i].isDisabled() && q[i].form == vform && typeof(q[i].reset) == 'function') {
				q[i].reset();
			}
		}
		return true;
	};
};

if (!Spry.Widget.Form.destroy) {
	Spry.Widget.Form.destroy = function(form)
	{
		var q = Spry.Widget.Form.onSubmitWidgetQueue;
		for (var i = 0; i < Spry.Widget.Form.onSubmitWidgetQueue.length; i++) {
			if (q[i].form == form && typeof(q[i].destroy) == 'function') {
				q[i].destroy();
				i--;
			}
		}
	}
};

if (!Spry.Widget.Form.destroyAll) {
	Spry.Widget.Form.destroyAll = function()
	{
		var q = Spry.Widget.Form.onSubmitWidgetQueue;
		for (var i = 0; i < Spry.Widget.Form.onSubmitWidgetQueue.length; i++) {
			if (typeof(q[i].destroy) == 'function') {
				q[i].destroy();
				i--;
			}
		}
	}
};

//////////////////////////////////////////////////////////////////////
//
// Spry.Widget.Utils
//
//////////////////////////////////////////////////////////////////////

if (!Spry.Widget.Utils)	Spry.Widget.Utils = {};

Spry.Widget.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.Widget.Utils.getFirstChildWithNodeNameAtAnyLevel = function(node, nodeName)
{
	var elements  = node.getElementsByTagName(nodeName);
	if (elements) {
		return elements[0];
	}
	return null;
};

Spry.Widget.Utils.getFirstParentWithNodeName = function(node, nodeName)
{
	while (node.parentNode
			&& node.parentNode.nodeName.toLowerCase() != nodeName.toLowerCase()
			&& node.parentNode.nodeName != 'BODY') {
		node = node.parentNode;
	}

	if (node.parentNode && node.parentNode.nodeName.toLowerCase() == nodeName.toLowerCase()) {
		return node.parentNode;
	} else {
		return null;
	}
};

Spry.Widget.Utils.destroyWidgets = function (container)
{
	if (typeof container == 'string') {
		container = document.getElementById(container);
	}

	var q = Spry.Widget.Form.onSubmitWidgetQueue;
	for (var i = 0; i < Spry.Widget.Form.onSubmitWidgetQueue.length; i++) {
		if (typeof(q[i].destroy) == 'function' && Spry.Widget.Utils.contains(container, q[i].element)) {
			q[i].destroy();
			i--;
		}
	}
};

Spry.Widget.Utils.contains = function (who, what)
{
	if (typeof who.contains == 'object') {
		return what && who && (who == what || who.contains(what));
	} else {
		var el = what;
		while(el) {
			if (el == who) {
				return true;
			}
			el = el.parentNode;
		}
		return false;
	}
};

Spry.Widget.Utils.addEventListener = function(element, eventType, handler, capture)
{
	try
	{
		if (element.addEventListener)
			element.addEventListener(eventType, handler, capture);
		else if (element.attachEvent)
			element.attachEvent("on" + eventType, handler, capture);
	}
	catch (e) {}
};

Spry.Widget.Utils.removeEventListener = function(element, eventType, handler, capture)
{
	try
	{
		if (element.removeEventListener)
			element.removeEventListener(eventType, handler, capture);
		else if (element.detachEvent)
			element.detachEvent("on" + eventType, handler, capture);
	}
	catch (e) {}
};






/* POPBOX */


/********************************************************************************************************************
* PopBox.js, v2.5 released December 18, 2007. Copyright (c) 2007, C6 Software, Inc. (http://www.c6software.com/)
* PopBox is released under the Creative Commons Attribution 3.0 license (http://creativecommons.org/licenses/by/3.0/)
* and is free to use in both commercial and non-commercial work, provided this header remains at the top.
* The latest version and documentation can be found at http://www.c6software.com/products/popbox/default.aspx.
* Questions and suggestions can be sent to john.reid@c6software.com. Please put "PopBox" somewhere in the
* email subject so I can easily filter. Send me your URL and I may post it!
* PopBox relies on many methods from Danny Goodman's (www.dannyg.com) javascript library DHTMLAPI.js
* and his books, without which scores of web developers would be totally lost. Thanks Danny.
********************************************************************************************************************/

// Seek nested NN4 layer from string name
function SeekLayer(doc, name) {
    var theObj;
    for (var i = 0; i < doc.layers.length; i++) {
        if (doc.layers[i].name == name) {
            theObj = doc.layers[i];
            break;
        }
        // dive into nested layers if necessary
        if (doc.layers[i].document.layers.length > 0) {
            theObj = SeekLayer(document.layers[i].document, name);
        }
    }
    return theObj;
}

// Convert object name string or object reference into a valid element object reference
function GetRawObject(obj) {
    var theObj;
    if (typeof obj == "string") {
		var isCSS = (document.body && document.body.style) ? true : false;
        if (isCSS && document.getElementById) {
            theObj = document.getElementById(obj);
        } else if (isCSS && document.all) {
            theObj = document.all(obj);
        } else if (document.layers) {
            theObj = SeekLayer(document, obj);
        }
    } else {
        // pass through object reference
        theObj = obj;
    }
    return theObj;
}

// Return the available content width and height space in browser window
function GetInsideWindowSize() {
    if (window.innerWidth) {
        return {x:window.innerWidth, y:window.innerHeight};
    } else if (document.compatMode && document.compatMode.indexOf("CSS1") >= 0) {
        return {x:document.body.parentNode.clientWidth, y:document.body.parentNode.clientHeight};
    } else if (document.body && document.body.clientWidth) {
        return {x:document.body.clientWidth, y:document.body.clientHeight};
    }
    return {x:0, y:0};
}

// Retrieve the padding around an object
function GetObjectPadding(obj) {
	var elem = GetRawObject(obj);

	var l = 0;
	var r = 0;
	var t = 0;
	var b = 0;
	if (elem.currentStyle)
	{
		if (elem.currentStyle.paddingLeft)
			l = parseInt(elem.currentStyle.paddingLeft, 10);
		if (elem.currentStyle.paddingRight)
			r = parseInt(elem.currentStyle.paddingRight, 10);
		if (elem.currentStyle.paddingTop)
			t = parseInt(elem.currentStyle.paddingTop, 10);
		if (elem.currentStyle.paddingBottom)
			b = parseInt(elem.currentStyle.paddingBottom, 10);
	}
	else if (window.getComputedStyle)
	{
		l = parseInt(window.getComputedStyle(elem,null).paddingLeft, 10);
		r = parseInt(window.getComputedStyle(elem,null).paddingRight, 10);
		t = parseInt(window.getComputedStyle(elem,null).paddingTop, 10);
		b = parseInt(window.getComputedStyle(elem,null).paddingBottom, 10);
	}
	if (isNaN(l) == true) l = 0;
	if (isNaN(r) == true) r = 0;
	if (isNaN(t) == true) t = 0;
	if (isNaN(b) == true) b = 0;

	return {l:(l),r:(r),t:(t),b:(b)};
}

// Retrieve the rendered size of an element
function GetObjectSize(obj)  {
    var elem = GetRawObject(obj);
    var w = 0;
    var h = 0;
    if (elem.offsetWidth) {
			w = elem.offsetWidth; h = elem.offsetHeight;
    } else if (elem.clip && elem.clip.width) {
			w = elem.clip.width; h = elem.clip.height;
    } else if (elem.style && elem.style.pixelWidth) {
			w = elem.style.pixelWidth; h = elem.style.pixelHeight;
    }
    
    w = parseInt(w, 10);
    h = parseInt(h, 10);
    
   // remove any original element padding
   var padding = GetObjectPadding(elem);
   w -= (padding.l + padding.r);
   h -= (padding.t + padding.b);

   return {w:(w), h:(h)};
}

// Return the element position in the page, not it's parent container
function GetElementPosition(obj)
{
	var elem = GetRawObject(obj);
	var left = 0;
	var top = 0;

	// add any original element padding
	var elemPadding = GetObjectPadding(elem);
	left = elemPadding.l;
	top = elemPadding.t;

	if (elem.offsetParent)
	{
		left += elem.offsetLeft;
		top += elem.offsetTop;
		var parent = elem.offsetParent;
		while (parent)
		{
			left += parent.offsetLeft;
			top += parent.offsetTop;
			var parentTagName = parent.tagName.toLowerCase();
			if (parentTagName != "table" &&
				parentTagName != "body" && 
				parentTagName != "html" && 
				parentTagName != "div" && 
				parent.clientTop && 
				parent.clientLeft)
			{
				left += parent.clientLeft;
				top += parent.clientTop;
			}

			parent = parent.offsetParent;
		}
	}
	else if (elem.left && elem.top)
	{
		left = elem.left;
		top = elem.top;
	}
	else
	{
		if (elem.x)
			left = elem.x;
		if (elem.y)
			top = elem.y;
	}
	return {x:left, y:top};
}

// return the number of pixels the scrollbar has moved the visible window
function GetScrollOffset()
{
    if (window.pageYOffset) {
        return {x:window.pageXOffset, y:window.pageYOffset};
    } else if (document.compatMode && document.compatMode.indexOf("CSS1") >= 0) {
        return {x:document.documentElement.scrollLeft, y:document.documentElement.scrollTop};
    } else if (document.body && document.body.clientWidth) {
        return {x:document.body.scrollLeft, y:document.body.scrollTop};
    }
    return {x:0, y:0};
}

function CreateRandomId()
{
	var randomNum = 0.0;
	while (randomNum == 0.0)
		randomNum = Math.random();
	var random = randomNum + "";
	return "id" + random.substr(2);
}

function MouseMoveRevert(e)
{
	if (pbMouseMoveRevert != null && pbMouseMoveRevert.length != 0)
	{
		var evt = (e) ? e : window.event;
		var mouse = {x:0, y:0};
		if (evt.pageX || evt.pageY)
		{
			mouse.x = evt.pageX;
			mouse.y = evt.pageY;
		}
		else if (evt.clientX || evt.clientY)
		{
			var scroll = GetScrollOffset();
			mouse.x = evt.clientX + scroll.x;
			mouse.y = evt.clientY + scroll.y;
		}
		
		for (var x = 0; x < pbMouseMoveRevert.length;)
		{
			if (pbMouseMoveRevert[x] != null)
			{
				var id = pbMouseMoveRevert[x].id;
				if (typeof popBox[id] != "undefined" && popBox[id] != null && popBox[id].hTarg != 0)
				{
					// if the mouse is outside the box then call revert
					if (mouse.x < popBox[id].xTarg || mouse.x > (popBox[id].xTarg + popBox[id].wTarg) || mouse.y < popBox[id].yTarg || mouse.y > (popBox[id].yTarg + popBox[id].hTarg))
					{
						var className = pbMouseMoveRevert[x].className;
						pbMouseMoveRevert.splice(x, 1);
						Revert(id, null, className);
						continue;
					}
				}				
			}
			
			x++;
		}
	}
}

// holds numerous properties related to position, size and motion
var popBox = new Array();
// holds positioning value for the z axis
var popBoxZ = 100;
// holds the popped image for each <img> tag with a pbsrc attribute
var pbSrc = new Array();
// holds the popbar function for each <img> tag with a pbShowPopBar attribute
var pbPopBarFunc = new Array();
// holds the array of image ids for onmousemove Revert calls
var pbMouseMoveRevert = null;

// add initialization to window.onload
if (typeof window.onload == 'function')
{
	var func = window.onload;
	window.onload = function(){func();InitPbSrc();InitPbPopBar();};
}
else
{
	window.onload = function(){InitPbSrc();InitPbPopBar();};
}

// loads all the popped src images
function InitPbSrc()
{
	var images = null;
	if (document.body)
	{
		if (document.body.getElementsByTagName)
			images = document.body.getElementsByTagName("img");
		else if (document.body.all)
			images = document.body.all.tags("img");
	}

	if (images != null)
	{
		for (var x = 0; x < images.length; x++)
		{
			var poppedSrc = images[x].getAttribute('pbSrc');
			if (poppedSrc != null)
			{
				if (images[x].id == "")
					images[x].id = CreateRandomId();
					
				if (pbSrc[images[x].id] == null)
				{
					pbSrc[images[x].id] = new Image();
					pbSrc[images[x].id].src = poppedSrc;
				}
			}
		}
	}
}

// adds PopBar to images
function InitPbPopBar()
{
	var images = null;
	if (document.body)
	{
		if (document.body.getElementsByTagName)
			images = document.body.getElementsByTagName("img");
		else if (document.body.all)
			images = document.body.all.tags("img");
	}

	if (images != null)
	{
		var imgArray = new Array();
		for (var x = 0; x < images.length; x++)
		{
			if (images[x].id == "")
				images[x].id = CreateRandomId();
			
			imgArray[x] = images[x];
		}

		for (var x = 0; x < imgArray.length; x++)
			CreatePopBar(imgArray[x]);
	}
}

// initialize default popbox object
function InitPopBox(obj)
{
	obj = GetRawObject(obj);
	if (typeof popBox[obj.id] != "undefined" && popBox[obj.id] != null)
		return obj;
		
	var parent = document.body;
	if (obj.id == "")
		obj.id = CreateRandomId();

	var elem = obj;
	var startPos = GetElementPosition(elem);
	var initSize = GetObjectSize(elem);

	if (elem.style.position == "absolute" || elem.style.position == "relative")
	{
		parent = elem.parentNode;
		startPos.x = parseInt(elem.style.left, 10);
		startPos.y = parseInt(elem.style.top, 10);
	}
	
	// if there is a pbsrc then create that, else if it's not absolute or relative then create a copy
	if (pbSrc[elem.id] != null || (elem.style.position != "absolute" && elem.style.position != "relative"))
	{
		var img = document.createElement("img");
		// copy image properties
		img.border = elem.border;
		img.className = elem.className;
		img.height = elem.height;
		img.id = "popcopy" + elem.id;
		img.src = (pbSrc[elem.id] != null) ? pbSrc[elem.id].src : elem.src;
		img.alt = elem.alt;
		img.title = elem.title;
		img.width = elem.width;
		img.onclick = elem.onclick;
		img.ondblclick = elem.ondblclick;
		img.onmouseout = elem.onmouseout;

		// remove event so the object doesn't jump
		elem.onmouseout = null;

		img.style.width = initSize.w;
		img.style.height = initSize.h;
		img.style.position = "absolute";
		img.style.left = startPos.x + "px";
		img.style.top = startPos.y + "px";
		img.style.cursor = elem.style.cursor;
		
		parent.appendChild(img);
		elem.style.visibility = "hidden";
		elem = img;
	}
	
	popBox[elem.id] = {	elemId:elem.id,
							xCurr:0.0,
							yCurr:0.0,
							xTarg:0.0,
							yTarg:0.0,
							wCurr:0.0,
							hCurr:0.0,
							wTarg:0.0,
							hTarg:0.0,
							xStep:0.0,
							yStep:0.0,
							wStep:0.0,
							hStep:0.0,
							xDelta:0.0,
							yDelta:0.0,
							wDelta:0.0,
							hDelta:0.0,
							xTravel:0.0,
							yTravel:0.0,
							wTravel:0.0,
							hTravel:0.0,
							velM:1.0,
							velS:1.0,
							interval:null,
							isAnimating:false,
							xOriginal:startPos.x,
							yOriginal:startPos.y,
							wOriginal:parseFloat(initSize.w),
							hOriginal:parseFloat(initSize.h),
							isPopped:false,
							fnClick:null,
							fnDone:null,
							fnPre:null,
							originalId:null,
							cursor:""
							};
							
	if (typeof obj.onclick == "function")
	{
		popBox[elem.id].fnClick = elem.onclick;
		
		if (popBoxAutoClose == true && (typeof obj.ondblclick != "function" || obj.ondblclick == null) && typeof obj.onmouseover != "function")
			elem.ondblclick = function(){Revert(elem.id, null, elem.className);};
	}

	if (popBoxAutoClose == true && typeof obj.onmouseover == "function" && (typeof obj.onmouseout != "function" || obj.onmouseout == null))
	{
		if (popBoxMouseMoveRevert == true)
		{
			if (pbMouseMoveRevert == null)
			{
				pbMouseMoveRevert = new Array();
				if (typeof document.onmousemove == 'function')
				{
					var func = document.onmousemove;
					document.onmousemove = function(e){func(e);MouseMoveRevert(e);};
				}
				else
				{
					document.onmousemove = MouseMoveRevert;
				}
			}
			
			pbMouseMoveRevert.push({id:elem.id, className:elem.className});
		}
		else
		{
			elem.onmouseout = function(){Revert(elem.id, null, elem.className);};
		}
	}

	if (obj.id != elem.id)
		popBox[elem.id].originalId = obj.id;
		
	return elem;
}

// calculate next steps and assign to style properties
function DoPopBox(elem)
{
	if (typeof elem == "string") elem = GetRawObject(elem);
	try
	{
		var bMDone = false;
		var bSDone = false;
		if ((popBox[elem.id].xTravel + Math.abs(popBox[elem.id].xStep)) < popBox[elem.id].xDelta)
		{
			var x = popBox[elem.id].xCurr + popBox[elem.id].xStep;
			elem.style.left = parseInt(x, 10) + "px";
			popBox[elem.id].xTravel += Math.abs(popBox[elem.id].xStep);
			popBox[elem.id].xCurr = x;
		} else {
			popBox[elem.id].xTravel += Math.abs(popBox[elem.id].xStep);
			elem.style.left = parseInt(popBox[elem.id].xTarg, 10) + "px";
			bMDone = true;
		}
		if ((popBox[elem.id].yTravel + Math.abs(popBox[elem.id].yStep)) < popBox[elem.id].yDelta)
		{
			var y = popBox[elem.id].yCurr + popBox[elem.id].yStep;
			elem.style.top = parseInt(y, 10) + "px";
			popBox[elem.id].yTravel += Math.abs(popBox[elem.id].yStep);
			popBox[elem.id].yCurr = y;
			bMDone = false;
		} else {
			popBox[elem.id].yTravel += Math.abs(popBox[elem.id].yStep);
			elem.style.top = parseInt(popBox[elem.id].yTarg, 10) + "px";
		}
		if ((popBox[elem.id].wTravel + Math.abs(popBox[elem.id].wStep)) < popBox[elem.id].wDelta)
		{
			var w = popBox[elem.id].wCurr + popBox[elem.id].wStep;
			elem.style.width = parseInt(w, 10) + "px";
			popBox[elem.id].wTravel += Math.abs(popBox[elem.id].wStep);
			popBox[elem.id].wCurr = w;
		} else {
			popBox[elem.id].wTravel += Math.abs(popBox[elem.id].wStep);
			elem.style.width = parseInt(popBox[elem.id].wTarg, 10) + "px";
			bSDone = true;
		}
		if ((popBox[elem.id].hTravel + Math.abs(popBox[elem.id].hStep)) < popBox[elem.id].hDelta)
		{
			var h = popBox[elem.id].hCurr + popBox[elem.id].hStep;
			elem.style.height = parseInt(h, 10) + "px";
			popBox[elem.id].hTravel += Math.abs(popBox[elem.id].hStep);
			popBox[elem.id].hCurr = h;
			bSDone = false;
		} else {
			popBox[elem.id].hTravel += Math.abs(popBox[elem.id].hStep);
			elem.style.height = parseInt(popBox[elem.id].hTarg, 10) + "px";
		}

		var obj = elem;
		
		if (bMDone == true && bSDone == true)
		{
			clearInterval(popBox[elem.id].interval);
			
			elem.style.cursor = popBox[elem.id].cursor;

			var func = null;
			if (popBox[elem.id].fnDone != null && typeof popBox[elem.id].fnDone == "function")
				func = popBox[elem.id].fnDone;
			
			if (popBox[elem.id].isPopped == true)
			{
				elem.style.zIndex = null;
	
				if (popBox[elem.id].originalId != null)
				{
					obj = GetRawObject(popBox[elem.id].originalId);
					obj.onmouseout = elem.onmouseout; // copy method back to original
					obj.style.visibility = "visible";
					
					// remove the copied object from the body and the array
					elem.parentNode.removeChild(elem);
				}
				else
				{
					elem.style.width = parseInt(popBox[elem.id].wOriginal, 10) + "px";
					elem.style.height = parseInt(popBox[elem.id].hOriginal, 10) + "px";
				
					if (typeof popBox[elem.id].fnClick == "function")
						elem.onclick = popBox[elem.id].fnClick;
				}

				delete popBox[elem.id];
				popBox[elem.id] = null;
				CreatePopBar(obj);
			}
			else
			{
				popBox[elem.id].isPopped = true;
				popBox[elem.id].isAnimating = false;
				CreateRevertBar(elem);
			}
				
			if (func != null && typeof func == "function")
				func(obj);
		}
	}
	catch(ex){}
}

function HasRevertBar(obj)
{
	if (typeof obj == "string") obj = GetRawObject(obj);
	
	var elem = obj;
	if (popBox[elem.id] != null && popBox[elem.id].originalId != null)
		elem = GetRawObject(popBox[elem.id].originalId);

	var pbShowBar = elem.getAttribute('pbShowRevertBar');
	var pbShowText = elem.getAttribute('pbShowRevertText');
	var pbShowImage = elem.getAttribute('pbShowRevertImage');
	pbShowBar = (pbShowBar != null) ? (pbShowBar == "true" || pbShowBar == false) : popBoxShowRevertBar;
	pbShowText = (pbShowText != null) ? (pbShowText == "true" || pbShowText == false) : popBoxShowRevertText;
	pbShowImage = (pbShowImage != null) ? (pbShowImage == "true" || pbShowImage == false) : popBoxShowRevertImage;
	
	return (pbShowBar || pbShowText || pbShowImage);
}

function HasCaption(obj)
{
	if (typeof obj == "string") obj = GetRawObject(obj);
	var elem = obj;
	if (popBox[elem.id] != null && popBox[elem.id].originalId != null)
		elem = GetRawObject(popBox[elem.id].originalId);

	var pbShowCaption = elem.getAttribute('pbShowCaption');
	pbShowCaption = (pbShowCaption != null) ? (pbShowCaption == "true" || pbShowCaption == true) : popBoxShowCaption;
	var pbCaption = null;
	if (pbShowCaption == true)
	{
		pbCaption = elem.getAttribute('pbCaption');
		if (pbCaption == null && elem.title != "") pbCaption = elem.title;
	}

	return (pbCaption != null && pbCaption != "");
}

function CreateRevertBar(obj)
{
	if (typeof obj == "string") obj = GetRawObject(obj);
	
	var elem = obj;
	if (popBox[elem.id] != null && popBox[elem.id].originalId != null)
		elem = GetRawObject(popBox[elem.id].originalId);

	var pbShowBar = elem.getAttribute('pbShowRevertBar');
	var pbShowText = elem.getAttribute('pbShowRevertText');
	var pbShowImage = elem.getAttribute('pbShowRevertImage');
	var pbText = elem.getAttribute('pbRevertText');
	var pbImage = elem.getAttribute('pbRevertImage');
	pbShowBar = (pbShowBar != null) ? (pbShowBar == "true" || pbShowBar == false) : popBoxShowRevertBar;
	pbShowText = (pbShowText != null) ? (pbShowText == "true" || pbShowText == false) : popBoxShowRevertText;
	pbShowImage = (pbShowImage != null) ? (pbShowImage == "true" || pbShowImage == false) : popBoxShowRevertImage;
	if (pbText == null) pbText = popBoxRevertText;
	if (pbImage == null) pbImage = popBoxRevertImage;

	var pbShowCaption = elem.getAttribute('pbShowCaption');
	pbShowCaption = (pbShowCaption != null) ? (pbShowCaption == "true" || pbShowCaption == true) : popBoxShowCaption;
	var pbCaption = null;
	if (pbShowCaption == true)
	{
		pbCaption = elem.getAttribute('pbCaption');
		if (pbCaption == null && elem.title != "") pbCaption = elem.title;
	}

	CreatePbBar(obj, pbShowBar, pbShowText, pbShowImage, pbText, pbImage, popBoxRevertBarAbove, true, pbCaption)
}

function CreatePopBar(obj)
{
	if (typeof obj == "string") obj = GetRawObject(obj);
	if (typeof pbPopBarFunc[obj.id] != 'undefined' && pbPopBarFunc[obj.id] != null) return;
	var pbShowBar = obj.getAttribute('pbShowPopBar');
	if (pbShowBar != null)
	{
		var pbShowText = obj.getAttribute('pbShowPopText');
		var pbShowImage = obj.getAttribute('pbShowPopImage');
		var pbText = obj.getAttribute('pbPopText');
		var pbImage = obj.getAttribute('pbPopImage');
		pbShowBar = (pbShowBar == "true" || pbShowBar == true);
		pbShowText = (pbShowText != null) ? (pbShowText == "true" || pbShowText == true) : popBoxShowPopText;
		pbShowImage = (pbShowImage != null) ? (pbShowImage == "true" || pbShowImage == true) : popBoxShowPopImage;
		if (pbText == null) pbText = popBoxPopText;
		if (pbImage == null) pbImage = popBoxPopImage;

		CreatePbBar(obj, pbShowBar, pbShowText, pbShowImage, pbText, pbImage, popBoxPopBarAbove, false, null)
	}
}

function CreatePbBar(obj, pbShowBar, pbShowText, pbShowImage, pbText, pbImage, pbBarAbove, isRevert, pbCaption)
{
	if (pbShowBar == false && pbShowText == false && pbShowImage == false && pbCaption == null) return;
	if (typeof obj == "string") obj = GetRawObject(obj);

	var objCursor = "hand";
	if (obj.currentStyle)
		objCursor = obj.currentStyle.cursor;
	else if (window.getComputedStyle)
		objCursor = window.getComputedStyle(obj,null).cursor;

	var fnClick = function(){if (typeof obj.onclick == 'function') obj.onclick();};
	var fnMouseOut = function(){if (typeof obj.onmouseout == 'function') obj.onmouseout();};
	var fnMouseOver = function(){if (typeof obj.onmouseover == 'function') obj.onmouseover();};
	var fnRemove = new Array();

	var isPositioned = (obj.style.position == "absolute" || obj.style.position == "relative");
	var left = 0;
	var top = 0;
	var parentNode = obj.parentNode;
	var objSpan = null;
	if (isPositioned == true)
	{
		left = parseInt(obj.style.left, 10);
		top = parseInt(obj.style.top, 10);
		var padding = GetObjectPadding(obj);
		left += padding.l;
		top += padding.t;	
	}
	else
	{
		objSpan = document.createElement("span");
		objSpan = (obj.nextSibling != null) ? parentNode.insertBefore(objSpan, obj.nextSibling) : parentNode.appendChild(objSpan);
		objSpan.style.position = "relative";
		objSpan.style.left = "0px";
		objSpan.style.top = "0px";
		var floatValue = "";
		if (obj.align == "left") floatValue = "left";
		else if (obj.align == "right") floatValue = "right";
		floatValue = (obj.style.styleFloat && obj.style.styleFloat != "") ? obj.style.styleFloat : (obj.style.cssFloat && obj.style.cssFloat != "") ? obj.style.cssFloat : floatValue;
		if (typeof obj.style.styleFloat != "undefined") objSpan.style.styleFloat = floatValue;
		else if (typeof obj.style.cssFloat != "undefined") objSpan.style.cssFloat = floatValue;
		
		var imgPos = GetElementPosition(obj);
		var spanPos = GetElementPosition(objSpan);
		objSpan.style.left = (spanPos.x > imgPos.x) ? (imgPos.x - spanPos.x) + "px" : imgPos.x - spanPos.x + "px";
		objSpan.style.top = (spanPos.y > imgPos.y) ? (imgPos.y - spanPos.y) + "px" : imgPos.y - spanPos.y + "px";
		
		objSpan.onclick = fnClick;
		if (isRevert == true)
			objSpan.onmouseout = fnMouseOut;
		else
			objSpan.onmouseover = fnMouseOver;
		parentNode = objSpan;
	}

	var width = parseInt(obj.style.width, 10);
	var height = parseInt(obj.style.height, 10);
	var size = GetObjectSize(obj);
	if (isNaN(width) == true)
		width = size.w;
	else if (size.w > width)
		left += ((size.w - width) / 2);
	if (isNaN(height) == true)
		height = size.h;
	else if (size.h > height)
		top += ((size.h - height) / 2);

	if (pbBarAbove == true) top -= 20;
	var z = obj.style.zIndex + 1;

	if (pbShowBar == true)
	{
		var divTrans = document.createElement("div");
		divTrans.id = "popBoxDivTrans" + z;
		divTrans.style.width = width + "px";
		divTrans.style.height = "20px";
		divTrans.style.borderStyle = "none";
		divTrans.style.padding = "0px";
		divTrans.style.margin = "0px";
		divTrans.style.position = "absolute";
		divTrans.style.left = left + "px";
		divTrans.style.top = top + "px";
		divTrans.style.backgroundColor = "#000000";
		divTrans.style.cursor = objCursor;
		divTrans.style.zIndex = z;
		if (pbBarAbove == false)
		{
			if (typeof divTrans.style.filter != 'undefined')
				divTrans.style.filter = "progid:DXImageTransform.Microsoft.Alpha(opacity=20)";
			if (typeof divTrans.style.opacity != 'undefined')
				divTrans.style.opacity = "0.2";
		}
		divTrans.onclick = fnClick;
		if (isRevert == true)
			divTrans.onmouseout = fnMouseOut;
		else
			divTrans.onmouseover = fnMouseOver;
		parentNode.appendChild(divTrans);
		
		fnRemove.push(function(){divTrans.parentNode.removeChild(divTrans);});
	}

	if (pbShowText == true)
	{
		var divText = document.createElement("div");
		divText.id = "popBoxDivText" + z;
		divText.style.width = width + "px";
		divText.style.height = "20px";
		divText.style.borderStyle = "none";
		divText.style.padding = "0px";
		divText.style.margin = "0px";
		divText.style.position = "absolute";
		divText.style.left = left + "px";
		divText.style.top = top + "px";
		divText.style.cursor = objCursor;
		divText.style.textAlign = "center";
		divText.style.fontFamily = "Arial, Verdana, Sans-Serif";
		divText.style.fontSize = "10pt";
		divText.style.backgroundColor = "Transparent";
		divText.style.color = "#ffffff";
		divText.style.zIndex = z;
		divText.innerHTML = pbText;
		divText.onclick = fnClick;
		if (isRevert == true)
			divText.onmouseout = fnMouseOut;
		else
			divText.onmouseover = fnMouseOver;
		parentNode.appendChild(divText);

		fnRemove.push(function(){divText.parentNode.removeChild(divText);});
	}
	
	if (pbShowImage == true)
	{
		var imgPopped = document.createElement("img");
		imgPopped.id = "popBoxImgPopped" + z;
		imgPopped.src = pbImage;
		imgPopped.style.width = "20px";
		imgPopped.style.height = "20px";
		imgPopped.style.borderStyle = "none";
		imgPopped.style.padding = "0px";
		imgPopped.style.margin = "0px";
		imgPopped.style.position = "absolute";
		imgPopped.style.left = (left + width - 20) + "px";
		imgPopped.style.top = top + "px";
		imgPopped.style.cursor = objCursor;
		imgPopped.style.zIndex = z;
		imgPopped.onclick = fnClick;
		if (isRevert == true)
			imgPopped.onmouseout = fnMouseOut;
		else
			imgPopped.onmouseover = fnMouseOver;
		parentNode.appendChild(imgPopped);

		fnRemove.push(function(){imgPopped.parentNode.removeChild(imgPopped);});
	}
	
	if (pbCaption != null && pbCaption != "")
	{
		top += (height - 20);
		if (pbBarAbove == true) top += 20;
		if (popBoxCaptionBelow == true)  top += 20;

		var divCapTrans = document.createElement("div");
		divCapTrans.id = "popBoxDivCapTrans" + z;
		divCapTrans.style.width = width - 2 + "px";
		divCapTrans.style.height = "20px";
		divCapTrans.style.borderStyle = "solid";
		divCapTrans.style.borderWidth = "1px";
		divCapTrans.style.borderColor = "#999999";
		divCapTrans.style.padding = "0px";
		divCapTrans.style.margin = "0px";
		divCapTrans.style.position = "absolute";
		divCapTrans.style.left = left + "px";
		divCapTrans.style.top = top - 1 + "px";
		divCapTrans.style.backgroundColor = "#ffffdd";
		divCapTrans.style.zIndex = z;
		if (popBoxCaptionBelow == false)
		{
			if (typeof divCapTrans.style.filter != 'undefined')
				divCapTrans.style.filter = "progid:DXImageTransform.Microsoft.Alpha(opacity=70)";
			if (typeof divCapTrans.style.opacity != 'undefined')
				divCapTrans.style.opacity = "0.7";
		}
		parentNode.appendChild(divCapTrans);
		fnRemove.push(function(){divCapTrans.parentNode.removeChild(divCapTrans);});

		var divCapText = document.createElement("div");
		divCapText.id = "popBoxDivCapText" + z;
		divCapText.style.width = width - 20 + "px";
		divCapText.style.height = "20px";
		divCapText.style.borderStyle = "none";
		divCapText.style.padding = "0px";
		divCapText.style.margin = "0px";
		divCapText.style.position = "absolute";
		divCapText.style.left = left + 10 + "px";
		divCapText.style.top = top + "px";
		divCapText.style.textAlign = "center";
		divCapText.style.fontFamily = "Arial, Verdana, Sans-Serif";
		divCapText.style.fontSize = "10pt";
		divCapText.style.overflowY = "hidden";
		divCapText.style.backgroundColor = "Transparent";
		divCapText.style.color = "#000000";
		divCapText.style.zIndex = z;
		parentNode.appendChild(divCapText);
		fnRemove.push(function(){divCapText.parentNode.removeChild(divCapText);});

		AddCaptionText(divCapTrans, divCapText, pbCaption);
	}

	if (fnRemove.length != 0)
	{
		if (objSpan != null)
			fnRemove.push(function(){objSpan.parentNode.removeChild(objSpan);});
		
		if (isRevert == true)
		{
			if(popBox[obj.id].fnPre != null && typeof(popBox[obj.id].fnPre) == 'function')
				fnRemove.push(popBox[obj.id].fnPre);
		
			popBox[obj.id].fnPre = function(){for(var x = 0; x < fnRemove.length; x++){fnRemove[x]();}};
		}
		else
		{
			pbPopBarFunc[obj.id] = function(){for(var x = 0; x < fnRemove.length; x++){fnRemove[x]();}};
		}
	}
}

function AddCaptionText(divCapTrans, divCapText, caption)
{
	var width = parseInt(divCapText.style.width, 10);
	var divSizer = document.createElement("div");
	divSizer.style.position = "absolute";
	divSizer.style.width = width + "px";
	divSizer.style.margin = "0px";
	divSizer.style.fontFamily = divCapText.style.fontFamily;
	divSizer.style.fontSize = divCapText.style.fontSize;
	divSizer.style.visibility = "hidden";
	divSizer.innerHTML = caption;
	document.body.appendChild(divSizer);
	var newSize = GetObjectSize(divSizer);
	if (newSize.h > 20)
	{
		divSizer.innerHTML = caption + "..." + popBoxCaptionLessText;

		newSize = GetObjectSize(divSizer);

		var fullCaption = caption;
		var charCount = parseInt(width * 0.14, 10) - 5; // safe estimate
		divCapText.innerHTML = caption.substr(0, charCount) + "...";
		
		var spanMore = document.createElement("span");
		spanMore.style.color = "#0000ff";
		spanMore.style.textDecoration = "underline";
		spanMore.style.cursor = "pointer";
		spanMore.onclick = function(){spanMore.parentNode.removeChild(spanMore);ResizeCaption(divCapTrans.id,divCapText.id,newSize.h,fullCaption);};
		spanMore.innerHTML = popBoxCaptionMoreText;
		divCapText.appendChild(spanMore);
	}
	else
		divCapText.innerHTML = caption;

	document.body.removeChild(divSizer);
}

function ResizeCaption(divCapTrans, divCapText, height, caption)
{
	if (typeof divCapTrans == "string") divCapTrans = GetRawObject(divCapTrans);
	if (typeof divCapText == "string") divCapText = GetRawObject(divCapText);

	var h = parseInt(divCapText.style.height, 10);
	var top = parseInt(divCapText.style.top, 10);
	
	if (h < height)
	{
		if (h == 20)
		{
			height += 10;
			divCapText.style.paddingTop = "5px";
			divCapText.innerHTML = caption + "...";
			
			var spanLess = document.createElement("span");
			spanLess.style.color = "#0000ff";
			spanLess.style.textDecoration = "underline";
			spanLess.style.cursor = "pointer";
			spanLess.onclick = function(){spanLess.parentNode.removeChild(spanLess);divCapText.innerHTML = caption;ResizeCaption(divCapTrans.id,divCapText.id,20,caption);};
			spanLess.innerHTML = popBoxCaptionLessText;
			divCapText.appendChild(spanLess);
			
			if (popBoxCaptionBelow == false)
			{
				if (typeof divCapTrans.style.filter != 'undefined')
					divCapTrans.style.filter = "";
				if (typeof divCapTrans.style.opacity != 'undefined')
					divCapTrans.style.opacity = "1.0";
			}
		}
		
		if ((h + 10) >= height)
		{
			top -= (height - h);
			h = height;
		}
		else
		{
			top -= 10;
			h += 10;
		}
		
		divCapTrans.style.height = h + "px";
		divCapText.style.height = h + "px";
		divCapTrans.style.top = (top - 1) + "px";
		divCapText.style.top = top + "px";

		if (h != height)
			setTimeout("ResizeCaption(\"" + divCapTrans.id + "\",\"" + divCapText.id + "\"," + height + ",\"" + caption + "\")", 10);
	}
	else
	{
		if ((h - 10) <= height)
		{
			top += (h - height);
			h = height;
		}
		else
		{
			top += 10;
			h -= 10;
		}
		
		divCapTrans.style.height = h + "px";
		divCapText.style.height = h + "px";
		divCapTrans.style.top = (top - 1) + "px";
		divCapText.style.top = top + "px";
		divCapText.style.paddingTop = "0px";

		if (h == height)
		{
			if (popBoxCaptionBelow == false)
			{
				if (typeof divCapTrans.style.filter != 'undefined')
					divCapTrans.style.filter = "progid:DXImageTransform.Microsoft.Alpha(opacity=70)";
				if (typeof divCapTrans.style.opacity != 'undefined')
					divCapTrans.style.opacity = "0.7";
			}

			AddCaptionText(divCapTrans, divCapText, caption);
		}
		else
		{
			setTimeout("ResizeCaption(\"" + divCapTrans.id + "\",\"" + divCapText.id + "\"," + height + ",\"" + caption + "\")", 10);
		}
	}
}

function CreateWaitImage(obj)
{
	if (typeof obj == "string") obj = GetRawObject(obj);

	var newId = "popBoxImgWait" + obj.id;
	var imgWait = GetRawObject(newId);
	if (imgWait != null)
		return imgWait;

	var left = 0;
	var top = 0;
	if (obj.style.position == "absolute" || obj.style.position == "relative")
	{
		left = parseInt(obj.style.left, 10);
		top = parseInt(obj.style.top, 10);
	}
	else
	{
		var xy = GetElementPosition(obj);
		left = xy.x;
		top = xy.y;
		var padding = GetObjectPadding(obj);
		left -= padding.l;
		top -= padding.t;
	}

	var width = parseInt(obj.style.width, 10);
	var height = parseInt(obj.style.height, 10);
	var size = GetObjectSize(obj);
	if (isNaN(width) == true)
		width = size.w;
	else if (size.w > width)
		left += ((size.w - width) / 2);
	if (isNaN(height) == true)
		height = size.h;
	else if (size.h > height)
		top += ((size.h - height) / 2);

	var parentNode = obj.parentNode;

	imgWait = document.createElement("img");
	imgWait.id = newId;
	imgWait.src = popBoxWaitImage.src;
	imgWait.style.position = "absolute";
	imgWait.style.left = (left + (width / 2) - (popBoxWaitImage.width / 2)) + "px";
	imgWait.style.top = (top + (height / 2) - (popBoxWaitImage.height / 2)) + "px";
	imgWait.style.cursor = obj.style.cursor;
	imgWait.style.zIndex = obj.style.zIndex + 1;
	parentNode.appendChild(imgWait);

	return imgWait;
}

// encapsulates the Popped image sizing logic
function CalculateImageDimensions(newWidth, newHeight, fullWidth, fullHeight, windowSize)
{
	if (newWidth == null)
	{
		if (newHeight == null)
		{
			newWidth = fullWidth;
			newHeight = fullHeight;
		}
		else if (newHeight == 0)
		{
			newHeight = Math.min(windowSize.y, fullHeight);
			var scale = parseFloat(newHeight) / parseFloat(fullHeight);
			newWidth = parseInt(fullWidth * scale);
		}
		else
		{
			var scale = parseFloat(newHeight) / parseFloat(fullHeight);
			newWidth = parseInt(fullWidth * scale);
		}
	}
	else if (newWidth == 0)
	{
		if (newHeight == null)
		{
			newWidth = Math.min(windowSize.x, fullWidth);
			var scale = parseFloat(newWidth) / parseFloat(fullWidth);
			newHeight = parseInt(fullHeight * scale);
		}
		else if (newHeight == 0)
		{
			if (windowSize.x < fullWidth || windowSize.y < fullHeight)
			{
				var scale = Math.min(parseFloat(windowSize.x) / parseFloat(fullWidth), parseFloat(windowSize.y) / parseFloat(fullHeight));
				newWidth = parseInt(fullWidth * scale);
				newHeight = parseInt(fullHeight * scale);
			}
			else
			{
				newWidth = fullWidth;
				newHeight = fullHeight;
			}
		}
		else
		{
			var scale = parseFloat(newHeight) / parseFloat(fullHeight);
			newWidth = Math.min(windowSize.x, parseInt(fullWidth * scale));
		}
	}
	else
	{
		if (newHeight == null)
		{
			var scale = parseFloat(newWidth) / parseFloat(fullWidth);
			newHeight = parseInt(fullHeight * scale);
		}
		else if (newHeight == 0)
		{
			var scale = parseFloat(newWidth) / parseFloat(fullWidth);
			newHeight = Math.min(windowSize.y, parseInt(fullHeight * scale));
		}
	}
	
	return {x:newWidth, y:newHeight};
}

/***************************************************************************************************
* This is where the user-callable section starts.
* Function signatures above this line are subject to change.
***************************************************************************************************/

// Globals you can assign
var popBoxAutoClose = true;
var popBoxMouseMoveRevert = true;
var popBoxWaitImage = new Image();
popBoxWaitImage.src = "/images/spinner40.gif";

var popBoxShowRevertBar = false;
var popBoxShowRevertText = false;
var popBoxShowRevertImage = false;
var popBoxRevertText = "Click the image to shrink it.";
var popBoxRevertImage = "/images/magminus.gif";
var popBoxRevertBarAbove = false;

// there is no popBoxShowPopBar global, but instead the pbShowPopBar attribute must be
// set on the img for the PopBar funtionality to work (can be true or false)
var popBoxShowPopText = true;
var popBoxShowPopImage = true;
var popBoxPopText = "Click to expand.";
var popBoxPopImage = "/images/magplus.gif";
var popBoxPopBarAbove = false;

var popBoxShowCaption = true;
var popBoxCaptionBelow = false;
var popBoxCaptionMoreText = "more";
var popBoxCaptionLessText = "less";

// these custom attributes on the <img> element will override the globals above
// pbShowRevertBar, pbShowRevertText, pbShowRevertImage, pbRevertText, pbRevertImage
// pbShowPopBar, pbShowPopText, pbShowPopImage, pbPopText, pbPopImage, pbShowCaption

// Advanced method to begin moves and resizes. (Use Pop/PopEx and Revert where possible instead.)
// X and Y postfixes refer to the top left pixel. W and H postfixes refer to the width and height
// speedM and speedS are the speeds of the move and size respectively
// className denotes the CSS class to apply to the object that is being moved and/or sized
// fnDone denotes the script method to run when the move/resize is complete. It must have a single
// parameter that holds the object itself.
function PopBox(obj, startX, startY, endX, endY, startW, startH, endW, endH, speedM, speedS, className, fnDone)
{
	if (typeof obj == "string") obj = GetRawObject(obj);
	if (obj == null || typeof obj != "object" || isNaN(startX) || isNaN(startY) || isNaN(endX) || isNaN(endY) || isNaN(startW) || isNaN(startH) || isNaN(endW) || isNaN(endH) || isNaN(speedM) || isNaN(speedS))
		return;
	var elem = InitPopBox(obj);

	if (popBox[elem.id].isAnimating == true)
	{
		var str = "PopBox('" + elem.id + "'," + startX + "," + startY + "," + endX + "," + endY + "," + startW + "," + startH + "," + endW + "," + endH + "," + speedM + "," + speedS + ",'" + className + "');";
		setTimeout(str, 10);
	}
	else
	{
		popBox[elem.id].isAnimating = true;
		popBox[elem.id].xCurr = parseFloat(startX);
		popBox[elem.id].yCurr = parseFloat(startY);
		popBox[elem.id].wCurr = parseFloat(startW);
		popBox[elem.id].hCurr = parseFloat(startH);
		popBox[elem.id].xTarg = parseFloat(endX);
		popBox[elem.id].yTarg = parseFloat(endY);
		popBox[elem.id].wTarg = parseFloat(endW);
		popBox[elem.id].hTarg = parseFloat(endH);
		popBox[elem.id].xDelta = Math.abs(parseFloat(endX) - parseFloat(startX));
		popBox[elem.id].yDelta = Math.abs(parseFloat(endY) - parseFloat(startY));
		popBox[elem.id].wDelta = Math.abs(parseFloat(endW) - parseFloat(startW));
		popBox[elem.id].hDelta = Math.abs(parseFloat(endH) - parseFloat(startH));
		popBox[elem.id].velM = (speedM) ? Math.abs(parseFloat(speedM)) : 1.0;
		popBox[elem.id].velS = (speedS) ? Math.abs(parseFloat(speedS)) : 1.0;
		popBox[elem.id].xTravel = 0.0;
		popBox[elem.id].yTravel = 0.0;
		popBox[elem.id].wTravel = 0.0;
		popBox[elem.id].hTravel = 0.0;
		// set element's start position
		elem.style.position = "absolute";
		elem.style.left = startX + "px";
		elem.style.top = startY + "px";
		// set element's start size
		elem.style.width = startW + "px";
		elem.style.height = startH + "px";
		elem.style.display = "inline";

		// the length of the line between start and end points
		var lenMove = Math.sqrt((Math.pow((startX - endX), 2)) + (Math.pow((startY - endY), 2)));
		var lenSize = Math.sqrt((Math.pow((startW - endW), 2)) + (Math.pow((startH - endH), 2)));
		// if the speeds are the same then they should be in sync
		if (popBox[elem.id].velM == popBox[elem.id].velS)
			lenMove = lenSize = Math.sqrt(Math.pow(lenMove, 2) + Math.pow(lenSize, 2));

		// how big the pixel steps are along each axis
		popBox[elem.id].xStep = ((popBox[elem.id].xTarg - popBox[elem.id].xCurr) / lenMove) * popBox[elem.id].velM;
		popBox[elem.id].yStep = ((popBox[elem.id].yTarg - popBox[elem.id].yCurr) / lenMove) * popBox[elem.id].velM;

		// how big the pixel steps are for each resize
		popBox[elem.id].wStep = ((popBox[elem.id].wTarg - popBox[elem.id].wCurr) / lenSize) * popBox[elem.id].velS;
		popBox[elem.id].hStep = ((popBox[elem.id].hTarg - popBox[elem.id].hCurr) / lenSize) * popBox[elem.id].velS;
		
		popBox[elem.id].fnDone = fnDone;
		if (className != null)
			elem.className = className;

		popBox[elem.id].cursor = elem.style.cursor;
		elem.style.cursor = "default";

		if (popBox[elem.id].isPopped == false)
			elem.style.zIndex = ++popBoxZ;

		var id = elem.id;
		if (popBox[elem.id].originalId != null) id = popBox[elem.id].originalId;
		if (pbPopBarFunc[id] != null)
		{
			pbPopBarFunc[id]();
			pbPopBarFunc[id] = null;
		}
			
		if (popBox[elem.id].fnPre != null && typeof popBox[elem.id].fnPre == 'function')
			popBox[elem.id].fnPre();

		// start the repeated invocation of the animation
		popBox[elem.id].interval = setInterval("DoPopBox('" + elem.id + "')", 10);
	}
}

/***************************************************************************************************
* Helper functions. Use these! They are much easier. Call Pop/PopEx and then Revert, or set the
* popBoxAutoClose global to true and Revert will be called for you.
***************************************************************************************************/

// this basic method centers the image in the browser and displays it at its full resolution, subject to window size.
function Pop(obj, speed, className)
{
	PopEx(obj, null, null, 0, 0, speed, className);
}

// If newLeft is null then the image is centered horizontally in the browser. Ditto for newTop (vertically).
// End newLeft and/or newTop with "A" for an absolute position, otherwise it is treated as a relative position.
// Ex: a newLeft of 20 would move right 20 pixels, "20A" would position 20 pixels from the left of it's containing element.
// If newWidth is 0 then the full image width is used, subject to scaling and window size. Ditto for newHeight.
// If newWidth is null the full width is used, regardless of window size, but still subject to scaling. Ditto for newHeight.
function PopEx(obj, newLeft, newTop, newWidth, newHeight, speed, className)
{
	if (typeof obj == "string") obj = GetRawObject(obj);
	if (obj.id == "")
		obj.id = CreateRandomId();

	var poppedSrc = obj.getAttribute('pbSrcNL');
	if (poppedSrc == null && pbSrc[obj.id] == null)
		poppedSrc = obj.getAttribute('pbSrc');

	if (poppedSrc != null)
	{
		var poppedImg = new Image();
		poppedImg.src = poppedSrc;
		
		if (pbSrc[obj.id] != null)
			delete pbSrc[obj.id];
			
		pbSrc[obj.id] = poppedImg;
	}
	
	var objToPop = (pbSrc[obj.id] != null) ? pbSrc[obj.id] : obj;
	var isReady = (typeof objToPop.readyState != 'undefined') ? (objToPop.readyState == "complete") : ((typeof objToPop.complete != 'undefined') ? (objToPop.complete == true) : true);
	if (isReady == false)
	{
		var imgWait = CreateWaitImage(obj);
		var str = "var imgWait = GetRawObject('" + imgWait.id + "'); if (imgWait != null) { imgWait.parentNode.removeChild(imgWait); PopEx('" + obj.id + "'," + newLeft + "," + newTop + "," + newWidth + "," + newHeight + "," + speed + ",'" + className + "'); }";
		objToPop.onload = new Function("", str);
		return;
	}

	var elem = InitPopBox(obj);

	if (popBox[elem.id].isPopped == true) return;

	if (typeof elem.ondblclick == "function")
		elem.onclick = elem.ondblclick;

	var startX = parseInt(elem.style.left);
	var startY = parseInt(elem.style.top);

	// figure out the popped image size and position
	var windowSize = GetInsideWindowSize();
	var hasRevertBar = HasRevertBar(obj);
	var hasCaption = HasCaption(obj);
	if (hasRevertBar == true && popBoxRevertBarAbove == true) windowSize.y -= 20;
	if (hasCaption == true && popBoxCaptionBelow == true) windowSize.y -= 20;

	var fullWidth = newWidth;
	var fullHeight = newHeight;

	if (newWidth == 0 || newHeight == 0 || newWidth == null || newHeight == null)
	{
		// get size from original object
		if (pbSrc[obj.id] != null)
		{
			fullWidth = pbSrc[obj.id].width;
			fullHeight = pbSrc[obj.id].height;
		}
		else if (obj.naturalWidth && obj.naturalHeight)
		{
			fullWidth = obj.naturalWidth;
			fullHeight = obj.naturalHeight;
		}
		else
		{
			var img = new Image();
			img.src = elem.src;
			fullWidth = img.width;
			fullHeight = img.height;
			delete img;
		}
		
		// some browsers have a race condition where it still doesn't get set so just fill the window
		if (fullWidth == 0 || fullHeight == 0)
		{
			var scale = Math.min(parseFloat(windowSize.x) / parseFloat(elem.width), parseFloat(windowSize.y) / parseFloat(elem.height));
			fullWidth = parseInt(elem.width * scale);
			fullHeight = parseInt(elem.height * scale);
		}
	}

	// adjust window size variables for new image boundaries
	if (newLeft != null)
	{
		if (typeof newLeft == "string" && newLeft.indexOf("A") == (newLeft.length - 1))
			newLeft = parseInt(newLeft, 10);
		else
			newLeft = popBox[elem.id].xOriginal + parseInt(newLeft, 10);
			
		windowSize.x -= newLeft;
	}

	if (newTop != null)
	{
		if (typeof newTop == "string" && newTop.indexOf("A") == (newTop.length - 1))
			newTop = parseInt(newTop, 10);
		else
			newTop = popBox[elem.id].yOriginal + parseInt(newTop, 10);
			
		windowSize.y -= newTop;
	}

	// adjust for scrollbars that might appear (quick compromise for browser incompatibilities)
	if (newWidth == null && newHeight == 0 && fullWidth > (windowSize.x - 20))
		windowSize.y -= 20;
	else if (newWidth == 0 && newHeight == null && fullHeight > (windowSize.y - 4))
		windowSize.x -= 4;

	var newSize = CalculateImageDimensions(newWidth, newHeight, fullWidth, fullHeight, windowSize);

	// width and height are now set, so position it
	if (newLeft == null || newTop == null)
	{
		var scroll = GetScrollOffset();

		if (newLeft == null)
		{
			newLeft = ((windowSize.x / 2) + scroll.x) - (newSize.x / 2);
			if (newLeft < 0) newLeft = 0;
		}
		
		if (newTop == null)
		{
			newTop = ((windowSize.y / 2) + scroll.y) - (newSize.y / 2);
			if (hasRevertBar == true && popBoxRevertBarAbove == true) newTop += 10;
			if (hasCaption == true && popBoxCaptionBelow == true) newTop -= 10;
			if (newTop < 0) newTop = 0;
		}
	}

	var func = null;
	if (typeof PostPopProcessing == "function")
		func = PostPopProcessing;

	if (typeof PrePopProcessing == "function")
		PrePopProcessing(obj);

	PopBox(elem, startX, startY, newLeft, newTop, popBox[elem.id].wOriginal, popBox[elem.id].hOriginal, newSize.x, newSize.y, speed, speed, className, func);
}

// Helper function for PopBox to move/resize the image back to its original position/size. Use this! It's much easier.
function Revert(obj, speed, className)
{
	if (typeof obj == "string") obj = GetRawObject(obj); 
	if (typeof popBox[obj.id] == "undefined" || popBox[obj.id] == null) return;

	if (typeof speed == 'undefined' || speed == null || speed == 0)
		speed = Math.max(popBox[obj.id].velM, popBox[obj.id].velS);
		
	if (typeof className == 'undefined')
		className = popBox[obj.id].originalClassName;
	
	var func = null;
	if (typeof PostRevertProcessing == "function")
		func = PostRevertProcessing;

	if (typeof PreRevertProcessing == "function")
		PreRevertProcessing(obj);

	PopBox(obj, popBox[obj.id].xTarg, popBox[obj.id].yTarg, popBox[obj.id].xOriginal, popBox[obj.id].yOriginal, popBox[obj.id].wTarg, popBox[obj.id].hTarg, popBox[obj.id].wOriginal, popBox[obj.id].hOriginal, speed, speed, className, func);
}


/***************************************************************************************************
* These methods are the pre and post processing events for Pop/PopEx and Revert.
* Feel free to copy them to your own page script and add your own code to the method bodies.
***************************************************************************************************/

// called before the Pop begins
// The parameter is the original object
//function PrePopProcessing(obj)
//{
//}

// called after the pop is complete
// The parameter is the copy of the object that is resized
//function PostPopProcessing(obj)
//{
//}

// called before the Revert begins
// The parameter is the copy of the object that is resized
//function PreRevertProcessing(obj)
//{
//}

// called after the Revert is complete
// The parameter is the original object
//function PostRevertProcessing(obj)
//{
//}



/* MENU */

function popUpDownloadWindow(){
popUpWindow('http://www.horner-apg.com/downloads.asp?files=cscape',240,150,520,380);
}

var popUpWin=0;

function popUpWindow(URLStr, left, top, width, height){
  if(popUpWin){
    if(!popUpWin.closed) popUpWin.close();
  }
  popUpWin = open(URLStr, 'popUpWin', 'toolbar=no,location=no,directories=no,status=no,menubar=no,scrollbars=yes,resizable=yes,copyhistory=yes,width='+width+',height='+height+',left='+left+', top='+top+',screenX='+left+',screenY='+top);
}



var currentMenu = 'homeMenu';
var currentActiveItem = null;

	function showMenu(m,s){
		document.getElementById(currentMenu).className = 'hideMe';
		if(currentActiveItem == null)currentActiveItem = document.getElementById('homeMenuLink');
		currentActiveItem.className = '';
		s.className = "active";
		var newMenu = document.getElementById(m);
		newMenu.className = 'showMe';
		currentMenu = newMenu.id;
		currentActiveItem=s;		
	}

	function getPageMenu()
	{
		re = /\/[a-z]{2}\/([a-zA-Z0-9\-_]+)\.[a-z]+$/;
  		el = re.exec(document.location.href);
		if(el)
		{
			if(el[1] == "default")
				return "homeMenu";
			else if(el[1] == "news")
				return "newsandeventsMenu";
			else if(el[1] == "distributors")
				return "wheretobuyMenu";
				else if(el[1] == "search")
				return "homeMenu";
				else if(el[1] == "login")
				return "homeMenu";
				else if(el[1] == "password_forget")
				return "homeMenu";
				else if(el[1] == "register")
				return "homeMenu";
				else if(el[1] == "MyAccount")
				return "homeMenu";
			else
				return (el[1].replace(/-/g, "") + "Menu");
		} else {
			re = /\/[a-z]{2}\/([a-zA-Z0-9\-_]+)\/([a-zA-Z0-9\-_]+\/)*[a-zA-Z0-9\-_]+\.[a-z]+$/;
			el = re.exec(document.location.href);
			if(el)
			{
				if(el[1] == "default")
					return "homeMenu";
				else if(el[1] == "news")
					return "newsandeventsMenu";
				else if(el[1] == "distributors")
				  return "wheretobuyMenu";
					else if(el[1] == "search")
				  return "homeMenu";
					else if(el[1] == "login")
				  return "homeMenu";
					else if(el[1] == "password_forget")
				  return "homeMenu";
					else if(el[1] == "register")
				  return "homeMenu";
					else if(el[1] == "MyAccount")
				  return "homeMenu";
				else
					return (el[1].replace(/-/g, "") + "Menu");
			} else {
				return "homeMenu";
			}
		}
	}




/* SWF FLASH */

//v1.0
//Copyright 2006 Adobe Systems, Inc. All rights reserved.
function AC_AddExtension(src, ext)
{
  if (src.indexOf('?') != -1)
    return src.replace(/\?/, ext+'?'); 
  else
    return src + ext;
}

function AC_Generateobj(objAttrs, params, embedAttrs) 
{ 
  var str = '<object ';
  for (var i in objAttrs)
    str += i + '="' + objAttrs[i] + '" ';
  str += '>';
  for (var i in params)
    str += '<param name="' + i + '" value="' + params[i] + '" /> ';
  str += '<embed ';
  for (var i in embedAttrs)
    str += i + '="' + embedAttrs[i] + '" ';
  str += ' ></embed></object>';

  document.write(str);
}

function AC_FL_RunContent(){
  var ret = 
    AC_GetArgs
    (  arguments, ".swf", "movie", "clsid:d27cdb6e-ae6d-11cf-96b8-444553540000"
     , "application/x-shockwave-flash"
    );
  AC_Generateobj(ret.objAttrs, ret.params, ret.embedAttrs);
}

function AC_SW_RunContent(){
  var ret = 
    AC_GetArgs
    (  arguments, ".dcr", "src", "clsid:166B1BCA-3F9C-11CF-8075-444553540000"
     , null
    );
  AC_Generateobj(ret.objAttrs, ret.params, ret.embedAttrs);
}

function AC_GetArgs(args, ext, srcParamName, classid, mimeType){
  var ret = new Object();
  ret.embedAttrs = new Object();
  ret.params = new Object();
  ret.objAttrs = new Object();
  for (var i=0; i < args.length; i=i+2){
    var currArg = args[i].toLowerCase();    

    switch (currArg){	
      case "classid":
        break;
      case "pluginspage":
        ret.embedAttrs[args[i]] = args[i+1];
        break;
      case "src":
      case "movie":	
        args[i+1] = AC_AddExtension(args[i+1], ext);
        ret.embedAttrs["src"] = args[i+1];
        ret.params[srcParamName] = args[i+1];
        break;
      case "onafterupdate":
      case "onbeforeupdate":
      case "onblur":
      case "oncellchange":
      case "onclick":
      case "ondblClick":
      case "ondrag":
      case "ondragend":
      case "ondragenter":
      case "ondragleave":
      case "ondragover":
      case "ondrop":
      case "onfinish":
      case "onfocus":
      case "onhelp":
      case "onmousedown":
      case "onmouseup":
      case "onmouseover":
      case "onmousemove":
      case "onmouseout":
      case "onkeypress":
      case "onkeydown":
      case "onkeyup":
      case "onload":
      case "onlosecapture":
      case "onpropertychange":
      case "onreadystatechange":
      case "onrowsdelete":
      case "onrowenter":
      case "onrowexit":
      case "onrowsinserted":
      case "onstart":
      case "onscroll":
      case "onbeforeeditfocus":
      case "onactivate":
      case "onbeforedeactivate":
      case "ondeactivate":
      case "type":
      case "codebase":
        ret.objAttrs[args[i]] = args[i+1];
        break;
      case "width":
      case "height":
      case "align":
      case "vspace": 
      case "hspace":
      case "class":
      case "title":
      case "accesskey":
      case "name":
      case "id":
      case "tabindex":
        ret.embedAttrs[args[i]] = ret.objAttrs[args[i]] = args[i+1];
        break;
      default:
        ret.embedAttrs[args[i]] = ret.params[args[i]] = args[i+1];
    }
  }
  ret.objAttrs["classid"] = classid;
  if (mimeType) ret.embedAttrs["type"] = mimeType;
  return ret;
}


/* POPUP WINDOW */

function popUpDownloadWindow(){
popUpWindow('http://www.horner-apg.com/downloads.asp?files=cscape',240,150,520,380);
}

var popUpWin=0;

function popUpWindow(URLStr, left, top, width, height){
  if(popUpWin){
    if(!popUpWin.closed) popUpWin.close();
  }
  popUpWin = open(URLStr, 'popUpWin', 'toolbar=no,location=no,directories=no,status=no,menubar=no,scrollbars=yes,resizable=yes,copyhistory=yes,width='+width+',height='+height+',left='+left+', top='+top+',screenX='+left+',screenY='+top);
}



/* POPUP WINDOW2 */

function MM_openBrWindow(theURL,winName,features) { //v2.0
  window.open(theURL,winName,features);
}

/* ? */

var chkSendStuff = 1
function sendMailItems(){
	chkSendStuff ++;
	if (chkSendStuff == 2) {
			document.getElementById('postTable').style.display='none'
		} else if(chkSendStuff == 3) {
			chkSendStuff=1;
			document.getElementById('postTable').style.display='block'
		}
	}

