
function Class(x,proto)
{
	if(typeof x == 'string')
		x = Package(x);
	
	for(var key in proto)
		x.prototype[key] = proto[key];
		
	return x;
}

//* wrap decl in eval to hide from YUI compressor - it doesn't like new.apply*/
Class.genericBase = function()
{
	var constructor = function() {
		eval ('this.new.apply(this,arguments);');
	}
	
	//constructor.extend = function() { return function(pc,proto){ return extend(constructor,pc,proto) } }
	return constructor;
};

function Package(x,pkg_methods)
{
	if(!x) return window;
	var m = x.split(/\./);
	var buff = [];
	for(var i=0;i<m.length;i++)
	{
		buff.push(m[i]);
		var ns = buff.join('.');
		if(!eval('window.'+ns))
			eval('window.'+ns+' = Class.genericBase()');
	}
	
	var pkg = eval('window.'+x);
	pkg.__PACKAGE__ = x;
	pkg.prototype.__PACKAGE__ = x;
	
	if(!pkg.__ISA__)
		pkg.__ISA__ = [ x ];
	if(!pkg.prototype.__ISA__)
		pkg.prototype.__ISA__ = [ x ];
	
	if(pkg_methods)
		for(var key in pkg_methods)
			pkg[key] = pkg_methods[key];
	
	return pkg;
}


Class.override = function(origclass, overrides)
{ 
	if(overrides)
	{
		var p = origclass.prototype;
		for(var method in overrides)
		{
			p[method] = overrides[method];
		}
	}
};

Class.apply = function(o, c, defaults)
{
    if(defaults){
        // no "this" reference for friendly out of scope calls
        Class.apply(o, defaults);
    }
    if(o && c && typeof c == 'object'){
        for(var p in c){
            o[p] = c[p];
        }
    }
    return o;
};

Class.applyIf = function(o, c)
{
	if(o && c)
	{
		for(var p in c){
			if(typeof o[p] == "undefined"){ o[p] = c[p]; }
		}
	}
	return o;
},

Class.extend = function()
{
    // inline overrides
	var io = function(o)
	{
for(var m in o){
		    this[m] = o[m];
		}
	};
	var oc = Object.prototype.constructor;

	return function(sb, sp, overrides)
	{
		if(typeof sp == 'object')
		{
			overrides = sp;
			sp = sb;
			sb = overrides.constructor != oc ? overrides.constructor : function(){sp.apply(this, arguments);};
		}
		var F = function(){}, sbp, spp = sp.prototype;
		F.prototype = spp;
		sbp = sb.prototype = new F();
		sbp.constructor=sb;
		sb.superclass=spp;
		if(spp.constructor == oc){
			spp.constructor=sp;
		}
		sb.override = function(o){
			Class.override(sb, o);
		};
		sbp.override = io;
		Class.override(sb, overrides);
		sb.extend = function(o){extend(sb, o);};
		return sb;
	};
}();


function ClassExtends(className,superclass,proto)
{
	Class(className);
	eval('window.'+className+' = Class.extend(window.'+className+',superclass,proto)');
	eval('window.'+className+'.prototype.superclass = {}'); 
	for(var key in superclass.prototype) {
		eval('window.'+className+'.prototype.superclass.'+key+' = function(){ superclass.prototype.'+key+'.apply(this,arguments) }');
	}
}

Class('Class.Observable',
{
	on : function(type, listener, scope) { this.registerEventListener(type,listener,scope) },
	
	registerEventListener	: function(type, listener, scope)
	{
		if(!this._event_listeners)
			this._event_listeners = [];
		if(!this._event_listeners[type])
			this._event_listeners[type]=[];
		this._event_listeners[type].push({handler:listener,scope:scope});

	},

	removeEventListener	: function(type, listener)
	{
		if(!this._event_listeners)
			this._event_listeners = [];
		var list = this._event_listeners[type];
		if(!list) return;
		for(var i=0;i<list.length;i++)
			if(list[i].handler == listener)
				return list.splice(i, 1);
		return false;
	},
	
	fire : function(type,options) { return this.fireEvent(type,options) },

	fireEvent	: function(type,options)
	{
		if(type!='*')
			this.fireEvent('*',{originalEventName:type,originalEventOptions:options});
		
		if(!this._event_listeners)
			this._event_listeners = [];
		var list = this._event_listeners[type];
		if(!list) return false;
		var i,l = list.length,f,retval;
		for(i=0;i<l;i++)
		{
			f = list[i];
			if(typeof(f.handler) == 'function')
			{
				//debug('found function '+f);
				if(list[i].scope) {
					retval = list[i].handler.apply(list[i].scope,[options]);
				}
				else {
					retval = list[i].handler.apply(this,[options]);
				}
				
				//retval = list[i](options);
				if(retval && retval.stopPropogation)
					return true;
			}
		}

		return true;
	}
});


Class.apply(Function.prototype,
{
/**
     * Creates a delegate (callback) that sets the scope to obj.
     * Call directly on any function. Example: <code>this.myFunction.createDelegate(this, [arg1, arg2])</code>
     * Will create a function that is automatically scoped to obj so that the <tt>this</tt> variable inside the
     * callback points to obj. Example usage:
     * <pre><code>
var sayHi = function(name){
    // Note this use of "this.text" here.  This function expects to
    // execute within a scope that contains a text property.  In this
    // example, the "this" variable is pointing to the btn object that
    // was passed in createDelegate below.
    alert('Hi, ' + name + '. You clicked the "' + this.text + '" button.');
}

var btn = new Ext.Button({
    text: 'Say Hi',
    renderTo: Ext.getBody()
});

// This callback will execute in the scope of the
// button instance. Clicking the button alerts
// "Hi, Fred. You clicked the "Say Hi" button."
btn.on('click', sayHi.createDelegate(btn, ['Fred']));
</code></pre>
     * @param {Object} obj (optional) The object for which the scope is set
     * @param {Array} args (optional) Overrides arguments for the call. (Defaults to the arguments passed by the caller)
     * @param {Boolean/Number} appendArgs (optional) if True args are appended to call args instead of overriding,
     *                                             if a number the args are inserted at the specified position
     * @return {Function} The new function
     */
    createDelegate : function(obj, args, appendArgs){
        var method = this;
        return function() {
            var callArgs = args || arguments;
            if(appendArgs === true){
                callArgs = Array.prototype.slice.call(arguments, 0);
                callArgs = callArgs.concat(args);
            }else if(typeof appendArgs == "number"){
                callArgs = Array.prototype.slice.call(arguments, 0); // copy arguments first
                var applyArgs = [appendArgs, 0].concat(args); // create method call params
                Array.prototype.splice.apply(callArgs, applyArgs); // splice them in
            }
            return method.apply(obj || window, callArgs);
        };
    },


    /**
     * Calls this function after the number of millseconds specified, optionally in a specific scope. Example usage:
     * <pre><code>
var sayHi = function(name){
    alert('Hi, ' + name);
}

// executes immediately:
sayHi('Fred');

// executes after 2 seconds:
sayHi.defer(2000, this, ['Fred']);

// this syntax is sometimes useful for deferring
// execution of an anonymous function:
(function(){
    alert('Anonymous');
}).defer(100);
</code></pre>
     * @param {Number} millis The number of milliseconds for the setTimeout call (if 0 the function is executed immediately)
     * @param {Object} obj (optional) The object for which the scope is set
     * @param {Array} args (optional) Overrides arguments for the call. (Defaults to the arguments passed by the caller)
     * @param {Boolean/Number} appendArgs (optional) if True args are appended to call args instead of overriding,
     *                                             if a number the args are inserted at the specified position
     * @return {Number} The timeout id that can be used with clearTimeout
     */
	    defer : function(millis, obj, args, appendArgs)
	    {
		var fn = this.createDelegate(obj, args, appendArgs);
		if(millis)
		{
			return setTimeout(fn, millis);
		}
		fn();
		return 0;
	    }

});


function $x(e,doc) {if(!doc) { doc=($x._default_document)?$x._default_document:doc=document;} return doc.getElementById(e)}
function $tags(e,elm) {if(e&&elm && typeof(elm)=='string'){var t=elm;elm=e;e=t;}if(!elm) elm=document;return elm.getElementsByTagName(e)}

function debug(v) 
{
	if(debug.quiet) return;
	if($('debug')==undefined)
		document.body.innerHTML+=debug.html;
	
	if(debug.add_count)
		v = rpad(''+(debug.count_start++),3) + ': '+ v;
	
	if(debug.reverse_mode)
		$('debug').value = v + "\n" + $('debug').value;
	else
		$('debug').value += v + "\n";
}

debug.reverse_mode = true;
debug.add_count    = true;
debug.count_start  = 0;
debug.html         = "Debug:<br><textarea id=debug rows=10 cols=90 style='width:99.25%; height:10%'></textarea>";

window.global = {}
global.errors = 
{
	no_xhttp	: function() 
	{ 
		if(global.errors.scope.error('no_xhttp')) return;
		alert("Your browser does not support XMLHTTP, therefore you cannot store data on our server.") 
	},
	cant_send_xhttp	: function(exception) 
	{ 
		if(global.errors.scope.error('cant_send_xhttp')) return;
		alert('Cannot send XMLHTTP request at this time: \n'+exception.message); 
	},
	xhttp_response_error : function(request) 
	{ 
		if(global.errors.scope.error('xhttp_response_error')) return;
		//alert((request.status?request.status:'Critical Error')+": Error on server.\nText:\n"+request.responseText); 
	},
	scope 		: 
	{
		levels	:
		{
			current	: 0,
			options	: []
		},
		
		start 	: function(opts)
		{
			global.errors.scope.levels.current ++;
			global.errors.scope.levels.options[global.errors.scope.levels.current] = opts;
		},
		
		end	: function(opts)
		{
			delete global.errors.scope.levels.options[global.errors.scope.levels.current];
			global.errors.scope.levels.current --;
			if(global.errors.scope.levels.current < 0)
				global.errors.scope.levels.current = 0;
		},
		
		error	: function(name)
		{
			var s = global.errors.scope.levels.options[global.errors.scope.levels.current];
			if(	s != undefined &&
			   	typeof(s) == 'object' &&
				typeof(s.onerror) == 'function')
				return s.onerror(name);
			return false;
		}
	}
	

	
};

function xhttp() 
{
	var req = undefined;
	if (window.XMLHttpRequest) 
		req = new XMLHttpRequest();
	else if (window.ActiveXObject) 
		req = new ActiveXObject("Microsoft.XMLHTTP");
	return req;
}

xhttp.stamp = function() { return '&js_date='+(new Date()).getTime() };


function iframe(aID,src)
{
	if(!src) src=document;
	var doc = null; 
	
	// if contentDocument exists, W3C compliant (Mozilla) 
	if ($(aID,src).contentDocument)
	{
		doc = $(aID,src).contentDocument; 
	}
	else 
	{
		// IE 
		doc = src.frames[aID].document; 
	}
	return doc;
}

function o(a,b)
{
	var c=0;
	while(a!=null)
	{
		c+=a["offset"+(b?"Left":"Top")];
		a=a.offsetParent;
	}
	return c;
}

// get_style from http://www.quirksmode.org/dom/getstyles.html
function get_style(el,styleProp)
{
        var x = el; //document.getElementById(el);
        if (x.currentStyle)
                var y = x.currentStyle[styleProp];
        else if (window.getComputedStyle)
                var y = document.defaultView.getComputedStyle(x,null).getPropertyValue(styleProp);
        return y;
}

function xhttp_req(url,cb)
{
	var req = xhttp();
	if(req)
	{
		req.onreadystatechange = function()
		{
			//try
			{
				if(req.readyState == 4)
					if(req.status != 200)
						global.errors.xhttp_response_error(req);
					else cb(req.responseText,req);
			}
			/*
			catch(e)
			{
				if($('debug')) 
					debug('Error from AJAX Request: '+e.message+'\ne:'+e);
			}
			*/
		};
		
		try
		{
			req.open("GET",url+xhttp.stamp(), true);
			req.send(null);	
		}
		catch(e)
		{
			global.errors.cant_send_xhttp(e);
		}
	}
	else
	{
		global.errors.no_xhttp();
	}
	return req;
}

function Replacer(t)
{
	var r = 
	{
		txt 	: t,
		clear 	: function()
		{
			if(this.vars) 
				delete this.vars;
			this.vars={};
		},
		add	: function(key,val)
		{
			this.vars[key] = val;
		},
		output	: function()
		{
			var tmp = this.txt;
			for(var key in this.vars)
				tmp = tmp.replace(typeof(key)=='string'?new RegExp(key,'gi'):key,this.vars[key]);
			return tmp;	
		},
		changeSource	: function(t)
		{
			 this.txt = t;
		}
	};
	
	r.clear();
	
	return r;
}

function defang_tags(val)
{
	if(val=='' && val==undefined)
		return '';
	val = String(val);
	val = val.replace(new RegExp('<','gi'),'&lt;');
	val = val.replace(new RegExp('>','gi'),'&gt;');
	val = val.replace(/\\(['"()])/gi,'$1');
	return val;
}

function show_status(txt,timer,flag)
{
	if(!$('status_div'))
		document.body.innerHTML+=show_status.html;
	document.body.style.cursor='progress';
	
	$('status_div').style.display='';
	$('status_text').innerHTML=txt;
	
	if(!flag && flag!=undefined)
	{
		$('status_spinner').style.display='none';
		$('status_div').style.top = '5px';
	}
	else
	{
		$('status_spinner').style.display='';
		$('status_div').style.top = '0px';
	}
		
	
	show_status.last_status = window.status;
	txt = txt.replace(/\&nbsp;/ig,' ');
	window.status=stripTags(txt);
	
	if(timer)
	{
		clearTimeout(global.status_timer_tid);
		global.status_timer_tid = setTimeout(hide_status,timer);
	}
}

function hide_status()
{
	$('status_div').style.display='none';
	document.body.style.cursor='default';
	window.status = show_status.last_status?show_status.last_status:'';
}

/*filter:alpha(opacity=80);*/
show_status.html = "<div style='display:none;-moz-opacity:.80;background:white;border:1px solid black;padding:1px;position:absolute;top:0px;left:0px;font-family:sans-serif;font-size:12px;z-index:500;' id='status_div'>"+
//	 IE doesn't honor abscenter on the image if we dont have it in a table, in which case its moot anyway -->
"	<table border=0 cellspacing=0 cellpadding=0>"+
"	<tr>"+
"		<td>"+
"			<img src='http://corp.productiveconcepts.com/images/rc/images/display/loading.gif' align=abscenter style='margin-right:1px' id='status_spinner'>"+
"		</td>"+
"		<td>"+
"			 <span id='status_text'>&nbsp;</span>"+
"		</td>"+
"	</tr>"+
"	</table>"+
"</div>";

// my own creation
// function dump_obj(obj,levels,show_func_flag)
// {	
// 	if(!obj && this) obj = this;
// 	if(!levels) levels=1;
// 	if(show_func_flag) show_func_flag=false;
// 	var tabs='';
// 	
// 	var t = dump_obj.ie_flag?"&nbsp;&nbsp;&nbsp;&nbsp;":"\t";
// 	var n = dump_obj.ie_flag?"<br>":"\n";
// 	for(var i=0;i<levels;i++)
// 		tabs+=t;
// 	
// 	var tv;
// 	var str = tabs+"{"+n;
// 	for(vars in obj)
// 	{
// 		tv = typeof(obj[vars]);
// 		str+=(tv=='function' && !show_func_flag) ? '' : tabs+t+vars+":"+t+
// 			(tv=='object'?
// 				(dump_obj.recurse_allowed||vars=='attributes'||vars=='0'?"\n"+dump_obj(obj[vars],levels+1,show_func_flag):'[object]'):
// 				(tv=='function'?
// 					'"(not stringifying function:\''+vars+'\')"':
// 					(tv=='number'?obj[vars]:"'"+obj[vars]+"'")
// 				)+","
// 			)+n;
// 		//dump_obj(obj[vars],levels+1)
// 	}
// 	str+=tabs+"}"+(levels==1?"":",")+n;
// 	return str;
// 	
// }
// dump_obj.recurse_allowed = false;
// dump_obj.ie_flag = false; //document.all?true:false;
// Object.prototype.toString=dump_obj;

// translated from some perl re cookbook
function commify(what)
{
        var tmp = ''+what;
	while(commify.rx.test.test(tmp))
		tmp = tmp.replace(commify.rx.replace,'$1,$2');
	return tmp;
}

commify.rx = 
{
	test : /^[-+]?\d+\d{3}/,
	replace : /^([-+]?\d+)(\d{3})/g
};

function rpad(what,len,chr)
{
        if(len==undefined) len = 2;
        if(chr==undefined) chr = '0';
	while(what.length<len)
        	what=chr+what; 
        return what;
}

function pa(a)
{
	var c=
	{
		x:get_style(a,'left'),
		y:get_style(a,'top')
	};
	if(c.x=='auto')
		c.x=undefined;
	if(c.y=='auto')
		c.y=undefined;
	return c;
}

function pr(a)
{
	var c={x:0,y:0};
	while(a!=null)
	{
		c.x+=a.offsetLeft;
		c.y+=a.offsetTop;
		a=a.offsetParent;
	}
	return c;
}

// from webfx.eae.net
function getInnerText(el) 
{
	if(el == undefined) return undefined;
	if (document.all) { return el.innerText; }
	var str = '';
	var cs = el.childNodes;
	var l = cs.length,t,i;
	for (i = 0; i < l; i++) 
	{
		t=cs[i].nodeType;
		if(t==1) //ELEMENT_NODE
			str += getInnerText(cs[i]);
		else if(t==3) //TEXT_NODE
			str += cs[i].nodeValue;
	}	
	return str;
}

function rect(a,flag)
{
	var r = flag?pr(a):pa(a);
	r.right = a.offsetWidth + r.x;
	r.bottom = a.offsetHeight + r.y;
	r.w = a.offsetWidth;
	r.h = a.offsetHeight;
	return r;
}

function _event(e)
{
	if (!e) var e = window.event;
	
	// The following 'hacks' are by 
	// Peter-Paul Koch from www.quirksmode.org
	
	// ***** Mouse Position
	var posx = 0;
	var posy = 0;
	if (e.pageX || e.pageY)
	{
		posx = e.pageX;
		posy = e.pageY;
	}
	else if (e.clientX || e.clientY)
	{
		posx = e.clientX + document.body.scrollLeft;
		posy = e.clientY + document.body.scrollTop;
	}
	e.mouse = {x:posx,y:posy};
	
	// ***** Mouse Button
	var rightclick;
	if (e.which) rightclick = (e.which == 3);
	else if (e.button) rightclick = (e.button == 2);
	e.mouse.button = { right:rightclick, left:!rightclick };
	
	
	// ***** Target of the event
	var targ;
	if (e.target) targ = e.target;
	else if (e.srcElement) targ = e.srcElement;
	if (targ.nodeType == 3) // defeat Safari bug
		targ = targ.parentNode;
	e.targ = targ;
	
	// ***** key code
	var code;
	if (e.keyCode) code = e.keyCode;
	else if (e.which) code = e.which;
	e.key = { 'code': code, str: String.fromCharCode(code) };
	
	return e;
}

function _debug(s,t){clearTimeout(_debug.tid);_debug.tid=setTimeout(function(){debug(s)},t||100)}

// from google ig.js
function update(el){el.parentNode.style.display="none";el.parentNode.style.display=""}


function clone(obj,deep)
{
	var oc=new obj.constructor();
	for(var property in obj) 
		if(!deep) 
			oc[property]=obj[property];
		else 
		if(typeof obj[property]=='object') 
			oc[property]=clone(obj[property],deep);
		else 
			oc[property]=obj[property];
		return oc;
}

Array.prototype.removeAt = function (iIndex) 
{
	return this.splice(iIndex, 1);
};

String.prototype.repeat = function(times)
{
	times = parseInt(times);
	if(isNaN(times) || !times)
		times = 0;
	var atemp = new Array(times + 1);
	return atemp.join(this);
} 

/**
 *   Array convenience method to remove element.
 *
 *   @param object element
 *   @returns boolean
 */
if(typeof([].remove) != 'function')
{
/*
	Array.prototype.remove = function (element)
	{
		var result = false;
		var array = [];
		for (var i = 0; i < this.length; i++)
		{
			if (this[i] == element)
			{
				result = true;
			} else {
				array.push(this[i]);
			}
		}
		this.length = 0;
		for (var i = 0; i < array.length; i++)
		{
			this.push(array[i]);
		}
		array = null;
		return result;
	};
*/
	Array.prototype.remove = function (element) 
	{
		var result = 0;
		for (var i = 0; i < this.length; ) 
		{
			if (this[i] == element) 
			{
				this.splice(i, 1);
				++result;
			} 
			else 
			{
				i++
			}
		}
		return result;
	}; 
	

}


/**
 *   Array convenience method to clear membership.
 *
 *   @param object element
 *   @returns void
 */
 /*
Array.prototype.clear = function () {
    this.length = 0;
};
*/

/**
 *   Array convenience method to check for membership.
 *
 *   @param object element
 *   @returns boolean
 */


if(typeof([].contains) != 'function')
{
	Array.prototype.contains = function (element)
	{
		for (var i = 0; i < this.length; i++)
			if (this[i] == element)
				return true;
		return false;
	};
}




var _eventUtils =
{
	// hash_of_arrays
	//_event_listeners : {},

	registerEventListener	: function(type, listener)
	{
		if(!this._event_listeners[type])
			this._event_listeners[type]=[];
		this._event_listeners[type].push(listener);

	},

	removeEventListener	: function(type, listener)
	{
		var list = this._event_listeners[type];
		if(!list) return;
		for(var i=0;i<list.length;i++)
			if(list[i] == listener)
				return list.splice(i, 1);
		return false;
	},

	fireEvent	: function(type,options)
	{
		if(type!='*')
			this.fireEvent('*',{originalEventName:type,originalEventOptions:options});
		
		var list = this._event_listeners[type];
		if(!list) return false;
		var i,l = list.length,f,retval;
		for(i=0;i<l;i++)
		{
			f = list[i];
			if(typeof(f) == 'function')
			{
				//debug('found function '+f);
				retval = list[i](options);
				if(retval && retval.stopPropogation)
					return true;
			}
		}

		return true;
	}
};

function applyEventUtils(obj)
{
	for(vars in _eventUtils)
	{
		obj[vars] = _eventUtils[vars];
	}
	obj._event_listeners = {};
}






function addEvent(elm, evType, fn, useCapture)
// addEvent and removeEvent
// cross-browser event handling for IE5+,  NS6 and Mozilla
// By Scott Andrew
{
	if (elm.addEventListener){
		elm.addEventListener(evType, fn, useCapture);
		return true;
	} else if (elm.attachEvent){
		var r = elm.attachEvent("on"+evType, fn);
		return r;
	} else {
		alert("Handler could not be removed");
	}
} 

// following three functions from protytpe.js
function stripTags(string) 
{
	return string.replace(/<\/?[^>]+>/gi, '');
}

function escapeHTML(string) 
{
	var div = document.createElement('div');
	var text = document.createTextNode(string);
	div.appendChild(text);
	return div.innerHTML;
}

function unescapeHTML(string) 
{
	var div = document.createElement('div');
	div.innerHTML = stripTags(string);
	return div.childNodes[0].nodeValue;
}

global.onload_queue = [];
function _do_onload_queue()
{
	var q=global.onload_queue;
	if(!q.length) 
		return;
	var i=0,l=q.length;
	for(;i<l;i++)
	{
		if(typeof(q[i])=='function')
			q[i]();
	}
}

global.onload = function(ref)
{
	if(typeof(ref)=='function')
		global.onload_queue.push(ref);
}

window.onload = _do_onload_queue;

// try      { global.init=1 }
// catch(e) { global = {init:1} };
//window.global = {init:1};

function encode(x)
	{
		return encodeURI(x)
			.replace(/#/g,'%23')
			.replace(/&/g,'%26')
			.replace(/@/g,'%40')
			.replace(/;/g,'%3b');
	}

function stopPropogation(e)
{
	if (!e) var e = window.event;
	e.cancelBubble = true;
	if (e.stopPropagation) e.stopPropagation();
}
