//Author: Robert Reid
//Created: 12/06/2008
//Updated to add handling for Chrome and CSS testing.
//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
var _w=window,_n=navigator,_d=document;
var 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
	opera:false, //Opera rendering engine is presto but no1 will remember that so use opera
	ie:false, //internet explorer
	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:true,
	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	
	anchorsEnabled: false,
	regexpEnabled: false,
	cookieEnabled: false,
	imagesEnabled: false,
	formsEnabled: false,
	linksEnabled: false,
	framesEnabled: false,
	javaEnabled: false,	
	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						
			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
				 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];
				 //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	
				 }
				 if(!this.spoof){
					if(/[a-z1-9]{20,}/i.test(a)){
						if(!this.name||this.name.length==0){
							this.name = "Fake Agent";
						}
						this.spoof = true;
					}
				 }
			}
			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{
					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 (@_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;				
				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)
			
			var fail = false;

			if(this.w3cDOM){
				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;}
				if(!fail){
					newDiv.style.position = 'absolute';
					newDiv.style.left = '10px';
					var leftVal = newDiv.offsetLeft;
					if(leftVal != 10) { _d.body.removeChild(newDiv); fail=true;}
				}
				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;}
				}
				if(!fail){
					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;
					if(top1 == top2){fail=true;}
				}
				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;}
				}
				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;}
				}
				if(!fail){
					if(_w.onresize == false){_d.body.removeChild(newDiv); fail=true;}
				}
				if(!fail){
					if(!_w.print){ _d.body.removeChild(newDiv); fail=true;}
				}
				if(!fail){
					if(_w.clientInformation && _w.opera){_d.body.removeChild(newDiv); fail=true;}
				}

				if(!fail){
					_d.body.removeChild(newDiv);										
					this.cssGradeA=true;
					return true;
				}				
			}			
			this.cssGradeA=false;
			return false;
		},
	SetUp: function(){
			this.ScriptTest();
			this.BrowserName();
			this.isSpoof();
			this.isBot();
			this.OperatingSystemTest();
			this.BrowserTest();			
			this.SniffFlash();			
			this.GUITest()			
		},
	GUITest: function(){ //test for CSS and Widgit Editor		
			this.CSSTest();
			//set up support for our widge editor 
			if(this.w3cDOM&&this.cssGradeA){ //DOM 3 and Good CSS				
				if(typeof(GetBody().contentEditable)!="undefined"||typeof(document.designMode)!="undefined"){
					this.widgeEditor = true;
				}				
			}			
		},
	Settings: function(){
			var s="UserAgent: "+this.userAgent+"<br />";
			for(var p in this){
				if(typeof(this[p])!="function"&&p!="userAgent"){
					s+=p+": "+this[p]+"<br />";
				}
			}
			return s;
		}
}