String.prototype.trim = function()
{
	return this.replace(/^[\s\xA0]+/, "").replace(/[\s\xA0]+$/, "");
}

String.prototype.trimstr = function(str)
{
	var bReg = new RegExp('^[\s\xA0]+' + str, 'i');
	var eReg = new RegExp(str + '[\s\xA0]+$', 'i');

	return this.replace(bReg, "").replace(eReg, "");
};

String.prototype.startsWith = function(str) 
{
	var starts = new RegExp("^"+str);
	return starts.test(this);
};

String.prototype.endsWith = function(str) 
{
	var ends = new RegExp(str+"$");
	return ends.test(this);
};

String.prototype.ConvertToLong = function()
{
	return parseInt(this);
};

String.prototype.stripLeading = function()
{
	return this.replace( /^\s+/g, "");
};

String.prototype.isNumeric = function()
{
	var validChars = "0123456789.";

	for (i = 0; i < this.length; i++) 
	{ 
		var chr = this.charAt(i); 
		if (validChars.indexOf(chr) == -1) 
			return false;
	}
	return this.length > 0;
};

String.prototype.stripTrailing = function()
{
	return this.replace( /\s+$/g, "");
};

String.prototype.strip = function()
{
	return this.stripLeading().stripTrailing();
};

String.prototype.before = function(str)
{
	var s = this;
	var i = s.indexOf(str);
	if (i==-1)
		return s;
	
	return s.substr(0,i);
};

String.prototype.Field = function(index)
{
	var fields = this.split('\t');
	return fields[index] || '';
};

// String.prototype.ReplaceSubString = function(oldStr,newStr)
// {
// //	return String.format(this,newStr);
// 	return this.replace(oldStr,newStr.toString());
// };
// 
String.prototype.after = function(str)
{
	var s = this;
	var i = s.indexOf(str);
	if (i==-1)
		return s;
	
	return s.substr(i + str.length, s.length -1);
};


String.prototype.pad = function(chr, len)
{
	var s = '';
	var diff = len - this.length;
	for (var i=0; i<diff; i++)
		s += chr;
	
	return s + this;
};

// len should always be > 3, or it is ignored
String.prototype.dot_truncate=function(len)
{
	var s = this;	
	if (this.length > len && len > 3)
	{

		s = s.substring(0, len - 3);
		s = s + "...";
	}
	
	return s;
};

Array.prototype.add = function(o)
{
	this[this.length] = o;
};
Array.prototype.insert = function(index,o)
{
	for (var i=this.length-1; i>=index; i--)
		this[i+1] = this[i];
	
	this[index] = o;
	return this;
};

Array.prototype.clone = function()
{
	var newArray = new Array;
	for (var i=0; i<this.length; i++)
		newArray[i] = this[i];
	
	return newArray;
};

Array.prototype.containsArray = function(obj) {
  var i = this.length;
  while (i--) {
    if (this[i].toString() === obj.toString()) {
      return true;
    }
  }
  return false;
};

// Array Remove - By John Resig (MIT Licensed)
 Array.prototype.removeRange = function(from, to) {
   var rest = this.slice((to || from) + 1 || this.length);
   this.length = from < 0 ? this.length + from : from;
   return this.push.apply(this, rest);
 };

if(!Array.toString){
	Array.toString = function(a)
	{
		var s = '';
		for(var i = 0; i < a.length; i++)
		{
			if(i > 0)
				s += ", ";
			s += a[i];
		}
		return s;
	}	
};


Date.prototype.AddDays = function(days) 
{
	this.setTime(this.getTime()+days*86400000);  
	return this;  
};

Date.prototype.indexString = function()
{
	return this.getFullYear().toString() + (this.getMonth()+1).toString().pad('0',2) + this.getDate().toString().pad('0',2);
};

// Import from databank date format.
Date.prototype.importIndex = function(str)
{
	var d = new Date();
	
	var year = str.substr(0,4);
	var month = str.substr(4,2);
	var day = str.substr(6,2);
		
	if (month[0] == "0")
		month = month.substring(1);
	var mInt =  parseInt(month); // can not have a leading zero, or is assumed hex/oct
	
	// month range is from 0 to 11
	d.setFullYear(year, mInt - 1, day);
	return d;
};  

function deepObjCopy (dupeObj) 
{ 
	if (dupeObj == null)
		return null;
		
	var retObj = new Object(); 
	if (typeof(dupeObj) == 'object') 
	{ 
		if (typeof(dupeObj.length) != 'undefined') 
			var retObj = new Array(); 
		for (var objInd in dupeObj) 
		{    
			if (dupeObj[objInd] && dupeObj[objInd].isLang) // language object
			{
				retObj[objInd] = dupeObj[objInd];
			}
			else if (typeof(dupeObj[objInd]) == 'object') 
			{ 
				retObj[objInd] = deepObjCopy(dupeObj[objInd]); 
			} 
			else if (typeof(dupeObj[objInd]) == 'string') 
			{ 
				retObj[objInd] = dupeObj[objInd]; 
			} 
			else if (typeof(dupeObj[objInd]) == 'number') 
			{ 
				retObj[objInd] = dupeObj[objInd]; 
			} 
			else if (typeof(dupeObj[objInd]) == 'boolean') 
			{ 
				((dupeObj[objInd] == true) ? retObj[objInd] = true : retObj[objInd] = false); 
			} 
			else if (typeof(dupeObj[objInd]) == 'function') 
			{ 
				retObj[objInd] = dupeObj[objInd];
			} 
		} 
	} 
	return retObj; 
}


var eShortDate = 1;
var eAbbreviatedDate = 2;
var eLongDate = 3;
var eTurboToolsDate = 4;
var eFileDate = 5;
Date.prototype.DtString = function(dispType, showWeekday)
{
	showWeekday = showWeekday || false;
	
	// For now, keep it simple. Add options as needed.
	return this.toLocaleDateString();
};

Date.prototype.TmString=function()
{
	return this.toLocaleTimeString();
};

// Date and time in uniform format.
Date.prototype.LogString=function()
{
	var dt=this.DtString(eShortDate);
	var tm=this.TmString();
	return dt + ' ' + tm;
};

var COMP=
{
	urlBase:'',
	libCache:{},
	cssCache:{},
	V:{ formDefault:'post' }, // vars like collection id
	nonIE:true,
	IE:false,
	isSafari3:navigator.userAgent.indexOf('Safari') > 1 && navigator.userAgent.indexOf('Version/3') > 1,
	isSafari4:navigator.userAgent.indexOf('Safari') > 1 && navigator.userAgent.indexOf('Version/4') > 1,
	isWebkit531:navigator.userAgent.indexOf('WebKit/531.0') > 1,
	isInstalled:navigator.userAgent.indexOf('Alexandria') > 1,
	postLoad:false,
	keepAliveInterval : 30000,

	Assert: function(condition, msg)
	{
		if (!condition)
		{
			if (COMP.V.debug == 'true' && confirm(msg && msg.message ? msg.message : msg || 'Assertion. Do you want to break?'))
			{
				if (window.console) console.assert(condition, msg);
				if (msg) throw msg;
				window.please.crash.now();
			}
		}
		
		return condition;
	},
	initialTicks : (new Date()).getTime(),
	// In milliseconds.
	GetCurrentTicks : function()
	{
		var d = new Date();
		return d.getTime() - COMP.initialTicks;
	},
	
	Root : function(url)
	{
        if (!url) return '';
        var i = url.indexOf('://');
        if (i == -1) return '';
        i += 3;
        i = url.indexOf('/', i);
        if (i == -1) return url+'/';
        return url.substr(0, i+1);		
	},
	
	Base : function(url)
	{
        if (!url) return '';
        var i = url.indexOf('://');
        if (i == -1) return '';
        i += 2;
        var last = url.lastIndexOf('/');
        if (i == last) return url + '/';
        return url.substr(0, last+1);
	},
	
	Location : function(withParams)
	{
		if (withParams != true) withParams = false;
		var url = window.location.href;
		
		if (!withParams)
		{
			url = url.before('?');
			url = url.before('#');
			if (url.endsWith('/'))
				url = url.substr(0,url.length-1);
		}
		
		return url;
	},
	
	Join : function(left, right)
	{
		if (!left)
		{
			left = window.location.href;
			left = left.substring(0, left.lastIndexOf('/')+1);
		}
		if (!right) return left;
		if (right.indexOf('://') != -1)
			return right;
		if (right.charAt(0) == '/')
			return COMP.Root(left) + right.substr(1);
		return COMP.Base(left) + right;
	}
	
	,StringToFourChar : function(str)
	{
		var num = 0;
		for (var i=0; i<str.length; i++)
			num = (num << 8) + str.charCodeAt(i);
		
		return num;
	}
	
	,FourCharToString : function(num)
	{
		var c = String.fromCharCode;
		return c((num & 0xFF000000) >> 24)
			+ c((num & 0x00FF0000) >> 16)
			+ c((num & 0x0000FF00) >> 8)
			+ c((num & 0x000000FF) >> 0);
	}
	
	,GetParamString : function(o, includeSession)
	{
		if (includeSession != true) includeSession = false;
		var str = '';
		for (var p in o)
		{
			if ((p != 'sessionid' || includeSession) && o[p] && o[p].length>0)
				str += p + '=' + o[p] + '&';
		}
		if (str.endsWith('&'))
			str = str.substr(0,str.length-1);
			
		return str;
	},
	
	GetSearchParams : function(url)
	{
		var params = {};
		
		if (!url)
			url = window.location.search.slice(1);
			
		var paramStrs = url.split('&');
		for (var i=0; i<paramStrs.length; i++)
		{
			var p = paramStrs[i].split('=');
			params[p[0]] = p[1];
		}
		return params;
	},
	
	LoadLibrary : function(name, fn, scope)
	{
		name = COMP.GetFullJSName(name);
		
		if (COMP.libCache[name] != undefined) return;
		COMP.libCache[name]='loading';
		
		if (COMP.postLoad)
		{
			var head = document.getElementsByTagName('body')[0];
			var e = document.createElement('script');	// create a script element
			e.setAttribute('type', 'text/javascript');
			e.setAttribute('src', name);			
			if (COMP.IE)
			{
				e.setAttribute('onreadystatechange', function () {
					if (this.readyState == 'complete' || this.readyState == 'loaded')
						COMP.libCache[this.src]='complete';
				});
			}
			else
				e.onload = function() { COMP.libCache[name]='complete' };
			
			head.appendChild(e);
		}
		else
		{
			if(COMP.IE)
			{
				var e = document.createElement('script');	// create a script element				
				e.setAttribute('type', 'text/javascript');
				e.setAttribute('src', name);
				e.setAttribute('onreadystatechange', function () {
					if (this.readyState == 'complete' || this.readyState == 'loaded')
						COMP.libCache[this.src]='complete';
				});
				document.getElementsByTagName('head')[0].appendChild(e);
			}
			else
				document.write('<scr'+'ipt type="text/javascript" src="' + name + '" onload=\'COMP.libCache["' + name + '"]="complete"\'>' + '<' + '\/scri' + 'pt>');
		}
		
		if (fn)
			COMP.RunOnceLoaded([name],fn,scope);
	},
	
	// After the libraries have completed loading, call fn();
	LoadLibraries : function(array,fn,scope,extraConfig)
	{
		for (var i=0; i<array.length; i++)
			COMP.LoadLibrary(array[i],undefined,scope);
		
		COMP.RunOnceLoaded(array,fn,scope,extraConfig);

// If we want to request all these scripts in one big file, we can:
//		for (var i=0; i<array.length; i++)
//			if (!array[i].endsWith('.js'))
//				array[i]=array[i]+'.js';
//		
//		COMP.LoadLibrary('/script_array/' + array.toString());
//		COMP.RunOnceLoaded(['/script_array/' + array.toString()],fn);
	},
	LoadConcatLibraries: function(array, fn, scope)
	{
		for (var i=0; i<array.length; i++)
			if (!array[i].endsWith('.js'))
					array[i]=array[i]+'.js';
		
		COMP.LoadLibrary('/script_array/' + array.toString());
		COMP.RunOnceLoaded(['/script_array/' + array.toString()],fn,scope);
	},
	
	RunOnceLoaded : function(array,fn,scope,extraConfig)
	{
		if (array.length<1 || fn == undefined) return;
		for (var i=0; i<array.length; i++)
			if (!COMP.LibraryLoaded(array[i]))
			{
				setTimeout(function() { COMP.RunOnceLoaded(array,fn,scope,extraConfig); }, 100);
				return;
			}
		
		if (typeof(fn) == "function")
			fn.call(scope,extraConfig);
		else if (typeof(fn) == "string" && fn.length > 0)
			eval(fn);
	},
	
	GetFullJSName : function(name)
	{
		name=COMP.Join(COMP.urlBase,name);
		if (name.indexOf('.js') < 0) name=name+'.js';
		if (name.indexOf('?vers=') < 0) name += "?vers=" + COMP.V.vers;

		if (name.indexOf('&collectioninfo=') < 0)
		{
			if (COMP.V.collectioninfo && COMP.V.collectioninfo.length)
				name += '&collectioninfo=' + COMP.V.collectioninfo;
		}
			
		return name;
	},
	
	LibraryLoaded : function(name)
	{
		return COMP.libCache[COMP.GetFullJSName(name)]=='complete';
	},
	
	LoadInitLibraries : function(array)
	{
		var scripts = document.getElementsByTagName('script');
		for (var i=0; i < scripts.length; i++)
		{
			var libs = scripts[i].getAttribute('libs');
			if (!libs) continue;
			libs = libs.split(' ');
			for (var j=0; j < libs.length; j++)
				COMP.LoadLibrary(libs[j].strip());
		}
		COMP.SetPostLoad();
	},
	// Once all libraries have completed loading, we need to switch
	// to an alternate method of loading libraries. Monitor the status
	// of all libraries, and when complete, set the flag.
	// In the future, we might want to use Ticks to check how long
	// it's taking for files to load. If a request has gone awry,
	// we could restart it.
	SetPostLoad : function()
	{
		for (var lib in COMP.libCache)
			if (COMP.libCache[lib] != 'complete')
			{
				setTimeout(COMP.SetPostLoad, 200);
				return;
			}
			
		COMP.postLoad=true;
	},
	
	LoadInitVariables : function()
	{
	    var scripts = document.getElementsByTagName('script');
	    for (var i=0; i < scripts.length; i++)
	    {
			var inits = scripts[i].getAttribute('V');
	        if (inits)
			{
				inits = inits.split(' ');
				for (var j=0; j<inits.length; j++)
				{
					var ar=inits[j].split('=');
					COMP.V[ar[0]] = ar[1];
				}
			}
			
			for (var j=0; j<scripts[i].attributes.length; j++)
			{
				var attr = scripts[i].attributes[j];
				if (attr.nodeName.startsWith("str_"))
				{
					var name=attr.nodeName.substring(4).toLowerCase();
					COMP.V[name]=attr.nodeValue;
				}
			}
	    }
	},
	
	LoadStylesheet : function(name)
	{
		name = COMP.Join(COMP.urlBase + "../css/", name);
	    if (!name.endsWith('.css'))
	        name = name + '.css';

		name += "?vers=" + COMP.V.vers;
	    if (COMP.cssCache[name]) return COMP.cssCache[name];

		if (COMP.nonIE)
		{
			var head = document.getElementsByTagName('head');
			if (head[0])
			{
				var css = document.createElement('link');
				css.rel = 'stylesheet';
				css.href = name;
				css.type = 'text/css';
				head[0].appendChild(css);
				COMP.cssCache[name] = document.styleSheets[document.styleSheets.length-1];
			}
		}
		else
		{
			COMP.cssCache[name] = document.createStyleSheet(name);
		}
		
		// COMP.ProcessStylesheet(COMP.cssCache[name]);

		return COMP.cssCache[name];
	}
	
	,ProcessStylesheet : function(sheet,attempt)
	{
		if (!sheet || COMP.nonIE)	return;
		
		attempt = (attempt||0) + 1;

		var retry = function()
		{
			if (attempt<20)
				setTimeout(function() {COMP.ProcessStylesheet(sheet,attempt)},250);
		}
	
		// Check if the sheet is loaded yet. If not, give it a few retries.
		try
		{
			if (COMP.nonIE)
			{
				if (!sheet.cssRules)
				{
					retry();
					return;
				}
			}
			else if (!sheet.rules || !sheet.rules.length)
			{
				retry();
				return;
			}
		}
		catch(e)
		{
			retry();
			return;
		}

		var rules = COMP.nonIE ? sheet.cssRules : sheet.rules;
		var rulesLen = rules.length; // We may add rules which could make this otherwise loop forever.
		for (var i=0; i<rulesLen; i++)
		{
			var rule = rules[i];

			// Deal with @import
			if (rule.styleSheet)
				COMP.ProcessStylesheet(rule.styleSheet);
				
			if (!rule.style)
				continue;

			// Mostly this varies between IE and non but
			// it may also help depending on how some
			// css has been written.
			var bTag = 'backgroundImage';
			var bimg = rule.style[bTag];
			if (!bimg)
			{
				bTag = 'background';
				bimg = rule.style[bTag];
			}
			
			if (bimg)
			{
				// Watch for pngs in IE and add the transformation code hack.
				// Also modify any relative paths for the sake of caching.
				if (!COMP.nonIE && bimg.toLowerCase().startsWith("url\\(") && bimg.toLowerCase().endsWith('\\.png\\)'))
				{
					var url = bimg.slice(4,-1);
					rule.style[bTag] = bimg;
					sheet.addRule(rule.selectorText, "_background:none");
					sheet.addRule(rule.selectorText, "_filter:progid:DXImageTransform.Microsoft.AlphaImageLoader(src='" + url + "')");
				}
			}
		}		
	}
	
	,LoadInitStyles : function()
	{
	    var scripts = document.getElementsByTagName('script');
	    for (var i=0; i < scripts.length; i++)
	    {
			var styles = scripts[i].getAttribute('styles');
	        if (!styles) continue;
	        styles = styles.split(' ');
	        for (var j=0; j<styles.length; j++)
				COMP.LoadStylesheet(styles[j])
	    }
	}
	,LoadInitPrefs : function()
	{
		var scripts = document.getElementsByTagName('script');
	    for (var i=0; i < scripts.length; i++)
	    {
			var prefs = scripts[i].getAttribute('prefs');
	        if (!prefs) continue;
			eval(prefs);
			break;
		}

	    if (!COMP.Prefs)
			COMP.Prefs = {};
	}
	,Init : function()
	{
		if (navigator.userAgent.toLowerCase().indexOf("msie") > 0)
			COMP.nonIE = false;
		
		COMP.IE = !COMP.nonIE;
		COMP.urlBase = COMP.Join(null, '');
		var tags = document.getElementsByTagName('script');
		for (var i=0; i < tags.length; i++)
		{
			var tag = tags[i];
			var src = tag.src;

			if (!src || src.lastIndexOf('COMP.js') < 1)
				continue;

			COMP.urlBase = COMP.Join(null, src.substring(0, src.lastIndexOf('COMP.js')));
		}
		
		COMP.LoadInitVariables();
		COMP.LoadInitPrefs();
		COMP.LoadInitLibraries();
		COMP.LoadInitStyles();
		COMP.InitTimeout();
	},
	
	// ExtendHandlers : function(o)
	// {
	// 	var c1 = Ext.get(o.id);
	// 	var c2 = Ext.getCmp(o.id);
	// 	
	// 	if (c1.handlers_extended==true)
	// 		return;
	// 		
	// 	for (var attr in o)
	// 		if (attr.length > 2 && attr.startsWith('on'))
	// 		{
	// 			var onStr=attr.toString().substring(2).toLowerCase();
	// 			c1.on(onStr, o[attr]);
	// 		}
	// 		
	// 	if (o.functions)
	// 		for (var funk in o.functions)
	// 		{
	// 			if (c1) c1[funk] = o.functions[funk];
	// 			if (c2) c2[funk] = o.functions[funk];
	// 		}
	// 	
	// 	c1.handlers_extended = true;
	// },
	
	NodeExists:function(path) {
		var nodes = path.split('.');
		
		var node = window;
		for (var i = 0, n = nodes.length; i < n; i++){
			var name = nodes[i];
			if (node && name) {
				node = node[name];
			}
			else {
				return false;
			}
		}
		
		return node;
	},
	Exists:function(path) {
		try {
			return Ext.decode(path);
		} catch(err) {
			return false;
		}
		//Code shouldnt ever get to here...?
		return true;
	},
	SetControlImage : function(o)
	{
		var g=o.ground;
		if (o.image!=undefined)
		{
			if (o.ground == 'fore'.toString())
			{
				Ext.getCmp(o.id).el.dom.src = o.image;
			}
			else
			{
				var center = o.center?' center':'';
				Ext.get(o.id).dom.style.background = "url(" + o.image + ") no-repeat"+center;
			}
		}
	},
	
	// Add the cgivars to the url, add collectioninfo if it is defined.
	AssembleURL : function(url, cgivars, addSession)
	{
		if (addSession==undefined) addSession=true;
		if (url==undefined) url = COMP.Base(url);
		
		url = COMP.AddCGIToURL(url, cgivars);
	
		if (addSession)
		{
			var cgi;
			if (COMP.V.collectioninfo)
				cgi = {sessionid:COMP.V.sessionid, collectioninfo:COMP.V.collectioninfo};
			else
				cgi = {sessionid:COMP.V.sessionid};
			
			
			url = COMP.AddCGIToURL(url, cgi);
		}
		
		return url;
	},
	
	AddCGIToURL : function(url, cgivars)
	{
		var sep = (url.indexOf('?')>=0) ? '&' : '?';
		
 		for (var cgi in cgivars)
		{
			url = url + sep + cgi + "=" + encodeURIComponent(cgivars[cgi]);
			sep='&';
		}
		return url;
	},
	
	Broadcast:function(event)
	{
		COMP.broadcastTracker[event] = true;
		
		for (var win in COMP.windows)
			COMP.windows[win].fireEvent(event);

		if (COMP.staticEventHandler)
			COMP.staticEventHandler.fireEvent(event);
	},
	
	broadcastTracker:{},
	
	LoadLibrariesSerial:function(libs, fn, scope, extraConfig)
	{
		if (!COMP.staticEventHandler)
			COMP.staticEventHandler = new Ext.util.Observable();
		
		var index = 0;
		function loadNext() { loadLib(); }
		function loadLib()
		{
			if (index >= libs.length)
			{
				if (fn)
				{
					if (typeof(fn) == "function")
						fn.call(scope || window, extraConfig);
					else if (typeof(fn) == "string")
						eval(fn);
				}
				return;
			}

			var lib = 'scriptLoaded_' + libs[index];
			if (COMP.broadcastTracker[lib])
			{
				index++;
				loadNext();
				return;
			}
			
			var em = {};
			em[lib] = loadNext;
			
			for (var e in em)
			{
				COMP.staticEventHandler.addEvents([e]);
				COMP.staticEventHandler.on(e,em[e],COMP.staticEventHandler);
			}
			
			COMP.LoadLibrary(libs[index]);
			index++;
			return;
		};
		loadNext();
	},
	
	// Override if desired.
	SecurityError : function(msg,wid)
	{
		if (wid)
			COMP.Dialog.MessageWin(msg, wid);
		else
			alert(msg);
	}
	,decode:function(o)
	{
		if (o)
		{
			if (typeof(o) == 'object')
			{
				return o;
			}
			else if (typeof(o) == 'string')
			{
				try
				{
					return Ext.decode(o);
				}
				catch(err)
				{
					// Dunno what it is
					COMP.Assert(false,"Asked to decode unknown object type: " + o);
					return o;
				}
			}
			else
			{
				// Dunno what it is
				COMP.Assert(false,"Asked to decode unknown object type: " + o);
				return o;
			}
		}
		
		return null;
	}
	,SendAjaxCommandOrdered : function(cmd, o, fn, deleteOnReturn)
	{
		var oScript = document.createElement("script");
	
		// Force non-cacheability
		o = o || {};
		o.ticks = COMP.GetCurrentTicks();
	
		oScript.src = COMP.AssembleURL('/ajax/' + cmd, o);
		oScript.onload = deleteOnReturn ? function() 
			{
				if (window.Ext) Ext.removeNode(oScript);
			} : fn;
		document.body.appendChild(oScript);
		return oScript;
	},
	
	// Temporary function for us as I convert everything.
	SendAjaxCommand : function(cmd, o, onsuccess, scope, onfailure)
	{
		if (!window.Ext || !window.Ext.Ajax)
		{
			COMP.SendAjaxCommandOrdered(cmd, o, onsuccess);
			return;
		}

		o = deepObjCopy(o || {});
		o.ticks = COMP.GetCurrentTicks();
		o.sessionid = COMP.V.sessionid;
		o.collectioninfo = COMP.V.collectioninfo || '';

		var ajaxID = Ext.Ajax.request({
			url:'/ajax/' + cmd
			,timeout: 60000
			,scope:scope || null
			,success:function(result, request)
			{
				try
				{
					var json = Ext.util.JSON.decode(result.responseText);
					if (json && json.securityError)
					{
						var wid = null;
						if (scope && scope.wid)
							wid = scope.wid;
						else if (request.wid)
							wid = request.wid
						COMP.SecurityError(json.securityError,wid);
						return;
					}
					if (onsuccess)
					{
						switch (typeof(onsuccess))
						{
							case 'function':
								try { onsuccess.call(scope,json); }
								catch (err)
								{
									//window.document.body.innerHTML = result.responseText;  //debug-line
									/*
									COMP.Assert(false, 'Command [' + cmd + '] failed. ' +
										'When calling onsuccess function [' + onsuccess + '], with scope [' + scope + ']. ' +
										'Error given was: ' + (err.message || err));   
									*/
									COMP.Assert(false,err);
								}
								break;
								
							case 'string':
								// if (request.wid)
								// 	COMP.WinExec(request.wid,fn,p1,p2,p3,p4,p5,p6,p7) ?????????
								// else
								// 	eval(fn + "(" + ????? + ")");
								break;
						}
					}
				}
				catch (err)
				{
					// Assume regular javascript. Try to execute it.
					try { eval(result.responseText); }
					catch (err)
					{
						//alert(result.responseText)  //debug-line
						COMP.Assert(false, msg);
					}
				}
			}
			,failure: function(){
				(onfailure || function(result, request) {
					COMP.Assert(false, "Ajax call failed: " + (request ? request.url : cmd)); // NOLS
				})();
			}
			,params:o
		});
		
		return ajaxID;
	},

	SetWindowLocation : function(url, ob, method)
	{
		var f = document.createElement('form');
		f.setAttribute('action', url);
		f.setAttribute('method', method || COMP.V.formDefault);
		
		COMP.AddHiddenVars(f, ob);

		document.body.appendChild(f);
		f.submit();
	},
	
	OpenWindow : function(url, fgs)
	{
        var features = fgs || 'status=1, toolbar=1, location=1';
		if (!(features instanceof String)) {
			var parsed = [];
			for (var key in features){
				parsed.push(key + '=' + features[key]);
			}
			features = parsed.join(',');
		}
		var win = window.open(url, '', features);
		if (!win) {
            alert(L(27417) + url); // ls_Unable_to_open_a_window
        }
		return win;
	},
	
	PopOpen : function (url, data, width, height) {
		var features = [];
		if (width) features.push('width=' + width);
		if (height) features.push('height=' + height);
		if (width || height) {
			features.push('modal');
			features.push('scrollbars');
		}
		
		var win = COMP.OpenWindow('', features);
		if (win) {
			
			var params = [];
			params.push('{');
			params.push('sessionid:"' + COMP.V.sessionid + '"');
			if (COMP.V.collectioninfo)
				params.push(',collectioninfo:"' + COMP.V.collectioninfo + '"');
			for (var i in data) 
				params.push(',' + i + ':"' + data[i] + '"');
			params.push('}');
			
			win.document.write([
				'<html>',
				'<head>',
					'<script type="text/javascript" src="/scripts/COMP.js?vers=', COMP.V.vers, '"></script>',
					'<script type="text/javascript">',
						'window.onload = function(){',
							'COMP.SetWindowLocation("', url, '", ', params.join(''), ');',
							'};',
							'</script>',
				'</head>',
				'<body></body>',
				'</html>'
			].join(''));
			win.document.close();
		}
	},
	Submit : function(f)
	{
		COMP.AddHiddenVars(f)
		f.submit();
	},
	
	LibSubmit : function(f)
	{
		var url = f.getAttribute("action");
		url = COMP.AssembleURL(url);
		f.setAttribute('action', url);
		f.submit();
	},
	
	AddHiddenVars : function(f, ob)
	{
		for (var i=1;i<=2;++i)
		{
			var o;
			if (i==1)
			{
				if (COMP.V.collectioninfo)
					o={sessionid:COMP.V.sessionid,collectioninfo:COMP.V.collectioninfo};
				else
					o = {sessionid:COMP.V.sessionid};
			}
			else
			{
				if (ob == undefined)
					continue;
				o = ob;
			}
			for (var v in o)
			{
				var h = document.createElement('input');
				h.setAttribute('type', 'hidden');
				h.setAttribute('name',v.toString());
				h.setAttribute('value',o[v]);
				f.appendChild(h);
			}
		}
	}
	
	,Reload : function()
	{
    //kokoni play sound on reset and before reset?
		// if (COMP.isSafari3 || COMP.isSafari4 || COMP.isWebkit531){	
		// 			if(COMP.V.refresh_loc == location.pathname)
//						location.reload(true);
		// 	else
		// 		COMP.SetWindowLocation(COMP.V.refresh_loc); //This causes problems in safari.
		// }else
		// 	window.location = COMP.V.refresh_loc; // old way on purpose
		
		// Dunno all the history here, but refresh_loc needs to be used.
		// With most recent changes, it is always ignored and it is being
		// assumed that when we "reload" we want to stay at the current url.
		// Sometimes reloading means reload the initial page and not the
		// current one.
		window.location = COMP.V.refresh_loc;
	}
	
	,InitTimeout : function()
	{
		COMP.ResetLastAction();
		if (COMP.Prefs && COMP.Prefs.timeout)
			COMP.V.session_timeout = COMP.Prefs.timeout;
			
		if (COMP.V.ignore_timeout != true && COMP.V.session_timeout != undefined && COMP.V.refresh_loc != undefined)
			setInterval(COMP.CheckTimeout, COMP.keepAliveInterval);
	},
	
	CheckTimeout : function()
	{
		if (COMP.GetCurrentTicks() - COMP.V.last_action > COMP.V.session_timeout * COMP.keepAliveInterval)
			COMP.Reload();
	},
	
	ResetLastAction : function(informServer) 
	{
		COMP.V.last_action = COMP.GetCurrentTicks();
		if (informServer != false)
			COMP.KeepSessionAlive();
	},
	
	mLastSessionKeepAlive : 0,
	
	KeepSessionAlive : function()
	{
		// Don't send these more than twice per minute.
		var now = COMP.GetCurrentTicks();
		if ((now - COMP.mLastSessionKeepAlive) > 30000 && document.body)
		{
			COMP.mLastSessionKeepAlive = now;
			COMP.SendAjaxCommand("keep_session_alive",{'now':now}, function() {
				COMP.ResetLastAction(false);
			}, null, function() {
				var yes = confirm(['Connection to server is lost.', 'Would you like to try and reload the console?'].join('\n'));
				if (yes) COMP.Reload();
			});
		}
	}
	
	// number formatting function
	// copyright Stephen Chapman 24th March 2006, 22nd August 2008
	// permission to use this function is granted provided
	// that this copyright notice is retained intact
	,formatNumber:function(num,dec,thou,pnt,curr1,curr2,n1,n2)
	{
		var x = Math.round(num * Math.pow(10,dec));
		if (x >= 0) n1=n2='';
		var y = (''+Math.abs(x)).split('');
		var z = y.length - dec;
		if (z<0) z--;
		for(var i = z; i < 0; i++)
			y.unshift('0'); 
		if (z<0) z = 1; 
		y.splice(z, 0, pnt); 
		if(y[0] == pnt) y.unshift('0'); 
		while (z > 3)
		{
			z-=3;
			y.splice(z,0,thou);
		}
		var r = curr1+n1+y.join('')+n2+curr2;
		return r;
	}
	,PenniesToString:function(amt, currencySign, commas)
	{
		return COMP.formatNumber(amt/100,2,(commas==true?',':''),'.',(currencySign==true?L('CurrencySymbol'):''),'','-','');
	}
	// This assumes that there is a decimal point $100.00 - seems reasonable for a money string.
	// This can use enhancements - handle poorly formatted strings, other languages, etc.
	,StringToPennies:function(s)
	{
		for (var i = 0; i < s.length;)
		{
			if (s.charAt(i) == '.')
				++i;
			else if (s.charAt(i) < '0' || s.charAt(i) > '9')
				s = s.substring(0, i - 1) + s.substring(i + 1, s.length);
			else
				++i;
		}
		
		var sp = s.split('.');
		var p = parseInt(sp[0]) * 100;
		if (sp.length > 1)
			p += parseInt(sp[1]);
		return p;
	}
	// Copied from the JavaScript Bible. 
	,getCookieData:function(labelName) 
	{ 
		var labelLen = labelName.length; 
		var cookieData = document.cookie; 
		var cLen = cookieData.length; 
		var i = 0; 
		var cEnd; 
		while (i < cLen){ 
			var j = i + labelLen; 
				if (cookieData.substring(i,j) == labelName) {
					cEnd = cookieData.indexOf(';', j);
					if (cEnd == -1) {
					cEnd = cookieData.length;
				}
				var cookie = cookieData.substring(j+1, cEnd);

				return decodeURIComponent(cookie);
			}
			i++;
		}
		return '';
	}
	,CombineObjects : function()
	{
		var o={};
		for (var i=0; i<arguments.length; i++)
			for (var a in arguments[i])
				o[a] = arguments[i][a];
		
		return o;
	}
	
	,ObjectsIdentical : function(o,o2)
	{
		if (typeof(o) != typeof(o2))
			return false;
		
		if (o == undefined)
			return true;
		
		if (typeof(o) == 'function')
			return true;
			
		if (typeof(o) == 'string')
			return o == o2;
			
		if (typeof(o) != 'object' && o != o2)
			return false;
			
		if (o.length != o2.length)
			return false;
		
		if (o.length)
		{
			for (var i=0; i<o.length; i++)
				if (!COMP.ObjectsIdentical(o[i],o2[i]))
					return false;
		}
		else
		{
			for (var x in o)
				if (!COMP.ObjectsIdentical(o[x],o2[x]))
					return false;
		}
		
		return true;
		
	}
	
	,AddingPound : function()
	{
//		COMP.initSearch = window.location.hash.toString();
		window.location.hash = '_';
		return false;
		
		var hash = window.location.hash;
		if (hash && hash.length) 
		{ 
//			if (loc != document.referrer) 
//			{ 
//				window.location = loc;					
//				return true;
//			}
			window.location.hash = '0';
		}
		
		return false;
	}
	
	,HandleExpired : function()
	{
		setTimeout(COMP.Reload, 5000); 
		alert(L(27418),L(27416),COMP.Reload); // ls_Session_has_expired_This_page, ls_Expired_Session
	}
	
	// Back exists only to be overridden.
	,Back : function() {}
	
	,AvoidBack : function(init)
	{
		if (init==true)
		{
			// Actually, do bother since we depend on this behavior to get
			// the back button to handle bread crumbs.
//			// Don't even bother if there's nothing to go back to.
//			if (history.length<=1)
//				return;
			
			// If someone goes directly to a url that contains the # sign,
			// this breaks the avoidBack method. So, simply forward them
			// to the same url without a # sign. This triggers a reload
			// but I think it's ok.
			COMP.AvoidBack(false);
		}
		else
		{
			if (!window.location.toString().endsWith("#")) 
			{ 
				window.location += "#" 
				COMP.Back(); 
			}
			setTimeout(function() { COMP.AvoidBack(false) },100);
		}
	}
	
	,DoWhen: function(f, whenFunc, timing, scope)
	{
		if (timing == undefined) timing = 200;
		
		if (!whenFunc())
		{
			setTimeout(f, timing);
			return;
		}
		
		f.call(scope);
	}
	
	,windows:{}
	,RegisterWindow:function(wid,win)
	{
		this.windows[wid] = win;
	}
	
	,UnregisterWindow:function(wid)
	{
		delete this.windows[wid];
	}
	,WinExec:function(wid, fnName)
	{
		var win = COMP.windows[wid];
		var args = Array.prototype.slice.call(arguments, 2);
		if (win)
		{
			var hier = fnName.split('.');
			var f = win;
			for (var i=0; i<hier.length; i++)
				f = f[hier[i]];

			return f.apply(win, args);
		}
		
		return null;
	}
	,Prefs:{}
	//Thanks, stack overflow! Why isn't this in ExtJS?
	//http://stackoverflow.com/questions/282758/truncate-a-string-nicely-to-fit-within-a-given-pixel-width
	,fitStringToWidth: function(str,width,className, noAbbr) {
		// str    A string where html-entities are allowed but no tags.
		// width  The maximum allowed width in pixels
		// className  A CSS class name with the desired font-name and font-size. (optional)
		// ----
		// _escTag is a helper to escape 'less than' and 'greater than'
		function _escTag(s){ 
			return s.replace("<","&lt;").replace(">","&gt;");
		}

	
		if(str.isLang)
			str = str.toString();
			
		//Create a span element that will be used to get the width
		var span = document.createElement("span");
		//Allow a classname to be set to get the right font-size.
		if(!className) className = 'slItem';
		if (className) span.className=className;
		span.style.display='inline';
		span.style.visibility = 'hidden';
		span.style.padding = '0px';
		document.body.appendChild(span);

		var result = _escTag(str); // default to the whole string
		span.innerHTML = result;
		// Check if the string will fit in the allowed width. NOTE: if the width
		// can't be determinated (offsetWidth==0) the whole string will be returned.
		if (span.offsetWidth > width) {
			var posStart = 0, posMid, posEnd = str.length, posLength;
			// Calculate (posEnd - posStart) integer division by 2 and
			// assign it to posLength. Repeat until posLength is zero.
			while (posLength = (posEnd - posStart) >> 1) {
				posMid = posStart + posLength;
				//Get the string from the begining up to posMid;
				span.innerHTML = _escTag(str.substring(0,posMid)) + '&hellip;';

				// Check if the current width is too wide (set new end)
				// or too narrow (set new start)
				if ( span.offsetWidth > width ) posEnd = posMid; else posStart=posMid;
			}
			
			if(!noAbbr){				
				result = '<abbr title="' +				
				str.replace("\"","&quot;") + '">' +
				_escTag(str.substring(0,posStart)) +
				'&hellip;<\/abbr>';
			}
			else{
				result = _escTag(str.substring(0,posStart)) + '...';
			}                    
		}
		else if (!noAbbr){
			result = '<abbr title="' +
			str.replace("\"","&quot;") + '">' +
			str + '<\/abbr>';
		}
		else result = str;
		document.body.removeChild(span);
		return result;
	}
	,SimplePrint:function(msg,title)
	{
		msg = msg.replace(/\r\n/g, '\r').replace(/\n/g, '\r').replace(/\r/g, '</br>');
		title = title || '';
		var html = '<head><title>' + title + '</title></head>';
		html += '<body>' + msg + '</body>';
		var win = window.open();
		win.document.write(html);
		win.document.close();
		win.print();
		win.close();
	}
	,PlaySound:function(soundName)
	{
		if (soundName && !COMP.isInstalled) Sound.play("/sounds/" + soundName + '.wav');
	}
	,soundPredetected:undefined
	,DetectSoundPlugin:function(){
		if (COMP.soundPredetected == true || COMP.soundPredetected == false)
			return COMP.soundPredetected;
			
		for(var i = 0; i < navigator.plugins.length; i ++){
			for(var j = 0; j < navigator.plugins[i].length; j ++){
				if(navigator.plugins[i][j].type === "audio/mp3" 
					|| navigator.plugins[i][j].type === "audio/x-mp3"
					|| navigator.plugins[i][j].type === "audio/mpeg3"
					|| navigator.plugins[i][j].type === "audio/x-mpeg3"
					|| navigator.plugins[i][j].type === "audio/mpeg"
					|| navigator.plugins[i][j].type === "audio/x-mpeg"){
					COMP.soundPredetected = true;
					return true;
				}
			}
		}
		COMP.soundPredetected = false;
		return false;
	}
	,IsAlex:function()
	{
		var ver = ' ' + COMP.V.versionnamenum;

		if (ver && ver.indexOf('Alex') != -1)
		 	return true;
		else
			return false;
	}

	,IsTT:function()
	{
		var ver = ' ' + COMP.V.versionnamenum;

		if (ver && ver.indexOf('Textbook') != -1)
		 	return true;
		else
			return false;
	}

	,Go2Help:function(id)
	{
		var link = COMP.GetGo2CompLink(id);
		COMP.OpenWindow(link, 'scrollbars=1');		
	}

	,GetGo2CompLink:function(id)
	{
		var link = "";
		var appStr = "";

		if (COMP.IsTT())
		{
			appStr = "TT";
		}
		else	
		{
		 	appStr = "ALEX";
		}

		link = 'http://www.go2comp.com/' + appStr;  //  this will move out of base later

		var tid = id.toString();
		while (tid.length < 4)					//ex:	0025
			tid = '0' + tid;

		link += tid;								//ex:	ALEX0025

		return link;
	}	
	
	,IsValidDuplicateBarcodeLeader:function(leader)
	{
		if (leader && typeof leader == 'string' && leader.length == 2 && leader[0] >= '0' && leader[0] <= '9' && leader[1] >= 'A' && leader[1] <= 'Z')
			return true;
		return false;
	}
	,IsValidDuplicateBarcode:function(bc)
	{
		var len = bc.length;
		if (len != 15)
			return false;
		
		var leader = bc.substring(0,2);
		if (!COMP.IsValidDuplicateBarcodeLeader(leader))
			return false;
		
		return true;
	}
	,MakeValidDuplicateBarcode:function(bc, leader)
	{
		if (COMP.IsValidDuplicateBarcode(bc))
			return bc;
		
		leader = leader || COMP.getCookieData('DUPBCID');
		if (!COMP.IsValidDuplicateBarcodeLeader(leader))
			return '';
				
		var newbc = bc;
		if (newbc.length > 13)
			newbc = newbc.substr(0, 13);
			
		while (newbc.length < 13)
			newbc = '0' + newbc;
		
		newbc = leader + newbc;
		return newbc;
	}
	,resizeImage:function(id, maxWidth, maxHeight){
		var image = Ext.get(id);
		var constrainedRatio = maxWidth / maxHeight;
		var imageRatio = image.getWidth() / image.getHeight() 
		if(imageRatio > constrainedRatio){
			image.setWidth(maxWidth);
			image.setHeight('auto');
		}
		else{
			image.setHeight(maxHeight);
			image.setWidth('auto');
		}
	}
};

COMP.Init();

COMP.Lang=
{
	id:COMP.Prefs.defaultLang || COMP.V.lang || 25856
	,ReloadAllStrings:function(item)
	{
		if (!item){
			if(typeof(RWS) != "undefined" && typeof(RWS.Viewport) != "undefined")	item=RWS.Viewport;
			else if(typeof(LWS) != "undefined"){	
				for (var win in COMP.windows)
					COMP.Lang.ReloadAllStrings(COMP.windows[win]);
				var launcher = LWS.Desktop.launcher;
				if(launcher)
				{
					COMP.Lang.ReloadAllStrings(launcher)
					for (var i=0; i<launcher.toolItems.length; i++)
						COMP.Lang.ReloadAllStrings(launcher.toolItems.items[i]);
				}
				return;
			}
		}
        
		if (item.items && item.items.items)
			for (var i=0; i<item.items.items.length; i++)
				COMP.Lang.ReloadAllStrings(item.items.items[i]);
				
		if (item.topToolbar && item.topToolbar.items)
				COMP.Lang.ReloadAllStrings(item.getTopToolbar());

		if (item.bottomToolbar && item.bottomToolbar.items)
				COMP.Lang.ReloadAllStrings(item.getBottomToolbar());
		
		if(item.fieldLabel && item.fieldLabel.isLang)
			if(item.label)
				item.label.update(item.fieldLabel);
				
		if(item.toolTip && item.fieldLabel.isLang) {
			if (item.refreshTooltip) item.refreshTooltip();
		}
		
		if (item.text && item.text.isLang)
		{
			if (item.setText)
				item.setText(item.text);
			else if (item.setHtml)
				item.setHtml(item.text);
		}
        else if (item.title && item.title.isLang)
		{
			if (item.setTitle)
				item.setTitle(item.title);
		}
		else if(item.xtype=='combo' && item.rendered && !item.noStringReload) //Ug, a bit of a hack but it's taking to much time to figure out a more elegant solution right now.
		{
			item.el.dom.value = item.value;
			var records = item.store.getRange(0,(item.store.getCount()-1));
			item.store.removeAll();
			item.store.add(records);
		}
		else if(item.xtype=="COMPpopupMenu" && item.rendered)
		{
			if (item.menu && item.menu.items && item.menu.items.items)
			{
				var data = item.menu.items.items;
				var val = item.getValue();
				item.createMenu(data);
				COMP.Lang.ReloadAllStrings(item.menu);
				item.setSelectedID(val);
			}
		}
		else if(item.xtype=="gearmenu" || item.xtype=="disablemenu" && item.rendered)
		{
			if (item.menu && item.menu.items && item.menu.items.items)
				COMP.Lang.ReloadAllStrings(item.menu);
		}
		var resultsTopActionBar = Ext.getCmp('resultsTopActionBar');
		if(resultsTopActionBar && resultsTopActionBar.rendered)
			resultsTopActionBar.doLayout();
            
        // if(RWS.Search)
        //     RWS.Search.Results.CreateTemplates();
	}
};

COMP.terms = new (function TermManager() {
	
	var terms = {};
	
	this.get = function get(name) {
		return terms[name];
	};
	
	this.set = function set(name, value) {
		return terms[name] = value;
	};
	
	this.load = function load(){
		COMP.SendAjaxCommand('lws_request_prefs', {uid:'PrefsPane_Terminology'}, function(data){
			var name;
			terms = {};
			for (var term in data) {
				if (term.startsWith('pref_')) {
					name = term.after('pref_');
				 	terms[name] = data[term];
				}
			}
		});
	};
	
	this.list = function list(){
		return terms;
	};
	
})();


function L(id,force)
{
	return new LANG(id,force);
};

function LANG(id, force) {
	this.id = id;
	this.isLang = true;
	this.force = force;
	this.appendStr = "";
    this.prependStr = "";
};

//Overriden in rws.js
LANG.prototype.getWidth=function() {return 0;}

LANG.prototype.append=function(str) {
	this.appendStr = str;
	return this;
};

LANG.prototype.prepend=function(str) {
	this.prependStr = str;
	return this;
};

LANG.prototype.toString = function() {
	//set english language if not already set (way too redundant)
	COMP.Lang.id = (COMP.Lang.id <= 0) ? 25856 : COMP.Lang.id;
	
	//get the term entered isntead, if is a string
	var string = (typeof this.id == 'string') ?
		COMP.terms.get(this.id) :
		COMP.Strings[this.force || COMP.Lang.id][this.id];
	
	return (string) ?
		this.prependStr + string + this.appendStr :
		this.prependStr + "*** (" + this.id + ") ***" + this.appendStr;
};

(function(window, undefined) {
	
	var audio, embed, flash, bgsound, iframe;
	
	function parse(html){
		var div = document.createElement('div');
		div.innerHTML = html;
		return div.childNodes[0];
	}
		
	//check for html5 audio object
	if (window.Audio) audio = true;
	
	//Collect Plugin Mimetype Information
	if (navigator && navigator.plugins && navigator.plugins.length) {
		var plugin, mimeType;
		for (var i = 0, n = navigator.plugins.length; i < n; i++) {
			plugin = navigator.plugins[i];
			if (plugin.length) {
				for (var ii = 0, nn = plugin.length; ii < nn; ii++) {
					mimeType = plugin[ii].type;
					embed = (mimeType == "audio/wav") ? true : embed;
					embed = (mimeType == "audio/x-wav") ? true : embed;
					flash = (mimeType == "application/x-shockwave-flash") ? true : flash;
				}
			}
		}
	}
	
	//check for IE background sound
	bgsound = (document.createElement('bgsound').toString && document.createElement('bgsound').toString() === '[object]');
	
	iframe = false; //TODO: check for opera, because I know it supports this method
	
	var engine = (
		audio   ? 'audio'   :
		embed   ? 'embed'   :
		bgsound ? 'bgsound' :
		iframe  ? 'iframe'  :
		flash   ? 'flash'   :
		null
	);
	
	var sounds = {};
	
	function Sound(src, autoplay){
		// Add in url routing information for use on the web router.
		src = COMP.AssembleURL(src);
		
		if (sounds[src]) return sounds[src];
		
		this.src = src;
		this.autoplay = autoplay;
		
		switch (engine) {
			case 'audio':
				this.element = new Audio(src);
			break;
			case 'embed':
				var style = 'style="position: absolute; left: -3000px;" ';
				this.source = '<embed src="SOURCE" autostart="true" hidden="true" STYLE/>'.replace('SOURCE', src).replace('STYLE', style);
			break;
			case 'bgsound':
				//do on play
			break;
			case 'iframe': break; //no support
			case 'flash': break; //no support
		}
		sounds[src] = this;
		if (autoplay) this.play();
	}
	
	Sound.play = function (src) {
		var snd = new Sound(src);
		snd.play();
		return snd;
	};
	
	Sound.stop = function (src) {
		var snd = new Sound(src);
		snd.stop();
		return snd;
	};
	
	Sound.playing = function (src) {
		return new Sound(src).playing();
	};
	
	Sound.loaded = function (src) {
		return !!sounds[src];
	};
	
	Sound.prototype.play = function () {
		this.stop();
		switch (engine) {
			case 'audio' : 
				this.element.play();
			break;
			case 'embed' :
				if (this.element) return;
				var parent = document.getElementsByTagName('body')[0];
				this.element = parse(this.source);
				if (parent) parent.appendChild( this.element );
			break;
			case 'bgsound' :
				if (this.element) return;
				var parent = document.getElementsByTagName('body')[0];
				var element = document.createElement('bgsound');
				element.src = this.src;
				element.loop = 1;
				this.element = element;
				if (parent) parent.appendChild( element );
			break;
		}
	};
	
	Sound.prototype.playing = function () {
		switch (engine) {
			case 'audio' : 
				//TODO: Implement
			break;
			case 'embed' :
				return this.element && this.element.parentNode;
			break;
			case 'bgsound' :
				return this.element && this.element.parentNode;
			break;
		}
	};
	
	Sound.prototype.stop = function () {
		switch (engine) {
			case 'audio' : 
				//TODO: Implement
			break;
			case 'embed' :
				if (!this.element) return;
				var parent = this.element.parentNode;
				if (parent) parent.removeChild( this.element );
				delete this.element;
			break;
			case 'bgsound' :
				if (!this.element) return;
				var parent = this.element.parentNode;
				if (parent) parent.removeChild( this.element );
				delete this.element;
			break;
		}
	};
	
	window.Sound = Sound;
	
})(window);
if (window.COMP) COMP.Broadcast('scriptLoaded_COMP');