// Javascript Global Constants - FOR DEV SERVER ONLY

var _j=Jobboard = {
	ProductName: "Jobweb", //name of product
	Version: 3, //current version of software
	Build: 2, //current build
	Company: "Strategies Group PLC",
	ServerType: "live",
	LogDebug: "/jobboard/scripts/ajax/LogDebug.asp",
	FirebugLite: "/jobboard/scripts/JS/firebug/", //path to my implementation of firebug lite
	VideoCVServer: "rtmp://arsenic.strategiesuk.net/simplevideostreaming/_definst_/", //path to the video CV server
	FlashPlayer: "/jobboard/flash/FlowPlayerDark.swf", //the flash player for the video CV
	FlashCounter: "/jobboard/flash/textCounter.swf", //the flash player for the counter
	FlashMaxCurVersion : 10, //the latest current version of flash available to download on web. Needed to check that user has flash installed
	FlashMinVersion: [6,65], //when using flash and checking for user version the min version they must have (will link to download page) [major,major+min] (eg 8.3 would be [8,83])
	FlashWrapper: "/jobboard/flash/c.swf", //the flash wrapper object used for IE to allow a common flash object approach
	CSSCounterImage: "/jobboard/images/cssCounter.png", //the image to use for the CSS bullet counter
	LogJSErrors: true, //Log JS errors to file
	SuppressJSErrors: 2, // 0 = no dont suppress errors, 1 = suppress all, 2 suppress errs in old browsers (doms 0,1,3 0=none,1=old IE, 3=old NN browsers)
	EnhanceCSSDoc: false, //if on then if browser supports enhanced CSS whether to run the enhance function that modifies CSS classes
	StoreListeners: false //if on then all event handlers are stored in DOM so we can see which objects have events
}

/*
To handle the fact that 
-This is the first JS script included on a page
-I want to be able to output debug messages to the console/file from methods here.
-The debug js file is not included until later and therefore showdebug will cause errors
I create a showdebug function here to store any messages in cache if no console is availble. Then when debug.js is loaded the showdebug function
is redefined and any cached messages are outputted.
*/
var _w=window,_n=navigator,_d=document;


var _db=Debugger = {
	ready: false,
	debugCache: [],
	hasCache: false,
	debugCount: 0,
	debugInterval: null,
	forceFirebug: false, //if true for IE/Safari/Chrome we will use firebug lite for debug not their own console
	isFirefox: (/Firefox/i.test(_n.userAgent))?true:false,
	debug: false,
	
	ClearCache: function(){		
		if(this.debugCache.length>0){
			for(var c=0;c<this.debugCache.length;c++){
				this.Log(this.debugCache[c]); //add all cached messages to debug console
			}
			this.debugCache.length=0;
			this.hasCache=false;
		}
	},

	FirebugReady: function(){		
		if(this.CheckFirebug()){
			this.ClearCache();
			clearInterval(this.debugInterval);
			this.debugInterval=null;
		}
	},

	CheckFirebug: function(){
		if(this.ready){
			return true;
		}else{
			if(this.forceFirebug && !this.isFirefox){				
				if((typeof(firebug)!="undefined") && firebug.env && firebug.env.init ){
					return this.ready = true;
				}else{
					return this.ready = false;
				}
			}else{
				return this.ready = (typeof(_w.console)!="undefined");
			}
		}
	},

	Log: function(msg){
		if(this.forceFirebug && !this.isFirefox){
			firebug.d.console.cmd.log(msg);
		}else{
			console.log(msg);
		}
	}
}

if(Jobboard.ServerType=="dev"){		
	ShowDebug = function(msg){
		if(Debugger){
			if(!Debugger.debug)return;
			Debugger.debugCount++;
			var logMsg = Debugger.debugCount.toString()+": "+msg
			if(!Debugger.CheckFirebug()){
				Debugger.debugCache.push(logMsg); //store in cache until console is loaded
				Debugger.hasCache=true;
				if(Debugger.debugInterval==null){Debugger.debugInterval = setInterval(function(){Debugger.FirebugReady();},1000);}//check every second until bug is loaded
			}else{
				if(Debugger.hasCache){Debugger.ClearCache()};				
				Debugger.Log(logMsg);
			}
		}
	}	
}else{
	ShowDebug=function(){}//create empty function as unless all references are removed it will still get called even if its not outputting anything to console
}


//Create a browser wrapper object which does some basic sniffing for common browsers and also the dom type. Try to always use dom but in some cases
//sniffing has to be done as there is no other way (e.g ScriptOnDemand object Safari/Opera dont Script.onload / onreadystatechange
(function(){
	_b=Browser={
		userAgent:_n.userAgent,
		platform:_n.platform,
		name:null,
		version:0,
		gecko:false,  //moz based rendering engine firefox,firebird
		khtml:false,  //konqueror rendering engine	
		webkit:false, //Safari,OmniWeb,Chrome
		webkitversion:0, //version of webkit as some sniffing is required
		opera:false, //Opera rendering engine is presto but no1 will remember that so use opera
		ie:false, //trident renering engine used by IE
		ieDocMode:5, //the document rendering engine mode, in IE8 this can be changed on the fly
		windows:false,
		mac:false,
		linux: false,
		xml:false,
		jscript:false,
		javascript:false,
		flashEnabled:false,//if use has flash available
		flashVersion:"0", //If user has flash installed it will have the max.min version number as a string 
		cssGradeA:null, //if browser supports grade A sex n violence CSS
		cssEnhanced:false, //whether the enchance function has been run (if cssGradeA=true) which toggles classes
		boxModel:false, //subset of cssEnhanced which tells us whether browser supports the box model width=width+padding+margin
		styleFloat:"cssFloat",
		opacity:false,
		anchorsEnabled: false,
		regexpEnabled: false,
		cookieEnabled: false,
		imagesEnabled: false,
		formsEnabled: false,
		linksEnabled: false,
		framesEnabled: false,
		javaEnabled: false,
		AJAXEnabled: undefined, //will be set to true/false on first time an ajax request is made.
		spoof:null,
		bot:null,
		widgeEditor:false,
		dom:_d.all?(_d.getElementById?2:1):(_d.getElementById?4:(_d.layers?3:0)), //set the dom type for browser (0=none,1=old IE,2=new IE,3=old NN browsers,4 modern moz)
		w3cDOM: typeof _d.getElementById != "undefined" && typeof _d.getElementsByTagName != "undefined" && typeof _d.createElement != "undefined", //check to see if browser is wc3 DOM enabled
		BrowserName: function(){  //Basic Browser sniffer uses name/agent and not object as IE may decide to support addEventListener instead of attachEvent etc
				ShowDebug("IN browserName")
				var a=this.userAgent;		
				if(this.name===null){ //do basic spoof testing (not accurate) obviously ppl can override functions but ppl do not tend to that for the event handlers so I will use those
					 ShowDebug("check for window opera = " + _w.opera + " and in agent = " + a + " is opera = " + (/Opera/i.test(a)))
					 if(/^\s*$/.test(a)){
						this.name = "Blank Agent";
						this.spoof = true;
					 }else if(/Opera/i.test(a) || _w.opera){					
						this.name = "Opera";
						this.opera = true;
						if(!(_w.attachEvent&&_w.addEventListener)){
							this.spoof = true; //opera supports both event models
						}else if(_w.opera && !(/Opera/i.test(a))){
							this.spoof = true; //we know its opera but no mention in agent
						}					
					 }else if(/WebKit/i.test(a) || /Apple/i.test(a)){
						this.webkit = true;
						if(/Chrome/i.test(a)){
							this.name = "Chrome";
						}else if(/Apple.*Mobile.*Safari/i.test(a)){
							this.name = "Mobile Safari";
						}else{
							this.name = "Safari";
						}
					 }else if(/msie/i.test(a) && (!_w.opera)){
						this.name = "Internet Explorer";
						this.ie = true;
						if(!_w.attachEvent || _w.addEventListener){ this.spoof = true; }
						else if(!_w.ActiveXObject || !this.jscript){ this.spoof = true; }
						if(!this.spoof){
							// find out in IE the document compatibility mode ie quirks, ie7, ie8
							this.ieDocMode = (_d.documentMode) ? _d.documentMode : (_d.compatMode && _d.compatMode=="CSS1Compat") ? 7 : 5;//default to quirks mode IE5						   
						}
					 }else if(/Firefox/i.test(a)||_n.vendor=="Firefox"){
						this.name = "Firefox";
						this.gecko = true;
					 }else if(/Firebird/i.test(a)||_n.vendor=="Firebird"){
						this.name =  "Firebird";
						this.gecko = true;
					 }else if(/konqueror/i.test(a) || /KHTML/i.test(a)){
						this.name = "Konqueror";
						this.khtml = true;
					 }else{
						this.name = _n.appName;
						// look for gecko engine
						if(_n.product&&_n.product.toLowerCase()=="gecko"&&a.indexOf('gecko')!=-1){
							this.gecko = true;
						}
					 }
					 this.version = (a.match( /.+(?:ox|rv|ion|ra|ie|me)[\/: ]([\d.]+)/i ) || [])[1];
			
					 if(this.webkit){
						this.webkitversion = (a.match(/AppleWebKit\/(\d+)/)[1]);
					 }


					 //do more spoof checks
					 if(!this.spoof && (this.gecko || this.khtml||this.webkit)){
						if(_w.attachEvent || !_w.addEventListener){ this.spoof = true;}
						else if(_w.ActiveXObject || this.jscript){ this.spoof = true;}
					 }
					 if(!this.spoof&&(this.khtml||this.webkit)){
						if(_d.all) this.spoof=true; //FF actually returns an object for this	
					 }				
				}
				ShowDebug("spoof = " + this.spoof);
				return this.name;
			},
		isSpoof: function(){ //detect whether user is trying to spoof the browser by switching useragent not 100% accurate so do not rely
				if(this.spoof===null){
					if(/(?:spoof|spoofer|fake|ripper)/i.test(this.userAgent)){
						this.spoof = true;
					}else if(/[a-z1-9]{20,}/i.test(this.userAgent)){
						if(!this.name||this.name.length==0){
							this.name = "Fake Agent";
						}
						this.spoof = true;
					}else{
						this.spoof = false;
					}
				}
				return this.spoof;
			},
		isBot: function(){
				if(this.bot===null){
					if(/(?:robot|bot\W|mine|archive|spider|crawl|job|@|https?:\/{2})/i.test(this.userAgent)){
						this.bot = true;
					}else{ 
						this.bot = false;
					}
				}
				return this.bot;
			},
		ScriptTest: function(){
				/*@cc_on				
					@if (@_jscript)
						this.jscript = true;
					@else */
						this.javascript = true;  /*
					@end
				@*/
			},
		OperatingSystemTest: function(){
				var p=this.platform.toLowerCase();			
				/*@cc_on
					@if (@_win32)
						this.windows = true;
					@elif (@_win16)
						this.windows = true;
					@elif (@_win64)
						this.windows = true;
					@elif (@_mac)
						this.mac = true;
					@elif (@_alpha)
						this.linux = true;
					@else */
						this.windows = p ? /win/i.test(p) : /win/.test(this.userAgent),
						this.mac = p ? /mac/i.test(p) : /mac/.test(this.userAgent),
						this.linux = p ? /linux/i.test(p) : /linux/i.test(this.userAgent);  /*
					@end
				@*/
			},
		SniffFlash: function(){			
				var a=[0,0],p=_n.plugins;
				if(p&&typeof p["Shockwave Flash"]=="object"){
					var b=p["Shockwave Flash"].description;
					if(typeof b!="undefined"){
						b=b.replace(/^.*\s+(\S+\s+\S+$)/,"$1");
						var c=parseInt(b.replace(/^(.*)\..*$/,"$1"),10);
						var d=/r/.test(b)?parseInt(b.replace(/^.*r(.*)$/,"$1"),10):0;
						a=[c,d]
					}
				}else if(_w.ActiveXObject) {
					var m=10;
					if(Jobboard.FlashMaxCurVersion&&Jobboard.FlashMaxCurVersion>9){
						m=Jobboard.FlashMaxCurVersion+1; //add 1 incase someone forgot to update when flash brought out latest version
					}
					for (var ii=m;ii>=4;ii--){			
						try {
							var f=eval("new ActiveXObject('ShockwaveFlash.ShockwaveFlash."+ii+"');");						
						}catch(e){}
					}
					if(typeof(f)=="object"){
						if(ii==6){
							f.AllowScriptAccess = "always"; //GetVariable("$version") crashes for versions 6.0.22 through 6.0.29,
						}
						try{
							var b=f.GetVariable("$version");
							if(typeof(b)!="undefined"){
								b=b.replace(/^\S+\s+(.*)$/,"$1").split(",");
								a=[parseInt(b[0],10),parseInt(b[2],10)]
							}
						}catch(e){
							if(ii>4){
								a=[ii,0];
							}
						}
					}		
				}
				if(a[0]==0&&a[1]==0){
					this.flashEnabled=false;	
				}else{	
					this.flashEnabled=true;
					this.flashVersion=a[0].toString()+'.'+a[1].toString();
				}
				return;
			},
		BrowserTest: function(){
				this.anchorsEnabled = (_d.anchors) ? "true":"false";
				this.regexpEnabled = (_w.RegExp) ? "true":"false";
				_d.cookie = "cookies=true";
				this.cookieEnabled = (_d.cookie) ? "true" : "false";
				this.imagesEnabled = (_d.images) ? "true":"false";
				this.formsEnabled = (_d.forms) ? "true" : "false";
				this.linksEnabled = (_d.links) ? "true" : "false";
				this.framesEnabled = (_w.frames) ? "true" : "false";
				this.javaEnabled = (_n.javaEnabled());
				var m = _d.getElementsByTagName("meta");			
				for (var i = 0; i < m.length; i++) {
					if (/content-type/i.test(m[i].getAttribute("http-equiv")) && /xml/i.test(m[i].getAttribute("content"))){
						this.xml=true;
						break;
					}
				}
				return;
			},
		CSSTest: function(){  //function to set the cssGradeA setting if not already set (may have called it specifically somewhere else)
				ShowDebug("IN CSSTest");
				if(this.cssGradeA===null){
					//check from cookie we set if cookies are enabled
					if(this.cookieEnabled){
						var ck = readCookie('enhanced');
						ShowDebug("Cookie val = " + ck);
						if(ck===true){
							ShowDebug("Cookie says enhanced already")
							this.cssGradeA=true; //set our flag so we know browser is CSS gradeA super cool
							ShowDebug("Do we run enhancement function to modify css = " + Jobboard.EnhanceCSSDoc);
							if(Jobboard.EnhanceCSSDoc){
								ShowDebug("Yes if not already been run has it? = " + this.cssEnhanced);
								if(!this.cssEnhanced){
									ShowDebug("RUN enhanceDocument()");
									enhanceDocument(); //call the enhance function if we decree it and not already done
								}
							}
							return true;
						}else if(ck===false){ //cookie says browser not enhanced
							ShowDebug("Cookie says not enhanced already so skip check")
							this.cssGradeA=false;//set flag so we know browser is not CSS gradeA its gradeB 
							return true;
						}else{ ShowDebug("cookie not set so run test now")}
					}
					// not cookie enabled or cookie not set so we need to check
				}	
				var fail = false;
				ShowDebug("check w3c DOM otherwise no point")
				if(this.w3cDOM){
					ShowDebug("DO CSS TEST")
					var newDiv = _d.createElement('div');
					newDiv.style.display = "none";
					//Nicked from JQuery so we check support not browser type
					newDiv.innerHTML = '<a href="/a" style="color:red;float:left;opacity:.5;">a</a>';
					_d.body.appendChild(newDiv);
					var a = newDiv.getElementsByTagName("a")[0];
					// (IE uses filter instead)
					this.opacity = a.style.opacity === "0.5";					
					// Verify style float existence
					// (IE uses styleFloat instead of cssFloat)
					this.styleFloat = (!!a.style.cssFloat) ? "cssFloat" : "styleFloat";
					 _d.body.removeChild(newDiv);
					// Check whether box model is supported
					ShowDebug("Check Box Model")
					var newDiv = _d.createElement('div');
					_d.body.appendChild(newDiv);
					newDiv.style.visibility = 'hidden';
					newDiv.style.padding = '10px';
					newDiv.style.width = '20px';				
					var divWidth = newDiv.offsetWidth;
						if(divWidth != 40) {_d.body.removeChild(newDiv); fail=true; ShowDebug("Failed on 1");}
					if(!fail){
						newDiv.style.position = 'absolute';
						newDiv.style.left = '10px';
						var leftVal = newDiv.offsetLeft;
						if(leftVal != 10) { _d.body.removeChild(newDiv); fail=true; ShowDebug("Failed on 2");}
					}
					//set boxModel flag, browser may fail rest of tests but still support box model
					if(!fail){this.boxModel = true;}
					ShowDebug("this.boxModel = " + this.boxModel)
					// The following tests are similar but unrelated to the previous
					// tapped from JQuery as well, starting to like it apart from a few bugs!		
					var innerDiv, checkDiv, table, td, rules, prop, bodyMarginTop = _d.body.style.marginTop;
					var newDiv2 = _d.createElement('div');
					// set up html we will test
					html = '<div style="position:absolute;top:0;left:0;margin:0;border:5px solid #000;padding:0;width:1px;height:1px;"><div></div></div><table style="position:absolute;top:0;left:0;margin:0;border:5px solid #000;padding:0;width:1px;height:1px;" cellpadding="0" cellspacing="0"><tr><td></td></tr></table>';
					rules = { position: 'absolute', top: 0, left: 0, margin: 0, border: 0, width: '1px', height: '1px', visibility: 'hidden' };
					for ( prop in rules ){ newDiv2.style[prop] = rules[prop];}
					newDiv2.innerHTML = html;
					_d.body.insertBefore(newDiv2, _d.body.firstChild);
					innerDiv = newDiv2.firstChild, checkDiv = innerDiv.firstChild, td = innerDiv.nextSibling.firstChild.firstChild;
					// Set some browser flags which are used when we calculate the position of elements within the DOM correctly
					this.doesNotAddBorder = (checkDiv.offsetTop !== 5);
					this.doesAddBorderForTableAndCells = (td.offsetTop === 5);
					innerDiv.style.overflow = 'hidden', innerDiv.style.position = 'relative';
					this.subtractsBorderForOverflowNotVisible = (checkDiv.offsetTop === -5);
					_d.body.style.marginTop = '1px';
					this.doesNotIncludeMarginInBodyOffset = (_d.body.offsetTop === 0);
					// reset body style to original settings
					_d.body.style.marginTop = bodyMarginTop;
					_d.body.removeChild(newDiv2); //clean up
					//ShowDebug("this.doesNotAddBorder = " + this.doesNotAddBorder)
					//ShowDebug("this.doesAddBorderForTableAndCells = " + this.doesAddBorderForTableAndCells)
					//ShowDebug("this.subtractsBorderForOverflowNotVisible = " + this.subtractsBorderForOverflowNotVisible)
					//ShowDebug("this.doesNotIncludeMarginInBodyOffset = " + this.doesNotIncludeMarginInBodyOffset)
					// Check float works correctly
					if(!fail){
						var newInnerDiv = _d.createElement('div');
						newInnerDiv.style.width = '5px';
						newInnerDiv.style.cssFloat = 'left';
						newInnerDiv.style.styleFloat = 'left';
						newDiv.appendChild(newInnerDiv);
						var secondInnerDiv = newInnerDiv.cloneNode(true); 
						newDiv.appendChild(secondInnerDiv);
						var newInnerDivTop = newInnerDiv.offsetTop;
						var secondInnerDivTop = secondInnerDiv.offsetTop;
						if(newInnerDivTop != secondInnerDivTop) { _d.body.removeChild(newDiv); fail=true; ShowDebug("Failed on 3");}
					}
					if(!fail){
						ShowDebug("Do Float Test on UL LI")
						newDiv.innerHTML = '<ul><li style="width: 5px; float: left;">test</li><li style="width: 5px; float: left;clear: left;">test</li></ul>';
						var top1 = newDiv.getElementsByTagName('li')[0].offsetTop;
						var top2 = newDiv.getElementsByTagName('li')[1].offsetTop;
						ShowDebug("top1 = " + top1 + " top2 = " + top2)
						if(top1 == top2){fail=true; ShowDebug("Failed on 4");}
					}
					if(!fail){
						newDiv.innerHTML = '<div style="height: 20px;"></div>';
						newDiv.style.padding = '0';
						newDiv.style.height = '10px';
						newDiv.style.overflow = 'auto';
						var newDivHeight = newDiv.offsetHeight;
						if(newDivHeight != 10){_d.body.removeChild(newDiv); fail=true; ShowDebug("Failed on 5");}
					}
					if(!fail){
						newDiv.innerHTML = '<div style="line-height: 2; font-size: 10px;">Te<br />st</div>';
						newDiv.style.padding = '0';
						newDiv.style.height = 'auto';
						newDiv.style.overflow = '';
						var newDivHeight = newDiv.offsetHeight;
						if(newDivHeight > 40){_d.body.removeChild(newDiv); fail=true; ShowDebug("Failed on 6");}
					}
					if(!fail){
						if(_w.onresize == false){_d.body.removeChild(newDiv); fail=true; ShowDebug("Failed on 7");}
					}
					if(!fail){
						if(!_w.print){ _d.body.removeChild(newDiv); fail=true; ShowDebug("Failed on 8");}
					}
					if(!fail){
						if(_w.clientInformation && _w.opera){_d.body.removeChild(newDiv); fail=true; ShowDebug("Failed on 9");}
					}
					ShowDebug("Did test fail = " + fail);
					if(!fail){
						ShowDebug("Remove Test Div")
						_d.body.removeChild(newDiv);					
						if(Jobboard.EnhanceCSSDoc){
							ShowDebug("Yes if not already been run has it? = " + this.cssEnhanced);
							if(!this.cssEnhanced){
								ShowDebug("RUN enhanceDocument()");
								enhanceDocument(); //call the enhance function if we decree it and not already done
							}
						}					
						ShowDebug("Set cookie so next time no need to run check");
						createCookie('enhanced', 'true');//set cookie so next time we do not have to carry out this check				
						this.cssGradeA=true;
						ShowDebug("DONE")
						return true;
					}				
				}
				ShowDebug("CSS failed GradeA check");
				createCookie('enhanced', 'false');//set cookie so next time we do not have to carry out this check
				this.cssGradeA=false;
				ShowDebug("FAILED");
				return false;
			},
		
		GUITest: function(){ //test for CSS and Widgit Editor support on onload
				ShowDebug("IN GUI Test");
				this.CSSTest();
				//set up support for our widge editor 
				ShowDebug("w3cDOM = "+this.w3cDOM + " GradeA CSS = " + this.cssGradeA);
				if(this.w3cDOM){ //DOM 3 and Good CSS
					ShowDebug("Check for contenteditable = " + typeof(GetBody().contentEditable))
					ShowDebug("Check for designMode = " + typeof(_d.designMode))
					if(typeof(GetBody().contentEditable)!="undefined"||typeof(document.designMode)!="undefined"){
						// Until I can fix 100% editor in Safari/Chrome/Opera don't allow WYSIWYG editing
						if(Browser.ie||Browser.gecko){
							this.widgeEditor = true;
						}
					}				
				}
				ShowDebug("widgeditor support = " + this.widgeEditor);
			},

		Settings: function(){
				var s="UserAgent: "+this.userAgent+"<br />",x=0;
				for(var p in this){
					if(typeof(this[p])!="function"&&p!="userAgent"){
						x++;
						s+=p+": "+this[p];
						if(x%4==0){s+="<br />"}else{s+=" - "}
					}
				}
				return s;
			}
	}
		

	/*	This object is used so we can reference when the window or DOM has loaded any OnDocument window.onload events will check the status
		of these flags before deciding whether to call the function then or attach the listener. E.g if the window has already loaded and some
		code attaches a window.onload event then that function needs to fire then as the window has already loaded otherwise it would never fire.
		You can also use this object to add your own listeners that only fire once an object has become available in the DOM. */
	_p=PageLoader = {
		Listeners : {}, //will hold references to any events added with my addEvent so we can make sure they are all removed on unload (IE) and dupes are not added
		DOMLoaded : false,
		BodyLoaded : false,
		WindowLoaded : false,
		HasUnloader : false, //only set to true if one or more unload events are attached or its IE
		DoCleanUp: false, //in IE we remove all listners on page unload
		AfterLoaded: false, //set to true after all window onload events have fired
		FreePos : [], //holds a queue of positions in the PageListener that have had events removed which we can fill when new events are to be added to the listener
		//FreePos: -1, //holds the last position in PageListener array we removed an event from so we can a new event there instead of always adding to the end
		WinUnloadArr : [],
		WinLoadArr : [],
		AfterLoadArr : [],
		DOMArr : [],
		BodyFuncs : {},
		DOMTimer : null, //for browsers with no DOM ready support we may use a timer to check when it is ready
		AddWindowUnloadEvent : function(fn){
			ShowDebug("IN AddWindowUnloadEvent = " + fn);
			if(typeof(fn)=="function"){
				if(inArray(this.WinUnloadArr,fn)>-1) { ShowDebug("Function already in array!!");return false; }
				//if window has already unloaded then this wont be running will it!!
				this.WinUnloadArr.push( fn );
				//if PageLoader not set to auto run an unload event due it to being firefox or other auto caching browser we set flag to override
				if(!this.HasUnloader){
					this.HasUnloader = true;							
					//get correct object as Opera 7 uses document I believe (well it does for onload so I am presuming same is true for unload)
					var obj = (_w.addEventListener) ? _w : (_d.addEventListener) ? _d : _w;
					//use special function to attach listener not addEvent				
					return this.addLoadListener(obj,"unload",function(){PageLoader.RunWindowUnload();});
				}
			}
			return false;
		},	
		AddWindowOnLoadEvent : function(fn,ignoreDupe){ //use this so all window onloads are fired in order they are added plus we fire my onafterload events afterwards
			ShowDebug("IN AddWindowOnLoadEvent = " + fn + ", " + ignoreDupe);
			if(typeof(fn)=="function"){
				//check not already been added
				if(!ignoreDupe&&inArray(this.WinLoadArr,fn)>-1) { ShowDebug("Function already in array!!");return false; }
				if(this.WindowLoaded){  
					ShowDebug("Window already loaded so run function now!!!")
					fn(); //window already loaded so run now
				}else{
					ShowDebug("Add function to Window array at pos "+ this.WinLoadArr.length);
					this.WinLoadArr.push(fn); //add to array so all fire in order
					ShowDebug("Add function to listener object");
					//this.Listeners[this.Listeners.length] = [_w,"load",fn];
					this.AddListener(_w,"ev__load",fn);
					this.ShowListeners( );
				}
			}
			return true;
		},	
		OnElement : function( elID, func ){
			ShowDebug("IN OnElement " + elID + " - " + func);
			var f=false,self=this;
			if(this.WindowLoaded||this.DOMLoaded){ //no point looking for an element if the DOM is not ready (DOM>Element>Window)
				ShowDebug("Got document.body");
				if(this[elID]){ ShowDebug("OnElement Function already run"); return; } //already run function
				if(getEl(elID)){
					ShowDebug("Got reference to " + elID);
					f=true; //found element so run function and set flag so any other pointers know its loaded and don't fire
					this[elID]=true; //set flag to prevent it running again
					if(Function.call){
						ShowDebug("Function.call supported run function = " + func);
						func.call(elID);
					}else{
						ShowDebug("Run function = " + func);
						func();
					}
					ShowDebug("After running functions");
					return true;
				}
			}
			if(!f){
				ShowDebug("SetTimeout to self.OnElement for " + elID);
				setTimeout(function(){self.OnElement(elID,func);},200);			
			}
			return true;
		},
		OnBody : function( func ){
			var self=this;
			ShowDebug("IN OnBody " + func);	
			if(typeof(func)!="function") return false;
			if(this.BodyLoaded || document.getElementsByTagName("body")[0] || document.body){
				if(this.BodyFuncs[func]){ return; }//function already loaded with onBody
				func(); //run function
				this.BodyFuncs[func]=true;//set flag so its not run again#
				this.BodyLoaded = true;
			}else{		
				ShowDebug("body has not loaded yet")
				setTimeout(function(){self.OnBody(func);},200);
			}
			return 
		},
		AddAfterLoadEvent : function(fn){
			ShowDebug("IN AddAfterLoadEvent = " + fn)
			if(this.AfterLoaded){
				fn(); //AfterLoaded already fired so run function now
			}else{
				this.AfterLoadArr.push( fn );
				this.AddListener(_w,"ev__AfterLoad",fn)
				//this.Listeners[this.Listeners.length] = [_w,"AfterLoad",fn];
			}	
			return true;
		},			
		AddDOMLoadEvent : function(fn){
			if (this.DOMLoaded) {
				fn();
			}
			else { 		
				ShowDebug("Add function = " + fn.toString() + " to DOMArr Array");
				this.DOMArr.push( fn ); 
				// Add to my listner array
				this.AddListener(_d,"ev__DOMLoad",fn)
				//this.Listeners[this.Listeners.length] = [_d,"DOMLoad",fn];
			}
			return true;
		},
		RunWindowUnload : function(){
			ShowDebug("IN RunWindowUnload");
			if(this.WinUnloadArr){
				for(var x=0;x<this.WinUnloadArr.length;x++){
					fn = this.WinUnloadArr[x];
					ShowDebug("function to unload = " + fn)
					//make sure we save any clean up til last
					if(fn.toString()!=="function(){PageLoader.CleanUp();}"){
						fn();
					}
				}
				if(this.DoCleanUp){
					PageLoader.CleanUp() //run last always
				}
			}
			ShowDebug("RETURN");
			return true;
		},
		RunWindowLoaded : function(){
			ShowDebug("IN RunWindowLoaded this.WindowLoaded = "+this.WindowLoaded)
			if(this.WindowLoaded){
				ShowDebug("Window already loaded so return");
				return;
			}else{
				ShowDebug("Run any DOMLoad functions that were not run on DOMLoaded")
				//It may be that the DOM functions that should have run on DOM Ready failed so we ensure anything that hasn't run does
				var da=this.DOMArr;
				for (var i = 0; i < da.length; i++) {
					if(da&&typeof(da)=="function"){
						ShowDebug("Fix Prev Problems By Running Function Loaded By DOMLoader = " + da[i].toString());
						try{
							da[i](); //if it fails again then tough luck
						}catch(e){};
					}
				}
				ShowDebug("Now run any functions in the WinLoadArr array = " + this.WinLoadArr.length +" functions to run");
				this.WindowLoaded = true;
				// make sure if an old DOM level 0 event has been added after any addWinLoadEvent function calls its still handled		
				for (var i = 0; i < this.WinLoadArr.length; i++) {
					if(typeof(this.WinLoadArr[i])=="function"){
						ShowDebug("Run Function Loaded By WindowLoader = " + this.WinLoadArr[i].toString());
						this.WinLoadArr[i]();
					}
				}
				//fire onafterload event
				ShowDebug("FIRE onAfterLoaded EVENT");
				this.onAfterLoaded();
			}
			return true;
		},
		RunDOMLoadFunctions : function() {		
			ShowDebug("IN RunDOMLoadFunctions")
			if (this.DOMLoaded) {
				return;
			}
			if (Browser.ie && Browser.windows) { // Test if we can really add elements to the DOM; we don't want to fire it too early
				var s = _d.createElement("span");
				try { // Avoid a possible Operation Aborted error
					var t = GetBody().appendChild(s);
					t.parentNode.removeChild(t);
				}
				catch (e) {
					//if we are not using a timer to poll for DOM readiness we set one up to ensure the DOM functions get run
					if(!this.DOMTimer){
						this.DOMTimer = setInterval(function(){ PageLoader.RunDOMLoadFunctions();},10);
					}
					return;
				}
			}
			ShowDebug("Set DOM Loaded = TRUE");
			this.BodyLoaded=true;
			this.DOMLoaded = true;
			if (this.DOMTimer) {
				clearInterval(this.DOMTimer);
				this.DOMTimer = null;
			}
			for (var i = 0; i < this.DOMArr.length; i++) {
				ShowDebug("Run Function Loaded By DOMLoader = " + this.DOMArr[i].toString());			
				this.DOMArr[i]();
				this.DOMArr[i] = kill( this.DOMArr[i] ); //clear ref as RunWindowLoaded function will run anything not run by DOM loaded due to failures
			}
			return true;
		},
		onDOMLoad : function(){  // this will call RunDOMLoadFunctions that will loop through the DOM array running any functions setup for it once the DOM is ready
			ShowDebug("IN onDocument should only run once!!!!");
			//for spoofers or older browsers we have no idea of true object support so revert to an onload
			if(Browser.w3cDOM && !Browser.spoof){
								
				// if webkit < 525 OR opera < 9 use timer as they have no DOMContentLoaded support
				if((Browser.webkit&&Browser.webkitversion<525)||Browser.khtml||(Browser.opera&&Browser.version<9)&&_d.readyState!="undefined"){
					PageLoader.DOMTimer = setInterval(function(){ if(/loaded|complete/.test(_d.readyState)){ PageLoader.RunDOMLoadFunctions(); }},10);
				}
				// DOM 2
				else if(_d.addEventListener){
					ShowDebug("Add DOMContentLoaded EventListener for RunDOMLoadFunctions");
					//_d.addEventListener("DOMContentLoaded", function(){PageLoader.RunDOMLoadFunctions();}, false);		
					addEvent(_d,"DOMContentLoaded",function(){
						removeEvent(_d,"DOMContentLoaded",arguments.callee); //remove straight away
						PageLoader.RunDOMLoadFunctions();}, false);	// run all DOM ready functions

				}
				// IE Event Model (try a couple of things)
				else if(_d.attachEvent){
					ShowDebug("Attach onReadyStateChangeEvent for RunDOMLoadFunctions");
					// This should fire before window.onload but might not
					addEvent(_d,"onreadystatechange",function(){
						if ( _d.readyState === "complete" ) {	
							removeEvent(_d,"onreadystatechange",arguments.callee); //remove straight away
							PageLoader.RunDOMLoadFunctions(); // run all DOM ready functions
						}},false);
					
					// Also try this hack by Diego Perini which will work if window is not an iframe and better than the defer method
					if ( _d.documentElement.doScroll && _w == _w.top ) (function(){
						if ( this.DOMLoaded ) return;

						try {
							// This will raise an error until DOM is ready
							_d.documentElement.doScroll("left");
						}catch(e) {
							setTimeout( arguments.callee, 0 );
							return;
						}

						// once try doesn't raise an error it means DOM is ready
						PageLoader.RunDOMLoadFunctions(); 
					})();
				}
			}
			ShowDebug("add a windows load event in case it all goes tits up");
			// Even if this is set any of the above that work will set the DOMLoaded flag so that the call isn't run multiple times this way if anything fails the window.onload still runs
			this.AddWindowOnLoadEvent( function(){PageLoader.RunDOMLoadFunctions();} );
			ShowDebug("END of onDocument");
			return true;
		},
		Setup : function(){  //called from pageinit and sets up all events for page loader		
			ShowDebug("IN PageLoader.Setup()");
			this.onDOMLoad(); //sets up one onDOM loaded event to fire all functions
			this.onWindowLoaded(); //set ups one window.onload event to fire all functions

			//add an unload handler for IE to remove anything added with attachEvent to prevent memory leaks
			//if browser is not IE and other unload events are attached then the cleanup will also fire. I don't automatically run clean up on unload
			//for firefox as it uses caching which is disabled if unload events are used.
			//check for my browser object and then for IE otherwise if no browser object make sure only attachEvent is supported (Opera supports attachEvent and addEventListener)
			if(Browser.windows && Browser.ie){	
				this.DoCleanUp = true; //want to run clean up of all listeners on window unload
				// Call function to add CleanUp function to listener array and ensure an unload event is run
				ShowDebug("so add unload clean up")
				this.AddWindowUnloadEvent(function(){PageLoader.CleanUp();});
			}
			return true;
		},
		CleanUp : function(){
			ShowDebug("IN CleanUp")
			if(!Jobboard.StoreListeners) return true; //no listeners stored so we cannot remove anything using this array!!
			var y,obj,ev,type,i,fn;
			for(var x in this.Listeners){
				obj = this.Listeners[x];
				for(y in o){ //loop through all event slots
					ev = o[y];
					if(ev && ev.length){
						ShowDebug("event key = " + ev);
						type=ev.replace(/^ev__/,""); //event key eg ev__load turn to load
						for(i=0;i<ev.length;i++){
							fn = ev[i];
							ShowDebug("fn = " + fn.toString());
							if(typeof(fn)=="function"){
								ShowDebug("remove event " + obj.guid + ", " + type + ", " + fn.toString());
								// Call removeEvent which will also destroy the DOM reference and the item in the listener array!!
								r = removeEvent( obj, type, fn, false );
							}
						}
					}
				}
			}
			ShowDebug("All listeners should have been removed");
			return true;
		},	
		onWindowLoaded : function(){ //set window onload event to fire all functions added to this handler in the order they were added		
			ShowDebug("IN onWindowLoaded Create onLoad event")
			var obj
			if(_w.addEventListener){
				obj = _w ; //gecko, safari, konqueror, standards
			}else if(_d.addEventListener){
				obj = _d //opera 7
			}else{
				obj = _w // IE
			}
			// add load listeners with special function not addEvent which is used for all other listeners
			this.addLoadListener(obj,"load", function(){PageLoader.RunWindowLoaded();} );
			return true;
		},
		onAfterLoaded : function(){ //fires after all window.onload events have fired and will run any code needed eg clean up code added with the addAfterLoad function
			//ShowDebug("IN onAfterLoaded");
			if(this.DOMLoaded && this.WindowLoaded){
				this.AfterLoaded = true;
				for(var x=0;x<this.AfterLoadArr.length;x++){
					ShowDebug("Run AfterLoaded function");
					this.AfterLoadArr[x](); //run function
				}
			}
			//ShowDebug("Run all onAfterLoaded functions")
			return true;
		},
		RunFunctions : function(e,obj,arr){ //generic function to loop through an array of functions and calling them with appropriate method
			//ShowDebug("IN RunFunctions")
			var rv=true;
			e=StandardiseEvent(e,obj);
			if(obj && arr && arr.length){
				for(var i=0;i<arr.length;i++){
					if(typeof(arr[i])=="function"){
						if(Function.call){
							rv = arr[i].call(obj, e ) && rv;
						}else{
							obj.RunFunction = arr[i];
							rv = obj.RunFunction( obj, e ) && rv;
						}
					}	
				}
				if(obj.RunFunction) obj.RunFunction=kill( obj.RunFunction ); //remove the temp function we created
			}
			return rv;
		},
		HasListener : function(obj,type,fn){
			//ShowDebug("IN HasListener - " + obj.id + " - " + type + " - " + fn)		
			if(!this.Listeners || !this.Listeners.length) return false;
			var pl = this.Listeners;
			for(var x=0;x<pl.length;x++){
				//ShowDebug("Check pos " + x + " does " + obj +" === " + pl[x][0] + " && " + type + " == " + pl[x][1] + " && " + fn + " == " +pl[x][2] + " ???");
				if(pl[x] && pl[x][0]===obj && pl[x][1]==type && pl[x][2].toString()===fn.toString()){
					ShowDebug("Found listener in array at pos " + x + " return true");
					return true;
				}			
			}
			//ShowDebug("Could not find listener");
			return false;
		}, 
		AddListener : function(obj,key,fn){  //only need PageListener arrays on dev!!!!
			if(!Jobboard.StoreListeners) return true;
			if(!obj||!key||!fn) return false;
			var id = getGUID(obj); //will get or create new guid for object
			ShowDebug("IN AddListners for object.guid = " + id + " key = " + key + " function = " + fn);
			var o = this.Listeners[id];
			if(!o){
				// this object has not had any events added yet so create new object slot
				o = this.Listeners[id] = {};
			}
			// Check to see if this object slot has an event slot and if not create one
			// The event slots are named after the key which is created in the addEvent functions using the event handler name
			var es = o[key];
			if(!es){
				// this object has not had any events of this type added yet so create new event slot
				es = o[key] = [];
			}
			// Add function to end of current functions in this slot or to first empty space we come to as functions will
			// get removed all the time and we dont want to expand the array too much if we dont have to
			for(var x=0;x<es.length;x++){
				ShowDebug("Check slot " + x + " == " + es[x])
				if(!es[x] || typeof(es[x])!="function"){
					ShowDebug("found empty slot so add function!")
					es[x] = fn;
					return true;
				}
			}
			ShowDebug("found no empty slot add at end slot = " + x);
			es[x] = fn;
			return true;
		},
		RemoveListener : function(obj,key,fn){ //only need PageListener arrays on dev!!!!
			if(!Jobboard.StoreListeners) return  true;
			if(!obj||!key||!fn) return false;
			var id = obj.guid; //if its null then its not been set by the AddListener and we should exit
			ShowDebug("IN RemoveListner look for obj." + id + " key = " + key + " function = " + fn);
			if(!id) return false;
			var o = this.Listeners[id];
			if(!o) return false;
			ShowDebug("got object look for event slots o["+key+"]");
			var es = o[key], ne = o[key].length,fs = fn.toString();
			if(!es || !es.length) return false;
			ShowDebug("Found event slots look for fn in array");
			var r=false,el = es.length, v=false;
			for(var x=0;x<el;x++){
				ShowDebug("Check slot " + x + " == " + es[x] + " - " + typeof(fn) + " - " + typeof(es[x]))
				if(es[x]){					
					if( fs===es[x].toString()){
						ShowDebug("found function = " + es[x].toString());
						es[x]=kill(es[x]);						
					}else{
						v=true;
					}
				}
			}
			if(!v){
				ShowDebug("We looped through array and all were null so remove the event slot")
				o[key]=kill(o[key]);
			}
			return r;
		},
		ShowListeners : function(){  // Show all listeners attached to this page using my addEvent functions			
			if(!Jobboard.StoreListeners) return  true;
			if(this.Listeners){
				var o;
				for(var x in this.Listeners){
					o = this.Listeners[x];
					ShowDebug("Show Listeners For Object " + x);

					for(var y in o){ //loop through all event slots
						ev = o[y];
						if(ev){
							ShowDebug("Show Listeners For Event Key " + x + " event array.length = " + ev.length);
							if(ev.length){
								for(i=0;i<ev.length;i++){
									ShowDebug("item " + i + " == " + ev[i])
									if(ev[i] && typeof(ev[i])=="function"){
										ShowDebug("Function["+i+"] = "+ ev[i]);
									}
								}
							}else{
								ShowDebug("No Listeners Found")
							}
						}					
					}
					
				}
			}
			return;
		},		

		ShowObjectListeners : function(obj,key){
			if(!Jobboard.StoreListeners) return  true;
			ShowDebug("IN ShowObjectListeners = " + obj.guid +" - " + key)
			if(obj.guid){
				//if key not passed in correctly format
				if(/^ev__/.test(key)){	
					ShowDebug("key is wrong fix")
					if(/^on/i.test(key)){
						key = key.replace(/^on/i,""); //remove the on from onClick
					}
					key += "ev__"+key; //correct format is ev__click;
					ShowDebug("key now = " + key);
				}
				var i;
				o = this.Listeners[obj.guid]; //get object holding all event slots for this object
				for(var x in o){ //loop through all event slots
					ev = o[x];
					if(ev){
						ShowDebug("Show Listeners For Event Key " + x + " event array.length = " + ev.length);
						if(ev.length){
							for(i=0;i<ev.length;i++){
								ShowDebug("item " + i + " == " + ev[i]);
								if(ev[i] && typeof(ev[i])=="function"){
									ShowDebug("Function["+i+"] = "+ ev[i]);
								}
							}
						}else{
							ShowDebug("No Listeners Found")
						}
					}					
				}
			}				
			return true;
		},
		// Function used to attach the window load/unload events as they have their own special arrays within the PageLoader and do not
		// require storage in the DOM like other listeners added with addEvent. 	
		addLoadListener : function(obj, type, fn){
			//ShowDebug("IN addLoadListener = " + obj + ", " + type + ", " + fn);
			var r=true, pl=PageLoader.Listeners;
			var Arr = (type=="load") ? PageLoader.WinLoadArr : PageLoader.WinUnloadArr;
			var key = "ev__"+type;
			if(inArray(Arr,fn) > -1){
				//ShowDebug("Function already in PageLoader.[un]load array");
				return false;
			}	

			if(typeof(fn)=="function"){
				if(obj.addEventListener){	
					// Add listener using DOM 2 method
					obj.addEventListener( type, fn, false ); 		
				}else if(obj.attachEvent){
					r = obj.attachEvent("on"+type,fn); 
				}else{
					obj["on"+type] = fn; //we only ever have one listener
				}
			}

			//ShowDebug("Add Event to stack at pos " + pl.length)
			//Add event to listener stack
			this.AddListener(obj,key,fn);
			//ShowDebug("Now look for it in PageListener array")

			return r;
		}
	}

	Browser.ScriptTest();
	Browser.BrowserName();
	Browser.isSpoof();
	Browser.isBot();
	Browser.OperatingSystemTest();
	Browser.BrowserTest();			
	Browser.SniffFlash();	

	PageLoader.Setup();

	addDOMLoadEvent( function(){Browser.GUITest();} );			

})();
