var Activa = {};

//DOM Getters
/**
 * did - Shorcut for getElementById
 * @param	id		The element id you are looking for
 * @return			Element with matching id
 */
function did(id) {
	return document.getElementById(id);
}
/**
 * dbn - Shorcut for getElementsByName
 * @param	name	The element name you are looking for
 * @param	parent	optional;The parent item to search within
 * @return			Collection of elements
 */
function dbn(name, parent) {
	return (parent || document).getElementsByName(name);
}
/**
 * dbt - Shorcut for getElementsByTagName
 * @param	tag		The element tagName you are looking for
 * @param	parent	optional;The parent item to search within
 * @return 			Collection of elements
 */
function dbt(tag, parent) {
	return (parent || document).getElementsByTagName(tag);
}
/**
 * dbc - Shortcut for getElementsByClassName
 * @param	className	The className you are looking for
 * @param	parent		optional;The parent item to search within
 * @return				Array of elements
 */
function dbc(className, parent) {
	className = 'dev_'+className;
	//use default provided by browser if it exists otherwise use our implementation
	if ( document.getElementsByClassName ) {
		return (parent || document).getElementsByClassName(className);
	}
	var nodes = Activa.toArray(dbt('*', parent)), elms = [];
	nodes.forEach(function(node){
		if ( Activa.DOM.hasClass(node, className) ) {
			elms.push(node);
		}
	});
	return elms;
}

Activa.Exception = function activaException(message, file, line, trace, url) {
	this.message = message;
	this.file = file;
	this.line = line;
	this.trace = trace;
	this.url = url;
};

Activa.urlencode = function urlencode(string) {
	return encodeURIComponent(string).replace("%20", "+");
};

Activa.NewWindow = function NewWindow(mypage, myname, w, h, scroll) {
	var winl = (screen.width - w) / 2;
	var wint = (screen.height - h) / 2;
	winprops = 'height='+h+',width='+w+',top='+wint+',left='+winl+',scrollbars='+scroll+',resizable=yes'
	win = window.open(mypage, myname, winprops)
	if ( parseInt(navigator.appVersion) >= 4 ) { 
		win.window.focus(); 
	}
	return win;
};

//Cookies
Activa.createCookie = function createCookie(name, value, days) {
	if ( days ) {
		var date = new Date();
		date.setTime(date.getTime() + (days * 24 * 60 * 60 * 1000));
		var expires = "; expires="+date.toGMTString();
	}
	else var expires = "";
	document.cookie = name+"="+value+expires+"; path=/";
};

Activa.getCookie = function getCookie(name) {
	var start = document.cookie.indexOf(name + "=");
	var len = start + name.length + 1;
	if ( start < 0 ) {
		return null;
	}
	if ( start == 1 ) { 
		return null; 
	}
	var end = document.cookie.indexOf(';', len);
	if ( end == -1 ) { 
		end = document.cookie.length; 
	}
	return unescape( document.cookie.substring(len, end) );
};

Activa.padString = function padString(str, len) {
	if ( typeof(str) != 'string' ) {
		str = String(str);
	}
	while ( str.length < len ) {
		str = '0'+str;
	}
	return str;
};

Activa.gaTrack = function gaTrack(str) {
	try {
		if ( typeof(pageTracker) != 'undefined' ) {
			pageTracker._trackPageview(str);
		} else if ( typeof(urchinTracker) != 'undefined' ) {
			urchinTracker(str);
		}
	} catch ( err ) {}
}

/**
 * Activa.fixEvent - Returns an event object with common properties/methods normalized for easier cross browser usage.
 * @param e		object	optional;Event object to normalize
 * @return		object	Event object after normalization
 */
Activa.fixEvent = function fixEvent(e) {
	var evnt = e || window.event;
	if ( !evnt ) { 
		return null; 
	}
	if ( !evnt.target ) { 
		evnt.target = evnt.srcElement; 
	}
	evnt.preventDefault = (evnt.preventDefault)? evnt.preventDefault : function() { this.returnValue = false; };
	evnt.stopPropagation = (evnt.stopPropagation)? evnt.stopPropagation : function() { this.cancelBubble = true; };
	var scroll = Activa.Dimensions.getScrollXY();
	if ( e.pageX === undefined ) { 
		e.pageX = e.clientX + scroll.x; 
	}
	if ( e.pageY === undefined ) { 
		e.pageY = e.clientY + scroll.y; 
	}
	if ( typeof e.layerX != 'number' ) { 
		e.layerX = e.offsetX; 
	}
	if ( typeof e.layerY != 'number' ) { 
		e.layerY = e.offsetY; 
	}
	if ( !e.relatedTarget ) {
		switch ( e.type ) {
			case 'mouseover':
			case 'mouseenter':
				e.relatedTarget = e.fromElement;
				break;
			case 'mouseleave':
			case 'mouseout':
				e.relatedTarget = e.toElement;
				break;
		}
	};
	return evnt;
};

Activa.createDelegate = function createDelegate(oObject, sMethodName, data) {
	return function () {
		if ( data ) {
			var args = [];
			var i = 0;
			while ( arguments[i] ) {
				args[i] = arguments[i];
				i++;
			}
			args[i] = data;
			return oObject[sMethodName].apply(oObject, args);
		}
		return oObject[sMethodName].apply(oObject, arguments);
	};
};

Activa.registerEvent = function registerEvent(elem, event, callback, capture) {
	capture = Boolean(capture);
	elem = Activa.DOM.check(elem);
	
	if ( event == 'allchange' ) {
		Activa.registerEvent(elem, 'change', callback, capture);
		Activa.registerEvent(elem, 'click', callback, capture);
		Activa.registerEvent(elem, 'keyup', callback, capture);
		return;
	}
	
	function wrapCallback(e) { 
		callback(Activa.fixEvent(e));
	};
	
	if ( elem.addEventListener ) {
		elem.addEventListener(event, wrapCallback, capture);
	} else {
		elem.attachEvent('on'+event, wrapCallback); 
	}
	
	return new Activa.registeredEvent(elem, event, wrapCallback, capture);
};

Activa.unregisterEvent = function unregisterEvent(elem, event, callback, capture) {
	capture = Boolean(capture);
	elem = Activa.DOM.check(elem);
	
	if ( event == 'allchange' ) {
		Activa.unregisterEvent(elem, 'change', callback, capture);
		Activa.unregisterEvent(elem, 'click', callback, capture);
		Activa.unregisterEvent(elem, 'keyup', callback, capture);
		return;
	}
	
	if ( elem.removeEventListener ) {
		elem.removeEventListener(event, callback, capture);
	} else {
		elem.detachEvent('on'+event, callback); 
	}
}

Activa.registeredEvent = function registeredEvent(elem, event, callback, capture) {
	this.elem = elem;
	this.event = event;
	this.callback = callback;
	this.capture = capture;
	
	this.unregister = function() {
		Activa.unregisterEvent(this.elem, this.event, this.callback, this.capture);
	}
};

/**
 * Activa.toArray - Shortcut for Array.prototype.slice.call(obj, idx)
 * @param	obj		mixed		Object to call array.slice on
 * @param	start	integer		Index at which to begin slicing
 * @param	end		integer		Index at which to end slicing
 * @return			array		New array containing values from the idx to the end of the obj
 *		Note: Useful for transforming arguments object and collections into regular arrays
 *		ex.	function(){ var args = Activa.toArray(arguments); alert(args instanceof Array); }
 */

Activa.toArray = function toArray(obj, start, end) {
	var ret = obj;
	if ( window.ActiveXObject ) {
		if ( typeof obj.length == 'undefined' ) { 
			obj.length = Activa.getLength(obj); 
		}
		ret = Array.prototype.map.call(obj, function mapCall(item) { return item; });
	}
	var args = [(start || 0)];
	if ( end && !isNaN(Number(end)) ) {
		args.push(Number(end));
	}
	return Array.prototype.slice.apply(ret, args);
};


/**
 * getLength - Finds the total number of all non-function properties owned by the object (see hasOwnProperty)
 * @param obj    object    The object whose properties to count.
 * @return       int       The length of the object or 0 if obj was invalid
 */
Activa.getLength = function getLength(obj) {
    if ( !obj ) {
    	return 0; 
    }
    var i=0;
    for ( var key in obj ) {
        if ( obj.hasOwnProperty(key) && typeof obj[key] !='function' ) { 
        	i++; 
        }
    }
    return i;
};


//Prototypes
String.prototype.ltrim = function() {
	return this.replace(/^\s*/, '');
};

String.prototype.rtrim = function() {
	return this.replace(/\s*$/, '');
};

String.prototype.trim = function() {
	return this.rtrim().ltrim();
};

/**
 * Array and object functions
 */
/**
 * Array methods indexOf, forEach, map, filter
 * 		Added for browsers without native support
 */
Array.prototype.indexOf = (function() {
	var fn;
	if ( typeof Array.prototype.indexOf == 'function' ) {
		fn = Array.prototype.indexOf; 
	} else {
		fn = function indexOf(obj, start){
			var len = this.length;
			start = Number(start) || 0;
			start = (start < 0) ? Math.ceil(start) : Math.floor(start);
			if ( start < 0 ) { 
				start+= len; 
			}
			for ( ; start < len; start++ ) {
				if ( start in this && this[start] === obj ) { 
					return start; 
				}
			}
			return -1;
		}
	}
	var indexOf = null;
	return fn;
})();
Array.prototype.forEach = (function() {
	var fn;
	if ( typeof Array.prototype.forEach == 'function' ) {
		fn = Array.prototype.forEach;
	} else {
		fn = function forEach(fn /*, bind*/){
			var len = this.length;
			if ( typeof(fn) != 'function' ) { 
				throw new TypeError(); 
			}
			var bind = arguments[1];
			for ( var i = 0; i < len; i++ ) {
				if ( i in this ) { 
					fn.call(bind, this[i], i, this); 
				}
			}
		};
	}
	var forEach = null;
	return fn;
})();
Array.prototype.map = (function() {
	var fn;
	if ( typeof Array.prototype.map == 'function' ) {
		fn = Array.prototype.map;
	} else {
		fn = function map(fn /*, bind*/){
			var len = this.length;
			if ( typeof(fn) != 'function' ) { 
				throw new TypeError(); 
			}
			var ret = [], bind = arguments[1];
			for ( var i = 0; i < len; i++ ) {
				if ( i in this ) { 
					ret[i] = fn.call(bind, this[i], i, this); 
				}
			}
			return ret;
		};
	}
	var map = null;
	return fn;
})();
Array.prototype.filter = (function() {
	var fn;
	if ( typeof Array.prototype.filter == 'function' ) {
		fn = Array.prototype.filter;
	} else {
		fn = function filter(fn /*, bind*/){
			var len = this.length;
			if ( typeof(fn) != 'function' ) { 
				throw new TypeError(); 
			}
			var ret = [], bind = arguments[1], val=null;
			for ( var i = 0; i < len; i++ ) {
				if ( i in this ) {
					val = this[i];
					if ( fn.call(bind,val,i,this) ) { 
						ret.push(val); 
					}
				}
			}
			return ret;
		};
	}
	var filter = null;
	return fn;
})();
Array.prototype.every = (function() {
	var fn;
	if ( typeof Array.prototype.every == 'function' ) {
		fn = Array.prototype.every;
	} else {
		fn = function every(fn /*, bind*/) {
			return this.filter.apply(this, Activa.toArray(arguments)).length === this.length;
		};
	}
	var every = null;
	return fn;
})();
Array.prototype.some = (function() {
	var fn;
	if ( typeof Array.prototype.some == 'function' ) {
		fn = Array.prototype.some;
	} else {
		fn = function some(fn /*, bind*/) {
			return this.filter.apply(this, Activa.toArray(arguments)).length > 0;
		};
	}
	var some = null;
	return fn;
})();

Array.prototype.inArray = function(targ) {
	if ( this.indexOf(targ) != -1 ) {
		return true;
	} else {
		return false;
	}
};

Array.prototype.remove = function(pos) {
	if ( pos != -1 ) {
		this.splice(pos, 1);
	}	
};

Date.prototype.format = function(format) {
	var res = '';
	for ( var i = 0; i < format.length; i++ ) {
		switch ( format.charAt(i) ) {
			case 'm':
				res += Activa.padString(this.getMonth() + 1, 2);
				break;
				
			case 'd':
				res += Activa.padString(this.getDate(), 2);
				break;
				
			case 'Y':
				res += this.getFullYear();
				break;
				
			case 'H':
				res += Activa.padString(this.getHours(), 2);
				break;
				
			case 'i':
				res += Activa.padString(this.getMinutes(), 2);
				break;
				
				
			default:
				res += format.charAt(i);
				break;
				
		}
	}
	return res;
};

Function.prototype.bind = function() {
	var fn = this;
	var args = Activa.toArray(arguments);
	var reference = args.shift();
	return function binded() {
		var arglist = args.concat(Activa.toArray(arguments));
		return fn.apply(reference, arglist);
	};
};


/**
 * Create a new Class.
 * 
 * Note:
 * 	Two things are available inside methods of the instances of the class:
 * 		parent	- Gives access to the instance of the parent object. ( use: this.parent )
 * 		root	- Call the parent's identically named method. ( use: this.root(arg1, arg2, ...) ) 
 * 
 * Usage:
 *	var className = new Class({
 *		[Extends: constructor,]
 *		[property: value..,]
 *		[method: function [funcname]() { .... }, ...]
 *	});
 * 
 * @param object params Properties and methods for the class as an object literal
 * @return Class		The constructor for the new class
 */
Activa.Class = function Class(params) {
	params = (params instanceof Function) ? {init: params} : params;
	
	var obj = function construct() {
		Activa.Class._clean(this);
		var simple = arguments.callee.simple;
		if ( simple ) {
			delete arguments.callee.simple;
		}
		return (this.init && !simple) ? this.init.apply(this, arguments) : this;
	}
	
	for ( var k in this ) {
		obj[k] = this[k];
	}
	obj._implement(params);
	obj.constructor = Class;
	obj.prototype.constructor = obj;
	return obj;
}

/**
 * Add static properties/methods to the class. They are accessible via the class name.
 * 
 * Usage:
 * 	myClass.statics({
 * 		[property: value..,]
 * 		[method: function [funcname]() { .... }, ...]
 *	});
 * 
 * @param object args	Static properties and methods for the class as an object literal 
 * @return Class		The class constructor that was extended
 */
Activa.Class.prototype.statics = function statics(args) {
	for ( var k in args ) {
		this[k] = args[k];
	}
	return this;
}

/**
 * Add properties/methods to instances of the class. Instances that already exist will gain
 * these as well.
 * 
 * Usage:
 * --Add a specific property/method from an object
 * 	myClass._implement(
 *	key,
 *	{
 * 		[property: value..,]
 * 		[method: function [funcname]() { .... }, ...]
 * 	});
 * 
 * or:
 * --Add all the properties/methods from an object
 * 	myClass._implement({
 * 		[property: value..,]
 * 		[method: function [funcname]() { .... }, ...]
 * 	});
 * 
 * @param string/object key		Key name or object depending on what to add (see usage above)
 * @param object value			Object to to get the property/method from when using key (see usage above)
 * @return Class				The class constructor that implemented the new properties/methods
 */
Activa.Class.prototype._implement = function _implement(key, value) {
	if ( typeof key == 'object' && !(key instanceof Array) ) {
		if ( 'Extends' in key ) {
			var parent = key.Extends;
			parent.simple = true;
			this.prototype = new parent();
			delete key.Extends;
		}
		for ( var k in key ) {
			this._implement(k, key[k]);
		}
		return this;
	}
	if ( typeof value == 'function' ) {
		var parentMethod = (key in this.prototype) ? this.prototype[key] : null;
		this.prototype[key] = function method() {
			var _root = null, _oldroot = null;
			if ( this.root ) {
				_oldroot = this.root;
				delete this.root;
			}
			this.root = _root = (_root) ? _root :
				function root() {
					if ( parentMethod == null ) {
						throw new ReferenceError("The method '"+key+"' does not exist in the parent class.");
					}
					return parentMethod.apply(this, arguments);
				}.bind(this);
			var ret = value.apply(this, arguments);
			delete this.root;
			if ( _oldroot ) {
				this.root = _oldroot;
				delete _oldroot;
			}
			return ret;
		}
		return this;
	} else if ( typeof value == 'object' && key in this.prototype ) {
		var obj = this.prototype[key];
		this.prototype[key] = value;
		for ( var k in obj ) {
			this.prototype[key][k] = obj[k];
		}
		return this;
	}
	this.prototype[key] = value;
	return this;
}
/**
 * Clean a new instance of a class so they do not share the prototype properties.
 * 
 * @param object object		The class instance to clean
 * @return object			The clean instance of the class
 */
Activa.Class._clean = function(object){
	function destruct(obj){
		var ret, type = (obj instanceof Array ? 'array' : typeof obj);
		switch ( type ) {
			case 'object':
				ret = {};
				for ( var k in obj ) {
					ret[k] = destruct(obj[k]);
				}
				break;
			case 'array':
				ret = [];
				for ( var i = 0, z = obj.length; i < z; i++ ) {
					ret[i] = destruct(obj[i]);
				}
				break;
			default: return obj;
		}
		return ret;
	}
	
	for ( var key in object ) {
		delete object[key];
		
		var type = (object[key] instanceof Array ? 'array' : typeof object[key]);
		switch ( type ) {
			case 'object':
				if ( object[key] === null ) {
					object[key] = null;
					continue;
				}
				var n = function(){};
				n.prototype = object[key];
				var o = new n();
				object[key] = Activa.Class._clean(o);
				break;
			case 'array':
				object[key] = destruct(object[key]);
				break;
		}
	}
	return object;
};

Activa.DOM = {
	/**
	 * next - Returns the next non-whitespace sibling element
	 * @param	el	object	Element node from which to start
	 * @return		object	Next non-whitespace child element
	 */
	next: function next(el) {
		if ( !el || !el.nextSibling ) { 
			return null; 
		}
		el = el.nextSibling;
		return (el.nodeType == 1) ? el : this.next(el);
	},
	/**
	 * previous - Returns the previous non-whitespace sibling element
	 * @param	el	object	Element node from which to start
	 * @return		object	Previous non-whitespace child element
	 */
	previous: function previous(el) {
		if ( !el || !el.previousSibling ) { 
			return null; 
		}
		el = el.previousSibling;
		return (el.nodeType==1) ? el : this.previous(el);
	},
	/**
	 * first - Returns the first non-whitespace child element
	 * @param	el	object	Parent element node
	 * @return		object	First non-whitespace child element
	 */
	first: function first(el) {
		if ( !el || !el.firstChild ) { 
			return null; 
		}
		el = el.firstChild;
		return (el.nodeType==1) ? el : this.next(el);
	},
	/**
	 * last - Returns the last non-whitespace child element
	 * @param	el	object	Parent element node
	 * @return		object	Last non-whitespace child element
	 */
	last: function last(el) {
		if ( !el || !el.lastChild ) { 
			return null; 
		}
		el = el.lastChild;
		return (el.nodeType==1) ? el : this.previous(el);
	},
	/**
	 * owner - Returns the parent of the element
	 * @param	el	object	Element from which to retrieve the parent
	 * @return		object	Parent node of the element
	 */
	owner: function owner(el){
		if ( !el || !el.parentNode ) { 
			return null; 
		}
		el = el.parentNode;
		return (el.nodeType == 1) ? el : this.owner(el);
	},
	/**
	 * contains - Returns whether the given node is contained with the element
	 * @param el object Element in which to check
	 * @param node object Element to check for
	 * @return boolean True if node is a child of el, false if not
	 */
	contains: function contains(el, node){
	  return el.contains ?
	    el != node && el.contains(node) :
	    !!(el.compareDocumentPosition(node) & 16);
	},
	/**
	 * hasClass - Returns true if the element has the given class applied
	 * @param	obj			Element on which to check for the class
	 * @param	className	Class to look for on the element
	 * @return				True (if element has the class) / False (if it doesn't)
	 */
	hasClass: function hasClass(obj, className) {
		if ( !obj || className.trim() == '' ) { 
			return false; 
		}
		return (String(obj.className).split(' ').indexOf(className) != -1);
	},
	/**
	 * addClass - Adds a class to an element
	 * @param	obj			Element on which to add the class
	 * @param	className	Class to add to the element
	 * @return				True (if successfully added class) / False (if invalid object or empty classname given)
	 */
	addClass: function addClass(obj, className) {
		if ( !obj || className.trim() == '' || this.hasClass(obj, className) ) { 
			return false; 
		}
		obj.className = String(obj.className).split(' ').concat([className]).join(' ').trim();
		return true;
	},
	/**
	 * removeClass - Removes a class from an element
	 * @param	obj			Element from which to remove the class
	 * @param	className	Class to remove from the element
	 * @return				True (if successfully removed class) / False (if invalid object or empty classname given)
	 */
	removeClass: function removeClass(obj, className) {
		if ( !obj || className.trim() == '' ) { 
			return false; 
		}
		obj.className = String(obj.className).split(' ').filter(function(cls) {
			return (cls != className);
		}).join(' ');
		return true;
	},
	/**
	 * check - Ensures an element object is returned.
	 * @param string el		ID or element object
	 * @return object		Element object
	 */
	check: function check(el) {
		return (typeof el == 'object') ? el : did(el);
	},
	/**
	 * showID - Display a hidden element
	 * @param string id		ID of the element to show
	 */
	showID: function showID(id){
		var el = this.check(id);
		if ( el ) {
			el.style.display = '';
		}
	},
	/**
	 * hideID - Hide an element
	 * @param string id		ID of the element to hide
	 */
	hideID: function hideID(id){
		var el = this.check(id);
		if ( el ) {
			el.style.display = 'none';
		}
	},
	/**
	 * toggleID - Toggle an element to be shown or hidden
	 * @param string id		ID of the element to toggle
	 */
	toggleID: function toggleID(id){
		var el = this.check(id);
		if ( el ) {
			var fn = el.style.display == 'none' ? 'showID' : 'hideID';
			this[fn](el);
		}
	},
	/**
	 * ready - Register callbacks to execute when the DOM is ready
	 * 
	 * Example using the optional object and args parameters:
	 *		-keyword 'this' will be the person object
	 * 		-arg1 and arg2 become 'name' and 'job'
	 * 		-function will log:
	 * 				name: bob job: plummer
	 * 		
	 * 		var person = {'name':'bob','job':'plummer'};
	 * 		domLoader.register(function(arg1, arg2){
	 * 			console.log(arg1+': '+this[arg1]+' arg2: '+this[arg2]);
	 * 		}, person, ['name','job']);
	 * 
	 *  @param fn
	 *  @param obj
	 *  @param args
	 *  @return
	 */
	ready: function ready(fn, obj, args) {
		var rdy = arguments.callee;
		rdy.isReady = rdy.isReady === undefined ? false : rdy.isReady;
		rdy.isBound = rdy.isBound === undefined ? false : rdy.isBound;
		rdy.queue = rdy.queue === undefined ? [] : rdy.queue;
		rdy.binds = rdy.binds === undefined ? [] : rdy.binds;
		rdy.args = rdy.args === undefined ? [] : rdy.args;
		
		rdy.register = function register(fn, obj, args) {
			rdy.checkReady();
			rdy.queue = (rdy.queue instanceof Array) ? rdy.queue : [];
			if ( !fn || typeof fn != 'function' ) {
				return;
			}
			
			var offset = rdy.queue.push(fn) - 1;
			
			obj = obj || null;
			rdy.binds = (rdy.binds instanceof Array) ? rdy.binds : [];
			rdy.binds[offset] = obj;
			
			args = args ? ((args instanceof Array) ? args :  Activa.toArray(args)) : [];
			rdy.args = (rdy.args instanceof Array) ? rdy.args : [];
			rdy.args[offset] = args;
			if ( rdy.isReady ) {
				rdy.exec();
			}
			return rdy;
		}
		
		rdy.checkReady = function checkReady() {
			if ( rdy.isBound ) {
				return;
			}
			rdy.isBound = true;
			//var self = this;
			var events = {'load': window};
			// Mozilla, Opera and Safari
			if ( document.addEventListener ) {
				events['DOMContentLoaded'] = document;
			// IE
			} else if ( document.attachEvent ) {
				events['onreadystatechange'] = document;
				// If IE and not an iframe -- continually check to see if the document is ready
				if ( document.documentElement.doScroll && window == window.top ) {
					(function ieDoScrollTest(){
						if ( rdy.isReady ) {
							return;
						}
						try {
							// If IE is used, use the trick by Diego Perini -- http://javascript.nwbox.com/IEContentLoaded/
							document.documentElement.doScroll("left");
							// and execute any waiting functions
							rdy.isReady = true;
							rdy.exec();
						} catch ( error ) {
							setTimeout(arguments.callee, 0);
							return;
						}
					})();
				}
			}
			var idDoScrollTest = null;
			//Faster Safari detection
			if ( typeof navigator.taintEnabled === 'undefined' ) {
				var timer = window.setInterval(function safariReadyTest() {
					if ( /loaded|complete/.test(document.readyState) ) {
						window.clearInterval(timer);
						if ( rdy.isReady ) {
							return;
						}
						rdy.isReady = true;
						rdy.exec();
					}
				}, 10);
			}
			var safariReadyTest = null;
			for ( var name in events ) {
				if ( !events.hasOwnProperty(name) ) {
					continue;
				}
				(function scopeFix(name, obj) {
					Activa.registerEvent(obj, name, function pageLoadEventWrapper() {
						Activa.unregisterEvent(obj, name, arguments.callee);
						if ( !rdy.isReady ) {
							rdy.isReady = true;
							rdy.exec();
						}
					});
					var pageLoadEventWrapper = null;
				})(name, events[name]);
			}
			var varprotector = null;
		}
		
		rdy.exec = function exec() {
			if ( !rdy.isReady || !(rdy.queue instanceof Array) ) {
				return;
			}
			var fn, obj, args;
			while ( fn = rdy.queue.shift() ) {
				obj = rdy.binds.shift() || window;
				args = rdy.args.shift();
				fn.apply(obj, args);
			}
			
			rdy.queue = [];
			rdy.binds = [];
			rdy.args = [];
		}
		rdy.register(fn, obj, args);
	}
};

/**
 * Activa.rpc
 * 
 * Parameters:
 * path			The destination of the remote file.
 * options		An object that holds optional arguments.
 * 
 * options:
 * input		mixed		Can either be a string or an object to send as POST data
 * errorHandler		function	Set the error handling function.  Defaults to console.error if exists.
 * async		bool		Boolean to set to enable/disable async.  Defaults to false unless a callback is specified.
 * method		string		Sets whether to use POST or GET.  Defaults to POST
 * onLoad		function	Function is called once when a XHR request is loading.
 * onComplete		function	Function is called when XHR is successful.  Has one parameter for responseText.
 * 
 * Usage:
 * new Activa.rpc('testPath', {input: {a:'Testing!'}, onComplete: function(res){ alert(res); }});
 * 
 */
Activa.rpc = new Activa.Class({
	options: {
		input: null,
		errorHandler: null,
		async: false,
		method: 'POST',
		onComplete: function(){},
		onLoad: function(){}
	},
	setOptions: function setOptions(options) {
		options = options || {};
		
		if ( typeof options.onComplete == 'function' && typeof options.async == 'undefined' ) {
			options.async = true;
		}
		
		for ( var k in this.options ) {
			this.options[k] = (k in options) ? options[k] : this.options[k];
		}
	},
	loaded:false,
	init: function init(path, options) {
		this.request = Activa.rpc.xhr();
		this.path = path;
							
		this.setOptions(options);

		this.input = Activa.rpc.objectToString(this.options.input);
		
		this.request.onreadystatechange = Activa.createDelegate(this, 'onReadyStateChange');

		this.request.open(this.options.method.toUpperCase(), 'rpc/'+this.path, this.options.async);
		this.request.setRequestHeader("Content-Type", "application/x-www-form-urlencoded");
		Activa.rpc.instances.push(this);
		this.request.send(this.input);
	},
	onReadyStateChange: function onReadyStateChange() {
		if ( 4 == this.request.readyState && 200 == this.request.status ) {
			Activa.rpc.instances.remove(Activa.rpc.instances.indexOf(this));
			eval("var res="+this.request.responseText);
			if ( res instanceof Activa.Exception ) {
				if ( typeof(this.options.errorHandler) == 'undefined' ) {
					if ( console && console.error ) {
						console.error(res);
					}
					return;
				} else {
					this.options.errorHandler(res);
					return;
				}
			}
			this.options.onComplete(res);
		} else if ( 4 == this.request.readyState && this.request.status != 0 ) {
			Activa.rpc.instances.remove(Activa.rpc.instances.indexOf(this));
			if ( typeof(this.errHandler) != "undefined" ) {
				this.options.errorHandler(new Activa.Exception("Server returned error code "+this.request.status));
			}
		} else if ( this.request.readyState != 4 && !this.loaded ) {
			this.options.onLoad();
			this.loaded = true;
		}
	},
	abort: function abort() {
		this.request.abort();
		Activa.rpc.instances.remove(Activa.rpc.instances.indexOf(this));
	}
});

Activa.rpc.statics({
	instances:[],
	callWhenReady: function callWhenReady(func) {
		if ( Activa.rpc.instances.length > 0 ) {
			window.setTimeout(function() { Activa.rpc.callWhenReady(func); }, 250);
			return;
		}
		func();
	},
	xhr: function xhr(){
		if ( window.XMLHttpRequest ) {
			return new XMLHttpRequest();
		} else if ( window.ActiveXObject ) {
			try {
				return new ActiveXObject( "Msxml2.XMLHTTP" );
			} catch ( error ) {
				try {
					return new ActiveXObject( "Microsoft.XMLHTTP" );
				} catch ( error2 ) {
					return alert("Fatal Error: No XMLHttp Interface Available");
				}
			}
		} else {
			return alert("Fatal Error: No XMLHttp Interface Available");
		}
	},
	objectToString: function objectToString(input) {
		if ( !input instanceof Object ) {
			return input;
		}
		
		var str = '';
		for ( var prop in input ) {
			if ( input[prop] instanceof Array ) {
				for ( var x = 0; x < input[prop].length; x++ ) {
					str += prop+'[]='+encodeURIComponent(input[prop][x])+'&';
				}
			} else if ( input[prop] instanceof Object ) {
				for ( var prop2 in input[prop] ) {
					if ( input[prop][prop2] instanceof Array ) {					
						for ( var x = 0; x < input[prop][prop2].length; x++ ) {
							str += prop+'['+prop2+'][]='+encodeURIComponent(input[prop][prop2][x])+'&';
						}
					} else if ( !(input[prop][prop2] instanceof Function) ) {
						str += prop+'['+prop2+']='+encodeURIComponent(input[prop][prop2])+'&';
					}
				}
			} else if ( !(input[prop] instanceof Function) ) {
				str += prop+'='+encodeURIComponent(input[prop])+'&';
			}
		}
		return str;
	},
	activaX: function activaX(element, url, input) {
		this.element = Activa.DOM.check(element);
		this.url = url;
		
		var request = Activa.rpc.xhr();
		this.request.onreadystatechange = Activa.createDelegate(this, 'callback');
		this.request.open("GET", this.url, true);
		this.request.send(null);
		
		this.callback = function() {
			this.element.innerHTML = this.request.responseText;
			this.element.innerHTML = this.element.innerHTML; //ie fix
		};		
	}
});

//Dimensions

Activa.Dimensions = {
	/**
	 * getDocSize - Get the size of the current viewable document area
	 * @param	boolean	scrollbar	Whether the width of the scrollbars should be included. ( default: false )
	 * @return	object		The width, height of the current viewable document area in a keyed object ( obj.width obj.height )
	 */
	getDocSize: function getDocSize(scrollbar) {
		scrollbar = scrollbar || false;
		var w = 0, h = 0;
		if ( typeof(window.innerWidth) == 'number' ) {
			//Non-IE
			w = window.innerWidth;
			h = window.innerHeight;
		} else {
			if ( document.compatMode == 'CSS1Compat' ) {
				w = document.documentElement.clientWidth;
				h = document.documentElement.clientHeight;
			} else {
				w = document.body.clientWidth;
				h = document.body.clientHeight;
			}
		}
		if ( !scrollbar ) {
			var sbar = this.getScrollbarWidth();
			w -= sbar.right;
			w = w < 0 ? 0 : w;
			h -= sbar.bottom;
			h = h < 0 ? 0 : h;
		}
		return {'width': w, 'height': h};
	},

	/**
	 * getMaxDocSize - Get the total document size
	 * @param	boolean	scrollbar	Whether the width of the scrollbars should be included. ( default: false )
	 * @return	object				The width, height of the total document size in a keyed object ( obj.width obj.height )
	 */
	getMaxDocSize: function getMaxDocSize(scrollbar) {
		scrollbar = scrollbar || false;
		var w = 0, h = 0, x = 0, y = 0;
		var docsize = this.getDocSize(true), sbar = this.getScrollbarWidth();
		if ( typeof window.scrollMaxY == 'number' ) {
			x = window.scrollMaxX;
			y = window.scrollMaxY;
		} else {
			var scroll = this.getScrollXY();
			x = scroll.x;
			y = scroll.y;
		}
		w = docsize.width + x - (scrollbar ? 0 : sbar.right);
		w = w < 0 ? 0 : w;
		h = docsize.height + y - (scrollbar ? 0 : sbar.bottom);
		h = h < 0 ? 0 : h;
		return {'width':w,'height':h};
	},

	/**
	 * getScrollbarWidth - Get the width of the scrollbars on the right/bottom
	 * @return	object		The width of the scrollbars on the right/bottom of the window if present ( obj.right, obj.bottom )
	 */
	getScrollbarWidth: function getScrollbarWidth() {
		var size = {'right': 0, 'bottom': 0};
		if ( !document || !document.documentElement ) {
			return size;
		}
		var docEl = document.documentElement;
		size['right'] = ((typeof window.innerWidth == 'number') ? window.innerWidth : docEl.offsetWidth) - docEl.clientWidth;
		size['bottom'] = ((typeof window.innerHeight == 'number') ? window.innerHeight : docEl.offsetHeight) - docEl.clientHeight;
		return size;
	},

	/**
	 * getScrollXY - Gets the distance the page has been scrolled vertically and horizontally
	 * @return	object		The x,y distance in a keyed object ( obj.x obj.y )
	 */
	getScrollXY: function getScrollXY() {
		var sX = 0, sY = 0;
		if ( typeof(window.pageYOffset)=='number' ){
			//Non-IE
			sY = window.pageYOffset;
			sX = window.pageXOffset;
		} else if ( document.documentElement && (document.documentElement.scrollLeft || document.documentElement.scrollTop) ) {
			//IE 6+ in 'standards mode'
			sY = document.documentElement.scrollTop;
			sX = document.documentElement.scrollLeft;
		} else if ( document.body && (document.body.scrollLeft || document.body.scrollTop) ){
			//IE 6 in 'strict mode' & some other browsers
			sY = document.body.scrollTop;
			sX = document.body.scrollLeft;
		}
		return {'x':sX,'y':sY};
	}
};

/**
 * Create a validaton engine
 * 
 * @param string form_id			ID of the form
 * @param string alert_type		Type of alert to show the user (current values: alert(default), inline)
 * @param string message_box	ID of the element in which to insert the message (only used for inline alerts)
 * @param bool autoload			Whether to automatically register the handler for the submit event (true(default), false(not recommended))
 */
var Validate = new Activa.Class({
	forms: {},
	message: '',
	invalid: '',
	currentForm: null,
	autoload: true,
	init: function initValidate(form_id, alert_type, message_box, autoload) {
		this.message_box = message_box || '';
		this.alert_type = alert_type && this.message_box ? alert_type : 'alert';
		this.autoload = autoload === undefined ? true : Boolean(autoload);
		this.form_id = form_id || '';
		if ( this.form_id ) {
			this.addForm(form_id);
		}
	},
	addForm: function addForm(form) {
		var form_id = '';
		if ( typeof form == 'object' ) {
			form_id = form.id;
			this.forms[form.id] = form;
		} else {
			form_id = form;
			this.forms[form] = new Form(form);
		}
		this.currentForm = form_id;
	},
	validate: function validate(form_id) {
		form_id = form_id || '';
		if ( form_id ) {
			return this.forms[form_id].validate();
		}
		for ( var id in this.forms ) {
			if ( !this.forms[id].validate() ) {
				return false;
			}
		}
		return true;
	},
	end: function end() {
		if ( !this.autoload ) {
			return;
		}
		for ( var id in this.forms ) {
			(function registerSubmit(form_id) {
				Activa.registerEvent(did(form_id), 'submit', function(e) {
					e.stopPropagation();
					if ( !this.validate(form_id) ) {
						e.preventDefault();
						this.message = this.forms[form_id].message;
						this.invalid = this.forms[form_id].invalid;
						return this.notify(this.forms[form_id], this.invalid);
					}
				}.bind(this));
			}.bind(this))(id);
		}
	},
	notify: function notify(form, field) {
		var form = did(form.id);
		if ( this.alert_type == 'inline' ) {
			var msgbox = did(this.message_box);
			if ( msgbox ) {
				msgbox.innerHTML = this.message;
				return false;
			}
		}
		alert(this.message);
		if ( field ) {
			field.focus();
		}
		return false;
	}
});

var RuleContainer = new Activa.Class({
	containerStack: [],
	currentContainer: null,
	addRule: function addRule(rule) {
		var args = Activa.toArray(arguments);
		rule = false;
		args.forEach(function(item) {
			if ( rule === false && (item instanceof Rule || item instanceof RuleContainer) ) {
				rule = item;
			}
		});
		if ( rule === false ) {
			return;
		}
		if ( this.currentContainer instanceof RuleContainer ) {
			this.currentContainer.addRule.apply(this.currentContainer, args);
			return false;
		}
		if ( rule instanceof RuleContainer ) {
			this.containerStack.push(rule);
			this.currentContainer = this.containerStack[this.containerStack.length-1];
		}
		return rule;
	},
	endContainer: function endContainer() {
		
		// if the currentContainer is a RuleContainer and it has a currentContainer end that container
		if ( this.currentContainer instanceof RuleContainer && this.currentContainer.currentContainer instanceof RuleContainer ) {
			this.currentContainer.endContainer();
			return;
		}
		
		this.containerStack.pop();
		if ( this.containerStack.length > 0 ) {
			this.currentContainer = this.containerStack[this.containerStack.length-1];
		} else {
			this.currentContainer = null;
		}
	}
});

/**
 * Define a form to attach validation rules to
 * 
 * @param string form_id		ID of the form
 */
var Form = new Activa.Class({
	Extends: RuleContainer,
	id: '',
	rules: [],
	message: '',
	invalid: '',
	init: function initForm(form_id) {
		this.id = form_id;
		var form = did(this.id);
		if ( !form ) {
			throw new ReferenceError('form_id "' + this.id + '" is not a valid id of a form.');
		}
	},
	addRule: function addRule(rule) {
		rule = this.root.apply(this, arguments);
		if ( !rule ) {
			return;
		}
		rule.setForm(this);
		this.rules.push(rule);
	},
	validate: function validateForm(){
		var rule;
		for ( var i = 0, z = this.rules.length; i < z; i++ ) {
			rule = this.rules[i];
			if ( !rule.validate() ) {
				this.message = rule.message;
				this.invalid = rule.field;
				return false;
			}
		}
		return true;
	}
});

/**
 * Define basic validation rule.
 * 
 * @param string field		ID of field
 * @param string message	Error message to display when validation fails
 * @param string match		Match value (available for rules to use to match a value)
 * @param bool required		Whether the rule is required for the form to successfully validate (true, false(default))
 */
var Rule = new Activa.Class({
	form: null,
	field: '',
	field_id: '',
	message: '',
	match: '',
	required: false,
	init: function initRule(field, message, match, required) {
		var ftmp = null;
		if ( typeof field == 'object' ) {
			if ( field.id ) {
				ftmp = did(field.id);
				this.field_id = field.id;
			} else if ( field.name ) {
				ftmp = dbn(field.name);
				ftmp = ftmp.length ? ftmp[0] : null;
				this.field_id = field.name;
			}
		} else {
			ftmp = did(field);
			this.field_id = field;
		}
		this.field = ftmp;	
		this.message = message || '';
		this.match = match || '';
		this.required = !!(required);
	},
	setForm: function setForm(form) {
		if ( !(form instanceof Form) ) {
			throw new TypeError('form must be a valid Form object');
		}
		this.form = form;
	},
	validate: function validateRule() {
		if ( this.field.type != 'select-multiple' ) {
			if ( !this.required ) {
				if ( this.field.value.trim() == '' ) {
					return true;
				}
			}
			return false;
		}
	}
});

/**
 * Create a container for conditional rules
 * 
 * @param string field
 */
var Conditional = new Activa.Class({
	Extends: RuleContainer,
	form: '',
	rules: {},
	elses: [],
	message: '',
	condField: '',
	field: '',
	field_id: '',
	init: function initConditional(field) {
		var ftmp = null;
		if ( typeof field == 'object' ) {
			if ( field.id ) {
				ftmp = did(field.id);
				this.field_id = field.id;
			} else if ( field.name ) {
				ftmp = dbn(field.name);
				ftmp = ftmp.length ? ftmp[0] : null;
				this.field_id = field.name;
			}
		} else {
			ftmp = did(field);
			this.field_id = field;
		}
		if ( !ftmp ) {
			throw new ReferenceError('field "' + this.field_id + '" must be a valid element id or name.');
		}
		this.condField = ftmp;
	},
	addRule: function addConditionalRule(matching, rule) {
		var args = Activa.toArray(arguments);
		rule = this.root.apply(null, args);
		if ( !rule ) {
			return;
		}
		if ( this.form ) {
			rule.setForm(this.form);
		}
		if ( matching === null ) {
			this.elses.push(rule);
		} else {
			if ( !this.rules[matching] ) {
				this.rules[matching] = [];
			}
			this.rules[matching].push(rule);
		}
	},
	setForm: function setConditionalForm(form) {
		if ( !(form instanceof Form) ) {
			throw new TypeException('form must be a valid Form object');
		}
		this.form = form;
		for ( var match in this.rules ) {
			this.rules[match].forEach(function(rule) {
				rule.setForm(form);
			});
		}
		this.elses.forEach(function(rule) {
			rule.setForm(form);
		});
	},
	validate: function validateConditional() {
		var validate_else = true;
		for ( var match in this.rules ) {
			var toggled = (this.condField.type == 'checkbox' || this.condField.type == 'radio');
			if ( this.condField && ((toggled && this.condField.checked) || (!toggled && this.condField.value == match)) ) {
				validate_else = false;
				var rule = null;
				for ( var i = 0, z = this.rules[match].length; i < z; i++ ) {
					rule = this.rules[match][i];
					if ( !rule.validate() ) {
						this.message = rule.message;
						this.field = rule.field;
						return false;
					}
				}
			}
		}
		if ( validate_else ) {
			var rule = null;
			for ( var i = 0, z = this.elses.length; i < z; i++ ) {
				rule = this.elses[i];
				if ( !rule.validate() ) {
					this.message = rule.message;
					this.field = rule.field;
					return false;
				}
			}
		}
		return true;
	}
});

/**
 * Create a container for conditional rules
 * 
 * @param Rule field
 */
var ConditionalRule = new Activa.Class({
	Extends: Conditional,
	condRule: null,
	init: function initConditionalRule(rule) {
		this.condRule = rule;
	},
	addRule: function addConditionalRuleRule(rule) {
		var args = Activa.toArray(arguments);
		// If there is more than 1 arg, we want to pass to a child RuleContainer
		if ( args.length > 1 || this.currentContainer instanceof RuleContainer ) {
			this.root.apply(null, args);
		} else {
			this.root("all", rule);
		}
	},
	validate: function validateConditionalRule() {
		if ( this.condRule.validate() ) {
			for ( var i = 0; i < this.rules["all"].length; i++ ) {
				var rule = this.rules["all"][i];
				if ( !rule.validate() ) {
					this.message = rule.message;
					this.field = rule.field;
					return false;
				}
			}
		}
		return true;
	}
});

/**
 *
 * Validation Rules
 * 
 */



var Rule_Alphanumeric = new Activa.Class({
	Extends: Rule,
	validate: function validateAlphanumeric() {
		if ( this.root() ) {
			return true;
		}
		return /^[a-zA-Z0-9]+$/.test(this.field.value);
	}
});

var Rule_Between = new Activa.Class({
	Extends: Rule,
	min: 0,
	max: 0,
	init: function initBetween(field, message, match, required) {
		this.root(field, message, match, required);
		var parts = match.split('-');
		this.min = parseFloat(parts[0] || 0);
		this.max = parseFloat(parts[1] || 0);
	},
	validate: function validateBetween() {
		if ( this.root() ) {
			return true;
		}
		var flen = parseFloat(this.field.value.length);
		return (flen >= this.min && flen <= this.max);
	}
});

var Rule_Captcha = new Activa.Class({
	Extends: Rule,
	validate: function validateCaptcha() {
		if ( this.root() ) {
			return true;
		}
		return this.field.value.trim() != '';
	}
});

var Rule_Checkbox = new Activa.Class({
	Extends: Rule,
	validate: function validateCheckbox() {
		if ( this.root() ) {
			return true;
		}
		return !!(this.field.checked);
	}
});

var Rule_Checkboxes = new Activa.Class({
	Extends: Rule,
	validate: function validateCheckboxes() {
		var boxes = Activa.toArray(dbn(this.field_id+'[]', this.form || null));
		var count = 0;
		boxes.forEach(function isChecked(box) {
			if ( box.checked ) {
				count++;
			}
		});
		return (count == this.match);
	}
});

var Rule_CreditCardNumber = new Activa.Class({
	Extends: Rule,
	validate: function validateCreditCardNumber() {
		if ( this.root()  ) {
			return true;
		}
		var len = (this.field.value.replace(/[^\d]/g, '')).length;
		if ( !(/^[\d -]+$/.test(this.field.value)) && (len < 13 || len > 16) ) {
			return false;
		}
		return true;
	}
});

var Rule_Email = new Activa.Class({
	Extends: Rule,
	regex: null,
	init: function initEmail(field, message, match, required) {
		this.root(field, message, match, required);
		this.regex = new RegExp(match);
	},
	validate: function validateBetween() {
		if ( this.root() ) {
			return true;
		}
		return (this.regex.test(this.field.value));
	}
});

var Rule_Equals_Field = new Activa.Class({
	Extends: Rule,
	validate: function validateEqualsField() {
		if ( this.root() ) {
			return true;
		}
		var field = did(this.match);
		return (field && this.field.value == field.value); 
	}
});

var Rule_Equals = new Activa.Class({
	Extends: Rule,
	validate: function validateEquals() {
		if ( this.root() ) {
			return true;
		}
		return (this.field.value == this.match);
	}
});

var Rule_Greater = new Activa.Class({
	Extends: Rule,
	min: 0,
	init: function initGreater(field, message, match, required) {
		this.root(field, message, match, required);
		this.min = parseFloat(match) || 0;
	},
	validate: function validateGreater() {
		if ( this.root() ) {
			return true;
		}
		var val = parseFloat(this.field.value);
		return (!isNaN(val) && val > this.min);
	}
});

var Rule_Length = new Activa.Class({
	Extends: Rule,
	len: 0,
	init: function initLength(field, message, match, required) {
		this.root(field, message, match, required);
		this.len = parseInt(this.match) || 0;
	},
	validate: function validateLength() {
		if ( this.root() ) {
			return true;
		}
		var len = parseInt(this.field.value.length);
		return (!isNaN(len) && len == this.len);
	}
});

var Rule_Less = new Activa.Class({
	Extends: Rule,
	max: 0,
	init: function initLess(field, message, match, required) {
		this.root(field, message, match, required);
		this.max = parseFloat(match) || 0;
	},
	validate: function validateLess() {
		if ( this.root() ) {
			return true;
		}
		var val = parseFloat(this.field.value);
		return (!isNaN(val) && val < max);
	}
});

var Rule_Longer = new Activa.Class({
	Extends: Rule,
	min: 0,
	init: function initLonger(field, message, match, required) {
		this.root(field, message, match, required);
		this.min = parseInt(match) || 0;
	},
	validate: function validateLonger() {
		if ( this.root() ) {
			return true;
		}
		return (parseInt(this.field.value.length) > this.min);
	}
});

var Rule_Match = new Activa.Class({
	Extends: Rule,
	regex: null,
	init: function initMatch(field, message, match, required) {
		this.root(field, message, match, required);
		this.regex = new RegExp(match);
	},
	validate: function validateMatch() {
		if ( this.root() ) {
			return true;
		}
		return (this.regex.test(this.field.value));
	}
});

var Rule_Notequals = new Activa.Class({
	Extends: Rule,
	validate: function validateNotEquals() {
		if ( this.root() ) {
			return true;
		}
		return (this.field.value != this.match);
	}
});

var Rule_Numeric = new Activa.Class({
	Extends: Rule,
	validate: function validateNumeric() {
		if ( this.root() ) {
			return true;
		}
		return /^[0-9]+$/.test(this.field.value);
	}
});

var Rule_Phone_Int = new Activa.Class({
	Extends: Rule,
	validate: function validatePhoneInt() {
		if ( this.root() ) {
			return true;
		}
		var val = this.field.value = this.field.value.trim().replace(/[\s-\(\)]/g, '');
		return /^\+?[\d+]{10,16}$/.test(val);
	}
});

var Rule_Phone = new Activa.Class({
	Extends: Rule,
	validate: function validatePhone() {
		if ( this.root() ) {
			return true;
		}
		var val = this.field.value = this.field.value.trim().replace(/[\s-\(\)]/g, '');
		return /\d{10}/.test(val);
	}
});

var Rule_RadioList = new Activa.Class({
	Extends: Rule,
	validate: function validateRadioList() {
		var radios = Activa.toArray(dbn(this.field_id));
		var value = false; 
		radios.forEach(function(radio) {
			if ( radio.checked ) {
				if ( this.match && this.match != '' && this.match != '0' ) {
					value = radio.value == this.match;
				} else {
					value = true;
				}
			}
		}.bind(this));
		return value;
	}
});

var Rule_Range = new Activa.Class({
	Extends: Rule_Between,
	validate: function validateBetween() {
		if ( this.root() ) {
			return true;
		}
		var flen = parseFloat(this.field.value);
		return (flen > this.min && flen < this.max);
	}
});

var Rule_Shorter = new Activa.Class({
	Extends: Rule,
	max: 0,
	init: function initShorter(field, message, match, required) {
		this.root(field, message, match, required);
		this.max = parseInt(match) || 0;
	},
	validate: function validateBetween() {
		if ( this.root() ) {
			return true;
		}
		var val = parseInt(this.field.value.length);
		return (!isNaN(val) && val < this.max);
	}
});

var Rule_Text = new Activa.Class({
	Extends: Rule,
	validate: function validateText() {
		if ( this.root() ) {
			return true;
		}
		return !this.field.value.trim() == '';
	}
});


function updateShipping(url) {
	if(did("service")) {
		url += "/" + did("service").value;
	}
	if(did("carrier")) {
		url += "/" + did("carrier").value;
	}
	new Activa.rpc(url, { onComplete: updateCheckoutModifiers });
}

function doUpdateShipping(obj) {	
	if(obj.shipping_method == 'order') {
		did("shipping_total").innerHTML = "$" + obj.shipping;
		did("grand_total").innerHTML = "$" + obj.grand_total;
		Activa.DOM.showID("grand_total_row");
	}
}

function updateCheckoutModifiers(obj) {

	var before_shipping = false;
	
	var tbody = did("checkout_table").tBodies[0];
	
	Activa.DOM.hideID("shipping_row");
	
	var sub_total_row_found = false;
	for(var i=0; i < tbody.childNodes.length; i++) {
		if(tbody.childNodes[i].nodeName == 'TR') {
			if(tbody.childNodes[i].id == 'grand_total_row') {
				break;
			} else if(tbody.childNodes[i].id == 'sub_total_row') {
				sub_total_row_found = true;
			} else if(sub_total_row_found) {
				if(tbody.childNodes[i].id != 'shipping_row') {
					tbody.childNodes[i].parentNode.removeChild(tbody.childNodes[i]);
					i--;
				}
			}		
		}	
	}
	
	var insert_before_target = did("shipping_row");
	
	for(var i=0; i < obj.modifiers.length; i++) {
		
		if(obj.modifiers[i].service) {
		
			if(did("carrier")) {
			
				while(did("carrier").options.length > 0) {
					did("carrier").remove(0);
				}
				
				for(var k=0; k < obj.modifiers[i].carriers.length; k++) {
					var optn = document.createElement("option");
					optn.text = obj.modifiers[i].carriers[k].text;
					optn.value = obj.modifiers[i].carriers[k].value;
					did("carrier").options.add(optn);
				}
				
				did("carrier").value = obj.modifiers[i].carrier;
			}
			
			if(did("service")) {
			
				while(did("service").options.length > 0) {
					did("service").remove(0);
				}
				
				for(var k=0; k < obj.modifiers[i].services.length; k++) {
					var optn = document.createElement("option");
					optn.text = obj.modifiers[i].services[k].text;
					optn.value = obj.modifiers[i].services[k].value;
					did("service").options.add(optn);
				}
				
				did("service").value = obj.modifiers[i].service;
			}
			
			did("shipping_total").innerHTML = obj.modifiers[i].amount;
		
			insert_before_target = did("grand_total_row");	
		} else {
			var tr = document.createElement('tr');
			tr.className = 'discount';
			
			var td = document.createElement('td');
			td.className = 'title';
			td.colSpan = '2';
			td.appendChild(document.createTextNode(obj.modifiers[i].name));
			tr.appendChild(td);
			
			var td = document.createElement('td');
			td.className = 'price ' + (obj.modifiers[i].positive ? 'green' : 'red');
			td.appendChild(document.createTextNode(obj.modifiers[i].amount));
			tr.appendChild(td);
			
			tbody.insertBefore(tr, insert_before_target);
		}
	}
	
	showID("shipping_row");
	
	if(obj.promo_error) {
		did('promo_code_error').innerHTML = obj.promo_error;
		Activa.DOM.showID('promo_code_error');
	} else {
		did('promo_code_error').innerHTML = 'fds';
		Activa.DOM.hideID('promo_code_error');
	}
	
	did("grand_total").innerHTML = "$" + obj.grand_total;
}

/**
 * rgbToHex - Converts an rgb color value into a hexidecimal value (without the leading '#')
 * @param	integer		r		Value of the color red in range 0-255;
 * @param	integer		g		Value of the color green in range 0-255;
 * @param	integer		b		Value of the color blue in range 0-255;
 * @return	string				Hexidecimal representation of the the color
 */
function rgbToHex(r,g,b){
	var rgb = Activa.toArray(arguments,0,3), hex = '0123456789ABCDEF';
	for(var i=0;i < 3;i++){
		rgb[i] = isNaN(parseInt(rgb[i],10)) ? 0 : parseInt(rgb[i],10);
		rgb[i] = Math.round(Math.max(Math.min(0,rgb[i]),255));
		rgb[i] = hex.charAt( (rgb[i]-rgb[i]%16)/16 ) + hex.charAt( rgb[i]%16 );
	}
	return rgb.join('');
}

/**
 * getStyle - Retrieve the value of the current style property
 * @param	mixed		obj		Element id or object
 * @param	string		prop	Name of style property to retrieve
 * @return	mixed				Value of the style property. *Note: Unit values will be returned as integers. (ie. 'px' will be stripped off)*
 */
function getStyle(obj,prop){
	obj = (typeof obj == 'object') ? obj : did(obj);
	if(!obj || !prop){ return null; }
	var regRGB = /rgb\((\d+),\s(\d+),\s(\d+)\)/i, color;
	var compVal = obj.currentStyle ? obj.currentStyle[prop] : window.getComputedStyle(obj,null).getPropertyValue(prop);
	if((color = regRGB.exec(compVal)) != null){ color.shift(); compVal = '#'+rgbToHex.apply(null,color); }
	return isNaN(parseFloat(compVal)) ? compVal : parseFloat(compVal);
}

/**
 * getStyles - Retrieve the current value of several styles
 * @param	mixed	obj		Element id or object
 * @param	string	prop	Name of style property to retrieve (pass another parameter for each style property)
 * @return	object			Object whose keys are the style property and values are the style property's value
 */
function getStyles(obj, prop) {
	var args = Activa.toArray(arguments,1);
	var styles = {};
	args.forEach(function(arg){
		this[arg] = getStyle(obj,arg);
	},styles);
	return styles;
}

/**
 * createElement - Shortcut for document.createElement
 * @param	tag		Tag of the element to create
 * @return			Element or null if no tag specified
 */
function createElement(tag) {
	if (!tag) { return null; }
	return document.createElement(tag);
}
/**
 * createTextNode - Shortcut for document.createTextNode
 * @param	text	Text to place inside the new node
 * @return			New text node
 */
function createTextNode(text) {
	return document.createTextNode(String(text));
}

var forms = new Array();

function minmax(object) {
	if ( object.parentNode.offsetHeight != 55 ) {
		//new Activa_FX('RollUp', object.parentNode, 250);
		//new Activa_FX('RollDown', object.parentNode, 250);
		
		if ( !object.id ) {
			object.id = forms.length;
			forms.push( object.parentNode.offsetHeight - 20 ); // -20 to make up for the forms padding
		}
		object.innerHTML = 'Expand';
		object.className = 'minmax on';
		object.parentNode.className = 'form_pod collapsed';
		object.parentNode.style.height = 35 + 'px';
	} else {
		object.innerHTML = 'Collapse';
		object.className = 'minmax';
		object.parentNode.className = 'form_pod';
		object.parentNode.style.height = forms[object.id] + 'px';
	}
}

function highlight(e) {
	var o = (document.all) ? e.srcElement : e.target;
	
	if ( o.nodeName != 'TH' && o.nodeName != 'TD' && o.nodeName != 'INPUT' && o.nodeName != 'B' && o.nodeName != 'LABEL' && o.nodeName != 'A' ) {
		console.log(o.nodeName);
		return;
	}
	
	var cellIndex;
	if ( o.nodeName == 'A' ) {
		cellIndex = o.parentNode.parentNode.cellIndex + 1;
	} else if ( o.nodeName == 'INPUT' || o.nodeName == 'B' || o.nodeName == 'LABEL' ) {
		cellIndex = o.parentNode.cellIndex + 1;
	} else {
		cellIndex = o.cellIndex + 1;
	}
	
	if ( did('col_'+cellIndex) ) {
		did('col_'+cellIndex).className = (e.type == 'mouseover') ? 'col_item_on' : '';
	}

	var rowIndex;
	if ( o.nodeName == 'A' ) {
		rowIndex = o.parentNode.parentNode.parentNode.rowIndex - 1;
	} else if ( o.nodeName == 'INPUT' || o.nodeName == 'B' || o.nodeName == 'LABEL' ) {
		rowIndex = o.parentNode.parentNode.rowIndex - 1;
	} else {
		rowIndex = o.parentNode.rowIndex - 1;
	}

	if ( did('row_'+rowIndex) ) {
		did('row_'+rowIndex).className = (e.type == 'mouseover') ? 'row_item on' : 'row_item';
	}

	if ( did('cell_'+cellIndex+'_'+rowIndex) ) {
		did('cell_'+cellIndex+'_'+rowIndex).className = (e.type == 'mouseover') ? 'center selected' : 'center';
	}
}

/**
 * Create a container to handle validation for dynamic fields
 * 
 * Example:
 * 	20 instances of a field named 'payee[]' and the 5th input is empty, then validation fails on the rule
 * 	for the payee field and the message will have the placeholder substituted with the number 5 
 * 
 * @param string placeholder	String to act as a placeholder in the message that will be replaced with
 * 								the index number of the field that fails.
 */
var Dynamic = new Activa.Class({
	Extends: RuleContainer,
	form: '',
	rules: [],
	placeholder: '|#|',
	message: '',
	field: '',
	field_id: '',
	init: function initDynamic(placeholder) {
		this.placeholder = placeholder !== undefined ? placeholder : this.placeholder;
	},
	addRule: function addDynamicRule(rule) {
		if ( this.form ) {
			rule.setForm(this.form);
		}
		this.rules.push(rule);
	},
	setForm: function setDynamicForm(frm) {
		if ( !(frm instanceof Form) ) {
			throw new TypeException('frm must be a valid Form object');
		}
		this.form = frm;
		this.rules.forEach(function(rule){
			rule.setForm(frm);
		});
	},
	validate: function validateDynamic() {
		var cols = [], rule = null, max = 0;
		for ( var i=0, z=this.rules.length; i < z; i++ ) {
			rule = this.rules[i];
			cols[rule.field_id] = dbn(rule.field_id+'[]');
			max = cols[rule.field_id].length > max ? cols[rule.field_id].length : max;
		}
		for ( var x=0; x < max; x++ ) {
			for ( var i=0, z=this.rules.length; i < z; i++ ) {
				rule = this.rules[i];
				if ( cols[rule.field_id].length > x ) {
					rule.field = cols[rule.field_id][x];
					if ( !rule.validate() ) {
						this.message = rule.message.replace(this.placeholder, x+1);
						this.field = rule.field;
						this.field_id = rule.field_id;
						return false;
					}
				}
			}
		}
		return true;
	}
});


var Rule_Float = new Activa.Class({
	Extends: Rule,
	regex: null,
	init: function(field, message, match, required, allowNeg){
		this.root(field, message, match, required);
		this.regex = new RegExp('^'+(allowNeg ? '-?':'')+'[0-9]+(\.|\.[0-9]+)?$');
	},
	validate: function validateFloat(){
		if ( this.root() ) {
			return true;
		}
		if ( !this.regex.test(this.field.value) ) {
			return false;
		}
		return true;
	}
});


var Rule_Date = new Activa.Class({
	Extends: Rule,
	defaultDelims: '-/',
	init: function initDate(field, message, match, required, delims) {
		this.root(field, message, match, required);
		delims = typeof delims == 'string' && delims ? delims : this.defaultDelims;
		this.regUS = new RegExp('^([1-9]|0[1-9]|1[0-2])['+delims+']([1-9]|0[1-9]|[1-2][0-9]|3[0-1])['+delims+'](\\d{4})$');
		this.regEU = new RegExp('^(\\d{4})['+delims+']([1-9]|0[1-9]|1[0-2])['+delims+']([1-9]|0[1-9]|[1-2][0-9]|3[0-1])$');
	},
	validate: function validateDate() {
		if ( this.root() ) {
			return true;
		}
		if ( !this.regUS.test(this.field.value) ) {
			if ( !this.regEU.test(this.field.value) ) {
				return false;
			}
		}
		return true;
	}
});

/* Rich Text Editor */
/* Copyright 2007 Active Media Architects   http://www.activema.com */

function wysiWindow(wysi_id){
	
	/* Dynamic customization here. this stuff can't be set in css */
	
	this.fontFamily = 'Arial, Helvetica, sans-serif';
	this.fontColor = '#000000';
	this.backgroundColor = '#FFFFFF';
	this.fontSize = 13;
	this.saveMethod = 'BBCODE'; // set to HTML or BBCODE
	this.maxLog = 100;
	
	/* End of configuration */
	
	this.rootid = '';
	this.WysiButtonState = [0,0,0];
	this.cmp_frame;
	this.mouseCheck;
	this.outputMethod = 1;
	this.fixNode;
	this.browser;
	this.initialized = false;
	this.debug = false;
	
	this.wysi_id = ('undefined' == typeof(wysi_id)) ? '' : wysi_id;
	this.evalthis = function(command) {
		eval(command);
	}
	
	this.spellCheck;
	
	this.message = function(string) {
		did('wysiMessage'+this.wysi_id).innerHTML = string;
	}
	
	this.wysInit = function() {
		if ( this.initialized ) { return; }
		if ( window.ActiveXObject ) {
			this.browser = 'IE';
			this.cmp_frame = window.parent.document.frames['wysiRichText'+this.wysi_id].document;
		} else if ( did('wysiRichText'+this.wysi_id).contentDocument ) {
			this.browser = 'Mozilla';
			this.cmp_frame = did('wysiRichText'+this.wysi_id).contentDocument;
		}
		
		var body = this.cmp_frame.getElementsByTagName('body')[0];
		body.style.fontFamily = this.fontFamily;
		body.style.backgroundColor = this.backgroundColor;
		body.style.fontSize = this.fontSize;
		body.style.color = this.fontColor;
		var inputString = did('wysiPlainTextarea'+this.wysi_id).value;
		// rewrite relative links with url to be stripped upon submission (see also - line 367: this.convertToHTML(); line 834: this.WysiSubmitLink())
		inputString = inputString.replace(/href="(?!https?:\/\/)([^"]*)"/g, 'href="http://this.is.a.relative.url$1"');
		inputString = inputString.replace(/onclick=\"NewWindow(?:2)?\(this.href, '_blank', 800, 600\); return false;\" href=\"/g, 'href="popup:');
		inputString = inputString.replace(/target=\"_blank\" href=\"/g, 'href="popup:');
		if ( this.browser == 'IE' ) {
			if ( inputString != '' ) { body.innerHTML = this.bbCode_to_IE(inputString); }
			body.contentEditable = true;
			this.cmp_frame.execCommand('LiveResize', false, true);
		} else if ( this.browser == 'Mozilla' ) {
			if ( inputString != '' ) { body.innerHTML = this.bbCode_to_Moz(inputString); }
			this.cmp_frame.designMode = "On"; 
		}
		this.importYouTube(body);
		this.spellCheck = new ActivaSpell('wysiRichText'+this.wysi_id, this.wysi_id);
		this.mouseCheck = window.setTimeout('wysiText'+this.wysi_id+'.mouseChecker()', 250);
		
		this.debug = did('wysiDebug'+this.wysi_id);
		
		this.initialized = true;
	}

	this.mouseChecker = function() {
		if ( !this.disableChecker ) {
			var editFrame = did('wysiRichText'+this.wysi_id);
			if ( !editFrame ) { return; }
			if ( this.browser=='Mozilla' ) {
				var editWin = editFrame.contentWindow;
				if ( editWin.getSelection ) {
					var selection = editWin.getSelection();
					var mousePos = selection.rangeCount > 0 ? selection.getRangeAt(0) : null;
					if ( mousePos != this.lastMousePos && selection.isCollapsed ) {
						this.getPosition();
						this.lastMousePos = mousePos;
					}
				} 
			} else {
				var selection = editFrame.contentWindow.document.selection;
				var mousePos = selection.createRange();
				if ( mousePos != this.lastMousePos && selection.type=='None' ) {
					this.getPosition();
					this.lastMousePos = mousePos;
				}
			}
		}
		this.mouseCheck = window.setTimeout('wysiText'+this.wysi_id+'.mouseChecker()', 250);
	}
	this.lastMousePos;
	this.disableChecker = 0;
	
	this.backLog = [];
	this.startlogging = false;
	this.logcount = 0;
	this.LOG = function(txt) {
		if ( !this.debug || !this.startlogging ) {
			this.backLog.push(txt);
			return;
		}
		while ( this.backLog.length > 0 ) {
			this.LOG(this.backLog.pop());
		}
		var self = this;
		function sendLogLine(innertxt) {
			var line = document.createElement('span');
			line.style.clear = 'left';
			line.style.borderBottom = '1px solid black';
			line.style.paddingTop = '2px';
			line.style.paddingBottom = '2px';
			line.style.width = '100%';
			line.style.display = 'block';
			line.innerHTML = innertxt;
			self.debug.appendChild(line);
			self.logcount++;
		}
		
		if ( this.logcount >= this.maxLog ) {
			var ln = this.first(this.debug);
			if ( ln ) {
				this.debug.removeChild(ln);
			}
		}
		var constructors = [String, Number, Boolean];
		
		if ( !txt ) {
			var str='error';
			if ( txt === null ) {
				str = 'null';
			} else if ( txt === undefined ) {
				str = 'undefined';
			}
			sendLogLine(str);
			return;
		}
		
		if ( typeof txt != 'object' ||  (typeof txt == 'object' && typeof txt.constructor == 'function' && 
			constructors.indexOf(txt.constructor) != -1) ) {
				sendLogLine(txt);
		} else if ( txt instanceof Array ) {
			sendLogLine('[');
			for ( var i = 0;i < txt.length;i++ ) {
				this.LOG(txt[i]);
			}
			sendLogLine(']');
		} else {
			sendLogLine('-----dumping object-----');
			for ( var k in txt ) {
				sendLogLine('&nbsp;&nbsp;<strong>'+k+'</strong>: '+txt[k]);
			}
		}
		
		var lastline = last(this.debug);
		lastline.scrollIntoView();
	}
	
	this.clearLog = function() {
		this.debug.innerHTML = '';
	}
	
	this.toggleLogging = function() {
		var btn = did('wysiDebugButton'+this.wysi_id);
		var enable = !!(btn.value == 'Start');
		btn.value = enable ? 'Stop' : 'Start';
		this.startlogging = enable;
	}
	
	this.sendExec = function(cmd, value) {
		if ( typeof(value) == 'undefined' ) { value = null; }
		this.cmp_frame.execCommand(cmd, false, value); return true;
	}

	this.selectionInFrame = function() {
		var node = document.frames['wysiRichText'+this.wysi_id].document.selection;
		var nodei = ( node.type == "Control" ) ? node.createRange().item(0) : node.createRange().parentElement();
		return (nodei.ownerDocument.location == 'about:blank') ? true : false;
	}
	
	this.WysiClickButton = function(type) {
		var types = [0, 'bold', 'italic', 'underline'];
		this.sendExec(types[type]);
		did('wysiRichText'+this.wysi_id).contentWindow.focus();
		if ( this.WysiButtonState[type] ) {
			this.setWysiButton(type,false);
			this.WysiButtonState[type] = 0;
		} else {
			this.setWysiButton(type,true);
			this.WysiButtonState[type] = 1;
		}
	}

	this.fontsize = function() {
		var selector = did('wysiFontsize'+this.wysi_id);
		var i = selector.selectedIndex;
		var value = selector[i].value;
		this.sendExec('fontsize', value);
		did('wysiRichText'+this.wysi_id).contentWindow.focus();
	}

	this.setWysiFontsize = function(size) {
		did('wysiFontsize'+this.wysi_id).value = size;
	}

	this.fontcolor = function() {
		if ( did('wysColorSelect'+this.wysi_id).style.display == 'block' ) {
			did('wysColorSelect'+this.wysi_id).style.display = 'none';
			did('wysiRichText'+this.wysi_id).contentWindow.focus();
		} else {
			this.closePanels();
			this.message('');
			did('wysColorSelect'+this.wysi_id).style.display = 'block';
		}
	}

	this.setWysiButton = function(id, state) {
		var btn = did('wysibutton'+id+this.wysi_id);
		if ( !btn ) {
			return;
		}
		var newcls = state ? 'On' : 'Off';
		var oldcls = state ? 'Off' : 'On';
		Activa.DOM.removeClass(btn, 'wysiButton'+oldcls);
		Activa.DOM.addClass(btn, 'wysiButton'+newcls);
	}

	this.selectColor = function(hex) {
		did('wysColorSelect'+this.wysi_id).style.display = 'none';
		did('wysFontColor'+this.wysi_id).style.backgroundColor = '#' + hex; 
		if ( did('wysiRichText'+this.wysi_id).contentDocument ) {
			this.sendExec( 'forecolor', '#' + hex); 
		} else {
			var tr = document.frames['wysiRichText'+this.wysi_id].document.selection.createRange();
			this.sendExec( 'forecolor', '#' + hex); 
		}
		did('wysiRichText'+this.wysi_id).contentWindow.focus();
	}
	
	this.getPosition = function() {
		this.closePanels();
		this.wysStyle.reset();
		var editFrame = did('wysiRichText'+this.wysi_id);
		var editWin = editFrame.contentWindow;
		var selection = editWin.getSelection ? editWin.getSelection() : editWin.document.selection;
		if ( editFrame.contentDocument ) {
			var range = selection.getRangeAt(0);
			var node = range.startContainer;
			this.walkStyle(node);
		} else {
			// ie
			var nodei = (selection.type == "Control") ? selection.createRange().item(0) : selection.createRange().parentElement();
			if ( nodei.ownerDocument != this.cmp_frame ) { return; }
			this.walkStyleIE(nodei);
		}
		
		var btns = {bold:1, italic:2, underline:3, list:4, ulist:5};
		for ( var k in btns ) {
			this.setWysiButton(btns[k], !!(this.wysStyle[k]));
		}
		
		var fontColor = did('wysFontColor'+this.wysi_id);
		if ( fontColor ) {
			fontColor.style.background = this.wysStyle.color ? this.wysStyle.color : '#000';
		}
		this.wysStyle.fontsize = this.wysStyle.fontsize || 2;
		var fontSize = did('wysiFontsize');
		if ( fontSize ) {
			this.setWysiFontsize(this.wysStyle.fontsize);
		}
	}
	
	this.wysStyle = {};
	this.wysStyle.link = '';
	this.wysStyle.reset = function() {
		this.underline = 0;
		this.bold = 0;
		this.italic = 0;
		this.fontsize = 0;
		this.color = 0;
		this.link = '';
		this.list = 0;
		this.ulist = 0;
	}
	
	this.walkStyle = function(n) { 
		// walk up looking for style changes
		var s = n.style;
		if ( s ) {
			if ( s.textDecoration == 'underline' ) { this.wysStyle.underline = 1; }
			if ( s.fontWeight == 'bold' ) { this.wysStyle.bold = 1;}
			if ( s.fontStyle == 'italic' ) { this.wysStyle.italic = 1;}
			if ( s.color && this.wysStyle.color == 0 ) { this.wysStyle.color = s.color; }
			if ( n.size && this.wysStyle.fontsize == 0 ) { this.wysStyle.fontsize = n.size; }
		}
		
		if ( n.tagName == 'A' ) { this.wysStyle.link = n.href; }
		if ( n.tagName == 'UL' ) { this.wysStyle.ulist = 1; }
		if ( n.tagName == 'OL' ) { this.wysStyle.list = 1; }
		if ( n.tagName != 'BODY' && n.tagName != 'HTML' ) {
			var parent = n.parentNode; 
			if ( parent.tagName != 'BODY' ) { this.walkStyle(parent); }
		}
	}

	this.walkStyleIE = function(n) {
		switch(n.tagName){
			case 'STRONG':
				this.wysStyle.bold = 1; break;
			case 'EM':
			case 'I':
				this.wysStyle.italic = 1; break;
			case 'U':
				this.wysStyle.underline = 1; break;
			case 'FONT':
				if ( n.size && this.wysStyle.fontsize == 0 ) { this.wysStyle.fontsize = n.size; }
				if ( n.color && this.wysStyle.color == 0 ) { this.wysStyle.color = n.color; }
				break;	
			case 'A':
				this.wysStyle.link = n.href;
				break;
			case 'OL':
				this.wysStyle.list = 1; break;
			case 'UL':
				this.wysStyle.ulist = 1; break;
		}
		if ( n.tagName != 'BODY' && n.tagName != 'HTML' ) {
			var parent = n.parentNode; 
			if ( parent.tagName != 'BODY' ) {this.walkStyleIE(parent);}
		}
	}

	this.colorPick = function(e, b) {
		if ( b == 1 ) {
			var cX = (e.clientX) ? e.clientX : window.event.clientX;
			var picker = did('picker'+this.wysi_id).offsetLeft;
			var colorSelect = did('wysColorSelect'+this.wysi_id);
			var h = cX - picker.offsetLeft  - colorSelect.offsetLeft - 4;
			if ( this.browser == 'Mozilla' ) {h-=180;}
			h = 1.0 * h / 100;
			var rgb = this.convertHSVToRGB(h, 1.0, 0.8);
			var hex = this.convertRGBToHex(rgb);
			this.selectColor(hex);
		}
	}

	this.convertRGBToHex = function(rgb) {
		var ret = ''
		for ( var i = 0; i < 3; i++ ) {
			var c = rgb[i];
			if ( c == 0 ) { 
				ret += '00';
			} else {
				ret += "0123456789ABCDEF".charAt((c-c%16)/16) + "0123456789ABCDEF".charAt(c%16);
			}
		}
		return ret;
	}

	this.convertHSVToRGB = function($H, $S, $V) {
		$Hi = Math.floor($H*6);
		$f = ($H*6) - $Hi;
		$p = $V * (1 - $S);
		$q = $V * (1 - $f*$S);
		$t = $V * (1 - (1-$f)*$S);
		var $R = 0; var $G = 0; var $B = 0;
		switch($Hi){
			case 0:
				$R = $V*255; $G = $t*255; $B = $p*255;
				break;
			case 1:
				$R = $q*255; $G = $V*255; $B = $p*255;
				break;
			case 2:
				$R = $p*255; $G = $V*255; $B = $t*255;
				break;
			case 3:
				$R = $p*255; $G = $q*255; $B = $V*255;
				break;
			case 4:
				$R = $t*255; $G = $p*255; $B = $V*255;
				break;
			case 5:
				$R = $V*255; $G = $p*255; $B = $q*255;
				break;
		}
		var ret = [Math.round($R), Math.round($G), Math.round($B)];
		return ret;
	}

	this.convertToBBCode = function() {
		if ( this.outputMethod == 0 ) {return true;} // no parsing required
		if ( !this.cmp_frame ) {
			return;
		}
		var body = this.cmp_frame.getElementsByTagName('body')[0];
		this.removeEmptyNodes(body);
		
		var output = this.browser == 'Mozilla' ? this.bbCodeWalk(body) : this.bbCodeWalkIE(body);
		output = this.cleanBBCode(output);
		output = output.replace(/\u201c|\u201d/g, '"').replace(/\u2018|\u2019/g, "'");
		
		var plain = did('wysiPlainTextarea'+this.wysi_id);
		plain.value = output;
		plain.style.display='block';
	}
	
	this.convertToHTML = function() {
		this.convertToWysiView();
		
		if ( this.outputMethod == 0 ) { return true; } // no parsing required
		if ( this.cmp_frame ) {
			this.removeEmptyNodes(this.cmp_frame.getElementsByTagName('body')[0]);
			var output = this.cmp_frame.getElementsByTagName('body')[0].innerHTML;
		} else {
			var output = this.did('wysiPlainTextarea'+this.wysi_id).value;
			output = this.bbCode_to_Moz(output);
		}
		// handle popup links
		output = output.replace(/href=\"popup:/g, 'target="_blank" href="');
		// strip out special addition to relative urls (see also - this.wysInit() and this.WysiSubmitLink())
		output = output.replace(/href="http:\/\/this\.is\.a\.relative\.url/g, 'href="');
		output = output.replace('#STARTparam', '<param');
		output = output.replace('#ENDparam', '>');
		//replace curly double/single quotes with regular straight quotes, replace &nbsp; with a space
		output = output.replace(/\u201c|\u201d/g, '"').replace(/\u2018|\u2019/g, "'").replace(/\ua0/g,' ').replace(/&nbsp;?/g, ' ');
		//strip out anything we missed
		output = output.replace(/[^\w\d\s!#\$%^&\*\(\)\+,-\.\/:\^;<\=>\?@\]\[\\'\{\}\|~\u2122\u00a9\u00ae]/g,'');
		//clean up any extra newlines
		output = output.replace(/\r/g, '').replace(/\n/g,'');
		
		var plain = did('wysiPlainTextarea'+this.wysi_id);
		plain.value = output;
		plain.style.display='block';
	}
	
	// convert graphic 
	this.convertYouTube = function(node) {
		var src_string = did('base')[0].href+'images/yt.gif?'
		var src_filter = new RegExp(src_string);
		for ( var c=0; c < node.childNodes.length; c++ ) {
			var n = node.childNodes[c];
			if ( n.nodeName == 'IMG' || n.nodeName == 'img' ) {
				if ( src_filter.test(n.src) ) {
					var width = n.width;
					var height = n.height;
					var url = n.src.substr(src_string.length);
					var string = '<object type="application/x-shockwave-flash" data="http://www.youtube.com/v/'+url+'" height="'+height+'" width="'+width+'">';
					if ( this.saveMethod == 'HTML' ) {string += '#STARTparam value="http://www.youtube.com/v/'+url+'" name="movie"#ENDparam';}
					string += '</object>';
					if ( node.childNodes.length == 1 ) {
						// attach to parent
						node.innerHTML = string;
					} else {
						// create a holder
						var attachholder = this.cmp_frame.createElement('span');
						node.replaceChild(attachholder, n);
						n = node.childNodes[c];
						n.innerHTML = string;
					}
				}
			}
			if ( n.nodeType == 1 ) {
				this.convertYouTube(n);
			}
		}
	}
	
	this.importYouTube = function(node) {
		for ( var c = 0; c < node.childNodes.length; c++ ) {
			var n = node.childNodes[c];
			if ( n.nodeName == 'object' || n.nodeName == 'OBJECT' ) {
				if ( n.type == 'application/x-shockwave-flash' ) {
					var newnode = this.cmp_frame.createElement('img');
					var w = newnode.width = n.width;
					var h = newnode.height = n.height;
					var url = n.data.substr(25);
					newnode.src = this.absLink('images/yt.gif?'+url);
					node.replaceChild(newnode, n);
					if ( this.browser == 'IE' ) { 
						this.imageResize(this.cmp_frame.getElementsByTagName('body')[0], newnode.src, w, h);
					}
				}
			} else if ( n.nodeType == 1 ) {
				this.importYouTube(n);
			}
		}
	}
	
	this.cleanBBCode = function(s) {
		s = s.replace(/\[(b|i|u|list|ulist|li|\*)\]\[\/(b|i|u|list|ulist|li|\*)\]/g, '');
		s = s.replace(/\[size=\w{1,2}\]\[\/size\]/g, '');
		s = s.replace(/\[color=#\w{6}\]\[\/color\]/g, '');
		s = s.replace(/\[quote(?:[^\]]*)\]\[\/quote\]/g, '');
		return s;
	}
	
	this.relLink = function(string) {
		// absolute path link->relative path link
		var base = document.getElementsByTagName('base')[0].href;
		return string.replace(base, '');
	}
	
	this.absLink = function(string) {
		var base = document.getElementsByTagName('base')[0].href;
		if ( string.substr(0,5)!='http:' && string.substr(0,5)!='HTTP:' ) {
			return base+string;
		}
		return string;
	}
	
	this.bbCodeWalk = function(node) { 
		var string = '';
		if ( node.hasChildNodes ) {
			var fchild = Activa.DOM.first(node);
			if ( fchild && fchild.tagName == 'BLOCKQUOTE' ) {
				var div = Activa.DOM.first(fchild);
				var parts = /<cite>([^<]*)<\/cite>(.*)/i.exec(div.innerHTML);
				if ( parts != null ) {
					var name = parts[1] || '';
					var tmp = document.createElement('div');
					tmp.innerHTML = (parts[2] || '');
					var data = this.bbCodeWalk(tmp);
					string += '[quote='+name+']'+data+'[/quote]';
				}
			}
			for ( var c=0; c < node.childNodes.length; c++ ) {
				var cn = node.childNodes[c];
				if ( cn.tagName == 'BLOCKQUOTE' ) {
					continue;
				}
				if ( cn.nodeType == 3 ) {
					string += cn.nodeValue;
				}
				if ( cn.nodeType == 1 ) {
					if ( cn.tagName == 'IMG' ) {
						if ( cn.width && cn.height ) {
							string += '[img='+cn.width+'x'+cn.height+']'+this.relLink(cn.src)+'[/img]';
						} else {
							string += '[img]'+this.relLink(cn.src)+'[/img]';
						}
					}
					if ( cn.tagName == 'OBJECT' && cn.type == "application/x-shockwave-flash" ) {string += '[youtube='+cn.width+'x'+cn.height+']'+cn.data+'[/youtube]';}
					if ( cn.tagName == 'BR' || cn.tagName == 'P' ) { string += "\n"; }
					if ( cn.tagName == 'OL' ) { string += '[list]'; }
					if ( cn.tagName == 'UL' ) { string += '[ulist]'; }
					if ( cn.tagName == 'LI' ) { string += '[li]'; }
					if ( cn.size ) { string += '[size='+(cn.size*4)+']'; }
					if ( cn.style.color ) { string += '[color='+this.getStyleColor(cn.style.color)+']'; }
					if ( cn.style.fontWeight == 'bold' ) { string += '[b]'; }
					if ( cn.style.fontStyle == 'italic' ) { string += '[i]'; }
					if ( cn.style.textDecoration == 'underline' ) {string += '[u]'; }
					if ( cn.href ) { string += '[url='+cn.href+']'; }
					string += this.bbCodeWalk(cn);
					if ( cn.href ) { string += '[/url]'; }
					if ( cn.style.textDecoration == 'underline' ) { string += '[/u]'; }
					if ( cn.style.fontStyle == 'italic' ) { string += '[/i]'; }
					if ( cn.style.fontWeight == 'bold' ) { string += '[/b]'; }
					if ( cn.style.color ) { string += '[/color]'; }
					if ( cn.size ) { string += '[/size]'; }
					if ( cn.tagName == 'LI' ) { string += '[/li]'; }
					if ( cn.tagName == 'UL' ) { string += '[/ulist]'; }
					if ( cn.tagName == 'OL' ) { string += '[/list]'; }
				}
			}
		}
		return string;
	}
	
	this.bbCodeWalkIE = function(node) { 
		var string = '';
		if ( node.hasChildNodes ) {
			var fchild = Activa.DOM.first(node);
			if ( fchild && fchild.tagName == 'BLOCKQUOTE' ) {
				var div = Activa.DOM.first(fchild);
				var parts = /<cite>([^<]*)<\/cite>(.*)/i.exec(div.innerHTML);
				if ( parts != null ) {
					var name = parts[1] || '';
					var tmp = document.createElement('div');
					tmp.innerHTML = (parts[2] || '');
					var data = this.bbCodeWalkIE(tmp);
					string += '[quote='+name+']'+data+'[/quote]';
				}
			}
			var children = node.childNodes;
			for ( c in children ) {
				var cn = children[c];
				if ( cn.tagName == 'BLOCKQUOTE' ) {
					continue;
				}
				if ( cn.nodeType == 3 ) {
					string += cn.nodeValue;
				}
				if ( cn.nodeType == 1 ) {
					switch(cn.tagName){
						case 'OBJECT':
							if ( cn.type == "application/x-shockwave-flash" ) {string += '[youtube='+cn.width+'x'+cn.height+']'+cn.data+'[/youtube]';}
							break;
						case 'STRONG':
							string += '[b]'; break;
						case 'EM':
							string += '[i]'; break;
						case 'U':
							string += '[u]'; break;
						case 'FONT':
							if ( cn.size ) {string += '[size='+(cn.size*4)+']';}
							if ( cn.color ) {string += '[color='+(cn.color)+']';}
							break;
						case 'A':
							string += '[url='+cn.href+']';break;
						case 'UL':
							string += '[ulist]'; break;
						case 'OL':
							string += '[list]'; break;
						case 'LI':
							string += '[li]'; break;
						case 'BR':
						case 'P':
							string += "\r\n";break;
						case 'IMG':
							if ( cn.width && cn.height ) {
								string += '[img='+cn.width+'x'+cn.height+']'+this.relLink(cn.src)+'[/img]'; 
							} else {
								string += '[img]'+this.relLink(cn.src)+'[/img]'; 
							}
							break;
					}
					string += this.bbCodeWalkIE(cn);
					
					switch(cn.tagName){
						case 'STRONG':
							string += '[/b]'; break;
						case 'EM':
							string += '[/i]'; break;
						case 'U':
							string += '[/u]'; break;
						case 'FONT':
							if ( cn.size ) { string += '[/size]'; }
							if ( cn.color ) { string += '[/color]'; }
							break;	
						case 'A':
							string += '[/url]'; break;
						case 'OL':
							string += '[/list]'; break;
						case 'UL':
							string += '[/ulist]'; break;
						case 'LI':
							string += '[/li]'; break;
					}
				}
			}
		}
		return string;
	}
	
	this.getStyleColor = function(string) {
		string = string.substring(4);
		var stringarray = string.split(')');
		var parts = stringarray[0].split(',');
		var ret = '#';
		for ( var i = 0; i < 3; i++ ) {
			var c = parseInt(parts[i]);
			if ( c == 0 ) { 
				ret += '00';
			} else {
				ret += "0123456789ABCDEF".charAt((c-c%16)/16) + "0123456789ABCDEF".charAt(c%16);
			}
		}
		return ret;
	}
	
	this.bbCode_to_Moz = function(s) {
		// string to mozilla/FF structure
		s = s.replace(/\[i\]\[\/i\]/g, '');
		s = s.replace(/\[b\]\[\/b\]/g, '');
		s = s.replace(/\[u\]\[\/u\]/g, '');
		s = s.replace(/\[size=\w{1,2}\]\[\/size\]/g, '');
		s = s.replace(/\[color=#\w{6}\]\[\/color\]/g, '');
		s = s.replace(/\[u?list\]\[\/u?list\]/g, '');
		
		var f;
		do{
			f = s.match(/\[size=\w+\]/);
			if ( f != null ) {
				var fontsize = f[0].match(/size=(\w+)/);
				var size = parseInt(fontsize[1]) / 4;
				var newf = '<font size="'+size+'">'; 
				s= s.replace(f, newf);
			}
		} while ( f != null );
		s = s.replace(/\[\/size\]/g, '</font>');

		var c;
		do{
			c = s.match(/\[color=#\w{6}\]/);
			if ( c != null ) {
				var color = c[0].match(/#(\w{6})/);
				var newc = '<span style="color:#'+color[1]+'">';
				s= s.replace(c, newc);
			}
		} while ( c != null );
		
		var i;
		do{
			i = s.match(/\[img\][a-zA-Z0-9_:\.\/\\\+\?\-]+\[\/img\]/);
			if ( i != null ) {
				var image = i[0].match(/\]([a-zA-Z0-9_:\.\/\\\+\?\-]+)\[/);
				var newi = '<img src="'+this.absLink(image[1])+'"/>';
				s= s.replace(i, newi);
			}
		} while ( i != null );
		
		do{
			i = s.match(/\[img=[0-9x]+\][a-zA-Z0-9_:\.\/\\\+\?\-]+\[\/img\]/);
			if ( i != null ) {
				var image = i[0].match(/=([0-9]+)x([0-9]+)\]([a-zA-Z0-9_:\.\/\\\+\?\-]+)\[/);
				var newi = '<img src="'+this.absLink(image[3])+'" height="'+image[2]+'" width="'+image[1]+'"/>';
				s= s.replace(i, newi);
			}
		} while ( i != null );
		
		do{
			i = s.match(/\[youtube=[0-9x]+\][a-zA-Z0-9_:\.\/\\\+\?\-]+\[\/youtube\]/);
			if ( i != null ) {
				var image = i[0].match(/=([0-9]+)x([0-9]+)\]([a-zA-Z0-9_:\.\/\\\+\?\-]+)\[/);
				var newi = '<object type="application/x-shockwave-flash" data="'+image[3]+'" height="'+image[2]+'" width="'+image[1]+'"><param value="'+image[3]+'" name="movie"/></object>';
				s= s.replace(i, newi); 
			}
		} while ( i != null );
		
		do {
			i = s.match(/\[quote(?:=(?=[^\]]+)([^\]]+))?\]/i);
			if ( i != null ) {
				var name = i[1] || '';
				var newi = '<blockquote><div><cite>'+name+'</cite> ';
				s= s.replace(i[0], newi);
			}
		} while ( i != null );
		
		s = s.replace(/\[\/quote\]/g, '</div></blockquote><br/>');
		s = s.replace(/\[\/color\]/g, '</span>');
		s = s.replace(/\[i\]/g, '<span style="font-style: italic;">');
		s = s.replace(/\[\/i\]/g, '</span>');
		s = s.replace(/\[b\]/g, '<span style="font-weight: bold;">');
		s = s.replace(/\[\/b\]/g, '</span>');
		s = s.replace(/\[u\]/g, '<span style="text-decoration: underline;">');
		s = s.replace(/\[\/u\]/g, '</span>');
		s = s.replace(/\[list\]/g, '<ol>');
		s = s.replace(/\[\/list\]/g, '</ol>');
		s = s.replace(/\[ulist\]/g, '<ul>');
		s = s.replace(/\[\/ulist\]/g, '</ul>');
		s = s.replace(/\[\*\]/g, '<li>');
		s = s.replace(/\[li\]/g, '<li>');
		s = s.replace(/\[\/li\]/g, '</li>');
		s = s.replace(/\n/g, '<br/>').replace(/\r/g, '');
		
		var L;
		do{
			L = s.match(/\[url=[a-zA-Z0-9_:\.\/\\\+\?\-]+\]/);
			if ( L != null ) {
				var link = L[0].match(/=([a-zA-Z0-9_:\.\/\\\+\?\-]+)\]/);
				var newL = '<a href="'+link[1]+'">';
				s= s.replace(L, newL);
			}
		} while (L != null);
		s = s.replace(/\[\/url\]/g, '</a>');
		
		return s;
	}
	
	this.bbCode_to_IE = function(s) {
		// string to IE structure
		s = s.replace(/\[i\]\[\/i\]/g, '');
		s = s.replace(/\[b\]\[\/b\]/g, '');
		s = s.replace(/\[u\]\[\/u\]/g, '');
		s = s.replace(/\[size=\w{1,2}\]\[\/size\]/g, '');
		s = s.replace(/\[color=#\w{6}\]\[\/color\]/g, '');
		
		var f;
		do{
			f = s.match(/\[size=\w+\]/);
			if ( f != null ) {
				var fontsize = f[0].match(/size=(\w+)/);
				var size = parseInt(fontsize[1]) / 4;
				var newf = '<font size="'+size+'">'; 
				s= s.replace(f, newf);
			}
		} while ( f != null );
		s = s.replace(/\[\/size\]/g, '</font>');
		
		var c;
		do{
			c = s.match(/\[color=#\w{6}\]/);
			if ( c != null ) {
				var color = c[0].match(/#(\w{6})/);
				var newc = '<font color="#'+color[1]+'">';
				s= s.replace(c, newc);
			}
		} while ( c != null );
		
		do{
			i = s.match(/\[img\][a-zA-Z0-9_:\.\/\\\+\?\-]+\[\/img\]/);
			if ( i != null ) {
				var image = i[0].match(/\]([a-zA-Z0-9_:\.\/\\\+\?\-]+)\[/);
				var newi = '<img src="'+this.absLink(image[1])+'"/>';
				s= s.replace(i, newi);
			}
		} while ( i != null );
		
		do{
			i = s.match(/\[img=[0-9x]+\][a-zA-Z0-9_:\.\/\\\+\?\-]+\[\/img\]/);
			if ( i != null ) {
				var image = i[0].match(/=([0-9]+)x([0-9]+)\]([a-zA-Z0-9_:\.\/\\\+\?\-]+)\[/);
				var newi = '<img src="'+this.absLink(image[3])+'" height="'+image[2]+'" width="'+image[1]+'"/>';
				s= s.replace(i, newi);
			}
		} while ( i != null );
		
		do{
			i = s.match(/\[youtube=[0-9x]+\][a-zA-Z0-9_:\.\/\\\+\?\-]+\[\/youtube\]/);
			if ( i != null ) {
				var image = i[0].match(/=([0-9]+)x([0-9]+)\]([a-zA-Z0-9_:\.\/\\\+\?\-]+)\[/);
				var newi = '<object type="application/x-shockwave-flash" data="'+image[3]+'" height="'+image[2]+'" width="'+image[1]+'"><param value="'+image[3]+'" name="movie"/></object>';
				s= s.replace(i, newi);
			}
		} while ( i != null );
		
		do {
			i = s.match(/\[quote(?:=(?=[^\]]+)([^\]]+))?\]/i);
			if ( i != null ) {
				var name = i[1] || '';
				var newi = '<blockquote><div><cite>'+name+'</cite> ';
				s= s.replace(i[0], newi);
			}
		} while ( i != null );
		
		s = s.replace(/\[\/quote\]/g, '</div></blockquote><br/>');
		
		s = s.replace(/\[\/color\]/g, '</font>');
		s = s.replace(/\[i\]/g, '<i>');
		s = s.replace(/\[\/i\]/g, '</i>');
		s = s.replace(/\[b\]/g, '<strong>');
		s = s.replace(/\[\/b\]/g, '</strong>');
		s = s.replace(/\[u\]/g, '<u>');
		s = s.replace(/\[\/u\]/g, '</u>');
		s = s.replace(/\[list\]/g, '<ol>');
		s = s.replace(/\[\/list\]/g, '</ol>');
		s = s.replace(/\[ulist\]/g, '<ul>');
		s = s.replace(/\[\/ulist\]/g, '</ul>');
		s = s.replace(/\[\*\]/g, '<li>');
		s = s.replace(/\[li\]/g, '<li>');
		s = s.replace(/\[\/li\]/g, '</li>');
		s = s.replace(/\n/g, '<br/>').replace(/\r/g, '');
		
		var L;
		do {
			L = s.match(/\[url=[a-zA-Z0-9_:\.\/\\\+\?\-]+\]/);
			if ( L != null ) {
				var link = L[0].match(/=([a-zA-Z0-9_:\.\/\\\+\?\-]+)\]/);
				var newL = '<a href="'+link[1]+'">';
				s= s.replace(L, newL);
			}
		} while ( L != null );
		s = s.replace(/\[\/url\]/g, '</a>');
		
		return s;
	}
	
	this.WysiClickLinkButton = function() {
		this.getPosition();
		var wysiPanel = did('wysiLinkwindow'+this.wysi_id);
		if ( !wysiPanel ) { return; }
		if ( wysiPanel.style.display == 'none' ) { this.closePanels(); }
		if ( wysiPanel.style.display=='' ) { this.closePanels(); return; }
		
		var editFrame = did('wysiRichText'+this.wysi_id);
		if ( !editFrame ) { return; }
		var editWin = editFrame.contentWindow;
		var selection = editWin.getSelection ? editWin.getSelection() : editWin.document.selection;
		//MOZILLA - isCollapsed, IE selection.type == 'None'     = no selected text
		if ( selection.isCollapsed || selection.type == 'None' ) {
			this.message('Please select text before creating link');
			return;
		}
		if ( selection.type ) { /* IE only to fix it losing focus */
			var range = selection.createRange();
			this.selectedRange = range.duplicate();
		}
		
		var defaultURL = this.wysStyle.link;
		var path_elm = did('wysiWeblink'+this.wysi_id);
		if ( path_elm ) { //hide the popup and relative url parts
			path_elm.value = defaultURL.replace(/popup:/g,'').replace(/http:\/\/this\.is\.a\.relative\.url/g,'');
		}
		var pop_elm = did('wysiLinkPop'+this.wysi_id);
		pop_elm.checked = defaultURL.substr(0,6)=='popup:' ? true : false;
		
		wysiPanel.style.display = '';
		this.setWysiButton(6, 1);
		this.linkbutton = 6;
		this.message('Please enter web link (example: http://www.yourlink.com)');
	}
	
	this.selectedRange = null;
	this.urlPath = /^(?:popup:)?https?:\/\//;
	
	this.WysiSubmitLink = function() {
		var editFrame = did('wysiRichText'+this.wysi_id);
		if ( !editFrame ) { return; }
		var editWin = editFrame.contentWindow;
		if ( this.selectedRange ) {
			editWin.focus();
			this.selectedRange.select();
			this.selectedRange = null;
		}
		var selection = editWin.getSelection ? editWin.getSelection() : editWin.document.selection;
		
		//MOZILLA - isCollapsed, IE selection.type == 'None'     = no selected text
		if ( selection.isCollapsed || selection.type == 'None' ) {
			this.message('Please select text before creating link');
			return;
		}
		
		var selText = editWin.getSelection ? selection.toString() : selection.createRange().text;
		
		var path_elm = did('wysiWeblink'+this.wysi_id);
		if ( !path_elm ) { return; }
		var url = path_elm.value;
		
		if ( url == '' ) {
			this.sendExec('unlink',url);
		}else{
			if ( !this.urlPath.test(url) ) { url = 'http://this.is.a.relative.url'+url; }
			var pop_elm = did('wysiLinkPop'+this.wysi_id);
			if ( pop_elm && pop_elm.checked ) {
				url = 'popup:'+url;
			}
			if ( editWin.getSelection ) {
				this.sendExec('createlink',url);
			}else{
				selection.createRange().pasteHTML('<a href="'+url+'">'+selText+'</a>');
			}
			pop_elm.checked = false;
		}
		
		var collapse = selection.collapseToStart || function() { this.empty(); };
		collapse.call(selection);
		
		path_elm.value = '';
		editWin.focus();
		this.message('');
		this.closePanels();
	}
	
	this.WysiClickListButton = function() {
		this.sendExec('insertorderedlist', '');
		did('wysiRichText'+this.wysi_id).contentWindow.focus();
	}
	
	this.WysiClickUListButton = function() {
		this.sendExec('insertunorderedlist', '');
		did('wysiRichText'+this.wysi_id).contentWindow.focus();
	}
	
	this.WysiClickSpellingButton = function() {
		if ( did('wysiSpellwindow'+this.wysi_id).style.display == '' ) {
			Activa.DOM.hideID('wysiSpellwindow'+this.wysi_id);
			this.closePanels();
			return;
		}
		this.closePanels();
		this.setWysiButton(7, 1);
		this.spellCheck.startCheck();
	}

	this.WysiEndSpellCheck = function() {
		Activa.DOM.hideID('wysiSpellwindow'+this.wysi_id);
		this.spellCheck.clearSelection();
		this.closePanels();
		this.message('');
	}
	
	this.WysiClickImageButton = function() {
		if ( did('wysiImageWindow'+this.wysi_id).style.display == '' ) {
			Activa.DOM.hideID('wysiImageWindow'+this.wysi_id);
			this.closePanels();
			return;
		}
		this.closePanels();
		this.setWysiButton(8, 1);
		if ( !did('wysiRichText'+this.wysi_id).contentDocument ) {
			// a fix for IE losing focus
			var range = document.frames['wysiRichText'+this.wysi_id].document.selection.createRange();
			if ( range.text != undefined ) {
				var range2 = range.duplicate();
				var range3 = range.duplicate();
				range2.setEndPoint('EndToEnd', range);
				var start = range2.text.length - range.text.length;
				var end = start + range.text.length;
				this.fixNode = [start, end, range3];
			}
		}
		// draw the contents of the inline frame for image submit
		
		var uploadForm = '<form name="imageform'+this.wysi_id+'" id="wysiImageForm'+this.wysi_id+'" method="post" enctype="multipart/form-data" action="'+this.absLink('wysiwyg/uploadImage')+'" style="margin:0;padding:0;"><input type="hidden" name="wysiwyg_id" value="'+this.wysi_id+'"/><input type="file" name="file" id="wysiImageFormFile'+this.wysi_id+'" style="font-size: 10px;margin:0;padding:0;"/></form>';
		var uploadFrame = did('uploadFrame'+this.wysi_id);
		var uploadDoc = uploadFrame.contentDocument ? uploadFrame.contentDocument : window.parent.document.frames['uploadFrame'+this.wysi_id].document;

		var uploadNode = uploadFrame.getElementsByTagName('body')[0];
		uploadNode.style.padding=0;
		uploadNode.style.margin=0;
		uploadNode.style.backgroundColor='#D7D7D7';
		uploadNode.innerHTML = uploadForm;
		did('wysiImageWindow'+this.wysi_id).style.display = '';
		this.message('Click \'Browse\' to select an image and \'Upload\' to place it.');
	}
	
	this.WysiImageCancel = function() {
		did('wysiImageWindow'+this.wysi_id).style.display = 'none';
		this.message('');
		this.closePanels();
	}
	
	this.WysiImageSubmit = function() {
		var uploadFrame = did('uploadFrame'+this.wysi_id);
		var uploadDoc = uploadFrame.contentDocument ? uploadFrame.contentDocument : window.parent.document.frames['uploadFrame'+this.wysi_id].document;
		uploadDoc.getElementById('wysiImageForm'+this.wysi_id).submit();
	}
	
	this.WysiAttachImage = function(filepath, x, y) {
		if ( filepath != '' ) {
			if ( this.fixNode ) {
				did('wysiRichText'+this.wysi_id).contentWindow.focus();			
				var range = this.fixNode[2];
				range.collapse();
				range.moveStart('character', this.fixNode[0]);
				range.moveEnd('character', this.fixNode[1]);
				range.select();
				this.fixNode = 0;
				did('wysiRichText'+this.wysi_id).contentWindow.focus();
			}
			this.cmp_frame.execCommand('InsertImage', false, filepath);
			this.imageResize(this.cmp_frame.getElementsByTagName('body')[0], filepath, 150, 150);
		}
		this.WysiImageCancel();
		this.message('');
		this.closePanels();
	}
	
	this.imageResize = function(node, src, x, y) {	
		for ( var c=0; c < node.childNodes.length; ++c ) {
			var n = node.childNodes[c];
			if ( (n.nodeName == 'IMG' || n.nodeName == 'img') && n.src == src ) {
				var w = x;
				var h = Math.round(n.height * x / n.width);
				if ( h > y ) {
					w = Math.round(n.width * y / n.height);
					h = y;
				}
				n.width=w; n.height=h; 
				if ( this.browser == 'IE' ) {
					did('wysiRichText'+this.wysi_id).contentWindow.focus();
				}
			} else {
				if ( (n) && n.nodeType == 1 ) {
					this.imageResize(n, src, x, y);
				}
			}
		}
	}
	
	this.closePanels = function() {
		var panels = ['wysColorSelect','wysiLinkwindow','wysiSpellwindow','wysiImageWindow','wysiLinkImage','wysiYouTubeWindow'];
		var el;
		for ( var i=0,l=panels.length;i < l;i++ ) {
			el = did(panels[i]+this.wysi_id);
			if ( el ) {
				Activa.DOM.hideID(panels[i]+this.wysi_id);
			}
		}
		this.setWysiButton(8, 0);
		this.setWysiButton(7, 0);
		this.setWysiButton(6, 0);
		this.setWysiButton(10, 0);
		this.setWysiButton(11, 0);
		this.message('');
	}
	
	this.WysiViewCodeButton = function() {
		if ( this.viewMode == 'code' ) {
			this.convertToWysiView();
			this.viewMode = 'richtext';
			this.setWysiButton(9, 0);
			this.enableWysiButtons();
		} else {
			this.convertToCodeView();
			this.viewMode = 'code';
			this.setWysiButton(9, 1);
			this.disableWysiButtons();
			this.closePanels();
		}
	}
	
	this.convertToWysiView = function() {
		if ( this.viewMode == 'code' ) {
			var body = this.cmp_frame.getElementsByTagName('body')[0];
			var html = this.browser == 'Mozilla' ? this.cmp_frame.createRange() : body.innerText;
			if ( this.browser == 'Mozilla' ) {
				html.selectNodeContents(body);
			}
			body.innerHTML = String(html);
		}
	}
	
	this.convertToCodeView = function() {
		if ( this.viewMode != 'code' ) {
			var body = this.cmp_frame.getElementsByTagName('body')[0];
			this.removeEmptyNodes(body);
			var html = this.browser=='Mozilla' ? this.cmp_frame.createTextNode(body.innerHTML) : body.innerHTML;
			if ( this.browser == 'Mozilla' ) {
				body.innerHTML = "";
				body.appendChild(html);
			} else if ( this.browser == 'IE' ) {
				body.innerText = html;
			}
		}
	}
	
	this.removeEmptyNodes = function(node) {
		var removeNodes = [], n = null;
		var lastNode = node.lastChild;
		/* If the last node (important: including text nodes!!) inside a node is a br then remove the br */
		if ( lastNode && lastNode.nodeName.toLowerCase() == 'br' ) {
			removeNodes.push(lastNode);
		}
		for ( var c = 0; c < node.childNodes.length; c++ ) {
			n = node.childNodes[c];
			if ( n.nodeType == 1 ) {
				if ( n.childNodes.length == 1 && n.childNodes[0].nodeValue=='' ) {
					removeNodes.push(n);
				}
				this.removeEmptyNodes(n);
			}
		}
		if ( removeNodes.length > 0 ) {
			var pnode = null;
			while(n = removeNodes.shift()) {
				pnode = Activa.DOM.owner(n);
				if ( n && pnode ) {
					pnode.removeChild(n);
				}
			}
		}
	}
	
	this.disableWysiButtons = function() {
		var list = ['wysibutton1', 'wysibutton2', 'wysibutton3', 'wysibutton4', 'wysibutton5', 'wysiFontsize', 'wysFontColor', 'wysibutton6', 'wysibutton7', 'wysibutton8', 'wysibutton10', 'wysibutton11'];
		for ( b in list ) {
			if ( did(list[b]+this.wysi_id) ) {
				did(list[b]+this.wysi_id).disabled='disabled';
			}
		}	
	}
	
	this.enableWysiButtons = function() {
		var list = ['wysibutton1', 'wysibutton2', 'wysibutton3', 'wysibutton4', 'wysibutton5', 'wysiFontsize', 'wysFontColor', 'wysibutton6', 'wysibutton7', 'wysibutton8', 'wysibutton10', 'wysibutton11'];
		for ( b in list ) {
			if ( did(list[b]+this.wysi_id) ) {
				did(list[b]+this.wysi_id).disabled='';
			}
		}	
	}
	
	this.WysiClickLinkImageButton = function() {
		if ( did('wysiLinkImage'+this.wysi_id).style.display=='' ) {
			did('wysiLinkImage'+this.wysi_id).style.display='none';
			this.closePanels();
			return;
		}
		if ( !window.parent.document.getElementById('wysiRichText'+this.wysi_id).contentDocument ) {
			// a fix for IE losing focus
			var range = document.frames['wysiRichText'+this.wysi_id].document.selection.createRange();
			if ( range.text != undefined ) {
				var range2 = range.duplicate();
				var range3 = range.duplicate();
				range2.setEndPoint('EndToEnd', range);
				var start = range2.text.length - range.text.length;
				var end = start + range.text.length;
				this.fixNode = [start, end, range3];
			}
		}
		this.closePanels();
		this.setWysiButton(10, 1);
		did('wysiLinkImage'+this.wysi_id).style.display='';
		this.message('Enter the URL of the image you wish to insert.');
	}
	
	this.WysiSubmitLinkImage = function() {
		var image = did('wysiImagelink'+this.wysi_id).value;
		if ( image != '' ) {
			if ( this.fixNode ) {
				document.getElementById('wysiRichText'+this.wysi_id).contentWindow.focus();			
				var range = this.fixNode[2];
				range.collapse();
				range.moveStart('character', this.fixNode[0]);
				range.moveEnd('character', this.fixNode[1]);
				range.select();
				this.fixNode = 0;
				document.getElementById('wysiRichText'+this.wysi_id).contentWindow.focus();
			}
			this.cmp_frame.execCommand('InsertImage', false, image);
			this.imageResize(this.cmp_frame.getElementsByTagName('body')[0], image, 150, 150);
		}
		this.closePanels();
	}
	
	this.WysiViewInsertYouTube = function(){
		if ( did('wysiYouTubeWindow'+this.wysi_id).style.display == '' ) {
			Activa.DOM.hideID('wysiYouTubeWindow'+this.wysi_id);
			this.closePanels();
			return;
		}
		if ( !did('wysiRichText'+this.wysi_id).contentDocument ) {
			// a fix for IE losing focus
			var range = document.frames['wysiRichText'+this.wysi_id].document.selection.createRange();
			if ( range.text != undefined ) {
				var range2 = range.duplicate();
				var range3 = range.duplicate();
				range2.setEndPoint('EndToEnd', range);
				var start = range2.text.length - range.text.length;
				var end = start + range.text.length;
				this.fixNode = [start, end, range3];
			}
		}
		this.closePanels();
		this.setWysiButton(11, 1);
		did('wysiYouTubeWindow'+this.wysi_id).style.display='';
		this.message('Enter the YouTube tag for the video you wish to insert.');
	}
	
	this.WysiSubmitYouTubeLink = function(){
		var url = did('wysiYouTubelink'+this.wysi_id).value;
		var reg = new RegExp(/^http:\/\/www.youtube.com[A-Za-z0-9\/\\=\? ]*/);
		if ( reg.test(url) ) {
			var parts = url.split("watch?v=");
			if ( parts.length == 2 ) {
				url = parts[1];
			} else {
				parts = url.split('/v/');
				if ( parts.length == 2 ) {url = parts[1];}
			}
			var image = this.absLink('images/yt.gif?'+url);
			this.cmp_frame.execCommand('InsertImage', false, image);
			this.imageResize(this.cmp_frame.getElementsByTagName('body')[0], image, 150, 150);
			
			this.closePanels();
		} else {
			this.message('Link must be a valid YouTube link.');
		}
	}
}

function wysiStart(){
	var submitCommands = [], startIDs = [];
	totalWysiInputs = (totalWysiInputs || 0);
	if ( !window['wysiStartIDs'] ) {
		for ( var i=1;i <= totalWysiInputs;i++ ) {
			startIDs.push(i);
		}
	} else {
		startIDs = (window['wysiStartIDs'] instanceof Array) ? window['wysiStartIDs'] : [window['wysiStartIDs']];
	}
	var id;
	for ( var i = 0; i < startIDs.length; i++ ) {
		id = startIDs[i];
		if ( !window['wysiText'+id] ) {
			window['wysiText'+id] = new wysiWindow(id);
		}
		var b = navigator.userAgent.toLowerCase().indexOf('khtml');
		if ( b != -1 ) {
			// not supporting rich text 
			window['wysiText'+id].outputMethod = 1;
			var lite = did('wysiLiteEditor'+id); 
			if ( lite ) {
				Activa.DOM.showID(lite.id);
				// Convert TO BB Code
				// Loads Into Preview, then walks the DOM on the preview
				showWysiPreview(id);
				var node = did('wysiLitePreview'+id);
				did('wysiPlainTextarea'+id).value = window['wysiText'+id].bbCodeWalk(node);
				hideWysiLite(id);
			}
		} else {
			// supporting
			var plain = did('wysiPlainText'+id);
			if ( plain ) {
				Activa.DOM.hideID(plain.id);
			}
			var rich = did('wysiEditor'+id); 
			if ( rich ) {
				Activa.DOM.showID(rich.id);
			}
			if ( (plain || rich) && !window['wysiText'+id].initialized ) {
				window['wysiText'+id].wysInit();
			}
		}
		if ( window['wysiText'+id].saveMethod == 'HTML' ) {
			submitCommands[id] = window['wysiText'+id].convertToHTML;
		} else if ( window['wysiText'+id].saveMethod == 'BBCODE' ) {
			submitCommands[id] = window['wysiText'+id].convertToBBCode;
		}
	}
	if ( submitCommands.length > 0 ) {
		// attach submit to proper location
		var elems = document.getElementsByTagName('FORM');
		for ( var e = 0; e < elems.length; e++ ) {
			Activa.registerEvent(elems[e], 'submit', function() {
				submitCommands.forEach(function(cmd,wid) {
					cmd.call(window['wysiText'+wid]);
				});
			});
		}
	}
}

function showWysiHelp(id){
	if ('undefined' == typeof(id)) id = '';
	Activa.DOM.hideID('wysiLitePreview'+id);
	Activa.DOM.showID('wysiLiteHelp'+id);
}

function showWysiPreview(id){
	id = id ? id : '';
	Activa.DOM.hideID('wysiLiteHelp'+id);
	var text = window['wysiText'+id].bbCode_to_Moz(did('wysiPlainTextarea'+id).value);
	text = text.replace(/\r/g, '').replace(/\n/g, '<br/>');
	var wysiLitePreview = did('wysiLitePreview'+id);
	if ( wysiLitePreview ) { 
		wysiLitePreview.innerHTML = text;
		Activa.DOM.showID(wysiLitePreview.id);
	}
}

function hideWysiLite(id){
	id = id ? id : '';
	Activa.DOM.hideID('wysiLitePreview'+id);
	Activa.DOM.hideID('wysiLiteHelp'+id);
}

function ActivaSpell(target, parentID){ // pass id of iframe
	
	this.targetWindow = target;
	this.parentID = parentID;
	
	if ( window.ActiveXObject ) {
		this.browser = 'IE';
		this.contentDoc = window.parent.document.frames[this.targetWindow].document;
	} else if ( did(this.targetWindow).contentDocument ) {
		this.browser = 'Mozilla';
		this.contentDoc = did(this.targetWindow).contentDocument;
	}
	
	this.SpInst = [];
	this.spellCounter = -1;
	this.spellList = [];
	this.spellignoreList = [];
	this.selectedRange = [];
	this.searchInt = 0;
	this.spellResults;
	this.selectionNode;
	this.selectionStart;
	this.selectionEnd;
	this.selectionWordInstance;
	this.selectionOffset;
			
	this.startCheck = function(){
		this.spellCounter = -1;
		var string = this.contentDoc.getElementsByTagName('body')[0].innerHTML;
		new Activa.rpc('wysiwyg/checkSpelling', { input: {s: string.trim()},
			onComplete: function(obj) {
				if(obj != '404'){
					this.spellResults = obj;
					if(this.activeWords() == 0){
						document.getElementById('wysiMessage'+this.parentID).innerHTML = 'No spelling errors found';
						return;
					}
					document.getElementById('wysiSpellwindow'+this.parentID).style.display='';
					this.gotoNext();
				}
			}.bind(this)
		});
	}
	
	this.activeWords = function(){
		var c = 0;
		for ( w in this.spellResults ) {
			if ( this.spellResults[w].active==1 ) {c++;}
		}
		return c;
	}
	
	this.ignoreWord = function(){
		this.spellResults[this.spellCounter].active=0;
		this.gotoNext();
	}
	
	this.ignoreWordAll = function(){
		if ( this.spellResults.length == 0 ) {return;}
		var word = this.getSpellWord(this.spellCounter);
		var spellResults = [];
		for (w in this.spellResults){
			if ( word != this.getSpellWord(w) ) { spellResults.push(this.spellResults[w]);}
		}
		this.spellResults = spellResults;
		this.spellCounter -= 1;
		this.gotoNext();
	}
	
	this.done = function(){
		did('wysiSpellSuggest'+this.parentID).innerHTML = '&nbsp;';
		did('wysiMessage'+this.parentID).innerHTML = 'Spell Check Finished';
	}
	
	this.spellSelect = function(word){
		word = word.replace('+', "'");
		var string1 = this.selectionNode.nodeValue;
		var string2 = '';
		if ( this.selectionStart > 0 ) { string2 += string1.substring(0, this.selectionStart); }
		string2 += word;
		if ( this.selectionEnd < string1.length-1 ) { string2 += string1.substring(this.selectionEnd); }
		this.selectionNode.nodeValue = string2;
		this.removeWordNode();
		this.gotoNext();
	}
	
	this.clearSelection = function(){
		if ( this.browser == 'Mozilla' ) {		
			var selection =  did(this.targetWindow).contentWindow.getSelection();
			var range = this.contentDoc.createRange();
			var node = this.contentDoc.getElementsByTagName('body')[0];
			range.setStart(node, 0);
			range.setEnd(node, 0);
			selection.removeAllRanges();
			selection.addRange(range);
		}
		if ( this.browser == 'IE' ) {
			var range = this.contentDoc.getElementsByTagName('body')[0].createTextRange();
			range.collapse(false);
			range.select();
		}
	}
	
	this.gotoNext = function(){
		if ( this.spellResults.length == 0 || this.activeWords() == 0 ) {
			// no more words
			this.clearSelection();
			this.done();
			return;
		}
		
		this.spellCounter++;
		if ( this.spellCounter >= this.spellResults.length ) { this.spellCounter = 0; this.searchInt = 0; }
		if ( this.spellResults[this.spellCounter].active != 1 ) {
			this.gotoNext();
		} else {
			var currWord = this.getSpellWord(this.spellCounter);
			
			var go = true;
			for ( i in this.spellignoreList ) {
				if ( this.spellignoreList[i] == currWord ) {go = false;}
			}
			if ( go ) {
				var sugList = this.getSpellSuggests(this.spellCounter);
	
				var wordlist = '';
				for ( var w = 0;w < sugList.length; w++ ) {
					var word = sugList[w];
					wordlist += '<a href="javascript:wysiText'+this.parentID+'.spellCheck.spellSelect(\''+word.replace("'",'+')+'\');">'+word+'</a>  ';
				}
				
				document.getElementById('wysiMessage'+this.parentID).innerHTML = 'Word: '+currWord;
				document.getElementById('wysiSpellSuggest'+this.parentID).innerHTML = 'Suggestions: '+wordlist;
				
				var c = this.countWordInstance(this.spellCounter); 
				this.selectionWordInstance = 0;
				this.selectionOffset = 0;
				var found = this.findAndSelect(this.contentDoc.getElementsByTagName('body')[0], currWord, c);
				if ( !found ) { this.ignoreWord(); }
			} else {
				this.ignoreWord();
			}
		}
	}
	
	this.getSpellWord = function(n){
		return this.spellResults[n].word;
	}
	
	this.getSpellSuggests = function(n){
		return this.spellResults[n].suggestions;
	}	
	
	this.removeWordNode = function(){
		var spellResults = [];
		for (w in this.spellResults){
			if ( w != this.spellCounter ) { spellResults.push(this.spellResults[w]); }
		}
		this.spellResults = spellResults;
		this.spellCounter -= 1;
	}
	
	this.findAndSelect = function(node, word, inst){
		for ( var c=0; c < node.childNodes.length; c++ ) {
			var n = node.childNodes[c];
			if ( n.nodeType == 3 ) {
				var  pattern = new RegExp( "(^|[^A-Za-z]{1})"+word+"($|[^A-Za-z]{1})"); 
				var start = n.nodeValue.search(pattern);
				if ( start > -1 ) {
					if ( n.nodeValue.charAt(start) != word.charAt(0) ) { start++; }
					this.selectionWordInstance +=1;
					if ( this.selectionWordInstance >= inst ) {
						end = start + word.length;
						this.selectTextInNode(n, start, end);
						this.selectionNode = n;
						this.selectionStart = start;
						this.selectionEnd = end;
						return true;
					} else {
						var stringOffset = start+word.length;
						var nodeString = n.nodeValue.substring(start+word.length);
						while(nodeString.search(pattern) > -1){
							start = nodeString.search(pattern)+1;
							this.selectionWordInstance +=1;
							if ( this.selectionWordInstance >= inst ) {
								start+=stringOffset;
								end = start + word.length;
								this.selectTextInNode(n, start, end);
								this.selectionNode = n;
								this.selectionStart = start;
								this.selectionEnd = end;
								return true;
							} else {
								stringOffset += start+word.length;
								nodeString = nodeString.substring(start+word.length);
							}
						}
					}
				}
				this.selectionOffset += n.nodeValue.length;
				
			} else if ( n.nodeType == 1 ) {
				var result = this.findAndSelect(n, word, inst);
				if ( result == true ) { return true; }
			}
		}
		return false;
	}
	
	this.selectTextInNode = function (node, start, end){
		if ( this.browser == 'Mozilla' ) {		
			var selection =  window.parent.document.getElementById(this.targetWindow).contentWindow.getSelection();
			var range = this.contentDoc.createRange();
			range.setStart(node, start);
			range.setEnd(node, end);
			selection.removeAllRanges();
			selection.addRange(range);
			
			var psib = node.previousSibling;
			if ( psib && psib.offsetTop ) {
				var oY = psib.offsetTop;
			} else {
				var oY = node.parentNode.offsetTop;
			}
			scroll = Math.round(oY + (16 * (end / 80)));
			this.contentDoc.getElementsByTagName('body')[0].scrollTop = scroll;
			
		}
		if ( this.browser == 'IE' ) {
			var range = this.contentDoc.getElementsByTagName('body')[0].createTextRange();
			range.collapse(true);
			range.moveEnd('character', end + this.selectionOffset);
			range.moveStart('character', start + this.selectionOffset);
			range.select();
		}
	}
	
	this.countWordInstance = function(n){
		var count = 0;
		var testword = this.getSpellWord(n);
		for ( w in this.spellResults ) {
			if ( this.spellResults[w].word == testword ) {count++;}
			if ( w == n ) { return count; }
		}
		return count;
	}
}

/**
 * LightBox - Builds and manages a set of elements that makeup a lightbox
 * options:
 * box					mixed		Element id or object that holds the content
 * overlay				mixed		Element id or object that acts as the overlay
 * closeButton			mixed		Element id or object that acts as a close button
 * boxClass				string		CSS class that can be added to the box
 * overlayClass			string		CSS class that can be added to the overlay
 * closeClass			string		CSS class that can be added to the close button (default: lbClose)
 * imgClass				string		CSS class used to identify images to preload
 * contentClass			string		CSS class used for content wrapper (default: lbContentWrapper)
 * frameClass			string		CSS class used for iframe wrapper (default: lbContentFrame)
 * autoRegisterEvents	boolean		Determines if click events for displaying images should be automatically created
 * makeCloseButton		boolean		Determines if a close button is created if one is not specified
 * hideOverlay			boolean		Prevents overlay from displaying when the lightbox is shown
 * onshow				function	Function to execute when the lightbox is shown
 * onhide				function	Function to execute when the lightbox is hidden
 * pinTop				boolean		Determines if the lightbox should pin it's top position in place (default: false)
 * pinLeft				boolean		Determines if the lightbox should pin it's left position in place (default: false)
 * autopin				boolean		Determines if the lightbox will automatically pin itself when it is larger than the viewable area. (default: true)
 *
 * @param	options		object		optional;Object specifying options
 * @return				LightBox	New LightBox instance
 */
var LightBox = new Activa.Class({
	_isShowing: false,
	_initialized: false,
	_isIE: false,
	_images: {},
	box: null,
	overlay: null,
	closeButton: null,
	options: {
		box: null,
		overlay: null,
		closeButton: null,
		boxClass: '',
		overlayClass: '',
		closeClass: 'lbClose',
		imgClass: '',
		contentClass: 'lbContentWrapper',
		frameClass: 'lbContentFrame',
		makeCloseButton: true,
		hideOverlay: false,
		autoRegisterEvents: true,
		pinTop: false,
		pinLeft: false,
		autopin: true
	},
	events: {
		onshow: null,
		onhide: null
	},
	init: function initLightBox(options) {
		options = options || {};
		this.setOptions(options);
		Activa.DOM.ready(function initialize() {
			this._isIE = window.ActiveXObject ? (window.XMLHttpRequest ? 7 : 6) : false;
			
			function isString(str) {
				return str && (typeof str == 'string' || (typeof str == 'object' && typeof str.valueOf() == 'string'));
			}
			
			var opts = this.options;
			var docbody = dbt('body')[0];
			
			//Get or create the main box
			if ( opts.box && isString(opts.box) ) {
				this.box = did(opts.box);
			} else if ( opts.box && typeof opts.box == 'object' ) {
				this.box = opts.box;
			}
			if ( !this.box ) {
				this.box = document.createElement('div');
				if ( opts.box ) {
					opts.box.id = opts.box;
				}
				docbody.insertBefore(this.box, Activa.DOM.first(docbody));
			}
			Activa.DOM.addClass(this.box, opts.boxClass);
			this.box.style.display = 'none';
			
			//Get or create the close button
			if ( opts.closeButton && isString(opts.closeButton) ) {
				this.closeButton = did(opts.closeButton);
			} else {
				this.closeButton = opts.closeButton;
			}
			if ( !this.closeButton && this.options.makeCloseButton ) {
				this.closeButton = document.createElement('a');
				if ( opts.closeButton ) {
					this.closeButton.id = opts.closeButton;
				}
				this.closeButton.innerHTML = 'Close X';
				this.box.appendChild(this.closeButton);
			}
			if ( this.closeButton ) {
				Activa.DOM.addClass(this.closeButton, this.options.closeClass);
			}
			
			//Get or create the overlay
			if ( opts.overlay && isString(opts.overlay) ) {
				this.overlay = did(opts.overlay);
			} else if ( opts.overlay && typeof opts.overlay == 'object' ) {
				this.overlay = opts.overlay;
			}
			if ( !this.overlay ) {
				this.overlay = document.createElement('div');
				if ( opts.overlay ) {
					this.overlay.id = opts.overlay;
				}
				docbody.insertBefore(this.overlay, this.box);
			}
			Activa.DOM.addClass(this.overlay, this.options.overlayClass);
			this.overlay.style.display = 'none';
			
			//Create the iframe shim to hide controls if we're using IE
			if ( this._isIE ) {
				this._shim = document.createElement('iframe');
				var overz = this.getStyle(this.overlay, 'z-index') - 1;
				this._shim.style.cssText = 'display: none;position: absolute;top: 0px;left: 0px;filter:progid:DXImageTransform.Microsoft.alpha(opacity=0);z-index:'+overz;
				this._shim.frameBorder = 0;
				this._shim.scrolling = 'no';
				this._shim.src = dbt('base')[0].href + 'blankpage';
				docbody.insertBefore(this._shim, this.overlay);
			}
			
			//Register necessary events
			Activa.registerEvent(window, 'resize', this._createCaller(this, this._resize));
			Activa.registerEvent(window, 'scroll', this._createCaller(this, this._resize));
			Activa.registerEvent(this.overlay, 'click', this._createCaller(this, this.hide));
			if ( this.closeButton ) {
				Activa.registerEvent(this.closeButton, 'click', this._createCaller(this, this.hide));
			}
			
			//Register lightbox custom events
			for ( var eName in this.events ) {
				if ( this.events.hasOwnProperty(eName) && typeof options[eName] == 'function' ) {
					this.events[eName] = options[eName];
					this.listen(eName, this.events[eName]);
				}
			}
			
			//Preload images.
			this.preloadImages();
			this.initialized = true;
		}, this);
	},
	setOptions: function setOptions(options) {
		options = options || {};
		for ( var k in this.options ) {
			if ( k in options ) {
				if ( /Class/i.test(k) ) {
					options[k] = String(options[k]).trim();
				}
				this.options[k] = options[k];
			}
		}
	},
	showHTML: function showHTML(html) {
		if ( !this.initialized ) {
			return false;
		}
		this._createWrapper(html);
		this.show();
		return this;
	},
	showDOM: function showDOM(node, clone) {
		if ( !this.initialized ) {
			return false;
		}
		this._createWrapper();
		clone = clone == null ? true : clone;
		if ( node && node.nodeType == 1 && !Activa.DOM.contains(this.content, node) ) {
			if ( clone ) {
				var nodeClone = node.cloneNode(true);
				this.content.appendChild(nodeClone);
				if ( nodeClone.style.display == 'none' ) {
					nodeClone.style.display = '';
				}
			} else {
				this.content.appendChild(node);
			}
		}
		this.show();
		this._resize();
		return this;
	},
	showIMG: function showIMG(src, width, height, alt) {
		if ( !this.initialized ) {
			return false;
		}
		this._createWrapper('<div>Loading Image...</div>');
		
		this.preloadImage(src, function displayImage(e) {
			var img = e.target;
			var attributes = {'width':(width || img.width), 'height':(height || img.height), 'alt':(alt || '')};
			for ( var atr in attributes ) {
				if ( attributes.hasOwnProperty(atr) ) {
					img.setAttribute(atr, attributes[atr]);
				}
			}
			this.content.innerHTML = '';
			this.showDOM(img, false);
		});
		return this;
	},
	showURL: function showURL(url) {
		if ( !this.initialized ){
			return false;
		}
		if ( this.content ){
			this.content.style.display = 'none';
		}
		if ( !this.frame ) {
			this.options.frameClass = this.options.frameClass || 'lbContentFrame';
			this.frame = document.createElement('iframe');
			this.frame.className = this.options.frameClass;
			this.frame.frameBorder = 0;
			this.box.appendChild(this.frame);
		}
		
		this.frame.src = url;
		this.frame.style.display = '';
		this.show();
		return this;
	},
	clear: function clear() {
		this.content.innerHTML = '';
		return this;
	},
	show: function show() {
		this._resize();
		this.box.style.display = '';
		if ( !this.options.hideOverlay ) {
			this.overlay.style.display = '';
		}
		if ( this._shim ) {
			this._shim.style.display = '';
		}
		if(!this._isShowing) {
			this._triggerEvent(this.box, 'focus', false, false);
		}
		this._isShowing = true;
		return this;
	},
	hide: function hide() {
		this.box.style.display = 'none';
		this.overlay.style.display = 'none';
		if ( this._shim ) {
			this._shim.style.display = 'none';
		}
		if ( this._isShowing ) {
			this._triggerEvent(this.box, 'blur', false, false);
		}
		this._isShowing = false;
		return this;
	},
	toggle: function toggle() {
		var fn = this._isShowing ? 'hide':'show';
		this[fn]();
	},
	pin: function pin() {
		var val = Activa.toArray(arguments).shift();
		switch(val) {
			case 'top':
				this.options.pinTop = true;
				break;
			case 'left':
				this.options.pinLeft = true;
				break;
			default:
				this.options.pinTop = true;
				this.options.pinLeft = true;
		}
	},
	unpin: function unpin() {
		var val = Activa.toArray(arguments).shift();
		switch(val) {
			case 'top':
				this.options.pinTop = false;
				break;
			case 'left':
				this.options.pinLeft = false;
				break;
			default:
				this.options.pinTop = false;
				this.options.pinLeft = false;
		}
	},
	preloadImages: function preloadImages(className) {
		className = (className ? String(className) : this.options.imgClass).trim();
		if(className == '') {
			return false;
		}
		var nodes = Activa.toArray(dbc(className));
		nodes.forEach(this.preloadImage, this);
	},
	preloadImage: function preloadImage(src, fn) {
		var node;
		var isobj = (typeof ((this._isIE) ? src : src.valueOf()) == 'object');
		if ( isobj && src.nodeType == 1 ) {
			node = src;
			var tag = node.tagName.toLowerCase();
			if ( tag == 'a' && node.href != '' ) {
				src = node.href;
			} else if ( tag == 'img' && node.src != '' ) {
				src = node.src;
			}
		}
		src = (src ? String(src) : '').trim();
		var fname = src.replace(/^(?:https?:\/\/|(\/))?(.*)$/i, '$1$2');
		if(!src || !fname) {
			return null;
		}
		
		if ( !this._images[fname] ) {
			this._images[fname] = new Image();
			if ( fn && typeof fn == 'function' ) {
				var self = this;
				Activa.registerEvent(this._images[fname], 'load', function imgLoad(e){
					if ( !e.target ) {
						e = {target: this._images[fname]};
					}
					Activa.unregisterEvent(e.target, 'load', arguments.callee);
					fn.call(this, e);
				}.bind(this));
			}
			this._images[fname].src = src;
		} else if ( typeof fn == 'function' ) {
			fn.call(this, {target: this._images[fname]});
		}
		if ( this.options.autoRegisterEvents && node ) {
			Activa.registerEvent(node, 'click', this._createCaller(this, this.showIMG, true, fname, '', '', ''));
		}
		return (fname in this._images) ? this._images[fname] : null;
	},
	_createWrapper: function _createWrapper(html) {
		if ( !this.initialized ){
			return false;
		}
		if ( this.frame ){ this.frame.style.display = 'none'; }
		if ( !this.content ) {
			this.options.contentClass = this.options.contentClass || 'lbContentWrapper';
			this.content = document.createElement('div');
			this.content.className = this.options.contentClass;
			this.box.appendChild(this.content);
		}
		html = html || '';
		if ( html != '' ) {
			this.content.innerHTML = String(html);
		}
		this.content.style.display = '';
	},
	_resize: function _resize() {
		var max = Activa.Dimensions.getMaxDocSize();
		if ( !this.options.hideOverlay ) {
			['overlay','shim'].forEach(function(item){
				if(this[item]) {
					this[item].style.width = max['width']+'px';
					this[item].style.height = max['height']+'px';
				}
			}, this);
		}
		
		var docsize = Activa.Dimensions.getDocSize(), scroll = Activa.Dimensions.getScrollXY();
		if ( !this._isShowing ) {
			this.box.style.visibility = 'hidden';
			this.box.style.display = '';
		}
		//var boxWidth = this._isIE ? this.box.offsetWidth : getStyle(this.box,'width')
		//var boxHeight = this._isIE ? this.box.offsetHeight : getStyle(this.box,'height');
		var boxWidth = this.box.offsetWidth;
		var boxHeight = this.box.offsetHeight;
		if ( !this._isShowing ) {
			this.box.style.display = 'none';
			this.box.style.visibility = 'visible';
		}
		boxWidth = isNaN(boxWidth) ? 0 : boxWidth;
		boxHeight = isNaN(boxHeight) ? 0 : boxHeight;
		var topleft = {
			'x':Math.round((docsize['width']-boxWidth)/2)+scroll['x'],
			'y':Math.round((docsize['height']-boxHeight)/2)+scroll['y']
		};
		if ( boxWidth > docsize['width'] ) {
			topleft.x = 0;
		}
		var pintop = this.options.autopin ? (boxHeight > docsize['height']) : false;
		var pinleft = this.options.autopin ? (boxWidth > docsize['width']) : false;
		this._setPosition(topleft.x, topleft.y, pinleft, pintop);
		return this;		 
	},
	_setPosition: function _setPosition(x, y, pinx, piny) {
		var pin = false;
		if ( this.options.autopin && !(pinx && piny) ) {
			pin = (pinx ? 'top' : (piny ? 'left' : false));
			this.unpin(pin);
		}
		x = (x || 0) < 0 ? 0 : x;
		y = (y || 0) < 0 ? 0 : y; 
		if ( !this.options.pinLeft && x != 'auto' ) {
			this.box.style.left = parseInt(x)+'px';
		}
		if ( !this.options.pinTop && y != 'auto' ) {
			this.box.style.top = parseInt(y)+'px';
		}
		if ( this.options.autopin && (pinx || piny) ) {
			pin = (pinx && piny) ? true : (pinx ? 'left' : (piny ? 'top': false));
			this.pin(pin);
		}
		if ( this.options.hideOverlay && this._shim ) {
			this._shim.style.position = 'absolute';
			['width','height','left','top'].forEach(function(prop) {
				var value = this.box.style[prop];
				if ( !value ) {
					value = this.getStyle(this.box, prop);
				}
				this._shim.style[prop] = value;
			}, this);
		}
	},
	getStyle: function getStyle(obj, prop) {
		obj = (typeof obj == 'object') ? obj : did(obj);
		if ( !obj || !prop ) {
			return null;
		}
		var compVal = obj.currentStyle ? obj.currentStyle[prop] : window.getComputedStyle(obj,null).getPropertyValue(prop);
		return isNaN(parseFloat(compVal)) ? compVal : parseFloat(compVal);
	},
	listen: function listen(event, fn) {
		if ( typeof fn != 'function' ) {
			return false;
		}
		var eventWrapper = (function eventWrapper(func, stopDefault, stopBubble) {
			return (function wrapping(e) {
				if(stopDefault) {
					e.preventDefault();
				}
				if(stopBubble) {
					e.stopPropagation();
				}
				func.call(this, e);
			}).bind(this);
		}).bind(this);
		switch(event) {
			case 'onshow':
				Activa.registerEvent(this.box, 'focus', eventWrapper(fn, true, true));
				break;
			case 'onhide':
				Activa.registerEvent(this.box, 'blur', eventWrapper(fn, true, true));
				break;
			default:
				break;
		}
	},
	_createCaller: function _createCaller(obj, fn, stopDefault){
		var args = Activa.toArray(arguments, 3);
		return function callMethod(e) {
			if ( stopDefault ) {
				e.preventDefault();
			}
			fn.apply(obj, args.concat([e]));
		}
	},
	_triggerEvent: function _triggerEvent(el, type, bubbles, cancelable) {
		try {
			el = Activa.DOM.check(el);
			bubbles = bubbles || true;
			cancelable = cancelable || true;
			if ( document.createEvent  ) {
				var groups = {
					UIEvents: ['focusin', 'focusout', 'activate', 'deactivate'],
					MouseEvents: ['click', 'dblclick', 'mousedown', 'mouseup', 'mouseover', 'mouseout', 'mousemove'],
					HTMLEvents: ['load', 'unload', 'abort', 'error', 'select', 'change', 'submit', 'reset', 'focus', 'blur', 'resize', 'scroll']
				};
				var groupName = null;
				for ( var group in groups ) {
					if ( groups[group].indexOf(type) != -1 ) {
						groupName = group;
						break;
					}
				}
				if ( groupName ) {
					var event = document.createEvent(groupName);
					event.initEvent(type, bubbles, cancelable);
					//safari 3 doesn't have window.dispatchEvent()
					(el == window && !el.dispatchEvent ? document : el).dispatchEvent(event);
					return true;
				}
			} else if ( document.createEventObject ) {
				var event = document.createEventObject();
				// IE6,IE7 thinks window==document and doesn't have window.fireEvent()
				// IE6,IE7 cannot properly call document.fireEvent()
				(el == document ? document.documentElement : el).fireEvent("on"+type, event);
				return true;
			}
		} catch ( e ) {
		}
		return false;
	}
});


var Rule_Forum_WYSI = new Activa.Class({
	Extends: Rule_Text,
	validate: function validateWYSIText() {
		wysiText1.convertToBBCode();
		return this.root();
	}
});



