// Framework Version

var $Version = "8.070608.0";

if (typeof($Debug)=="undefined")
{
	window.$Debug = 
	{
		enabled : false,
		trace : function() {}
	}
}

function registerNamespace() {
	var iArgs = arguments.length;
	for (var a=0;a<iArgs;a++)
	{
		var rootObject = this, namespaceParts = arguments[a].split('.'), iCount = namespaceParts.length;
		for (var i = 0; i < iCount; i++) {
	
			var currentPart = namespaceParts[i];
			if (!rootObject[currentPart]) {
				rootObject[currentPart] = function() {};
			}
			rootObject = rootObject[currentPart];
		}
	}
	return rootObject;
}


Function.prototype.registerNamespace = registerNamespace;

registerNamespace("$Browser");

// If  Compatibility library not installed, then we must be running IE
if (!$Browser.isMozilla)
{
	$Browser.isSafari = $Browser.isOpera = $Browser.isMozilla = function() {return false};
	$Browser.Button = {LEFT:1,RIGHT:2,MIDDLE:4};

	$Browser._isIE = true;
	var _ce = document.createElement, _ge = document.getElementById, _get = document.getElementsByTagName;
	try
	{
		document.execCommand("BackgroundImageCache",false,true);
	}
	catch(ex)
	{
	}
	
	var w = window;
	
	if (!w.XMLHttpRequest) {
		w.XMLHttpRequest = function() {
			var progIDs = [ 'Msxml2.XMLHTTP', 'Microsoft.XMLHTTP' ];
			for (var i = 0; i < progIDs.length; i++) {
				try {
					var xmlHttp = new ActiveXObject(progIDs[i]);
					return xmlHttp;
				}
				catch (ex) {
				}
			}
		    
			return null;
		}
	};

	if (!w.DOMParser) {
		w.DOMParser = function()
		{
				return new function()
				{
					this.parseFromString = function(xml,mimetype)
					{
							var xmlDocument;
							try 
							{
								xmlDocument = new ActiveXObject("Microsoft.XMLDOM");
								xmlDocument.async=false;
								xmlDocument.loadXML(xml);
							}

							catch (ex)
							{
								xmlDocument = null;
							}
					return xmlDocument;
					}
				}                 
		}
	};
}
else
	$Browser._isIE = false;

$Browser.isIE = function()
{
	return ($Browser._isIE);
}

var _dh = document.head;

if (!_dh) {_dh = document.head = _get("head")[0];}

// TODO: Change this to look for index of following semi-colon
var _appVersion = window.navigator.appVersion;
$Browser.version = ($Browser._isIE ? parseFloat(_appVersion.substr(_appVersion.indexOf("MSIE") + 5, 3)) : parseFloat(_appVersion.appVersion));


new function()
{
	var F = window.Function,f = F.prototype;
	
	F.abstractMethod = function() {
		throw new Error('Requires implementation');
	}

	F.createCallback = function(method, context) {
	    return function() {
			if (context)
				return method.apply(this,context);
			else
				return method.apply(this);
		}
	}


	F.createDelegate = function(instance,method) {
	    return function(args) {
			if (args)	    
				return method.apply(instance,arguments);
			else
				return method.apply(instance);
		}
	}

	
	F.emptyFunction = F.emptyMethod = function() {};
	F._htClasses = {};
	F.parse = function $F$parse(functionName){
		var fn = F._htClasses[functionName];
		
		if (!fn && functionName) {
			try {
				var i=0,strSplit = functionName.split("."), fn = window, iCount = strSplit.length;
				for (i;i<iCount;i++)
				{
					fn = fn[strSplit[i]];
					if (!fn) 
					{
						fn = null;
						break;
					}
				}
				
				if (typeof(fn) != 'function') {
					fn = null;
				}
				else {
					F._htClasses[functionName] = fn;
				}
			}
			catch (ex) {
				fn = null;
			}
		}

		return fn;
	}


	F.eventHelper = function(p_varCancel,p_boolBubble)
	{
		function Exec()
		{
			if (Object.isBoolean(p_boolBubble))
				event.cancelBubble = p_boolBubble;
	        
			if (p_varCancel!=null)
			{    
				event.returnValue = p_varCancel;
				if (Object.isBoolean(p_varCancel))
				{
					return p_varCancel;        // Only return value if boolean is set
				}
			}
		}
		return Exec;
	}

	F.KillEvent = Function.eventHelper(false,true);
	F.CancelBubble = Function.eventHelper(null,true);
	F.CancelDefault = Function.eventHelper(false);


	f.getBaseMethod = function $Fp$getBaseMethod(instance, methodName) {
		var baseType = this.base || (Function.parse(this.__baseType));
		if (baseType) {
			var directBaseType = baseType, _ibm = instance._baseMethods;
			if (_ibm) {
				// Retrieves the named method from the instance vtable implemented
				// by a base type.
				while (baseType) {
					var method = _ibm[baseType.__typeName + '.' + methodName];
					if (method) return method;
					baseType = baseType.base || Function.parse(baseType.__baseType);
				}
			}

			return directBaseType.prototype[methodName];
		}
	    
		return null;	
	}
	
	f.registerBaseMethod = function(instance, methodName) {
		// Stores the method with a type-qualified name into an instance vtable,
		// so it can be retrieved by a derived class.
	    if (!instance._baseMethods) {
			instance._baseMethods = { };
		}
		instance._baseMethods[this.__typeName + '.' + methodName] = instance[methodName];
	}

	f._processBaseType  = function $Fp$processBaseType(p_objRoot) {
		if (p_objRoot.__basePrototypePending && this.__baseType) {
			// inherit from all the types
			var fncType = this.base || (this.__baseType instanceof Function ? this.__baseType : F.parse(this.__baseType));
			// Perf optimization - allows us to quickly scan inheritance and dependency chain
			if (!fncType._parentBase)
				fncType._parentBase = [p_objRoot.__typeName];
			else
				fncType._parentBase.push(p_objRoot.__typeName);
			if (!p_objRoot._childBase) 
			{
				p_objRoot._childBase= [fncType.__typeName];
			}
			else
				p_objRoot._childBase.push(fncType.__typeName);
			if (fncType && (this != fncType) && (!fncType.inheritsFrom(this)) && !fncType._sealed) {
				this.base = fncType;
				fncType._processBaseType(p_objRoot);
				var objProto = fncType.prototype, objSrcProto = this.prototype;
				for (var memberName in objProto) {
					var memberValue = objProto[memberName];
					if (!objSrcProto[memberName]) {
						objSrcProto[memberName] = memberValue;
					}
				}            
			}
			delete this.__basePrototypePending; 		       
		}
	}

	f.callBaseMethod = function $Fp$callBaseMethod(instance, methodName, baseArguments) {
		var baseMethod = this.getBaseMethod(instance, methodName);
		if (baseMethod) {
			if (!baseArguments) {
				return baseMethod.apply(instance);
			}
			else {
				return baseMethod.apply(instance, baseArguments);
			}
		}
	    
		return null;
	}


	f.implementsInterface = function(interfaceType) {
		var interfaces = this.__interfaces;
		if (interfaces) {
			for (var i=0; i < interfaces.length; i++)
			{
				if (Object.compare(interfaces[i],interfaceType))
					return true;
			}
		}
	        
		if (this.base)
		{
			if (this.base.implementsInterface(interfaceType))
			{
					return true;
			}
		}
		else
			if (this.__baseType)
			{
				this.base = typeof (this.__baseType)=="function" ? this.__baseType : F.parse(this.__baseType);
				if (this.base.implementsInterface(interfaceType))
					return true;		    
			}
	    
		return false;
	}

	f.inheritsFrom = function(parentType) {
		if (parentType == this) {
			return true;
		}
		if (this.base) {
			return (this.base.inheritsFrom(parentType));
		}
		else
		{
			if (this.__baseType)
			{
				this.base = typeof (this.__baseType)=="function" ? this.__baseType : F.parse(this.__baseType);
				return (this.base.inheritsFrom(parentType));
			}
		}
		return false;
	}

	f.initializeBase = function(instance, baseArguments) {
		// This method initializes the base type in the context
		// of a given instance object (to keep track of the base type, and to
		// effectively inherit the object model of the base class, and
		// initializing members of the base class)

		if (!this._parentBase)    // Only run once
		{
			this._childBase = [this.__typeName];
			this._parentBase = [this.__typeName];
			if (this.__interfaces)
			{
				this._parentBase.addRange(this.__interfaces);        
				this._childBase.addRange(this.__interfaces);
			}
		} 
		var arrI = this.__interfaces;    
		if (arrI) {
			var iCount = arrI.length;
			for (var i = 0; i < iCount; i++) {
				var objI = arrI[i];
				objI = objI instanceof Function ? objI  : F.parse(objI);
				objI.call(instance);
			}
		}

		this._processBaseType(this);
	    
		if (this.base) {
			if (!baseArguments) {
				this.base.apply(instance);
			}
			else {
				this.base.apply(instance, baseArguments);
			}
		}
	    
		return instance;
	}

	f.isImplementedBy = function(instance) {
		if (!instance) return false;
		var instanceType = Object.getType(instance);
		if (!instanceType.implementsInterface) {
			return false;
		}    
		return instanceType.implementsInterface(this);
	}

	f.isInstanceOfType = function(instance) {
		if (typeof(instance) == 'undefined' || instance == null) {
			return false;
		}

		if (instance instanceof this) {
			return true;
		}
	    
		var instanceType = Object.getType(instance);
		if (instanceType == this) {
			return true;
		}
		if (!instanceType.inheritsFrom) {
			return false;
		}
		return instanceType.inheritsFrom(this) || instanceType.isImplementedBy(this);
	}


	f.registerClass = function(typeName, baseType, interfaceType) {
		// Registers a class (represented by its ctor function), and
		// optional base type, followed by any number of interfaces.
	   
	 	Function._htClasses[typeName] = this.prototype.constructor = this; // Make sure constructor is available for prototype-based inheritance
		this.__typeName = typeName;
		if (baseType) {
			if (baseType==="Web.Bindings.Base") baseType ="$Binding"; // Legacy support	
			this.__baseType = baseType;
			this.__basePrototypePending = true;
		}
	    
		if (interfaceType) {
			this.__interfaces = [];
			var iCount = arguments.length;
			for (var i = 2; i < iCount; i++) {
				this.__interfaces.push(arguments[i]);
			}
		}
	    
		return this;
	}

	f.registerAbstractClass = function(typeName, baseType) {
		this.registerClass(typeName,baseType);
		this._abstract = true;
	    
		return this;
	}

	f.registerSealedClass = function(typeName, baseType) {
		this.registerClass(typeName,baseType);
		this._sealed = true;
	    
		return this;
	}

	f.registerInterface = function(typeName) {
		this.__typeName = typeName;
		this._interface = this._abstract = this._sealed = true;    
		return this;
	}

	f.applyClass = function(blnEvents)
	{

		function generateClass(o)
		{
				var str = ((!o.skipClass) && (o.__typeName && o.__typeName.replace(/\./g, "_"))) || "";
				if (o.base)
				{
					str += " " + generateClass(o.base);
					if (blnEvents)
						o.Events = (o.Events || new $Enum).extend(o.base.Events);	// Establish event chain
				}
				return str;
		}
		if (!this._className)	// Use cached value if available
		{
			this._className = generateClass(this).trim();
		}
		return this._className;
	}

	f.removeClass = function(p_strClass)
	{
		if (this._className)
		{
			if (!this._arrClasses)	// Cache
				this._arrClasses = this._className.split(" ");
			var arrClass = this._arrClasses, iCount = arrClass.length;
			for (var intApplied=0;intApplied<iCount;intApplied++)
			{				
				p_strClass = p_strClass.replace( new RegExp('^' + arrClass[intApplied] + '( |$)| ' + arrClass[intApplied]+ '(?= |$)'), '');
			}							
			return p_strClass;	// Assign reverted class back on element
		}
		else
			return p_strClass;
	}

	F.__typeName = 'Function';
};


// Shortcut for making functional methods that assign properties and return the current object
function $FN(p)
{
	return function(v)
	{
		this[p]=v;
		return this;
	}
}

// Shortcuts for common functions
var $CD = Function.createDelegate;
var $CC = Function.createCallback;


///////////////////////////////////////////////////////////////////////////////
// Boolean Extensions

Boolean.parse = function(value) {
    if (typeof(value) == 'string') {
        return (value.trim().toLowerCase() == 'true');
    }
    return value ? true : false;
}

Boolean.__typeName = 'Boolean';
///////////////////////////////////////////////////////////////////////////////
// Number Extensions

Number.parse = function(value) {
    if (!value || (value.length == 0)) {
        return 0;
    }
    return parseFloat(value);
}

Number.coerceInt = function(p_i) 
{
	p_i = parseInt(p_i);
	return(isNaN(p_i) ? 0 : p_i);
}

Number.coerceFloat = function(p_f) 
{
	p_f = parseFloat(p_f);
	return(isNaN(p_f) ? 0.0 : p_f);
}

Number.__typeName = 'Number';

Date.__typeName = 'Date';
// Object Extensions

new function()
{
	var o = window.Object;

	o.isString = function(p_var)
	{
		return (typeof(p_var)==="string")
	}

	o.isArray = function(p_obj)
	{
		return p_obj instanceof Array;
	}
	
	o.isFunction  = function(p_var)
	{
		return (typeof(p_var)==="function");
	}
	
	o.isObject = function(p_var)
	{
		return (p_var && (typeof(p_var)==="object"));
	}
	
	o.isBoolean = function(p_var)
	{
		return (typeof(p_var)==="boolean")
	}
	
	o.isNumber = function(p_var)
	{
		return (typeof(p_var)==="number")
	}

	o.resolve = function $O$Resolve(p_vType)
	{
		// return the underlying type (constructor function)
		// given a function, string, or instance
		try
		{
			if (typeof(p_vType) === "string")
			{
				p_vType = Function.parse(p_vType);	
			}
			else if (typeof(p_vType) === "object")
			{	
				p_vType = p_vType.constructor;
			}
			else if (typeof(p_vType) !== "function")
			{
				p_vType = null;
			}
		}
		catch (ex)
		{
			return(null);
		}
		return(p_vType);
	};

	o.compare = function $O$Compare(p_vTypeA, p_vTypeB)
	{
		// Compares the types of two bindings.  
		// The two arguments can be any of the following:
		//    A binding instance
		//	  A binding type (the binding type must exist or an error may occur)
		//	  A binding type as a string value (use this if the binding type may not exist)

		var fncTypeA = this.resolve(p_vTypeA);
		return(fncTypeA && (fncTypeA == this.resolve(p_vTypeB)));
	};
		
	o.getType = function $O$getType(instance) {
		var ctor = instance.constructor;
		if (!ctor || (typeof(ctor) != "function") || !ctor.__typeName) {
			return Object;
		}
		return instance.constructor;
	}


	o.isNull = function $O$isNull(o)
	{
		return (null == o || undefined == o);
	}

	o.getTypeName = function $O$getTypeName(instance) {
		return Object.getType(instance).__typeName;
	}

	o.fromJSON = function $O$fromJSON(text) {
		try {
				if ((/^("(\\.|[^"\\\n\r])*?"|[,:{}\[\]0-9.\-+Eaeflnr-u \n\r\t])+?$/.test(text)))
					return eval('(' + text + ')');
		} catch (e) {
		}
		return null;
	};


	o.toJSON = function $O$toJSON(object)
	{
		var _this = Object.toJSON, json =  "null", _wt = Object;
		if (!Object.isNull(object))
		{
				if (_wt.isArray(object))
				{
					json = [];
					for (var i = 0; i < object.length; i++)
					{
							json.push(_this(object[i]))
					}
					json = "[" + json.join(",") + "]";
				}
				else if (_wt.isObject(object))
				{
					json = [];
					for (var name in object)
					{
							json.push("\"" + name + "\":" + _this(object[name]));
					}
					json = "{" + json.join(",") + "}";
				}
				else if (_wt.isString(object))
				{
					json = "\"" + object.replace(_this._reBackslash, "\\\\").replace(_this._reDoubleQuote, "\\\"").replace(_this._reNewLine, "\\n").replace(_this._reCarriageReturn, "\\r") + "\"";
				}
				else if (!_wt.isFunction(object))
				{
					json = object.toString();
				}
		}
		return json;
	}
	
	var oJS = o.toJSON;

	oJS._reBackslash = /\\/g;
	oJS._reDoubleQuote = /"/g;
	oJS._reNewLine = /\n/g;
	oJS._reCarriageReturn = /\r/g;

	o.__typeName = 'Object';	
}

///////////////////////////////////////////////////////////////////////////////
// Array Extensions

new function()
{
	var A = Array, a = A.prototype;

	a.add = a.queue = function(item) {
		return this.push(item);
	}
	
	a.addRange = function(items) {
		if (items) this.push.apply(this,items);
		return this;
	}
	
	a.clear = function() {
		if (this.length > 0) 
			this.splice(0, this.length);
	}

	a.clone = function() {
		return [].addRange(this);
	}

	a.contains =  a.exists =  function(item) {
		return (this.indexOf(item)>=0);
	}

	a.dequeue = a.shift;

	if (!a.indexOf) {
		a.indexOf = function(item, startIndex) {
			var length = this.length;
			if (length != 0) {
				// if startIndex is not specified, use 0
				startIndex = startIndex || 0;
				// Negative start indices start from the end
				if (startIndex < 0) {
					startIndex = Math.max(0, length + startIndex);
				}
				for (var i = startIndex; i < length; i++) {
					if (this[i] == item) {
						return i;
					}
				}
			}
			return -1;
		}
	}

	if (!a.forEach)
	{
		a.forEach = function (fnCb, objContext) {
			var l = this.length;	
			for (var i = 0; i < l; i++) {
				fnCb.call(objContext, this[i], i, this);
			}
		}
	}

	a.insert = function(index, item) {
		this.splice(index, 0, item);
	}

	a.remove = function(item) {
		var index = this.indexOf(item);
		if (index >= 0) {
			this.splice(index, 1);
		}
		return (index>=0);
	}
	a.removeAt = function(index) {
		return this.splice(index, 1)[0];
	}

	A.__typeName = 'Array';	
}

// Enumerations

// Dependency on $StringBuilder

var $Enum = function()
{
	var iCount = arguments.length;
	this._values = {};
	for (var i = 0; i < iCount; i ++) {
		this._values[arguments[i]] = this[arguments[i]] = arguments[i];
	}
	return this;
}

$Enum.prototype = 
{
	getValues :  function() {
        return this._values;
    },
    parse :  function(s) {
		if (s) {
			for (var f in this._values) {
				if (f.toLowerCase() === s.toLowerCase()) {
					return this._values[f];
				}
			}
		}
        return null;
    },
    toString : function(value) {
		if (!value) return this._values;
        for (var i in this._values) {
            if (this._values[i] === value) {
                return i;
            }
        }
        throw new Error('Invalid Enumeration Value');
    },
    extend : function(p_addEnum)
    {
    	if (p_addEnum)
		{
			for (var i in p_addEnum._values)
			{
				this._values[i] = this[i] = i;
			}
		}
		return this;
    }
}

var $Flags  = function() 
{
	var iCount = arguments.length;
	this._values = {};
	for (var i = 0; i < iCount; i+=2) {
		this._values[arguments[i]] = this[arguments[i]] = arguments[i + 1];
	}
	return this;
}

$Flags.prototype = 
{
	getValues :  function() {return this._values},
	parse : function(s) {
		var parts = s.split('|'), value = 0;

		for (var i = parts.length - 1; i >= 0; i--) {
			var part = parts[i].trim(), found = false;
	        
			for (var f in this) {
				if (f == part) {
					value |= this[f];
					found = true;
					break;
				}
			}
			if (found == false) {
				throw new Error('Invalid Enumeration Value');
			}
		}
		return value;
	},
	toString : function(value) {
		var sb = new $StringBuilder();
		for (var i in this) {
			if ((this[i] & value) != 0) {
				if (sb.isEmpty() == false) {
					sb.append(' | ');
				}
				sb.append(i);
			}
		}
		return sb.toString();
	}
}


// String extensions

new function()
{

	var S = String,s = S.prototype;

	s.endsWith = function(suffix) {
		return (this.substr(this.length - suffix.length) == suffix);
	}
	s.startsWith = function(prefix) {
		return (this.substr(0, prefix.length) == prefix);
	}
	s.lTrim = s.trimStart = function() {
		return this.replace(/^\s*/, "");
	}
	s.rTrim = s.trimEnd = function() {
		return this.replace(/\s*$/, "");
	}
	s.trim = function() {
		return this.replace(/^\s+|\s+$/g, "");
	}

	s.format = function()
	{
		var s = this, aRE = s.format.aRegExp, iCount = arguments.length;
		for (var i=0; i < iCount; i++)
		{
				if (!aRE[i])
				{
					aRE[i] = new RegExp("\\{" + i + "\\}", "g");
				}
				s = s.replace(aRE[i], arguments[i]);
		}
		return(s);
	}
	s.format.aRegExp = [];


	s.formatTokens = function(oFormat) {
		var result = [];    
		for (var i=0;;) {
			var next = this.indexOf("{", i);
			if (next < 0) {
				result.push(this.slice(i));
				break;
			}
	        
			result.push(this.slice(i, next));
			i = next+1;
	        
			if (this.charAt(i) == '{') {
				result.push('{');
				i++;
				continue;
			}
	        
			// Find the closing brace
			next = this.indexOf("}", i);
			if (next>=i)
			{
				var key = this.slice(i, next) 
				result.push(oFormat[key] || oFormat[key.toLowerCase()] || "{" + key + "}");        
				i = next+1;
			}
			else
			{
				result.push("{" + key);
				break;
			}
		}    
		return result.join("");
	}

	s.removeSpaces = function()
	{
		return this.replace(/ /g,'');
	}

	s.removeExtraSpaces = function()
	{
		return(this.replace(s.removeExtraSpaces.re, " "));
	}
	s.removeExtraSpaces.re = /\s+/g;

	// Replace with proper regular expression 
	s.removeSpaceDelimitedString = function(r) 
	{
		var s = " " + this.trim() + " ";
		return s.replace(" " + r + " "," ").trim();
	}

	s.addSpaceDelimitedString = function(r)
	{
		return this.removeSpaceDelimitedString(r) + " " + r;
	}

	// Escaping Routines
	s.encodeHtmlAttributeURI = s.encodeURI = function()
	{

		return encodeURI(this).replace(/'/g, "%27");
	}

	s.encodeURIComponent = function()
	{
		return encodeURIComponent(this).replace(/'/g, "%27");
	}

	s.encodeHtml = function()
	{
		return this.replace(/&/g, "&amp;").replace(/>/g, "&gt;").replace(/</g, "&lt;").replace(/'/g, "&#039;").replace(/"/g, "&quot;");
	}

	s.encodeHtmlAttribute = function()
	{
		return this.encodeHtml().replace(/\r/g,"&#13;").replace(/\n/g,"&#10;").replace(/ /g,"&#32;");
	}

	s.decodeURI = function() {return unescape(this)}

	S.__typeName = 'String';
}
// $StringBuilder

// Provides more optimized mechanism to concat a sequence of strings.
var $StringBuilder = function(initialText) {
    this._parts = [];
    
    if ((typeof(initialText) === 'string') &&
        (initialText.length !== 0)) {
        this._parts.push(initialText);
    }
}

$StringBuilder.prototype =
{
	append : function(text) {
		if ((text === null) || (typeof(text) == 'undefined') || ((typeof(text) === 'string') && (text.length === 0))) {
			return;
		}	    
		this._parts.push(text);
	},
    appendLine : function(text) {
        this.append(text);
        this._parts.push('\r\n');
    },
    clear : function() {
        this._parts = [];
    },
    isEmpty : function() {
        return (this._parts.length === 0);
    },
    toString : function(delimiter) {
        return this._parts.join(delimiter || '');
    }
}

///////////////////////////////////////////////////////////////////////////////
// $Event - base event code
// Allows creating events in a Gadget


// Define a base event type
// 
// Boolean autoInvoke: The event is only going to fire once. After firing, any additional event handlers will be automatically executed
var $Event = function(autoInvoke)
{
    this._handlers = [];
    this.autoInvoke = autoInvoke;		// Whether this event only occurs once
    this.isInvoked = false;				// Whether this event has been fired
    this.task = null;
}

$Event.prototype = {
    dispose : function() {				// Clear all event handlers
		this._handlers = [];
     },
	attach : function(p_fnc)			// Attach a new event handler to the event
	{
		if (!p_fnc) return false;
		var objEvent = this;
		
		// TODO - reuse single funciton to do callback (look at fire duplication)
		function Run() {
			p_fnc(objEvent.vPackage);
		}
		
		if (this.autoInvoke && this.isInvoked) {		// If auto-invoked and event has occurred, call back 
			this._timer = window.setTimeout(Run, 1);	// Use timer to prevent timing issues (call will occur after thread relinguishes)
			return(true);
		}
		else if (!this._handlers.exists(p_fnc))
		{
			this._handlers.push(p_fnc);
			return(true);
		}
		return(false);					// Event handler already exists - not added again
	},
	detach : function(handler) {		// Remove the event handler
		return this._handlers.remove(handler);	// Boolean whether the event handler was found
	},
	fire : function(p_vPackage, p_fncAsyncCallback)	// Fires the event,  Event occurs asynchronously if a callback function is specified
	{
		var objEvent = this;
		p_vPackage = (p_vPackage==null) ? {} : p_vPackage;
		function Done()
		{
			if (objEvent.autoInvoke)
			{
				objEvent.dispose();					// If auto-invoked, we clear any outstanding event handlers after firing them
				objEvent.vPackage = p_vPackage;
			}
			if (p_fncAsyncCallback)
				p_fncAsyncCallback();
			objEvent.isInvoked = true;
		}

		function Fire(p_fnc)
		{
			try
			{
				p_fnc(p_vPackage);
			}
			catch (ex)
			{					
				if ($Debug.enabled)
					throw (ex);		   
				$Tracing.Error.submitFromException(ex,null,$Tracing.Error.Fire);
			}
		}
	
		if (!$Runtime.unloading && p_fncAsyncCallback)	
		{
			var t = new $Task(Done);
			this._handlers.forEach(
				function(o)
				{
					t.add($CC(Fire,[o]),Array.$Prioritizer.High);
				}
			)
			t.run();
		}
		else
		{
			this._handlers.forEach(Fire);
			Done();
		}
	}
}


// $Event.DOM - DOM-like event pattern

$Event.DOM = function() {}

$Event.DOM.prototype = 
{
	_self : $Event.DOM,
	_event: $Event,
	exists : function(p_strEventName)
	{
		var c = this.constructor;
		if (c && c.Events && c.Events[p_strEventName])
			return true;
		else
			throw new Error("Invalid Event Name");
		return false;
	},
	_defined : function(p_strEventName)
	{
		return (this._htEvents && this._htEvents[p_strEventName]);
	},
	attachEvent : function(p_strEventName, p_fncCallback)
	{
		// Enable event support.  Events the binding supports must be specified
		// via BindingClass.Events = new $Enum("eventName1","eventName2",...")
		// Trying to attach or detach an invalid event name generates an exection
		if (this.exists(p_strEventName))
		{
			if (!this._htEvents) this._htEvents = {};
			if (!this._htEvents[p_strEventName]) this._htEvents[p_strEventName] = new $Event();
			this._htEvents[p_strEventName].attach(p_fncCallback);
		}
	},
	detachEvent :  function(p_strEventName, p_fncCallback)
	{
		// Detach the callback from the specified event
	
		if (this._defined(p_strEventName))
			this._htEvents[p_strEventName].detach(p_fncCallback);
		else
			this.exists(p_strEventName);	// To throw exception if bad event name
	},
	fire : function(p_strEventName, p_objPackage)
	{
		// Fire the specified event, using the giving event args package
	
		var returnValue;

		if (this.exists(p_strEventName) && this._defined(p_strEventName))
		{
			var objPackage = {srcBinding:this,eventName:p_strEventName,Package:p_objPackage,returnValue:null};
			this._htEvents[p_strEventName].fire(objPackage);
			returnValue = objPackage.returnValue;
		}
		return returnValue;
	},
	dispose : function()
	{
		if (this._htEvents)
			for (var i in this._htEvents)
				this._htEvents[i].dispose();
		this._htEvents = null;
	}
}
$Event.DOM.skipClass = true;
$Event.DOM.registerClass("$Event.DOM");



$Event.Manager = function(p_source,p_events)
{
	for (var i in p_events)
		p_source.attachEvent(i,p_events[i]);
	this.source = p_source;
	this.events = p_events;
}

$Event.Manager.prototype = 
{
	dispose : function()
	{
		for (var i in this.events)
			this.source.detachEvent(i,this.events[i]);
		this.source = this.events = null;		
	}
}

// Priority Stack

// Dependencies: Array and Flags

new function()
{
	var A = Array;
		
	A.$Prioritizer = function()
	{
		this.clear();
	};
	
	
	var pri = new $Flags("High",1,"Medium",2,"Low",3,"Lowest",4),P = A.$Prioritizer;
	P.Priorities = pri;
	
	P.prototype = 
	{
		priorities : P.Priorities.getValues(),
		_add : function(f,p,i)
		{			
			if (this.list[p])
			{
				if (i.key && this.keys[i.key])
				{
					if (p<this.keys[i.key])
					{
						this.removeItem(i);
					}
					else 
						return null;	// Already in the list at a higher priority
				}
				f.call(this.list[p],i);
				if (i.key)
					this.keys[i.key] = p;
			}
			return i;
		},
		push : function(obj, priority)
		{
			if (!priority) priority = pri.Medium;
			return this._add(this.list[priority].unshift,priority,obj);
		},		
		queue : function(obj,priority)
		{
			if (!priority) priority= pri.Medium;
			return this._add(this.list[priority].queue,priority,obj);
		},
		dequeue : function()
		{
			for (var p in this.priorities)
			{
				var obj = this.list[this.priorities[p]];
				if (obj.length>0)	// Remove extra check when object prototype is not extended
				{
					var o = obj.dequeue();
					if (o)
					{
						if (o.key)
							delete this.keys[o.key];
						return o;
					}
				}
			}
			return null;
		},
		// Removes all instances of item from anywhere in the queue
		removeItem : function(obj)
		{
			var bRemoved = false;
			if (obj.key)
			{
				var p = this.keys[obj.key];
				if (p)
				{
					bRemoved = this.list[p].remove(obj);
					delete this.keys[obj.key];
				}
			}
			if (!bRemoved)	// Double check lists
				for (var p in this.priorities)
				{
					bRemoved = this.priorities[p].remove(obj) || bRemoved;
				}
			return bRemoved;
		},
		removeKey : function(k)
		{
			var p = this.keys[k];
			if (p)
			{
				var s = this.list[p];
				for (var i=s.length-1;i>=0;i--)
				{
					if (s[i] && s[i].key==k)
					{
						s.splice(i,1);						
						delete this.keys[k];
						return true;
					}
				}
			}
			return false;
		},
		isEmpty : function()
		{
			for (var i in this.list)
			{
				if (this.list[i].length>0) return false;
			}
			return true;
		},
		findByProperty : function(p_strProperty,p_strValue)
		{
			for (var p in this.priorities)
			{
				var objQ = this.priorities[p];
				for (var item in objQ)
				{
					if (objQ[item][p_strProperty]===p_strValue)
						return objQ[item];
				}
			}
			return null;
		},
		clear : function()
		{
			this.list = {};		
			this.keys = {};
			for (var p in this.priorities)
			{
				this.list[this.priorities[p]] = [];
			}		
		}
		
	}
}


$Dom = 
{
	Css : new function()
	{
	    var objCss = this, _d = document,__ge = _ge,_de = _d.documentElement;
    
		objCss.Rule = function(p_strSingleSelector)
		{
			p_strSingleSelector.search(objCss.Rule.reCssSelector);
			this.strTagName = RegExp.$1.toLowerCase();
			this.strClassName = RegExp.$2.toLowerCase();
			this.strID = RegExp.$3;
		}
		objCss.Rule.reCssSelector = /([^\.#]*)\.?([^#]*)#?(.*)/;

	    
		objCss.createRules = function(p_strSelector)
		{
			// if there is no selector specified, then return an empty array
			if (!p_strSelector)
			{
				return([]);
			}
			// split the selector terms by white space
			var aSelectorTerms = p_strSelector.trim().split(objCss.createRules.reWhiteSpace), iCount = aSelectorTerms.length, _or = objCss.Rule;
			for (var i=0; i < iCount; i++)
			{
				// parse each term, and build an object for that term
				aSelectorTerms[i] = new _or(aSelectorTerms[i]);
			}
			return(aSelectorTerms);
		}
		objCss.createRules.reWhiteSpace = /\s+/;
	    
	    
		// Evaluates a specific single rule from selector stack against the element
		objCss.doesElementPassRule = function(p_el, p_objRule)
		{
			return (p_objRule && p_el.nodeType===1 && ((!p_objRule.strTagName) || (p_objRule.strTagName === p_el.tagName.toLowerCase())) && ((!p_objRule.strClassName) || ((" " + p_el.className.toLowerCase() + " ").indexOf(" " + p_objRule.strClassName + " ") !== -1)) && ((!p_objRule.strID) || (p_objRule.strID === p_el.id)));
		}


		// Evaluates a set of rules against the element
		// the rules must also apply within the specified p_elRoot
		objCss.doesElementPassRules = function(p_el, p_aobjRules, p_elRoot)
		{            
			// if there are no rules, then it does not pass
	        
			var iCount = p_aobjRules.length;
	        
			if ((!p_aobjRules) || (iCount == 0) || !p_el)
			{
				return(false);
			}
	        
			var iRuleIndex = iCount-1, _pr = objCss.doesElementPassRule;
	        
			if (!_pr(p_el, p_aobjRules[iRuleIndex--]))
			{
				return(false);
			}

			if (!p_elRoot || p_elRoot==document)
			{
				p_elRoot = _de;
			}
	        
			// true if p_el is assumed to be contained within p_elRoot
			var blnMustContain = (p_elRoot == _de);
	        
			// check the rest of the rules
	        
	        
			while (p_el && (blnMustContain || p_elRoot.contains(p_el)) && (iRuleIndex >= 0))
			{
				p_el = p_el.parentElement;
				if (p_el && _pr(p_el, p_aobjRules[iRuleIndex]))
				{
					iRuleIndex--;
				}
			}
	        
			return(p_el && (blnMustContain || p_elRoot.contains(p_el)));
		}
	    
		// Support for rich css selectors when defining bindings
		// For example "BODY DIV.Items .Lists" can be used.
		// We can optionally choose to support richer constructs (e.g., firstChild, etc)
		// We do support custom tag referencing - e.g., spaces:foo.bar  all spaces:foo tags with class=bar.
		objCss.getElementsByCssSelectorRules = function(p_aRules, p_elRoot)
		{
		
			var aElements = [], irCount = p_aRules.length, _pr = objCss.doesElementPassRule;            
			p_elRoot = p_elRoot || _d;
			
	        // Recursive function for doing CSS selection
			function GetElements(p_elScope, p_iIndex)
			{ 
				// this will hold the list of potential elements to scan
				var objRule= p_aRules[p_iIndex];

			    // get a list of elements from under the specified root, 
			    // filtered by either id, tagName, or none.  If ID is specified, 
			    // then the element that matches ID is returned.  If tagName is
			    // specified, then the elements are filtered by tagName.  If only
			    // className is specified, then all the elements are returned.	            
				if (objRule)
				{
				    var elPotentials = [];
					if (objRule.strID)
					{
						// the ID was specified, so filter by ID
						var elByID = _ge(objRule.strID);
						if (elByID && (p_elScope==_d) || (p_elScope.tagName && p_elScope.contains(elByID)))
						{
							elPotentials = [__ge(objRule.strID)];
						}
					}
					else if (objRule.strTagName)
					{
						// the tagName was specified, so filter by tagName
						elPotentials = $Dom.getAnyElementByTagName(objRule.strTagName, p_elScope);
					}
					else if (objRule.strClassName)
					{
						// only the className was specified, so get all the elements
						elPotentials = p_elScope.all || p_elScope.getElementsByTagName("*");
					}
                    var intCount = elPotentials.length;
				    // scan the list of potentials
    	            
				    for (var i=0; i<intCount; i++)
				    {
					    // verify that this element matches the rule
					    var elItem = elPotentials[i];
					    if (_pr(elItem, objRule))
					    {
						    if (p_iIndex+1<irCount)
							    GetElements(elItem, p_iIndex+1)
						    else
							    aElements.push(elItem);
					    }
				    }        
				}	            
			}

			if (irCount>0)
				GetElements(p_elRoot, 0);
			// return the list of elements
			return(aElements);
	        
		};

	
		objCss.getElementsByCssSelector = function(p_strSelector, p_elRoot)
		{
			return($Dom.Css.getElementsByCssSelectorRules($Dom.Css.createRules(p_strSelector), p_elRoot));
		}


	},
	getAnyElementByTagName : function(tagName,root,bForceNoCompat)
	{
		var _d = document, elList = [], idx = tagName.indexOf(":"), __fn = $Dom.getAnyElementByTagName;

		if (!root) root = _d;

		if (idx>=0)
		{
			if (__fn._scanMode)
			{
				var ns = tagName.substring(0,idx), tagName = tagName.substring(idx+1);
				if (ns && __fn._ns[ns]==null)
				{
					if (ns!="" && _d.namespaces && !_d.namespaces[ns]) 
					{
						throw new Error("IE Requirement - Add xmlns:" + ns + " to the HTML tag.");
					}
					else
					{
						__fn._ns[ns] = true; // Cache
					}
				}
	             
				var elTemp = root.getElementsByTagName(tagName), iCount = elTemp.length;
				for (var i=0;i<iCount;i++)
				{
					var objItem = elTemp[i], strProp = objItem[__fn._propName];
					if (strProp && strProp.toLowerCase()==ns.toLowerCase())    // Make sure element is in proper scope
						elList.push(objItem);
				}
			}
			else if ($Browser.MozillaCompatMode && !bForceNoCompat)
			{
				elList = [];
				var elTemp = root.getElementsByTagName("div");
				for (var i=0;i<elTemp.length;i++)
					if (elTemp[i].className.indexOf(tagName)>-1)
						elList.push(elTemp[i]);
			}
			else
			{
				elList = root.getElementsByTagName(tagName);
			}
		}
		else
			elList = root.getElementsByTagName(tagName); 
		return elList;
	}
}

new function()
{
	var wdg = $Dom.getAnyElementByTagName;
	wdg._scanMode = ($Browser._isIE || ($Browser.isOpera() && $Browser.version<9))
	wdg._propName = $Browser._isIE ? "scopeName" : "prefix";
	wdg._ns = [];
}

function $(s) 
{
	return (typeof s=="object") ? s : _ge(s);
}

// Takes a parser and xml object and process it


var $Xml = 
{
	resolveTagName : function(p_el)
	{
		var strTagName;
		if (p_el.document && p_el.document==document)
		{
			strTagName = p_el.tagName;
			if (strTagName && $Browser._isIE && p_el.scopeName!="")
				strTagName = p_el.scopeName + ":" + strTagName;
		}
		else
			strTagName = p_el.nodeName;
		return strTagName.toLowerCase();
	},
	getDocumentRoot : function(response)
	{
		if (!response)
			return null;
		if (response.document && response.document==document)	// XML Node in current page
			return response;

		if (response.responseText.length == 4)	// Firefox hack
		{
			return null;
		}

		var root = null;

		if ((!response.responseXML || response.responseXML.xml == "") && (response.status!=404))
		{
			// Try reloading the stream as the content-type from the server may not be text/xml
			try
			{
				var doc = new ActiveXObject("Microsoft.XMLDOM");
				doc.loadXML(response.responseText);
				root = doc.documentElement;
			}
			catch (e)
			{
				root = null;
			}
		}
		else
		{
			try
			{
				root = response.responseXML.documentElement;
			}
			catch (e)
			{
				root = null;
			}
		}
			
		return root;
	}, 
	getTextProperty : function(obj)
	{
		if (obj.text == "")
			return "";
		else
			return obj.text || obj.textContent;
	}  // Web.Parser.getTextProperty

}


var $Parser = function(m_parserRoot, m_xml, m_context)
{
	var root = $Xml.getDocumentRoot(m_xml),r  =$Xml.resolveTagName,obj = m_context || {};
		
	function processItem(m_parser, m_node, m_context, m_target)
	{		
		if (m_parser.processor)
			m_target = m_parser.processor(m_parser, m_node, m_context,m_target)
		var iCount = m_node.childNodes.length,blnChild = m_parser.children;
		for (var i = 0; i < iCount; i++)
		{

			var ni = m_node.childNodes[i];
			if (ni.nodeType===1)
			{
				var pr = blnChild && m_parser.children[r(ni)];
				if (pr)
				{
					processItem(pr,ni,m_context, m_target);
				}
				else if (m_parser.childProcessor)
				{
					m_parser.childProcessor(pr,ni,m_context,m_target);				
				}
			}
		}
	}

	if (root)	
	{
		processItem(m_parserRoot,root,obj,obj);
	}
	return obj;
}

// Parser factory classes - Used to create XML DOM Parsers
$Parser.root = function(p_process)
{
	var parser = {};
	parser.children = {};
	for (var i=1;i<arguments.length;i++)
	{
		parser.children[arguments[i].tagName] = arguments[i];
	}
	parser.processor = p_process;
	return parser;	
}

$Parser.tag = function(p_tagName, p_process, p_default, p_children)
{
	var parser = {};
	parser.tagName = p_tagName;
	parser.processor = p_process;
	parser.childProcessor = p_default;
	if (p_children)
	{
		var aCount = arguments.length;
		if (aCount>3)
		{
			parser.children = {};
			for (var i=3;i<aCount;i++)
			{
				parser.children[arguments[i].tagName] = arguments[i];
			}
		}	
	}
	return parser;
}


// TODO - have the binding parser generate a $Binding.definition (not an intermediary type) and return it.

var $Parsers = 
{
	// Parsers
	Definitions : // TODO - create instance on first request.
	{	
		Binding : new function()		// Parses a web:binding definition
		{
			var t = $Parser.tag;
			function pRoot(x,n,c,t)
			{
			
				if (c && c.def)
				{
					var g = c.def;
					g.type = n.getAttribute("type");
					g._async= n.getAttribute("attach")=="async";
					
					var root = n.getAttribute("root"),sel = n.getAttribute("selector"),elRoot;

					if (root)
					{
						switch (root.toLowerCase())
						{
							case "parent":
								elRoot = n.parentElement;
								break;
							case "previous":
								elRoot = n.previousSibling;
								while (elRoot.nodeType!=1)
									elRoot = elRoot.previousSibling;
								break;
							case "next":
								elRoot = n.nextSibling;
								while (elRoot.nodeType!=1)
									elRoot = elRoot.nextSibling;
								break;
							case "none":
								elRoot = null;
								break;
						}
						g.root = elRoot;
					}
					g.preload = n.getAttributeNode("preload")!=null;
					if (sel)
					{
						sel = sel.split(",");
						for (var i=sel.length-1;i>=0;i--)
							g.bindCss(sel[i],g.root);
					}
					g.ns = n.getAttribute("namespace");
				}
				return c;
			}
			
			function pRef(x,n,c,t)
			{
				c.network = new $Network();
				$Parser($Parsers.Definitions.References,n,c.network);
				return c;
			}
			
			function pRequire(x,n,c,t)	// WARNING: requires must be the last tag in the definition (following defaults)
			{
				var s = n.getAttribute("src");
				if (s.startsWith("#"))
				{
					var elRoot = document.getElementById(s.substring(1))
					if (elRoot)
						$Parser($Parsers.Definitions.Binding,elRoot,c)
					else
						throw new Error ("Binding id (" + s + ") is missing");
				}
				return c;
			}
			
			function pDefaults(x,n,c,t)
			{
				if (!c.def._defaults) c.def._defaults = {};				
			}
			
			function pParam(x,n,c,t)
			{
				var strName = n.getAttribute("name");
				if (strName)
				{
					strName = strName.toLowerCase();
					if (!c.def._defaults[strName])
						c.def._defaults[strName] = n.value || n.getAttribute("value");
				}
			}
			
			function pChain(x,n,c,t)
			{
				c.def.addChain($Parser($Parsers.Definitions.Binding,n,{def: $Binding.define()}));
			}
			
			return $Parser.root(pRoot,t("web:references",pRef),t("web:defaults",pDefaults,null,t("web:param",pParam)),t("web:chains",null,null,t("web:chain",pChain)),t("web:requires",null,null,t("web:add",pRequire)));
		},
		References : new function()
		{

			function pRoot(x,n,objContext)
			{
				if (!objContext.config.priority)
				{
					var strPriority = n.getAttribute("priority");
					if (strPriority)
						objContext.config.priority = Array.$Prioritizer.Priorities[strPriority.substring(0,1).toUpperCase() + strPriority.substring(1).toLowerCase()];
					objContext.config.base = n.getAttribute("base");
				}
				return objContext;
			}
			
			function pAdd(x,n,c,t)
			{
				// TODO - change to request objects
				var strType = n.getAttribute("type") || "script",strSourceValue = n.getAttribute("src"),strKeyValue = n.getAttribute("key"),r = $Request,arrItem = c.config.items,eType = $Network.Type,arrSources = strSourceValue.split("|"),arrKeys = strKeyValue && strKeyValue.split("|"),iSource = arrSources.length;
          		for (var i=0;i<iSource;i++)
          		{
          			var strSource = arrSources[i].trim();
          			if (c.config.base)
          				strSource = $Request.resolveUrl(strSource,c.config.base);
					if (strSource)
					{
						switch (strType.toLowerCase())
						{
							case "fpp":
								if (!$Network._fppReady)
									arrItem.push(new r("{framework.base}/FireAnt.js",eType.Script).setHash("FireAnt.js"));
							case "text/javascript":
							case "script":
								arrItem.push(new r(strSource,eType.Script).setHash(arrKeys && arrKeys[i]));
								break;
							case "text/xml":
							case "xml":
								var oRequest = new r(strSource,eType.XML).setName(n.getAttribute("name")),strProxy = n.getAttribute("proxy");
								if (strProxy && (strProxy.toLowerCase()=="true"))
									oRequest.setFlags($Network.Flags.CLIENTPROXY);
								arrItem.push(oRequest);
								break;
							case "text/css":
							case "css":
								arrItem.push(new r(strSource,eType.CSS));
								break;
							case "image":	// Only do pre-caching trick for pre-IE7
								arrItem.push(new r(strSource,eType.Image));
								break;						
							case "reference":
								if (strSource.startsWith("#"))	// Only support ID references for this release
								{
									var elRoot = n.document.getElementById(strSource.substring(1));
									if (elRoot)
										c = $Parser($Parsers.Definitions.References,elRoot,c);
									else
										throw new Error("Error locating reference: " + strSource)
								}
								break;				
						}
					}	
				}
				return c;			
			}

			return $Parser.root(pRoot,$Parser.tag("web:add",pAdd));

		}
	},
	References : function(response)
	{
		return $Parser($Parsers.Definitions.References, response);
	},
	Binding : function(response)
	{
		return $Parser($Parsers.Definitions.Binding, response);
	}
}


$Runtime = 
{
	unloading : false,
	onunload : new $Event(true),
	_shutdown : function()
	{
	    if ($Runtime.unloading) return; // protect reentrancy
		$Runtime.unloading = true;
		$Runtime.onunload.fire();
		$Runtime.onunload = $Binding.Scope.prototype.__de = null;
		if ($Browser._isIE)
		{
			try
			{
				document.execCommand("BackgroundImageCache",false,false);
			}
			catch(ex)
			{
			}
		}	
		if (typeof($Network)!="undefined")
		{
			$Network.Execute.Script.outstanding = $Network.Execute.scriptList = null;
		}
		if (typeof(Web)!="undefined")
			Web = null;

		CollectGarbage();
	},
	_loaded : function()
	{
		window.document.detachEvent("onstop",$Runtime._shutdown);			
	}	
}

$Counter = 0;	

if (typeof($Config)=="undefined") 
{
	$Config = {Themes:{},TraceData:{disable:1}};
}

$Config.culture = $Config.culture || document.documentElement.getAttribute("web:culture") || "en-US";
$Config.applyAppDomain = $Config.applyAppDomain || false;


if ($Browser._isIE)
{
	window.document.attachEvent("onstop",$Runtime._shutdown);
	window.attachEvent("onload",$Runtime._loaded);
}
window.attachEvent("onunload",$Runtime._shutdown); 


// Task Manager

// Dependencies
//	Array
//	Enum
//	Prioritizer

$Task = function(fCb)
{
	this.id = "$T" + ($Task._counter++);	
	if (fCb)
		$Task.Scheduler._setCB(this.id,fCb);
}

$Task.prototype = 
{
	add : function(cb,pri)
	{
		$Task.Scheduler.add(cb,pri,this.id);
		return this;
	},
	run : function()
	{
		$Task.Scheduler.run();
		return this;
	},
	dispose : function()
	{
		$Task.Scheduler.remove(this.id);
	}
}

$Task._counter = 0;

$Task.Scheduler = new function()
{	
	var key = 0,k = [],oTimer = null,me = this,startTime;

	function next()
	{
		me.isRunning = false;
		exec();
	}

	function exec()
	{
		if (!me.isRunning)
		{
			startTime = new Date();
			me.isRunning = true;
			var objTask = me.queue.dequeue();
			while (objTask)
			{
				var id = objTask.id;
				objTask.cb();							// TODO: Add debug wrapper
				if (id)
				{
					if (--k[id]._count===0 && k[id]._cb)
					{
						k[id]._cb(id);
					};
					delete k[id][objTask.key];
				}
			
				if ((new Date()-startTime<me.lock))
					objTask = me.queue.dequeue();
				else
					objTask = null;
			}
			if (!me.queue.isEmpty())			
			{
				if (!oTimer)
					oTimer = setInterval(next,1);
			}
			else
			{
				me.isRunning = false;
				clearInterval(oTimer);
				oTimer = null;
			}
		}	
	}

	this._setCB = function(id,cb)
	{
		if (!k[id])
		{
			k[id]= {};
			k[id]._count = 0;		
		}
		k[id]._cb = cb;
	}
	this.shouldSleep = function()
	{
		return startTime && (new Date()-startTime<this.lock);
	}
	this.lock = 175;	// minimum milliseconds to execute before release
	this.run = exec;
	this.isRunning = false;
	this.queue = new Array.$Prioritizer();
	this.add = function(cb,pri,id)
	{
		key++;
		if (this.queue.queue({id:id,key:key,cb:cb},pri) && id)
		{
			if (!k[id]) 
			{
				k[id]= {};
				k[id]._count = 0;
			}
			k[id][key] = true;
			k[id]._count++;
		};
		return key;
	};
	this.remove = function(id)
	{
		if (!k) return;
		for (var i in k[id])
		{
			if (i!=="_count" && i!=="_cb")
				this.queue.removeKey(i);
		}
		delete k[id];
		if (this.queue.isEmpty()) // Check if we can sleep	
		{
			if (oTimer)
				clearInterval(oTimer);
			oTimer = null;
			this.isRunning = false;
		}
	};
	
	function dispose()
	{
		if (oTimer)
			clearInterval(oTimer);
		k = null;;
		this.queue = null;
	}
	
	$Runtime.onunload.attach(dispose);
}

// JScript source code
var $Memory = function()
{
    var M = $Memory;
    this.Nodes = new M.Nodes();
    this.Events = new M.Events();
    this.Properties = new M.Properties();
}

new function()
{
    var M = $Memory;
    var s = "__0";

    M.prototype = 
    {
        dispose : function()
        {
            this.Events.dispose();  // Dispose events first
            this.Nodes.dispose();
            this.Properties.dispose();
        }
    }
    
    M.Groups = function()
    {
        this.groups = {};
    }

    M.Groups.prototype = 
    {
        create : function(n)
        {
            if (!n) n=s;
            if (!this.groups[n])
                this.groups[n] = new $Memory();
            return this.groups[n];
        },
        disposeGroup : function(n)
        {
            if (!n) n=s;
            if (this.groups[n])
                this.groups[n].dispose();
        },
        dispose : function()
        {
            for (var i in this.groups)
                this.groups[i].dispose();
            this.groups = {};
        }
    }

    
    M.Events = function()
    {
        this.events = new M.Properties();
    }

    M.Events.prototype = 
    {
        register : function(p_source, p_events)
        {
            this.add(new $Event.Manager(p_source, p_events));
        },
        add : function(o)   // add existing Event Manager
        {
            this.events.register(o);
        },
        dispose : function()
        {
            this.events.dispose();
            this.events = new M.Properties();            
        }
    }

    M.Nodes = function()
    {
        this.nodes = [];
    }

    M.Nodes.prototype =
    {
        register : function(el)
        {
            this.nodes.push(el);
            return el;
        },
        create:  function(tag,properties,styles,parent)
        {
            var el = _ce(tag);
            for(var p in properties)
    	        el[p] = properties[p];
	        for(var p in styles)
    	        el.style[p] = styles[p];
	        if(parent)
    	        parent.appendChild(el);
    	    return this.register(el);
        },
        dispose : function()
        {
            var n = this.nodes;
	        for(var i=n.length-1;i>=0;i--)
	        {
	            if (!$Runtime.unloading)
	                n[i].removeNode(true);
	            n[i]=null;
	        }
	        this.nodes=[];         
        }
    }
    
    M.Properties = function()
    {
        this.properties = [];
    }
    
    M.Properties.prototype = 
    {
        register : function(o)
        {
            if (o.initialize)
                o.initialize();
            this.properties.push(o);
        },
        dispose : function()
        {
            var blnUnload = $Runtime.unloading;
            var n = this.properties;
	        for(var i=n.length-1;i>=0;i--)
	        {
	            if (n[i].dispose)
                    n[i].dispose(blnUnload);
	            n[i]=null;
	        }
	        this.properties=[];                 
        }
    }
}
/* Network Stack */

/* Create a new Network request.  

Use when you are requesting more than one file.  
For requesting a single file, use $Network.fetch 

Usage: var n = new $Network();

*/

function $Network(p_priority, p_objContext, p_arrItems)
{
	this.config = 
	{
		priority : p_priority,
		context : p_objContext,
		items : p_arrItems || []
	}

	this.response = [];
	this.isLoaded = this.isExecuting = false;
}

$Network.prototype = 
{
    /* A blob that is past to the callback with the requested resources */
	setContext : function(o)
	{
		this.config.context = o;
		return this;
	},
	/* Add a new resource to be requested 
	usage:
	
	n.add($Network.Type.Script,"script1.js","script2.js");
	
	The resources can also be Request objects:
	
	n.add($Network.Type.Script, new $Request("script.js"), new $Request("script2.js"))
	*/
	add : function(p_strType)
	{
	
		var me = this,arrItems = me.config.items;
		function addOne(o)
		{
			var o = o instanceof $Request ? o : new $Request(o,p_strType);	// Support strings
			if (p_strType==$Network.Type.Fpp)
			{
				o.type = $Network.Type.Script;
				if (!$Network._fppReady)
				{
					var f = new $Request("{framework.base}/FireAnt.js",o.type).setHash("FireAnt.js");
					arrItems.push(f);
				}
			}
			arrItems.push(o);
		}
		for (var i=arguments.length-1;i>0;i--)
		{
			var a = arguments[i];
			if (a)
			{
				if (a instanceof Array)
					a.forEach(addOne);
				else
					addOne(a);
			}
		}		
		return this;	
	},
	/* Internal method  called when the request is complete */
	_complete : function(oResponse,oContext,oRequest)
	{
		this._received++;
		oRequest.resource = oResponse;
		this.response.push(oRequest);
		if(this._received===this.config.items.length)
		{
			this.isExecuting = false;
			this.isLoaded = true;
			if (this._callback)
				this._callback(this.response,this.config.context);
		}
	},
	/* Load the resources */
	/* When complete, the past in callback is executed */
	load : function(p_cb)
	{
		if (!this.isExecuting)
		{
			this.isExecuting=true;
			this._callback = p_cb;
			this._received = 0;
			var arrItems = this.config.items,iCount = arrItems.length;
			this._delCallback = $CD(this,this._complete);
			if (iCount>0)
				for (var i=0;i<arrItems.length;i++)
				{
					$Network.fetch(this._delCallback,arrItems[i]);
				}
			else
			{
				this.isExecuting = false;
				this._callback(this.response,this.config.context);
			}

		}
		else
		{
			this._callback = p_cb;
		}
	},
	/* Abort the network requests */
	abort : function()
	{
		if (this.isExecuting)
		{
		    var cb = this._delCallback;
			this.config.items.forEach(function(o) {$Network.abortRequest(cb,o)} );
			this._received = 0;
		}
		this.isExecuting = false;
	},
	/* Dispose the network object */
	dispose : function()
	{
		if (!$Runtime.unloading)	// If unloading, the network stacks themselves will abort so we can shortcircuit
			this.abort();
		this.config = this.response = this._callback = this._delCallback = null;
	}
}

/* Network helper methods */
new function()
{
	var N = $Network;
	
	/* Alias to the array priorities 
	
	eg., Network.Priority.High 
	
	*/
	N.Priority = Array.$Prioritizer.Priorities;
	
	/*
	    Retrieve a specific cookie value
	*/
	N.getCookie = function(sCookieName)
	{
	    var self = $Network.getCookie;
        var cookie = document.cookie;
        if (cookie!==self.cookieString) // Optimize to only do this when necessary
        {
            self.cookies = {};
		    cookie.split(";").forEach(
		        function (c)
		        {
			        var equals = c.indexOf("=");
			        if (equals !== -1)
			        {
				        self.cookies[c.substring(0, equals).trim()] = c.substring(equals+1, c.length).trim();
			        }
		        }
		    );
		    self.cookieString = cookie;
        };
		return self.cookies[sCookieName];
	}
	
	/* Internal cache variables */
	N.getCookie.cookieString = "";
	N.getCookie.cookies = {};

	N.abortGroup = function(p_strGroup)
	{
		for (var i in N.Domain.List)
			N.Domain.List[i].abortGroup(p_strGroup);
	}

	N.Domain = function(p_strDomain)
	{
		this.domain = p_strDomain;
		var _wp = Array.$Prioritizer;

		this.domain = p_strDomain;

		this.groups = {};
		this.parallel = new _wp();	// Queue of parallel requests
		this.serial  =	new	_wp();	// Queue of serialized requests
		this.requests = {};	// list of all outstanding requests
		this._active = 0;	// Currently running requests
		this._serialActive = this._proxy = this._hasSerial = false;
		this._proxyTarget = null;	
	}

	var E = $Event, isMoz = $Browser.isMozilla();
	
	N.Events = 
	{
		onrequest : new E(),
		oncomplete : new E(),
		onerror : new E(),
		onhttperror : new E(),
		oninvoke : new E(),
		onfinished : new E(),
		onprofile : new E()
	}


	N.Domain.prototype = 
	{
		_assignProxy : function(w)
		{
			this._proxyTarget = w;
			this._continue();
		},
		add : function(p_callback, p_request)
		{
			if (p_callback && !p_callback._id)
			{
				p_callback._id = $Counter++;
			}
			
			var group = p_request.group,objDetail = {request: p_request, callback : p_callback};
			if (group)
			{
				if (!this.groups[group]) this.groups[group] = [];
				this.groups[group].push(objDetail);
			}
				
			if (p_request.flags & N.Flags.CLIENTPROXY)
			{
			    if (this.domain!= N.currentDomain)
			    {
				    N.Proxy.generate(this.domain);
				    this._proxy = true;
				}
				else
				    p_request.flags = p_request.flags ^ N.Flags.CLIENTPROXY;
			}
				
			var strKey = p_request._key = p_request.getKey();
		    var k = this.requests[strKey];
			if (k)
			{
				if (p_callback && !k._items[p_callback._id])
				{
					if (k._complete)	// Short circuit if same item is requested in the callback
						p_callback(k._complete,p_request)
					else
						k._items[p_callback._id] = objDetail;
				}
				k._count++;				
			}
			else
			{

				var _wnt = N.Type,objStack;
				if (p_request.flags & N.Flags.SERIALIZE) 
				{
					objStack = this.serial;
					this._hasSerial = true;
				}
				else
				{
					objStack = this.parallel;
				}
				var _typ = p_request.type;
				if (_typ==_wnt.XML || _typ==_wnt.XMLGet || _typ==_wnt.XMLPost || _typ==_wnt.Stream)
				{
					objStack.push(p_request,p_request.priority); // Data to the front of the line
				}
				else
				{
					objStack.queue(p_request,p_request.priority);
				}
				k = this.requests[strKey] = {};
				k._items = {};
				k._count = 1;
				if (p_callback)
					k._items[p_callback._id] = {request: p_request, callback : p_callback};
			};
			this._continue();
			return k;
		},
		_continue : function()
		{
			var	o;
			if (this._active<N.Config.maxActive && (!this._proxy || (this._proxy && this._proxyTarget)))
			{			
				if (!this._serialActive) //	Serialized Request always get a	slot 
				{
					o =	this.serial.dequeue();
					if (o) this._serialActive =	true;
				}
				if (!o)				// If slot not taken, check parallel queue
				{
					o =	this.parallel.dequeue();
				}

				if (o)
				{
					var _wnt = $Network.Type,finished = $CD(this,this._complete);
					var strKey = o._key;
					var k = this.requests[strKey];
					if (o.flags & N.Flags.CLIENTPROXY && (o.type==_wnt.XMLGet || o.type==_wnt.XML || o.type==_wnt.XMLPost))
						k._executing = this._proxyTarget.fetchXML(o,$Network.Events,null,finished);
					else		
						k._executing = N.Execute.load(finished,o);
					if (k._executing)
						this._active++;
				}
			}
		}
		,
		_complete : function(el,o)
		{
			// remove from group
			N.Events.oncomplete.fire(o);
			if (isMoz && (o.flags & N.Flags.CLIENTPROXY))
			{
				var _wnt = $Network.Type;
				// Do Mozilla Security Cleanup - note - only works in FireFox 1.5.0.1 or later - otherwise proxy request fails
				switch (o.type)
				{
					case _wnt.XMLGet:
					case _wnt.XML:
					case _wnt.XMLPost:
						el = Web.Browser._Private.cleanupFirefox(el,o);	// From Compatibility Layer
						break;
				}	
			}
			
			if (o.flags & N.Flags.SERIALIZE)
			    this._serialActive = false;

			
			this._active--;
			var sKey = o._key;
			var oKey = this.requests[sKey],boolNotify =  false;
			if (oKey)
			{
			    oKey._complete = el;
    			var items = oKey._items;
			    for (var i in items)
			    {
			        var r = items[i].request;
				    boolNotify = boolNotify || (r.flags & $Network.Flags.Notify);
				    items[i].callback(el,r.context,r);				
			    }
    			delete this.requests[sKey];
            }
            
			if (o.group)	
			{
				this.groups[o.group].remove(o);
				if (this.groups[o.group].length===0)
					delete this.groups[o.group];
			}
			if (boolNotify)
				$Accessibility.notify();

			this._continue();
		},
		abortRequest : function(p_cb, p_request)
		{
			var strKey = p_request.getKey();
		    var k = this.requests[strKey];
		    if (k)
		    {
		        if (p_cb && p_cb._id)
		        {
		            if (k._items[p_cb._id])
		            {
		                delete k._items[p_cb._id];
    		            k._count--;
    		        }
		        }
		        if (k._count===0)  // Noone is listening - get rid of this
		        {
		            if (k._executing) 
		                k._executing.abort();
		            else
		            {
		                var objStack = (p_request.flags & N.Flags.SERIALIZE) ? this.serial : this.parallel;
		                var objItem = objStack.findByProperty("_key",strKey);		                
		                if (objItem) objStack.remove(objItem);
		            }
		            delete this.requests[strKey];
		        }
		    }
		},
		abort : function()
		{
			for (var i in this.requests)
			{
			    if (this.requests[i]._executing)
			        this.requests[i]._executing.abort();
			}
			this.groups = {};
			this.requests = {};	
			this.intActive = 0;
		},
		abortGroup : function(strGroup)	 // TODO - validate
		{
			var oGroup = this.groups[strGroup];
			if (oGroup)
			{
				for (var o in oGroup)
				{
				    this.abortRequest(o.callback,o.request);
				}
				delete this.groups[strGroup];
			}
			
		},
		dispose : function()
		{
			this.abort();
			this.parallel = this.serial = null;
		}
	}
   
    N.abortRequest = function(p_callback, p_request)
    {
		var d = N.Domain.list[p_request.domain]
		if (d)
		{
            d.abortRequest(p_callback, p_request)
		}        
    }
   
	N.fetch = function(p_callback,p_request)
	{
		var strDomain  = p_request.domain,d = N.Domain.list[strDomain]
		if (!d)
		{
			d = N.Domain.list[strDomain] = new N.Domain(strDomain)
		}
		return d.add(p_callback,p_request);
	}


	N.Domain.list = {};

	N.Domain.abort = function()
	{
		for (var i in N.Domain.list)
		{
			N.Domain.list[i].abort();
		}
	}

	$Runtime.onunload.attach(N.Domain.abort)

	N.Type = new $Enum("XML","Image","Script","XMLPost","XMLGet","CSS","Stream","Fpp");; // Alias 
	N.Verbs = new $Enum("GET","POST","DELETE","PUT");
	N.Flags = new $Flags("SERIALIZE",1,"DUPLICATE",2,"NOTIFY",4,"CACHEONLY",8,"CLIENTPROXY",16,"CANARY",32); // Added CLIENTPROXY as a flag

	N.Execute = new function()
	{

		var _isMoz = $Browser.isMozilla();
		var _isIE = $Browser._isIE;
		var iFailedImages = 0;
		var blnCssChecked = blnImagesChecked = false;
		
		this.load  = function(cb,o)
		{
			var _wnt = N.Type,objExecute,t=o.type,u=o.url;
			$Network.Events.onrequest.fire(o);
			if (u!=o.url)
			    o.resolve();
			
			switch (t)	
			{
				case _wnt.XMLGet:
				case _wnt.XML:
				case _wnt.XMLPost:
					objExecute= new this.XML(cb,o,_wnt.XMLPost==t);
					break;
				case _wnt.Stream:
					if (!this.Stream)
					{
						throw new Error("Streaming not installed");
    					break;
    			    };
				case _wnt.Image:
				case _wnt.Script:
				case _wnt.CSS:
					objExecute	= new this[t](cb,o);
					break;
			} 
			return objExecute;
		}
			
		this.XML = function(cb,o,method) 
		{
			this.callback = cb;
			this.request = o;
			this.isRunning = true;

			var xml = this.resource = new XMLHttpRequest();

			//notify start callback that operation is about to begin
			$Network.Events.onrequest.fire(o);
			var url = o.absoluteUrl;
			if (method)
			{
				xml.open("POST",url,true);
				if (_isIE)
				{
					xml.setRequestHeader("Accept-Encoding", "gzip, deflate");
				}		
			}
			else
			{
				xml.open(o.verb,url,true);
			}
			if (o.headers)
			{
				for (var h in o.headers)
				{
					xml.setRequestHeader(h,o.headers[h]);
				}
			}				
			if (o.canary)
			    xml.setRequestHeader(o.canary.n,o.canary.v);
			xml.onreadystatechange = $CD(this,this.complete);
				
			if (o.timeout)
				o.timer = setTimeout($CD(this,this.timeout),o.timeout);	
			try
			{
				// Hotmail 114425:  if you are working offline (IE setting), the xml.send
				// can throw an exception here
				// IE7 send can't be called with a null value - Other browser's must have value past in (even if null)
				if (o.postString || _isMoz)
					xml.send(o.postString);
				else
					xml.send();
			}
			catch (ex)
			{
				$Network.Events.onhttperror.fire(xml);
				if (o && o.timer)
					clearTimeout(o.timer);
				this.complete(true);
			}
			
		};
		
		this.XML.prototype = 
		{
			_cleanup : function()
			{
				var o = this.request,xml = this.resource;
				if (o && o.timer)
					clearTimeout(o.timer);			
				try
				{
					xml.onreadystatechange = Function.emptyFunction;			
				}
				catch (ex) {};			
			},
			abort : function() 
			{	
				this._cleanup();		
				try
				{
					this.resource.abort();
				}
				catch(ex){};
				if (!$Runtime.unloading)
					this.callback({status:0},this.request);
			},
			timeout : function()
			{
				this._cleanup();
				this.callback({status:404},this.request);
			},
			complete : function(p_blnForce) 
			{
				this.isRunning = false;
				var xml = this.resource;
				if (xml && (p_blnForce || 4===xml.readyState))
				{
					this._cleanup();
					this.callback(xml,this.request);
				}			
			}
		};
		
		
		this.Cache = function(cb, o)
		{
		    this.callback = cb;
		    this.request = o;
		    
	        var el = this.cache = new Image();
	        el.onabort = el.onerror = $CD(this,this._onerror);
	        el.onload = $CD(this,this._onload);
	        el.src = o.absoluteUrl;
	        if (o.timeout)
	            this.timeout = setTimeout($CD(this,this._timeout),o.timeout);
		}
		
		this.Cache.prototype = 
		{
		    _onerror : function()
		    {
		        this.cache.status = 404;
		        blnImagesChecked = this.cache.blnError = true;
		        this.dispose();
		    },
		    _onload : function()
		    {
		        this.cache.status = 200;
			    blnImagesChecked = true; // Once you get a good one, stop checking
		        this.dispose();
		    },
		    _timeout : function()
		    {
		        this.cache.status = 408;
		        this.cache.blnTimeout = true;
		        this.dispose();
		    },
		    abort : function()
		    {
                this.cache.removeAttribute("src");
                this.cache.status = 0;
                this.dispose();		        
		    },
		    dispose : function()
		    {
		        if (!$Runtime.unloading)
		        {
       		        this.callback(this.cache,this.request);
		        }
                if (this.timeout) clearTimeout(this.timeout);
                this.timeout = this.cache = this.callback = this.request = this.cache.onerror = this.cache.onload = this.cache.onabort = null;
		    }
		}
		
		

	this.CSS = function(cb,o) 
		{
			this.callback = cb;
			this.request = o;
			this.isRunning = true;
			this.url = o.absoluteUrl;
			this.resource = null;

			var el = this.getElement(this.url);			
			if (!el)
			{
			    if ((blnCssChecked) || (iFailedImages>=2 && !blnImagesChecked))
			        this._next()
			    else
			    {
			        if (_isIE && !blnImagesChecked && (!o.timeout || o.timeout<3500))
			            this.timeout = setTimeout($CD(this,this._timeout),3500);
			        this.cache = new $Network.Execute.Cache($CD(this,this._next),o);    // Pre-cache using image to avoid blocking UI thread
			    }
			}
			else			    
				cb(el,o);
		}		
		
		this.CSS.prototype = 
		{
		    _timeout : function()
		    {
		        if (this.cache && this.cache.cache.readyState==="uninitialized")
		        {
		            blnCssChecked = true;
		            this.cache.abort();
		        }
		    },
			_next : function()
			{
			    if (this.timeout) clearTimeout(this.timeout);

			    var objPool = this.request.pool;
				var el = this.resource = objPool || _ce("link");
	            if (!objPool)
                {				
				    el.rel = "stylesheet";
				    el.type  ="text/css";				
				}
				el.href = this.url;	
				el.onreadystatechange= $CD(this,this.complete);														
				if (!objPool)
				    _dh.appendChild(el);
				if (!_isIE)
				{
					el.readyState = "complete";
				}
				this.cache = null;
				this.complete();				
			},
			getElement : function(strUrl)
			{
				var arLink = _get("link");
				for (var i=arLink.length-1;i>=0;i--)
				{
					var obj = arLink[i];
					if ($Request.resolveUrl(obj.href)===strUrl)
						return obj;
				}
				return null;
			},
			abort : function() 
			{
			    if (this.timeout) clearTimeout(this.timeout);
			    var el = this.resource;
			    if (el)
			    {
				    try
				    {
					    el.removeAttribute("href");
					    el.onreadystatechange = null;
				    }
				    catch (ex)
				    {
				    }
                }
                if (this.cache)
                    this.cache.abort();
				this.cache = this.resource = null;
				if (!$Runtime.unloading)			
					this.callback({status:0},this.request);
			},
			complete : function() 
			{
				this.isRunning = false;
				var el = this.resource;
				if (el && el.onreadystatechange && ("loaded"===el.readyState || "complete"===el.readyState))
				{
					this.callback(el,this.request);
					this.resource = el = el.onreadystatechange = null;
				}			
			}
		};
		
		
		this.Image= function(cb,o) 
		{
			this.callback = cb;
			this.request = o;
			this.isRunning = true;


			if (iFailedImages==2 && !blnImagesChecked)	// If two fail to start downloading, assume images disabled for all future requests
				this.complete({status:408,src:o.absoluteUrl});
		    else
		    {
        		if (!blnImagesChecked && _isIE && (!o.timeout || o.timeout<3500))
        		    this.timeout = setTimeout($CD(this,this._timeout),3500);
        		this.cache = new $Network.Execute.Cache($CD(this,this.complete),o)
        	}


		};
		this.Image.prototype = 
		{
		    _timeout : function()
		    {
		        if (this.cache && this.cache.cache.readyState==="uninitialized")
   		            this.cache._timeout();
		    },
			abort : function() 
			{
			    if (this.timeout) clearTimeout(this.timeout);
				if (!$Runtime.unloading)				
					this.callback({status:0},this.request);
			    if (this.cache)
			        this.cache.abort();
			    this.isRunning = false;
			    this.request = this.callback = this.cache = null;
			},
			complete : function(el) 
			{
			    if (this.timeout) clearTimeout(this.timeout);
			    if (_isIE && el.status===408 && el.readyState==="uninitialized")
                    iFailedImages++;
				this.isRunning = false;
				if (!$Runtime.unloading)				
					this.callback(el,this.request);
				this.request = this.callback = null;
			}
		};


		this.Script= function(cb,o) 
		{
			this.callback = cb;
			this.request = o;
			this._count = 0;
			this._run();
		};
		this.Script.prototype = 
		{
		
			_run : function()
			{

				var o = this.request,strUrl = this.url = o.absoluteUrl.toLowerCase(),el  = this.resource = this.getElement(strUrl),elExec,cbDel;
				if (_isIE && !(this.request.flags & N.Flags.CACHEONLY))
				    cbDel = $CD(this,this._precomplete);
				else
				    cbDel = $CD(this,this.complete);

			    if (_isIE && !el)
   					elExec = this.resource = N.Execute.Script.outstanding[strUrl];


				this._count++;						
				
				if (o.hash)
				{
					this.hash = true;
					this.constructor.hash[o.hash] = false;
				}
				
				if (!el)
				{
					if (elExec)
					{
						if (elExec.callbacks == null)
							elExec.callbacks = [];
						elExec.callbacks.push(cbDel);
					}
					else
						{
						this.resource = el = _ce("script");
						if (!_isIE)
						{
							el.onload = el.onerror = cbDel;
							el.readyState = "loaded";
							N.Execute.scriptList[strUrl] = el;	// Add to hash					
						
							el.src = o.absoluteUrl;				
							_dh.appendChild(el);					
						}
						else
						{
							N.Execute.Script.outstanding[strUrl] = el;
							el.onreadystatechange= cbDel;
							el.src = o.absoluteUrl;
						}
					}
				}
				else
				{
					if (!_isIE)
					{
						el.readyState = "complete";
						if (el.onload==null)
						{
							this.complete();
						}
						else					
						{
							if (el.callbacks == null)
								el.callbacks = [];
							el.callbacks.push(cbDel);
						}
					}
					else
					{
						this.complete();
					}
				}	
				elExec = null;
			},
			abort : function() 
			{	
				try
				{
					this.resource.removeAttribute("src");
				}
				catch (ex)
				{}
				if (!$Runtime.unloading)
					this.callback({status:0},this.request);
				if (this.resource)
					this.resource=  this.resource.callbacks = null;
				this.onreadystatechange = this.callback = this.request = null;								
			},
			_precomplete : function()
			{
		        var el = this.resource;
				if (el && ("loaded"===el.readyState || "complete"===el.readyState))
				{
					if (!N.Execute.scriptList[this.url])	// Validate it before adding it again
					{
    					el.onreadystatechange = $CD(this,this.complete);
						_dh.appendChild(el);
					}
					else
					    this.complete();
				}							
			},			
			complete : function() 
			{
				var el = this.resource;
				if (el && ("loaded"===el.readyState || "complete"===el.readyState))
				{
					el.onreadystatechange = el.onload = el.onerror = null;
					if (!(this.request.flags & N.Flags.CACHEONLY))
					{
						if (_isIE && this.hash && !this.constructor.hash[this.request.hash])
						{
							if (this._count<=N.Config.retryScripts)	// TODO - implement retry for non IE
							{
								this._run();
								return;
							}
							else
							{
								throw new Error("Script " + this.url + ": " + this.request.hash + "  failed to load.");
							}
						}															
						else
						{
							N.Execute.scriptList[this.url] = el;	// Add to hash
							delete (N.Execute.Script.outstanding[this.url]);
						}
					}
					el.status = 200;
					if (el.callbacks)
					{
						var p = el.callbacks.pop();
						while (p)
						{
							p(el,this.request);
							p = el.callbacks.pop();
						}
						this.callbacks = null;								
					}
					this.callback(el,this.request);
					el = null;
					
				}
			},
			getElement : function(p_strSrc)
			{
				var _rl = $Request.resolveUrl,l = N.Execute.scriptList;
				if (!l)	// Optimize by caching URLs
				{
					l = N.Execute.scriptList = {};
					var i = 0, arrScripts = document.scripts, iCount = arrScripts.length;
					for (i = 0; i < iCount; i++)
					{
						var elScript = arrScripts[i];
						l[_rl(elScript.src).toLowerCase()] = elScript;
					}				
				}
				return l[_rl(p_strSrc).toLowerCase()];
			}
		};
		this.Script.prototype.constructor = this.Script;
		
		
		this.Script.hash = {};
		this.Script.outstanding = {};	// TODO - track outstanding scripts - extra validation against duplicate script requests especially if script errors or alerts
	}	
	


	N.registerScript = function(hash)
	{
		N.Execute.Script.hash[hash] = true;
	}
	
	N._fppReady = false;
	N._fppProxies = [];
	
	N.registerFpp = function(strType,fnClass)
	{
		if (N._fppReady)
		{
			N.FppProxy.create(strType,fnClass)
		}
		else
			N._fppProxies.push({type:strType,classType:fnClass});
	}
	
	var blnRegister = false;

	N.Proxy = 
	{
		proxies : {},
		_registerProxy : 	function(w)
		{
			var strDomain= $Request.extractHost(w.location.href, false);
			N.Domain.list[strDomain]._assignProxy(w);
		},
		generate : function(p_strDomain)
		{
			if (!blnRegister)
			{
				N.registerBaseDomain();
			}

			if (!this.proxies[p_strDomain])
			{
				if (((p_strDomain.endsWith(document.domain)) || (p_strDomain.indexOf(":") > 0 && p_strDomain.substring(0, p_strDomain.indexOf(":")).endsWith(document.domain))))
				{
					var elProxy = _ce("iframe");
					elProxy.style.display = "none";
					elProxy.src = location.protocol + "//" + p_strDomain + "/xmlProxy.htm?vn=" + $Version + "&domain=" + document.domain;		
					document.body.insertAdjacentElement("afterBegin",elProxy);
					this.proxies[p_strDomain] = elProxy;
				}
			}
			return this.proxies[p_strDomain];
		}
	}

	N.registerBaseDomain = function()	// Register the base domain name for proxying
	{
		if (blnRegister) return;
	    var strDomain = N.currentDomain;
	    if (strDomain.indexOf(":") > 0)  // Strip off port number
	    {
		    strDomain = strDomain.substring(0, strDomain.indexOf(":"));
	    }
		if ($Config.domain && !$Config.domain.endsWith("."))
        {
		    document.domain = $Config.domain;
		}
		else if (strDomain.endsWith(".com"))
		{
		    var idx = strDomain.indexOf(".");		
		    if (idx>0)
		    {
			    document.domain = strDomain;
			    try
			    {
				    while (idx>=0)
				    {
					    strDomain = strDomain.substring(idx+1);
					    if (strDomain!="com")
					    {
						    document.domain = strDomain;
						    idx = strDomain.indexOf(".");
					    }
					    else
						    idx = -1;
				    }
			    }
			    catch (ex)
			    {
			    }
		    }
		}
		else
		    throw new Error("Missing/Bad $Config.domain");
		blnRegister = true;
	}

	// TODO : Move all config variables into this object (or move this out);
	N.Config = 
	{	
		retryScripts : 2,		// How many times to auto-retry a failed script that uses a hash
		maxActive : 2
	}
	
	if ($Config.applyAppDomain)	// Normalize the domain depending on the configuration
		N.registerBaseDomain();
}

function $Request(p_strUrl,p_type,p_flags,p_context) // Change to config object
{
	this.id = $Request.counter++;	// Generate a unique id
	this.url = p_strUrl;
	this.flags = p_flags;
	this.context = p_context;
	this.headersString = "";
	this.type = p_type;
	this.domain = $Request.extractHost(this.resolve());
	this.verb = "GET";
}

$Request.prototype = 
{
	setHeaders : function(p) {
		this.headers = p;
		var strList = "";
		for (var s in p)
		{
			strList	+= s + "=" + p[s] + "&";
		}		
		this.headersString = strList;
		return this;
	},
	setVerb : $FN("verb"),
	setTimeout : $FN("timeout"),
	setContext : $FN("context"), 
	setFlags : $FN("flags"),
	setPriority : $FN("priority"),
	setObject : $FN("pool"),
	setHash : $FN("hash"), 
	setGroup : $FN("group"),
	setName : $FN("name"),
	setHost : $FN("host"),
	setPostString : function(s)
	{
		this.postString = s;
		this.resolve();
		return this;
	},
	toString : function() {return this.url},
	setUrl : function(s)
	{
		this.url = s;
		this.resolve();
	},
	_flags : $Network.Flags,
	getKey : function() 
	{
		var strKey = this.absoluteUrl + "!" + this.headersString;	
		if (this.flags & this._flags.DUPLICATE)
		{
			strKey += "!" + (this.id);	// Make unique key
		}
		return strKey;
	},
	setCanary : function(c)
	{
		if (typeof(c) == "string")
		{
			// Canary support:  locate the cookie and append it to the query string
			this.canary = {n:c,v: $Network.getCookie(c) || ""};
			this.resolve();
		}
		return this;
	},
	resolve : function(strHost) 
	{
		var strUrl = $Request.resolveUrl(this.url,strHost || this.host);
    	if (this.canary && this.type==$Network.Type.Script)
		{
		    strUrl += (strUrl.indexOf("?")>0 ? "&" : "?") + this.canary.n + "=" + this.canary.v;
		}
		if (strHost)
			this.domain = $Request.extractHost(strUrl);
		this.absoluteUrl = strUrl;
		return strUrl;
	}
}

new function()
{
	var R = $Request;
	var objTokens = null;
	
	R.extractHost = function(p_strUrl, p_blnIncludePrototcol)
	{
		var re =   p_blnIncludePrototcol ? this.extractHost.reProtocolAndHost : this.extractHost.reHost;
        var p_strUrl = R.resolveUrl(p_strUrl).toLowerCase();

		return String(p_strUrl).search(re) < 0
			? ''
			: RegExp.$1;
	}
	R.extractHost.reHost            = /^(?:http|https|ftp):\/\/([-.a-z0-9]+(?::[0-9]+)?)(?:\/|$)/i;
	R.extractHost.reProtocolAndHost = /^((?:http|https|ftp):\/\/[-.a-z0-9]+(?::[0-9]+)?)(?:\/|$)/i;

	R.expandUrl = function(str)
	{
		if (str.indexOf("{")>=0) 
		{
			if (!objTokens) // Do this once
			{
				var  c = $Config;
				objTokens = {"culture": c.culture,"theme.url":c.Themes.url,"framework.base":c.baseUrl};
				if (c.URL)
					for (var i in c.URL)
						objTokens[i] = objTokens[i.toLowerCase()]=  c.URL[i];
			}
			str = str.formatTokens(objTokens);
		}
		return str;
	}

	R.resolveUrl = function(str, strSource)
	{
		// Resolves a URL. If no strSource is specified, the base of the current page is used
		if (str==null) return "";
		var str = str.toString();
		
		str = R.expandUrl(str);
		if (!strSource)
		{
			if (!R.Base)
			{
				var elBases = _get("base");
				if (elBases.length>0 && elBases[0].href!="") 
					strSource = R.Base = elBases[0].href;				
				else
				{
					var tmp = location.protocol + "//" + location.host + location.pathname;
				    strSource = R.Base = tmp.substring(0, tmp.lastIndexOf((location.protocol==="file:") ? "\\" : "/")+1);
				}
			}
			else
				strSource = R.Base;
		}
		else
		{
			strSource = R.expandUrl(strSource.toString());		
			strSource = strSource.toString().substring(0,strSource.toString().lastIndexOf("/") + 1);
		}
		if (str.startsWith("/")) str = location.protocol + "//" + location.host + str;
		if (str.indexOf("//")===-1) str = strSource + str;	
		
		function DeleteDoubleDots(p_strPath)
		{
			while(DeleteDoubleDots.reDoubleDot.test(p_strPath))
			{
				p_strPath = p_strPath.replace(DeleteDoubleDots.reDoubleDot, "");
			}
			return(p_strPath);
		}
		
		DeleteDoubleDots.reDoubleDot = /\/[^\/]*\/\.\./;
		
		return DeleteDoubleDots(str);
	}
	
	$Network.currentDomain = $Request.extractHost(document.location);
}



$Accessibility = 
{
	notify : function()
	{
		if (this.enabled)
		{
			if (!this._frame)
			{
				var o = this._frame = _ce("iframe");
				var s = o.style;
				s.width = s.height = "1px";
				s.top = s.left = "-1000px";
				s.position = "absolute";
				o.tabIndex = -1;
				document.body.insertAdjacentElement("afterBegin",o);
			}
			this._frame.contentWindow.location.replace("about:blank");
		}
	},
	_frame : null,
	enabled : ($Config.accessibility!=null ? $Config.accessibility : true)
}


$Counter = 0;

// $Binding base class - replacement for Web.Bindings.Base

// TODO - double check owner versus _owner properties

// Gadget will be used for manifest and securing "bindings"


// -----------------------------------------------------------
// Class: Binding
// Description: The Base class for all client-based components
// Arguments:
//    p_element: The element to bind each class instance too
//    p_htParams: The class-level defaults 
//    p_strNamespace: The associated namespace to look for instance level defaults
//
//  To merge instance level defaults with the class level, you must call this.getParameters in your constructor

var $Binding = function(p_element,p_htParams,p_strNamespace)
{
	$Binding.initializeBase(this,arguments);
    this._element = p_element;
    this._htParams = p_htParams || {};
    this._strNamespace = p_strNamespace;
    var sType = (p_htParams && typeof(p_htParams.skipclass)) || "";
    // Fix skip class to work at a class by class basis (basically look if the return value is an "" rather than the value)
	this._blnClass = (!p_htParams || sType==="undefined") ? this.constructor.skipClass : (sType==="boolean" && p_htParams.skipclass) || p_htParams.skipclass=="true";        
	var strClass = this.constructor.applyClass(true);
    if (!this._blnClass && strClass!="")	
	    p_element.className += " " + strClass;   
    if (!p_element.webBindings)
        p_element.webBindings = [this];
    else
		p_element.webBindings.push(this);                
	return this;
}

new function()
{
    
    //$Binding.prototype = new $Event.DOM();

	var G = $Binding,g = G.prototype;
    
    
    function BindingChangedNotification(p_objChangedBinding, p_blnRemoved)	// Fire onbinding/onunbinding events
    {
        // notify any bindings on the same element as this binding
        var wb = p_objChangedBinding._element.webBindings;
        if (wb)
        {
			var iCount = wb.length;
			if (iCount>1)	// No reason to run the loop if only 1 item
			{
				var strEventName = p_blnRemoved ? "onunbinding" : "onbinding";			
				for (var i=0; i < iCount; i++)
				{
					var objBinding = wb[i];
					if (objBinding != p_objChangedBinding)
					{
						objBinding[strEventName](p_objChangedBinding);                            
						if (!p_blnRemoved)
							p_objChangedBinding[strEventName](objBinding);
					}
				}
			}
        }
    }    
    
	g.initialize = function(p_owner, p_blnSkipRegister) 
    {
		// p_blnSkipRegister allows you to manually control when the callback sequence occurs
		// If past in, then you must call register()
        // add this binding to the list of bindings on the scope
		if (!this._registered)
		{
			if (!p_blnSkipRegister)
			{
					this._registered = true;
					this.parentScope = (p_owner && p_owner.scope) || $Binding.Scope.Root;    
					this.parentScope.add(this); // Validate
					BindingChangedNotification(this, false);
			}
			else
				this._owner = p_owner;
		}
    }    
    
	g.getParameters = function()
    {
        // merge the namespace'd arguments from the given element with
		// the values from the given hashtable (p_htSource)

		function MergeArguments(p_el, p_strNamespace, p_htSource, p_objList)
		{

			var htMerged = {};
			if (p_strNamespace)
			{
				for (var i in p_objList)
				{
					var strAttribName = i.toLowerCase();
					try
					{
						var strValue = p_el.getAttribute(p_strNamespace + ":" + strAttribName);
						if (strValue)
						{
							htMerged[strAttribName] = new String(strValue);
							htMerged[strAttribName].Default = p_htSource[strAttribName];
						}
						else
							htMerged[strAttribName] = p_htSource[strAttribName];
					}
					catch (ex)
					{
						htMerged[strAttribName] = p_htSource && p_htSource[strAttribName];
					}
				}
			}
			return(htMerged);
		}
        if (!this._blnMerge && this._strNamespace && this.constructor.Params)    // Only merge when required
		{
			this._htParams = MergeArguments(this._element, this._strNamespace, this._htParams, this.constructor.Params);
            this._blnMerge = true;
        }
        return this._htParams;
                            
	};

	g.registerFor = function(fnType, p_fncCallback, p_elScope)
    {
		// Register for notifications when a specific binding class is created.
		// RegisterFor("*") registers for all bindings that share the same scope
		// RegisterFor("class") or RegisterFor(classType) registers for all bindings of that type that share the same scope
        if (!this._aobjRegistrations) this._aobjRegistrations = [];        
        this._aobjRegistrations.push(this.parentScope.registerFor(fnType, p_fncCallback, p_elScope))
    };


    g.getIdentity = function()
	{
		return this._element.getAttribute(this._strNamespace + ":id") || "";
	};


	g.isChained = function(strClass)	// TODO - Populate chains with chained classes and also dispose of chains when parent is disposed
	{
		return this.chains && this.chains[strClass];
	}
	
	g.addChain = function(objBinding)
	{
		if (!this.chains) this.chains = {};
		this.chains[objBinding.constructor.__typeName] = objBinding;
	}

	g.dispose = function(p_blnUnloading) 
	{    
		// Only called when page is unloaded or the element is being unbound
		// NOTE - if an element with bindings is deleted from the document without first
		// calling the removeBindings method, this function will not be called (basically a misuse of this library)
		if (!this._element || this.__disposing) return;
		this.__disposing = true; // Used to prevent reentry at teardown
		$Binding.callBaseMethod(this,"dispose",arguments);
		this.parentScope.remove(this);

		var obj = this._element.webBindings;
		if (obj)
			if (p_blnUnloading)	// Tear-down optimization
				this._element.webBindings = null;
			else                
				if (obj.length===1)
					this._element.removeAttribute("webBindings");
				else
					obj.remove(this);                        // Remove registration from element
		
		if (!$Runtime.unloading)                // Remove class names only if not unloading page
		{
			BindingChangedNotification(this, true);                            // Send notifications            
			if (!this._blnClass)
			{
				var strReplace = this.constructor.removeClass(this._element.className);
				if (strReplace!=this._element.className)
					this._element.className = strReplace;
			}
			if (this._aobjRegistrations)    // Remove registrations
			{
				var objReg = this._aobjRegistrations.pop();
				while (objReg)
				{
					this.parentScope.unregisterFor(objReg);
					objReg = this._aobjRegistrations.pop();
				}
			}
			if (this.chains)
			{
				for (var i in this.chains)
					this.chains[i].dispose();
			}
		}
	    
		if (this._htParams && this._htParams.xmlSources)
			this._htParams.xmlSources = null;
	
		this.chains = this._aobjRegistrations = this._rule = this._definition = this._htEvents = this._owner = this._htParams = this.scope = this._element = this.parentScope = null; // Null everything out
	};

	g.register = function()
	{
		this.initialize(this._owner);
    };
    
	g.getType = function() {return this.constructor.__typeName},
	g.onchain = g.onunbinding = g.onbinding = Function.emptyFunction;

    G.skipClass = true;
    G.registerClass("$Binding","$Event.DOM");
}

$Binding.Scope = function(p_objBinding)
{
	this.owner = p_objBinding;			// Binding that owns this scope 
	this.childBindings = {"_untyped":[]}; // Bindings keyed by type
	this.aobjRegistrations = [];	// registerFor callbacks for this scope
	this.definitions = {};			// Gadget Definitions 
}

$Binding.Scope.prototype = 
{
	getBinding : function()
	{
		return this.owner || $Binding.Scope.Root;
	},
	__de : document.documentElement,
	_checkRegistration : function(p_objBinding,p_blnLoad)
	{
		var childList = p_objBinding.constructor._childBase;
        if (childList)
        {
			var iCount = childList.length;
            for (var iClass=0;iClass<iCount;iClass++)
            {
            
                var objReg = this.aobjRegistrations[childList[iClass]];
                if (objReg)
                {
                    for (var i=objReg.length-1;i>=0;i--)
                    {
						var objItem = objReg[i];
                        if (objItem.elRoot===this.__de || objItem.elRoot.contains(p_objBinding._element))
                        {
                            objItem.fnCallback(p_objBinding,p_blnLoad);
                        }
                    }
                }
            }
        }	
	},
	add : function(p_objBinding)
	{
        var strType = p_objBinding.constructor.__typeName || "_untyped";
        if (!this.childBindings[strType])
            this.childBindings[strType] = [p_objBinding];
        else
			this.childBindings[strType].push(p_objBinding);
        this._checkRegistration(p_objBinding,true);
	},
    remove : function(p_objBinding)
    {
		if (!$Runtime.unloading)	// Short circuit
		{
			this._checkRegistration(p_objBinding,false);
			this.childBindings[p_objBinding.constructor.__typeName || "_untyped"].remove(p_objBinding);
		}
        if (p_objBinding.scope)
        {            
            p_objBinding.scope.dispose();
        }
    },
    unregisterFor : function(objItem)
    {
		objItem.task.dispose();
        this.aobjRegistrations[objItem.strBinding].remove(objItem);
		if ($Binding.Scope.Root!=this)
		{
			this.owner.parentScope.unregisterFor(objItem);
		}
        
    },
	registerFor : function(p_vBinding, p_fnCallback, p_elRoot)
    {
        var strBinding = p_vBinding;
        if (strBinding==="*" || strBinding==="Web.Bindings.Base")    // Support old "*" and legacy base binding 
        {
            strBinding = "$Binding";
            p_vBinding = $Binding;
        }
		else
			if (typeof(p_vBinding)==="function")    // Support old function approach
			{
				strBinding = p_vBinding.__typeName;
			}
			else
			{
				p_vBinding = Function.parse(p_vBinding);
			}
        
        
        var objItem = {fnCallback:p_fnCallback,elRoot:p_elRoot || this.__de,strBinding:strBinding,task : new $Task()}, pb = p_vBinding && p_vBinding._parentBase;
        if (!this.aobjRegistrations[strBinding])
            this.aobjRegistrations[strBinding] = [objItem];
        else
			this.aobjRegistrations[strBinding].push(objItem);
        if (pb)
        {
			var i=0,iCount = pb.length;
			for (i;i<iCount;i++)
			{
				var objRef = this.childBindings[pb[i]]
				if (objRef)
				{
					var iRefCount = objRef.length;
					for (var iRef=0;iRef<iRefCount;iRef++)
					{
						if (objItem.elRoot===this.__de || objItem.elRoot.contains(objRef[iRef]._element))
						{
							objItem.task.add($CC(objItem.fnCallback,[objRef[iRef],true]));
						}
					}              				
				}
			}
        }
		if ($Binding.Scope.Root!=this)
		{
			this.owner.parentScope.registerFor(p_vBinding, p_fnCallback, p_elRoot);
		}
		objItem.task.run();
        return objItem;
    },
	dispose : function()
    {
		var i,l = $Runtime.unloading;
		for (i in this.childBindings)
		{
			var c = this.childBindings[i];
			for (var j=c.length-1;j>=0;j--)
			{
			    var b = c[j];
			    if (b && !b.__disposing)
				    b.dispose(l);
			}
		}
        
		for (var d in this.definitions)
		{
			this.definitions[d].dispose();
		}

		this.owner = this.childBindings = this.aobjRegistrations = this.definitions = this.elements = null;
    }              
}

$Binding.Scope.Root = new $Binding.Scope();


// Gadget Definition
// Defines the resources that make up a gadget
// Returns a gadget type (but the type can also be "pre-bound" to elements as a short-circuit)

// Definition Factory - returns a gadget definition
// p_strType : the type of the gadget
// p_owner : A gadget manifest type (optional) - specifies the manifest creating the definition // TO-DO: Simplify network object to come from manifest
$Binding.define = function(p_strType, p_root, p_owner)
{
	return new $Binding.Definition(p_strType,p_root,p_owner);	
}


// Gadget Definition Class
$Binding.Definition = function(p_strType, p_root, p_owner)
{
	this.id = "$Def" + ($Counter++);
	this.type = p_strType;
	this.owner = p_owner;
	this.root = p_root || document.documentElement;
	this.task = null;
	this.isAttaching = this.isBound = false;
}

$Binding.Definition.prototype = 
{
	bindTo : function(p_element,p_extra) 
	{
		if (!this._elements) this._elements = [];
		this._elements.push(p_element);
		if (p_extra)
			for (var i=1;i<arguments.length;i++)
				this._elements.push(arguments[i]);		
		return this;
	},
	// Attach to a selector
	bindCss :  function(p_selector, p_root) 
	{
		if (!p_selector) return this;
		if (!this._selectors) this._selectors = [];	
		if (p_selector instanceof Array)
			for (var i=0;i<p_selector.length;i++)
				this._selectors.push({sel: p_selector[i], root: p_root || this.root});
		else
			this._selectors.push({sel: p_selector, root: p_root || this.root});
		return this;
	},
	setDefaults : $FN("_defaults"),
	setNS : $FN("ns"),
	addChain : function()
	{
		var argLength = arguments.length;
		if (!this.chain) 
		{
			this.chain = []; 
			this.readyChain = [];	
		}
		for (var i=0;i<argLength;i++)
		{
			var a = arguments[i];
			if (a instanceof Array)
				this.chain.addRange(a)
			else
				this.chain.push(a);			
		}
		return this;
	},
	setScope : function (p_objBinding)
	{
		this.scope = (!p_objBinding || p_objBinding===$Binding.Scope.Root) ? $Binding.Scope.Root : p_objBinding.scope;
		if (!this.scope)
			this.scope = p_objBinding.scope = new $Binding.Scope(p_objBinding);
		return this;
	},
	setSync : $FN("_async"),
	_onchain : function(r,o)	
	{
		this.readyChain.push(o);
		o.def.attach();
	},
	create : function(el,r,p_callback)	
	{		
	    if (!el) return;
		var g = this.gadgetDef =  (this.gadgetDef || Function.parse(this.type)),arrBindings = el.webBindings,objOwner = null;
		if (arrBindings)
		{
			// if this element is already bound to this type,
			// then just return the binding instance
			for (var i=arrBindings.length-1; i >= 0; i--)
			{
				var objBinding = arrBindings[i];
				if (g === objBinding.constructor)
				{
					return(objBinding);
				}
				if (this.parent && (objBinding.constructor===this.parent.gadgetDef))	// Validate parent chain still exists
				{
					objOwner = objBinding;
				}
				
			}
		}
		if (!objOwner || (objOwner && !objOwner.__disposing))	 	
		{
			var objBinding = new g((window.$MemoryMgr!=null) ? $(el) : el,this._defaults || {},this.ns)  
			objBinding._definition = this;
			objBinding._rule = r;
			try
			{
			    objBinding.initialize(this.scope);
			}
			catch (ex) 
			{
	            $Tracing.Error.submitFromException(ex,this.type,$Tracing.Error.BindingInit);
	            if ($Debug.enabled)
	                throw (ex);
			}
			
			if (this.readyChain)
				for (var i=this.readyChain.length-1;i>=0;i--)
				{
					this.readyChain[i].def.create(el,r,null);
				}
			if (p_callback || !this._async)
				this.objBindings.push(objBinding);
			if (objOwner)
			{
				objOwner.onchain(objBinding);
				objBinding.onchain(objOwner);
			}
		}
	},

	attach : function(p_callback)
	{
		var me = this,chainCount = me.chain && me.chain.length;
		if (this.isBound) throw new Error("Binding Complete");
		this.isAttaching = this.isBound = true;
		this.scope = me.scope || $Binding.Scope.Root;
		this.scope.definitions[this.id] = this;

		if (p_callback || !this._async)	// Only track if we have to
			this.objBindings = [];
		function onfinished()
		{
			me.task = null;
			me.isAttaching = false;
			if (p_callback)
				p_callback(me.objBindings,me);
			if (chainCount>0)
			{
				for (var i=0;i<chainCount;i++)
				{
					var o = me.chain[i],def = o.def;
					if (def.type)
					{							
						def.root = me.root;
						def.owner = me.owner;
						def.parent = me;

						if (def.type && def.root!=null && !(def.preload))
						{
							if (o.network && !o.network.isLoaded)
							{
								o.network.setContext(o).load($CD(me,me._onchain));
							}
							else
								me._onchain(null,o);	
						}
						else
							if (o.network)
								o.network.load()
					}
				}
			}
		}	
		
		
		if (me._selectors || me._elements)
		{
			if (this._async)
				this.task = new $Task(onfinished);
			if (me._elements)
			{
				me._elements.forEach(function(o) {
					if (me._async)
					{
						me.task.add($CC($CD(me,me.create),[o,false,p_callback]));
					}
					else
					{
						me.create(o,false);
					}
				});
				if (chainCount>0)
					for (var j=0;j<chainCount;j++)
					{
						me.chain[j].def.bindTo.apply(me.chain[j].def,me._elements);
					}

			}
			if (me._selectors)
			{
				var objCss = $Dom.Css;
				for (var i=me._selectors.length-1;i>=0;i--)
				{

					var o = me._selectors[i];
					if (!o.selector) o.rules = objCss.createRules(o.sel);
					var m = objCss.getElementsByCssSelectorRules(o.rules,o.root),oCount = m.length;
					if (oCount>0)
					{
						if (chainCount>0)
							for (var j=0;j<chainCount;j++)
							{
								me.chain[j].def.bindTo.apply(me.chain[j].def,m);
							}
						for (var j=0;j<oCount;j++)
						{
							if (this._async)
								me.task.add($CC($CD(this,this.create,p_callback),[m[j],o]));
							else
								this.create(m[j],o);						
						}
					}
				}
			}

			if (chainCount>0)
			{
				for (var i=0;i<me.chain.length;i++)
				{
					var o = me.chain[i];
					if (o.network)
						o.network.load(); // Start pre-loading
				}
			}
			
			
			if (this._async)
				this.task.run();
			else
				onfinished();			
		}
		else
		{
			if (this.root)
			{
				this.create(this.root,false);
			}
			onfinished();
		}	
		return this.objBindings;
	},
	isValid : function(el,blnAdd)  // TODO - fix this
	{
		var blnMatch = false;
		if (this._selectors)
		{
			for (var i=0;i<this._selectors.length;i++)
			{
				var o = this._selectors[i];
				
				if ($Dom.Css.doesElementPassRules(el,o.rules,o.root))
				{
					this.create(el,o.rules);
					blnMatch = true;
				}				
			}
		}
		
		// TO DO - check that binding is on element
		return blnMatch;
	},
	validate : function()  // fix - if duplicate binding, the wrong selector will be tested (not the one for this definition);
	{
		if (this._selectors && this.isBound)
		{
			var str = this.gadgetDef.__typeName || "_untyped",ch = this.scope.childBindings[str];
			if (ch)
			{
				for (var i=ch.length-1;i>=0;i--)
				{
					var o = ch[i];
					if (o._definition===this && o._rule)
					{
						var e = o._element;
						if (!$Dom.Css.doesElementPassRules(e,o._rule.rules,o._rule.root))
						{
							o.dispose();
						}
					}					
				}
			}
		}
	},
	dispose : function()
	{	
		var blnUnload = $Runtime.unloading;
		if (this.isBound)
		{
			if (this.task)
				this.task.dispose();
			if (!blnUnload && this.gadgetDef)
			{
				var str = this.gadgetDef.__typeName || "_untyped",ch = this.scope.childBindings[str];
				if (ch)
				{
					for (var i=ch.length-1;i>=0;i--)
					{
						if (ch[i]._definition===this)
						{
						    try
						    {
							    ch[i].dispose(blnUnload);
                            }
                            catch (ex)
                            {
                                if ($Debug.enabled)
                                    throw(ex);
                            }							
						}
					}
				}
				if (this.chain)
					for (var i=this.chain.length-1;i>=0;i--)
					{
						var o = this.chain[i];
						if (o.network && o.network.isExecuting) o.network.abort();
						o.def.dispose();
					}
			}
		}		
		this.parent = this.readyChain = this.chain = this.owner = this.root = this.scope = this._elements = this._selectors = this.objBindings = null;
		this.isBound = this.isAttaching = false;
	}
}


$Binding.load = function(p_cb,p_def,p_net)
{
	if (p_def)
	{
		if (!p_net)
			p_def.attach(p_cb)
		else
			p_net.load($Binding._runBind(p_cb,p_def,p_def._key))
	}
}

$Binding.extend = function(p_el, p_objScope)
{
	var objScope = p_objScope || $Binding.Scope.Root,elDefs = objScope.definitions;
	for (var d in elDefs)
	{
		elDefs[d].isValid(p_el,true);
	}
}

$Binding.validate = function(p_el, p_objScope)	// TODO - Fix
{	
	if (p_el.webBindings)
	{
		for (var i=p_el.webBindings.length-1;i>=0;i--)
		{
			var b = p_el.webBindings[i];
			if ((!p_objScope || b.scope===p_objScope || (p_objScope && p_objScope.scope===b.scope)) && b._definition)
			{
				if (!b._definition.isValid(p_el))
					b.dispose();
			}
		}
	}
	$Binding.extend(p_el,p_objScope);
}

// Removes all bindings from an element
$Binding.remove = function(p_el)
{
    if (p_el && p_el.webBindings)
    {
        for (var i=p_el.webBindings.length-1; i >=0; i--)
        {
            p_el.webBindings[i].dispose(false);
        }
                
        p_el.removeAttribute("webBindings");
        return true;
    }
    return false;
}


$Runtime.onunload.attach(function()
{
	$Binding.Scope.Root.dispose();
}
);

// These two functions are to create a smaller-scoped closure than including them in the "parse" routine 
// TODO - eliminate need for below callbacks if no callback is specified (hardwire directly in parse)
$Binding._complete = function(c,s)
{
	return function()
	{
		if (!s)
		{
			if (c)
				c();
			return;
		}
			
		$Binding._runCount[s]--;
		if ($Binding._runCount[s]===0)
		{
			delete $Binding._runCount[s];
			if (c)
				c();
		    if (s===$Binding._init && $Config && $Config.TraceData && $Config.TraceData.SkipInitPLT != "1")
		    {
		        $Tracing.PLT();
		    }
		}
	}
}

$Binding._runBind = function(c,d,s)
{
	return function(o)
	{
        if (o)
        {
		    var arrXml = [], iCount = o.length;
		    for (var i=0;i<iCount;i++)
		    {
			    var objSource = o[i];
			    if (objSource.type===$Network.Type.XML)
			    {
			        arrXml.push(objSource.resource);
			        if (objSource.name)
    				    arrXml[objSource.name] = objSource.resource;						
    		    }
		    }
		    if (!d._defaults) d._defaults = {};
		    d._defaults.xmlSources = arrXml;
	    }
		d.attach($Binding._complete(c,s));	
	}
}

$Binding._runCount = {};

$Binding.parse = function(p_callback, p_blnAsync, p_elementRoot, p_rootScope, p_blnPreload)
{
	var elRoot = p_elementRoot || document,_ganyel = $Dom.getAnyElementByTagName,_wup = Array.$Prioritizer,_wupp = _wup.Priorities,strPrefix = "web:",aelWebBindings;
    if (p_elementRoot && (p_elementRoot.tagName===(strPrefix + "binding")))
		aelWebBindings = [p_elementRoot];
	else
		aelWebBindings =_ganyel(strPrefix + "binding",elRoot,true);  
    var iBindingCount = aelWebBindings.length,arrBindings = new _wup(),strPriority,definitions = [],G = $Binding;
    var blnInit = $Binding._init;
    
    if (!blnInit)
        $Tracing.initTime = (new Date()).getTime();
	if (iBindingCount>0)
	{
	    var sIndex = "r" +($Counter++);
	    G._runCount[sIndex] = iBindingCount;
	    
        if (!blnInit)
            $Binding._init = sIndex;
		for (var i=0; i < iBindingCount; i++)
		{
			var elItem = aelWebBindings[i],elChild = _ganyel(strPrefix + "references",elItem,true),strPriority=null;
			if (elChild && elChild.length>0)
				strPriority = elChild[0].getAttribute("priority");
			strPriority = (strPriority && _wupp[strPriority.substring(0,1).toUpperCase() + strPriority.substring(1).toLowerCase()]) || _wupp.High;
			arrBindings.queue(elItem,strPriority);
		}
		var elDef = arrBindings.dequeue(),p = $Parser,pb = $Parsers.Definitions.Binding,g = G.Definition,bl = $Binding.load;
		while (elDef)
		{
			var o = p(pb,elDef,{def: new g(null,p_elementRoot,p_rootScope)}),def = o.def;
			// Override defaults
			if (p_blnAsync!=null)
				def._async = p_blnAsync;
			if (p_blnPreload!=null)
				def.preload = p_blnPreload;
			if (def.type && def.root!=null && !(def.preload))
			{
				def._key = sIndex;
				bl($Binding._complete(p_callback,sIndex),def,o.network);
				definitions.push(def);	
				o = null;
			}
			else
				if (o.network)
					o.network.load($Binding._complete(p_callback,sIndex))
				else
					G._runCount[sIndex]--;	// Skip
			elDef = arrBindings.dequeue();		
		}
	}
	else
	{
		if (p_callback) p_callback();
		if (!blnInit && $Config && $Config.TraceData && $Config.TraceData.SkipInitPLT != "1")
		{
		    $Binding._init = true;
		    $Tracing.PLT();
		}
		    
    }
	return definitions;
}


$Binding.version = "1.0";

///////////////////////////////////////////////////////////////////////////////
// Error Manager
// global onerror handler

var $Tracing = 
{
    Error :
    {
    Script:34, //constant for script Errors
    Extraction:35, //constant for error generated during extraction
    Multiple:36, //this EC will be set when we're have multiple script errors on the page.
    BindingInit:37,
    Fire:38,
    Count:0,
    TraceLevels:new $Flags("NoTrace",0,"NoMessage",1,"NoStackTrace",2,"NoParameters",3,"FullTrace",4),
    Submit:function(p_Msg,p_Url,p_Ln,p_Data,p_iErrorCode,p_stackTrace)
    {
        function ExpandArguments(s,a)
        {
            try
            {
                var newargs=[],alist=s.split(",");
                if(alist[0])
                {
                    for (var g = 0; g < alist.length; g++)
                    {
                        if (g > 0)
                        {
                            newargs.push(",");
                        }

                        var sarg = a[g];
                        if (!sarg)
                        {
                            sarg = "null";
                        }
                        newargs.push(alist[g].trim() + "=");

                        if (typeof(sarg) == "string")
                        {
                            newargs.push("'" + sarg + "'");
                        }
                        else if (typeof(sarg) == "function")
                        {
                        
                            var str = sarg.toString(),fname = str.substr(0, str.indexOf("(")); 
                            if(fname=="function")
                            {
                                //we return the 1st 20 characters of the function
                                fname = str.substr(0, str.indexOf("(")+20).trim()+"...}";
                            }
                            newargs.push(fname);
                            
                        }
                        else if (typeof(sarg) == "object")
                        {
                            newargs.push("object");
                        }
                        else
                        {
                            //this gets hit for numbers for example       
                            newargs.push("["+typeof(sarg)+"]"+sarg);
                        }
                    }
                }
                return newargs.join("");
            }
            catch(e){
                var d = e.description;
                if(!d) d = e;
            return "~ERRORIN~ExpandArguments~ "+d;}
        }        
          
        function SubmitTrace(errorCode,stackTrace)
        {
            var cancelTrace = false;
            try{
                if($Tracing.Error.CallBack && $Tracing.Error.CallBack(errorCode,stackTrace,$Config.TraceData.Target)===-1)            
                    cancelTrace = true;            
            }catch(e){}
            
            if(!cancelTrace && $Tracing.Error.Count<4)//arbitrary maximum of 3 errors per page, file downloads may be reported as errors so we want side effects from that too.
            {
                var targetImg = new Image();
                targetImg.src = $Config.TraceData.target+"&ec="+errorCode+"&pl="+escape(stackTrace);
            }        
        }
        
        if(!p_iErrorCode)
        {
            if($Tracing.Error.Count>0)
            {
                p_iErrorCode = $Tracing.Error.Multiple;
            }
            else
            {
                p_iErrorCode = $Tracing.Error.Script;
            }
        }
        $Tracing.Error.Count++;
        
        if(!$Config.TraceData || $Config.TraceData.disable =='1')//nothing to do since we have nowhere to upload
        {
            return false;
        }
        var infoTrace = new Array();
        try
        {
            var traceLevel = parseInt($Config.TraceData.traceLevel);
            var TraceLevels = $Tracing.Error.TraceLevels;
            if(traceLevel > TraceLevels.NoTrace)
            {                
                if(traceLevel>TraceLevels.NoMessage)
                {
                    infoTrace.push("msg=" + p_Msg);
                }
                infoTrace.push("~url=" + p_Url,"~ln=" + p_Ln);
                if(traceLevel>TraceLevels.NoStackTrace)
                {
                    if(p_stackTrace)
                    {
                        infoTrace.push("~cs="+p_stackTrace);
                    }
                    else if (arguments.caller)
                    {
                        // set this...whoever was the caller of $Tracing.Error.Submit
                        var callStack = arguments.caller.callee,depth=0;
                        if (p_Data)
                        {
                            // skip another level (get out of SubmitFromException or equivalent)
                            callStack=callStack.caller;
                        }
                        while (callStack && (depth < 10))
                        {
                            var f = callStack.toString(),args = "-";
                            if(traceLevel >TraceLevels.NoParameters)
                            {
                                args = ExpandArguments(f.substring((f.indexOf("(") + 1), (f.indexOf(")")) ), callStack.arguments);
                            }
                            var fname = (f.substring(0,f.indexOf(")")+1).trim());
                            if(fname.substring(0,9) == "function(")
                            {
                                fname = f.substring(0,f.indexOf(")")+20).trim()+"...}";
                            }
                            infoTrace.push("~cs" + depth + "=" + fname +" "+ args);
                            callStack=callStack.caller;
                            depth++;
                        }
                    }
                }
                infoTrace.push("~fv="+$Version);        
                if (p_Data)
                {
                    infoTrace.push("~data=" + p_Data);
                }                                    
            SubmitTrace(p_iErrorCode,infoTrace.join("").replace(/</g,'%3C'));           
            }
        }
        catch(ex)
        {
            try
            {
                var d = ex.description;
                if(!d)
                {
                    d = ex;
                }
                SubmitTrace($Tracing.Error.Extraction,d);
            }
            catch(ex2){}
        }
    },
    submitFromException:function(p_ex,p_location,p_iErrorCode,p_Data)
    {
        if(p_ex.traced)//if an exception gets rethrown, only trace it the 1st time
        {
            return;
        }
        
        p_ex.traced=true; 
        var ln =0,url = document.location.href,msg = p_ex.description,stck = null;
           
        if($Browser.isMozilla())//firefox has better granularity.
        {
            url = p_ex.fileName;
            ln = p_ex.lineNumber;        
            msg = p_ex.message;
            stck = p_ex.stack;
        }    
        if(p_location)    
            msg+="@"+p_location;    
        if(!p_Data)    
            p_Data = p_location;  
        $Tracing.Error.Submit(msg,url,ln,p_Data,p_iErrorCode,stck);
    }
    },
    _complete : false,
    onplttrace : new $Event(),
    PLT : function()
    {
   	    function pltCallback()
	    {
		    document.title += " PLT: "+$Network.getCookie('plt');
	    }

	    if(!this._complete && $Config.TraceData && $Config.TraceData.disable !="1")
	    {
	        this._complete =true;
	        // only trace it once, if the trace fails then we loose it.
	        try
	        {
	            if(Math.floor(Math.random()*1001)<=parseInt($Config.TraceData.PLTRate))
	            {
	                var _now = (new Date()).getTime();	                	
	                var _pltTarget = $Config.TraceData.target + "&ec=0&it=" + (_now - ($Tracing.initTime || _now)) +"&hft="+(($Config.headerRenderTime)?(_now - $Config.headerRenderTime):"");
	                $Network.fetch($Config.TraceData.renderPLT == "1" ? pltCallback : null, new $Request(_pltTarget,$Network.Type.Image).setPriority($Network.Priority.Lowest));
	                this.onplttrace.fire(_pltTarget);
	            }
	        }
	        catch(ex)
	        {
	        }  
        }                          
    }
}

if($Config.TraceData && $Config.TraceData.disable !='1')
{
    if(!$Browser.isMozilla())
    {
        window.attachEvent("onerror", $Tracing.Error.Submit);
    }
    else //using AttachEvent for onerror on firefox doesn't dispatch the right parameters to $Tracing.Error.Submit
    {
        window.onerror = $Tracing.Error.Submit;
    }
}

// JScript source code

