/* ----- BEDetector ------------------------------------------------------------------------------------------------
 *
 *  A JavaScript browser/engine detector by
 *    Klaus Kohler <kko@freakmail.de>
 *
 *  Copyright (C) 2008 Klaus Kohler <kko@freakmail.de>
 *
 *  This program is free software; you can redistribute it and/or modify
 *  it under the terms of the GNU General Public License as published by
 *  the Free Software Foundation; either version 2, or (at your option)
 *  any later version.
 *
 *  This program is distributed in the hope that it will be useful,
 *  but WITHOUT ANY WARRANTY; without even the implied warranty of
 *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 *  GNU General Public License for more details.
 *
 *  You should have received a copy of the GNU General Public License
 *  along with this program; if not, write to the Free Software
 *  Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
 */

var BEDetector = function() {

var version = "0.2",
    engines = [],
    engine  = {
	is		: {},
	name		: null,
	prettyName	: null,
	build		: { string: null, number: null },
	version		: { string: null, number: null }
    },
    browser = {};

var _publicInterface = {
	version: version,
	engines: engines,
	engine : engine,
	browser: browser,

	getPrettyName: getPrettyName,
	getIntFromString: getIntFromString,
	getFloatFromString: getFloatFromString,
	getFloatFromVersionString: getFloatFromVersionString,
	regExpEscape: regExpEscape
    };

function getPrettyName(s,t) {
	return s + (t ? (" "+t) : "");
}
function getIntFromString(s) {
	var n = parseInt(s);
	return isNaN(n) ? 0 : n;
}
function getFloatFromString(s) {
	var n = parseFloat(s);
	return isNaN(n) ? 0 : n;
}
function getFloatFromVersionString(s,n) {
	for(var a=s.split("."),b="",c,i=0;i<a.length;i++) {
		a[i] = parseInt(a[i]);
		if(isNaN(a[i])) a[i] = 0;
		if(i==n.length) n[i] = 0;
		c = a[i].toString();
		for(var j=c.length;j<n[i];j++) c = "0" + c;
		b += (i?c:a[i]) + (i?"":".");
	}
	return parseFloat(b);
}
function regExpEscape(s) {
	return s.replace(/\\/g,"\\\\").replace(/\$/g,"\\$").replace(/\(/g,"\\(").replace(/\)/g,"\\)").replace(/\*/g,"\\*").replace(/\+/g,"\\+").replace(/\./g,"\\.").replace(/\[/g,"\\[").replace(/\?/g,"\\?").replace(/\^/g,"\\^").replace(/\{/g,"\\{").replace(/\|/g,"\\|");
}
function initEngine(o) {
	if(o.getBuildString)
		engine.build.string = o.getBuildString();
	if(o.getBuildNumber)
		engine.build.number = o.getBuildNumber();
	if(o.getVersionString)
		engine.version.string = o.getVersionString();
	if(o.getVersionNumber)
		engine.version.number = o.getVersionNumber();
	if(o.getPrettyName)
		engine.prettyName = o.getPrettyName(o.prettyName,engine.version.string);
	else
		engine.prettyName = getPrettyName(o.prettyName,engine.version.string);
}

var _engine, _engines = {

// Mozilla/Gecko
Gecko: function() {

	this.getBuildString = function() {
		// Gecko build date (YYYYMMDD, YYYYMMDDxx since 1.9)
		return navigator.productSub;
	};

	this.getBuildNumber = function() {
		// Floating point Gecko build date (YYYYMMDD, YYYYMMDD.xx since 1.9)
		// ensure comparable build numbers avoiding rounding issues:
		for(var i=0,n="";i<engine.build.string.length;i++)
			n += (i==8?".":"") + engine.build.string[i];
		return getFloatFromString(n);
	};

	this.getVersionString = function() {
		// Mozilla CVS branch tag (NOT the Firefox version number)
		var r, s="",t="", u=navigator.userAgent;
		// The exact CVS branch tag can be determined from the original User Agent string only.
		// This may fail or lead to incorrect result if the User Agent string is faked.
		if(u) {
			// Leading "rv:" since version 0.9
			r = / rv:([^;)]+)\)/.exec(u);
			if(r) s = r[1]; else {
				// No leading "rv:" in versions 0.6, 0.7, 0.8, 0.8.1
				r = / (0\.[6-8](?:\.1)?)\)/.exec(u);
				if(r) s = r[1]; else {
					// Leading "m" in milestones (M3-M18, M19=0.6)
					r = / [m|M]([1-9][0-8]?)\)/.exec(u);
					if(r) s = "M" + r[1];
				}
			}
		}
		// An approximate CVS branch tag can be determined from the object model.
		with(navigator) {
			if(typeof(onLine)=="boolean") t = "1.8.0.x";
			if(typeof(registerContentHandler)=="function") t = "1.8.1.x";
			if(typeof(mozIsLocallyAvailable)=="function") t = "1.9.0.x";
			if(typeof(geolocation)=="object") t = "1.9.1.x";
		}
		if(s)
			return (t && (parseFloat(t)>parseFloat(s))) ? t : s;
		else
			return t;
	};

	this.getVersionNumber = function() {
		// Floating point Mozilla version number calculated from CVS branch tag
		var s = engine.version.string;
		if(s.indexOf("M")==0) {
			// Milestones (M3-M18, M19=0.6) get an artificial version number (0.03-0.18)
			var n = parseFloat(s.substring(1,s.length));
			if(isNaN(n)) return 0; else return n/100;
		}
		var t = getFloatFromString(s);
		if(t>=1.8)
			// Versioning: <major>.<minor>[.<minorSub>[.<minorSubSub>]]
			//   <minor> and <minorSub> are assumed to be lesser than 10
			//   <minorSubSub> can be greater than 10 (lesser than 100)
			// 1.8.1.1  becomes 1.8101
			// 1.8.1.16 becomes 1.8116
			// 1.9.0.1  becomes 1.9001
			return getFloatFromVersionString(s,[0,1,1,2]);
		if(t>=1.7)
			// Versioning: <major>.<minor>[.<minorSub>]
			//   1.7, 1.7.1-13
			//   http://www-archive.mozilla.org/releases/
			//   <minor> is lesser than 10
			//   <minorSub> can be greater than 10 (lesser than 100)
			// 1.7.1  becomes 1.701
			// 1.7.13 becomes 1.713
			return getFloatFromVersionString(s,[0,1,2]);
		else
			// All minor versions are lesser than 10
			//   0.6-9, 0.8.1, 0.9.1-9, 0.9.2.1, 0.9.4.1
			//   http://www-archive.mozilla.org/releases/
			return getFloatFromVersionString(s,[]);
	};

	this.getPrettyName = function(s,t) {
		return getPrettyName(getPrettyName(s,t)+"/Gecko",engine.build.string);
	};

	this.prettyName = "Mozilla";

	this.isGecko = function() {
		with(navigator) {
			if(typeof(product)=="string" && product=="Gecko" && typeof(productSub)=="string" && typeof(vendor)=="string" && typeof(vendorSub)=="string") {
				if(typeof(window.clientInformation)=="object")
					// No, this is WebKit
					return false;
				return true;
			}
			return false;
		}
	}();

	if(this.isGecko) initEngine(this);
},

// KHTML/Konqueror
KHTML: function() {

	this.getVersionString = function() {
		var s = "";
		// appVersion may contain "KHTML/<version>"
		var r = / KHTML\/([^ ]+) /.exec(navigator.appVersion);
		if(r) s = r[1];
		return s;
	};

	this.getVersionNumber = function() {
		// Versioning: <major>.<minor>.<minorSub>
		//   <minor> and <minorSub> are assumed to be lesser than 10
		var s = engine.version.string;
		return getFloatFromVersionString(s,[]);
	};

	this.prettyName = "KHTML";

	this.isKHTML = function() {
		with(navigator) {
			if(typeof(product)=="string" && product=="Konqueror/khtml" && typeof(vendor)=="string" && vendor=="KDE") {
				return true;
			}
			return false;
		}
	}();

	if(this.isKHTML) initEngine(this);
},

// MSHTML/Internet Explorer
MSHTML: function() {

	this.getVersionString = function() {
		var s = "";

//		if(bedetector.addBehavior)
//			bedetector.addBehavior("#default#clientCaps");
		if((typeof(bedetector)=="object")&&(typeof(bedetector.getComponentVersion)!="undefined"))
			// http://msdn.microsoft.com/en-us/library/ms531416(VS.85).aspx
			// HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Active Setup\Installed Components
			s = bedetector.getComponentVersion("{89820200-ECBD-11CF-8B85-00AA005B4383}","ComponentID").replace(/,/g,".");
			// Versioning: <major>.<minor>.<minorSub>.<minorSubSub>
		if(!s) {
			// appVersion contains "MSIE <version>" (or "MSIE<version>" ?)
			var r = /MSIE ?([^;)]+)/.exec(navigator.appVersion);
			if(r) {
				s = r[1];
				// appMinorVersion may contain ";SP<number>;" (Service Pack)
				r = /;(SP[^;]+);/.exec(navigator.appMinorVersion);
				if(r) s += " " + r[1];
			}
		}
		return s;
	};

	this.getVersionNumber = function() {
		// Versioning: <major>.<minor>[.<minorSub>[.<minorSubSub>]]
		//   <minor> can have two digits
		//   <minorSub> can have four digits
		//   <minorSubSub> can have four or five digits
		var s = engine.version.string,
		    t = getIntFromString(s);
		if(t>6)
			return getFloatFromVersionString(s,[0,2,4,5]);
		else
			return getFloatFromVersionString(s,[0,2,4,4]);
	};

	this.prettyName = "MSHTML";

	this.isMSHTML = function() {
		// MSHTML meaning: Internet Explorer 4+ for Windows (Trident engine).
		// No warranty for other flavors of IE!
		with(navigator) {
			if(typeof(appMinorVersion)=="string" && typeof(cpuClass)=="string" && typeof(opsProfile)=="object" && typeof(systemLanguage)=="string" && typeof(userLanguage)=="string" && typeof(userProfile)=="object") {
				return true;
			}
			return false;
		}
	}();

	if(this.isMSHTML) initEngine(this);
},

// Netscape 4
Netscape4: function() {

	this.getVersionString = function() {
		var r, s="", u=navigator.userAgent;
		if(u) {
			// Leading "Mozilla/"
			r = /^Mozilla\/([^ ]+) /.exec(u);
			if(r) s = r[1];
		}
		return s;
	};

	this.getVersionNumber = function() {
		// Versioning: <major>.<minor>
		//   Converting directly, so 4.01 stays 4.01 and 4.5 stays 4.5
		var s = engine.version.string;
		return getFloatFromString(s);
	};

	this.prettyName = "Netscape";

	this.isNetscape4 = function() {
		return document.layers ? true : false;
	}();

	if(this.isNetscape4) initEngine(this);
},

// Opera
Opera: function() {

	this.getBuildString = function() {
		return (typeof(window.opera.buildNumber)=="function") ? window.opera.buildNumber() : null;
	};

	this.getBuildNumber = function() {
		return (typeof(window.opera.buildNumber)=="function") ? getFloatFromString(window.opera.buildNumber()) : null;
	};

	this.getVersionString = function() {
		if(typeof(window.opera.version)=="function")
			// Since Opera 8 (since Opera 9, faked User Agents may not contain "Opera")
			return window.opera.version().toString();
		else
			if(navigator.appName=="Opera")
				// Opera User Agent (appVersion is "<version> (<OS>; ...)")
				return navigator.appVersion.substring(0,navigator.appVersion.indexOf(" "));
			else {
				// Faked User Agent (userAgent contains "Opera <version>")
				for(var s=navigator.userAgent.split(" "),i=1;i<s.length;i++) if(s[i-1]=="Opera") return s[i];
				return "";
			}
	};

	this.getVersionNumber = function() {
		// Versioning: <major>.<minor>
		//   <minor> can be greater than 10 (lesser than 100)
		var s = engine.version.string;
		return getFloatFromVersionString(s,[0,2]);
	};

	this.getPrettyName = function(s,t) {
		return getPrettyName(s,t) + (engine.build.string?(" ("+engine.build.string+")"):"");
	};

	this.prettyName = "Opera";

	this.isOpera = function() {
		return window.opera ? true : false;
	}();

	if(this.isOpera) initEngine(this);
},

// WebKit
WebKit: function() {

	this.getVersionString = function() {
		var r, s="", u=navigator.appVersion;
		if(u) {
			// Leading "AppleWebKit/"
			r = / AppleWebKit\/([^ ]+) /.exec(u);
			if(r) s = r[1];
		}
		return s;
	};

	this.getVersionNumber = function() {
		// Versioning: <major>.<minor>.<minorSub>
		//   <minor> can be greater than 10 (lesser than 100)
		//   <minorSub> is assumed to be lesser than 10
		var s = engine.version.string;
		return getFloatFromVersionString(s,[0,2]);
	};

	this.prettyName = "WebKit";

	this.isWebKit = function() {
		with(navigator) {
			if(typeof(product)=="string" && product=="Gecko" && typeof(vendor)=="string" && typeof(window.clientInformation)=="object") {
				return true;
			}
			return false;
		}
	}();

	if(this.isWebKit) initEngine(this);
}

};

for(var i in _engines) {
	engines[engines.length] = i;
	engine.is[i] = false;
}
for(i in _engines) {
	_engine = new _engines[i]();
	if(_engine["is"+i]) {
		engine.is[i] = true;
		engine.name = i;
		break;
	}
}
_engine  = null;
_engines = null;

return _publicInterface;

}();

/* -------------------------------------------------------------------------------------------------------------- */