/**
 * Creates a sniffer object.  All parsing and regex matching is deferred until a method call requires it.
 * 
 * EPSniffer
 *   +getBrowser():EPBrowser
 *   +getOperatingSystem():EPOperatingSystem
 *   +getDevice():EPDevice
 *   -getUserAgent():String
 *   isFlashEnabled():boolean
 *   isPopupEnabled():boolean
 *   getJavascriptVersion():double
 *
 * Example Usage:
 *   var sniffer = new EPSniffer();
 *   var browserId = sniffer.getBrowser().getIdentifier();
 *   var osId = sniffer.getOperatingSystem().getIdentifier();
 *   var formFactor = sniffer.getDevice().getFormFactor();
 *   var isTablet = sniffer.getDevice().isTablet();
 *   var isMobile = sniffer.getDevice().isMobile();
 */
function EPSniffer(userAgent) {
	this.userAgentString = (userAgent == null || userAgent == "" ? navigator.userAgent.toLowerCase() : new String(userAgent).toLowerCase());
	
	this.getUserAgent = _eps_getUserAgent;
	this.getBrowser = _eps_getBrowser;
	this.getOperatingSystem = _eps_getOperatingSystem;
	this.getDevice = _eps_getDevice;
}

function _eps_getBrowser() {
	if (this.browser == null) {
		this.browser = new EPBrowser(this.getUserAgent());
	}
	return this.browser;
}

function _eps_getOperatingSystem() {
	if (this.os == null) {
		this.os = new EPOperatingSystem(this.getUserAgent());
	}
	return this.os;
}

function _eps_getDevice() {
	if (this.device == null) {
		this.device = new EPDevice(this.getUserAgent());
	}
	return this.device;
}

function _eps_getUserAgent() {
	return this.userAgentString;
}

/**
 * A browser object returned by EPSniffer's getBrowser() method.
 * 
 * EPBrowser
 *   +getIdentifier():String
 *   +getVersion():String
 *   +getMajorVersion():String
 *   +getMinorVersion():String
 *   +getEngine():String
 *   +getEngineVersion():String
 */
function EPBrowser(userAgent) {
	this.userAgentString = userAgent;
	this.getIdentifier = _epb_getIdentifier;
	this.getProfile = _epb_getProfile;
	this.getVersion = _epb_getVersion;
	this.getMajorVersion = _epb_getMajorVersion;
	this.getMinorVersion = _epb_getMinorVersion;
	this.getEngine = _epb_getEngine;
	this.getEngineVersion = _epb_getEngineVersion;
}

function _epb_getProfile() {
	if (this.profile == null) {
		var ua = this.userAgentString;
		var profiles = new Array();
		var i = 0;
		var msieEngineVersionPattern = /msie\s(\d+(\.?\d)*)/;
		var geckoEngineVersionPattern = /gecko\/([0-9]+)/;
		// ----------------------- BROWSER PROFILES -------------------------------
		//(browserIdentifier[0], regexMatchPattern, versionValueOrPattern[1], engineValueOrPattern[2], engineVersionValueOrPattern[3])
		//profiles[i++] = new EPBrowserProfile("omniweb", /omniweb[\/\s]v?(\d+([\.-]\d)*)/);
		profiles[i++] = new EPBrowserProfile("opera", /opera[\/\s](\d+(\.?\d)*)/, /opera[\/\s](\d+(\.?\d)*)/, "opera", /opera[\/\s](\d+(\.?\d)*)/);
		profiles[i++] = new EPBrowserProfile("crazy", /crazy\s?browser\s(\d+(\.?\d)*)/, /crazy\s?browser\s(\d+(\.?\d)*)/, "msie", /msie\s(\d+(\.?\d)*)/);
		profiles[i++] = new EPBrowserProfile("myie2", /myie2/, null, "msie", /msie\s(\d+(\.?\d)*)/);
		profiles[i++] = new EPBrowserProfile("netcaptor", /netcaptor/, /netcaptor\s(\d+(\.?\d)*)/, "msie", msieEngineVersionPattern);
		profiles[i++] = new EPBrowserProfile("avantbrowser", /avant\sbrowser/, null, "msie", msieEngineVersionPattern);
		profiles[i++] = new EPBrowserProfile("msn", /msn\s(\d+(\.?\d)*)/, /msn\s(\d+(\.?\d)*)/, "msie", msieEngineVersionPattern);
        profiles[i++] = new EPBrowserProfile("msie", /msie\s(\d+(\.?\d)*)/, msieEngineVersionPattern, "msie", msieEngineVersionPattern);
        profiles[i++] = new EPBrowserProfile("powermarks", /powermarks\/(\d+(\.?\d)*)/, /powermarks\/(\d+(\.?\d)*)/, "msie", msieEngineVersionPattern);
		profiles[i++] = new EPBrowserProfile("konqueror", /konqueror[\/\s](\d+([\.-]\d)*)/, /konqueror[\/\s](\d+([\.-]\d)*)/, "khtml");
		profiles[i++] = new EPBrowserProfile("safari", /version\/[\d\.-]+.*safari\/(\d)*/, /version\/(\d+(\.?\d*)*)/, "khtml", /applewebkit\/(\d+(\.?\d*)*)/);
		profiles[i++] = new EPBrowserProfile("safari", /safari\/(\d)*/, /safari\/(\d+(\.?\d*)*)/, "khtml", /applewebkit\/(\d+(\.?\d*)*)/);
        profiles[i++] = new EPBrowserProfile("zyborg", /zyborg/, /zyborg\/(\d+(\.?\d)*)/, "robot");
		profiles[i++] = new EPBrowserProfile("netscape", /netscape6[\/\s](\d+([\.-]\d)*)/, /netscape6[\/\s](\d+([\.-]\d)*)/, "gecko", geckoEngineVersionPattern);
        profiles[i++] = new EPBrowserProfile("netscape", /netscape\/(7\.\d*)/, /netscape\/(7\.\d*)/, "gecko", geckoEngineVersionPattern);
        profiles[i++] = new EPBrowserProfile("galeon", /galeon[\/\s](\d+([\.-]\d)*)/, /galeon[\/\s](\d+([\.-]\d)*)/, "gecko", geckoEngineVersionPattern);
        profiles[i++] = new EPBrowserProfile("nautilus", /nautilus[\/\s](\d+([\.-]\d)*)/, /nautilus[\/\s](\d+([\.-]\d)*)/, "gecko", geckoEngineVersionPattern);
        profiles[i++] = new EPBrowserProfile("firefox", /firefox[\/\s](\d+([\.-]\d)*)/, /firefox[\/\s](\d+([\.\d-])*)/, "gecko", geckoEngineVersionPattern);
        profiles[i++] = new EPBrowserProfile("kmeleon", /k-meleon[\/\s](\d+([\.-]\d)*)/, /k-meleon[\/\s](\d+([\.-]\d)*)/, "gecko", geckoEngineVersionPattern);
        profiles[i++] = new EPBrowserProfile("netfront", /playstation\s3/, "2.81", "playstation3", /playstation\s3;\s(\d+\.\d+)/);
        profiles[i++] = new EPBrowserProfile("firebird", /firebird[\/\s](\d+([\.-]\d)*)/, /firebird[\/\s](\d+([\.-]\d)*)/, "gecko", geckoEngineVersionPattern);
        profiles[i++] = new EPBrowserProfile("phoenix", /phoenix[\/\s](\d+([\.-]\d)*)/, /phoenix[\/\s](\d+([\.-]\d)*)/, "gecko", geckoEngineVersionPattern);
        profiles[i++] = new EPBrowserProfile("camino", /camino[\/\s](\d+([\.-]\d)*)/, /camino[\/\s](\d+([\.-]\d)*)/, "gecko", geckoEngineVersionPattern);
        profiles[i++] = new EPBrowserProfile("epiphany", /epiphany[\/\s](\d+([\.-]\d)*)/, /epiphany[\/\s](\d+([\.-]\d)*)/, "gecko", geckoEngineVersionPattern);
        profiles[i++] = new EPBrowserProfile("chimera", /chimera[\/\s](\d+([\.-]\d)*)/, /chimera[\/\s](\d+([\.-]\d)*)/, "gecko", geckoEngineVersionPattern);
        profiles[i++] = new EPBrowserProfile("icab", /icab[\s\/]?(\d+(\.?\d)*)/, /icab[\s\/]?(\d+(\.?\d)*)/, "icab", /icab[\s\/]?(\d+(\.?\d)*)/);
        profiles[i++] = new EPBrowserProfile("netfront", /netfront\/(\d+([\._]\d)*)/, /netfront\/(\d+([\._]\d)*)/, "netfront", /netfront\/(\d+([\._]\d)*)/);
        profiles[i++] = new EPBrowserProfile("netscape", /netscape4\/(\d+([\.-]\d)*)/, /netscape4\/(\d+([\.-]\d)*)/, "mozold", /netscape4\/(\d+([\.-]\d)*)/);
        //profiles[i++] = new EPBrowserProfile("netscape", /mozilla\/(4.\d*)/ && /msie\s(\d+(\.?\d)*)/, /mozilla\/(4.\d*)/, "mozold", /mozilla\/(4.\d*)/);
        profiles[i++] = new EPBrowserProfile("mozsea", /mozilla\/5.0/ && /gecko\//, /rv\x3a(\d+(\.?\d)*)/, "gecko", geckoEngineVersionPattern);
        //profiles[i++] = new EPBrowserProfile("elinks", /elinks/, (brs.search(/elinks\/(\d+(\.?\d)*)/) == -1 ? b[1]=brs.match(/elinks\s\x28(\d+(\.?\d)*)/)[1] : b[1]=brs.match(/elinks\/(\d+(\.?\d)*)/)[1]), "elinks", sameAsb[1]);
        //profiles[i++] = new EPBrowserProfile("w3m", /w3m\/(\d+(\.?\d)*)/, brs.match(/(^w3m|\sw3m)\/(\d+(\.?\d)*)/)[2], "w3m", brs.match(/(^w3m|\sw3m)\/(\d+(\.?\d)*)/)[2]);
        //profiles[i++] = new EPBrowserProfile("links", /links/, (brs.search(/links\/(\d+(\.?\d)*)/) == -1 ? b[1]=brs.match(/links\s\x28(\d+(\.?\d)*)/)[1] : b[1]=brs.match(/links\/(\d+(\.?\d)*)/)[1]), "links", sameAsb[1]);
        profiles[i++] = new EPBrowserProfile("java", /java[\/\s]?(\d+([\._]\d)*)/, /java[\/\s]?(\d+([\._]\d)*)/, "java", /java[\/\s]?(\d+([\._]\d)*)/);
        profiles[i++] = new EPBrowserProfile("lynx", /lynx/, /lynx\/(\d+(\.?\d)*)/, "libwww-fm", /libwww-fm\/(\d+(\.?\d)*)/);
        profiles[i++] = new EPBrowserProfile("dillo", /dillo/, /dillo\s*\/*(\d+(\.?\d)*)/, "dillo", /dillo\s*\/*(\d+(\.?\d)*)/);
        profiles[i++] = new EPBrowserProfile("wget", /wget/, /wget\/(\d+(\.?\d)*)/, "robot");
        profiles[i++] = new EPBrowserProfile("googlebotimg", /googlebot\-image/, /googlebot\-image\/(\d+(\.?\d)*)/, "robot");
        profiles[i++] = new EPBrowserProfile("googlebot", /googlebot/, /googlebot\/(\d+(\.?\d)*)/, "robot");
        profiles[i++] = new EPBrowserProfile("msnbot", /msnbot/, /msnbot\/(\d+(\.?\d)*)/, "robot");
        profiles[i++] = new EPBrowserProfile("turnitinbot", /turnitinbot/, /turnitinbot\/(\d+(\.?\d)*)/, "robot");
		for (var p = 0; p < i; p++) {
			var prf = profiles[p];
			if (prf.isMatch(ua)) {
				this.profile = prf;
				return this.profile;
			}
		}
		this.profile = new EPBrowserProfile("unknown");
	}
	return this.profile;
}

function _epb_getIdentifier() {
	return this.getProfile().getIdentifier();
}

function _epb_getVersion() {
	if (this.version == null) {
		this.version = this.getProfile().getVersion(this.userAgentString);
	}
	return this.version;
}

function _epb_getMajorVersion() {
	var version = this.getVersion();
	if (version != null && version != "") {
		if (version.search(/\./) == -1) {
			return version;
		}
		else {
			return version.match(/(\d*)(\.\d*)*/)[1];
		}
	}
	return "";
}

function _epb_getMinorVersion() {
	var version = this.getVersion();
	if (version != null && version != "") {
		if (version.search(/\./) == -1) {
			return "0";
		}
		else {
			return version.match(/\.(\d*([-\.]\d*)*)/)[1];
		}
	}
	return "";
}

function _epb_getEngine() {
	if (this.engine == null) {
		this.engine = this.getProfile().getEngine(this.userAgentString);
	}
	return this.engine;
}

function _epb_getEngineVersion() {
	if (this.engineVersion == null) {
		this.engineVersion = this.getProfile().getEngineVersion(this.userAgentString);
	}
	return this.engineVersion;
}

/** A BrowserProfile defines a specific browser, how to match it in the user-agent string, and how to determine it's version. */
function EPBrowserProfile(browserIdentifier, regexMatchPattern, versionValueOrPattern, engineValueOrPattern, engineVersionValueOrPattern) {
	this.identifier = browserIdentifier;
	this.matchPattern = regexMatchPattern;
	this.versionValueOrPattern = versionValueOrPattern;
	this.engineValueOrPattern = engineValueOrPattern;
	this.engineVersionValueOrPattern = engineVersionValueOrPattern;
	this.getIdentifier = _epbp_getIdentifier;
	this.getVersion = _epbp_getVersion;
	this.isMatch = _epbp_isMatch;
	this.getEngine = _epbp_getEngine;
	this.getEngineVersion = _epbp_getEngineVersion;
}

function _epbp_isMatch(userAgent) {
	return (userAgent.search(this.matchPattern) != -1);
}

function _epbp_getIdentifier() {
	return this.identifier;
}

function _epbp_getVersion(userAgent) {
	if (this.versionValueOrPattern != null) {
		if (this.versionValueOrPattern instanceof RegExp) {
			return userAgent.match(this.versionValueOrPattern)[1];
		}
		else {
			return this.versionValueOrPattern;
		}
	}
	return "unknown";
}

function _epbp_getEngine(userAgent) {
	if (this.engineValueOrPattern != null) {
		if (this.engineValueOrPattern instanceof RegExp) {
			return userAgent.match(this.engineValueOrPattern)[1];
		}
		else {
			return this.engineValueOrPattern;
		}
	}
	return "unknown";
}

function _epbp_getEngineVersion(userAgent) {
	if (this.engineVersionValueOrPattern != null) {
		if (this.engineVersionValueOrPattern instanceof RegExp) {
			return userAgent.match(this.engineVersionValueOrPattern)[1];
		}
		else {
			return this.engineVersionValueOrPattern;
		}
	}
	return "unknown";
}

/**
 * An OS object returned by EPSniffer's getOperatingSystem() method.
 * 
 * EPOperatingSystem
 *   +getIdentifier():String
 *   +getVersion():String
 */
function EPOperatingSystem(userAgent) {
	this.userAgentString = userAgent;
	this.getIdentifier = _epos_getIdentifier;
	this.getVersion = _epos_getVersion;
	this.getProfile = _epos_getProfile;
}

function _epos_getProfile() {
	if (this.profile == null) {
		var ua = this.userAgentString;
		var profiles = new Array();
		var i = 0;
		// -------------------------- OS PROFILES ---------------------------------
		profiles[i++] = new EPOperatingSystemProfile("wince", /windows\sce/, /windows\sce\/(\d+(\.?\d)*)/);
        profiles[i++] = new EPOperatingSystemProfile("win", /windows/);
		profiles[i-1].getVersion = _eposp_getWindowsVersion;
		profiles[i++] = new EPOperatingSystemProfile("win", /win9\d{1}/);
		profiles[i-1].getVersion = _eposp_getWindowsVersion;
		profiles[i++] = new EPOperatingSystemProfile("android", /android/, /android\s?(\d+(\.?\d)*)/);
		profiles[i++] = new EPOperatingSystemProfile("linux", /linux/, /linux\s?(\d+(\.?\d)*)/);
		profiles[i++] = new EPOperatingSystemProfile("macosx", /mac\sos\sx/);
		profiles[i++] = new EPOperatingSystemProfile("freebsd", /freebsd/, /freebsd\s(\d(\.\d)*)*/);
		profiles[i++] = new EPOperatingSystemProfile("sunos", /sunos/, /sunos\s(\d(\.\d)*)*/);
		profiles[i++] = new EPOperatingSystemProfile("irix", /irix/, /irix\s(\d(\.\d)*)*/);
		profiles[i++] = new EPOperatingSystemProfile("openbsd", /openbsd/, /openbsd\s(\d(\.\d)*)*/);
		profiles[i++] = new EPOperatingSystemProfile("macclassic",/macintosh/);
		profiles[i++] = new EPOperatingSystemProfile("macclassic",/mac\x5fpowerpc/);
		profiles[i++] = new EPOperatingSystemProfile("os2", /os\/2/, /warp\s((\d(\.\d)*)*)/);
		profiles[i++] = new EPOperatingSystemProfile("openvms", /openvms/, /openvms\sv((\d(\.\d)*)*)/);
		profiles[i++] = new EPOperatingSystemProfile("amigaos", /amigaos/, /amigaos\s?(\d(\.\d)*)*/);
		profiles[i++] = new EPOperatingSystemProfile("amigaos", /amiga/, /amigaos\s?(\d(\.\d)*)*/);
        profiles[i++] = new EPOperatingSystemProfile("hurd", /hurd/);
		profiles[i++] = new EPOperatingSystemProfile("hpux", /hp\-ux/, /hp\-ux\sb\.[\/\s]?(\d+([\._]\d)*)/);
		profiles[i++] = new EPOperatingSystemProfile("unix", /unix/);
		profiles[i++] = new EPOperatingSystemProfile("unix", /x11/);
		profiles[i++] = new EPOperatingSystemProfile("cygwin", /cygwin/);
		profiles[i++] = new EPOperatingSystemProfile("java", /java[\/\s]?(\d+([\._]\d)*)/, /java[\/\s]?(\d+([\._]\d)*)/);
		profiles[i++] = new EPOperatingSystemProfile("palmos", /palmos/);
		profiles[i++] = new EPOperatingSystemProfile("webos", /webos/);
		profiles[i++] = new EPOperatingSystemProfile("symbian", /symbian\s?os\/(\d+([\._]\d)*)/, /symbian\s?os\/(\d+([\._]\d)*)/);
		profiles[i++] = new EPOperatingSystemProfile("blackberry", /blackberry/, /blackberry\s+([\w-\;]+)\;/);
		profiles[i++] = new EPOperatingSystemProfile("rimtabletos", /rim tablet os/, /rim tablet os\s+([\w-\;]+)\;/);

		for (var p = 0; p < i; p++) {
			var prf = profiles[p];
			if (prf.isMatch(ua)) {
				this.profile = prf;
				return this.profile;
			}
		}
		this.profile = new EPOperatingSystemProfile("unknown");
	}
	return this.profile;
}

function _epos_getIdentifier() {
	return this.getProfile().getIdentifier();
}

function _epos_getVersion() {
	if (this.version == null) {
		this.version = this.getProfile().getVersion(this.userAgentString);
	}
	return this.version;
}

/** A Operating System Profile defines a specific OS, how to match it in the user-agent string, and how to determine it's version. */
function EPOperatingSystemProfile(id, matchPattern, versionPattern) {
	this.identifier = id;
	this.matchPattern = matchPattern;
	this.versionPattern = versionPattern;
	this.getIdentifier = _eposp_getIdentifier;
	this.isMatch = _eposp_isMatch;
	this.getVersion = _eposp_getVersion;
}

function _eposp_getIdentifier() {
	return this.identifier;
}

function _eposp_isMatch(userAgent) {
	return (userAgent.search(this.matchPattern) != -1);
}

function _eposp_getVersion(userAgent) {
	if (this.versionPattern != null) {
		try {
			return userAgent.match(this.versionPattern)[1];
        }
		catch (e) {
		}
	}
	return "unknown";
}

/** Custom replacement method for determining version on Windows. */
function _eposp_getWindowsVersion(userAgent) {
	if (userAgent.search(/nt\s6\.1/) != -1) {
		return "win7";
	} else if (userAgent.search(/nt\s5\.1/) != -1) {
		return "xp";
	} else if (userAgent.search(/nt\s5\.0/) != -1) {
		return "2000";
	} else if ( (userAgent.search(/win98/) != -1) || (userAgent.search(/windows\s98/)!=-1 ) ) {
		return "98";
	} else if (userAgent.search(/windows\sme/) != -1) {
		return "me";
	} else if (userAgent.search(/nt\s5\.2/) != -1) {
		return "win2k3";
	} else if ( (userAgent.search(/windows\s95/) != -1) || (userAgent.search(/win95/)!=-1 ) ) {
		return "95";
	} else if ( (userAgent.search(/nt\s4\.0/) != -1) || (userAgent.search(/nt4\.0/) ) !=-1) {
		return "nt4";
	}
	else return "unknown"
}

/**
 * An OS object returned by EPSniffer's getDevice() method.
 * 
 * EPDevice
 *   +getIdentifier():String
 *   +getFormFactor():String     one of: ('mobile','tablet','console', or 'default')
 *   +getModel():String
 *   +isTablet():boolean      convenience method for (getFormFactor() == 'tablet')
 *   +isMobile():boolean      convenience method for (getFormFactor() == 'mobile')
 */
function EPDevice(userAgent) {
	this.userAgentString = userAgent;
	this.getIdentifier = _epd_getIdentifier;
	this.getFormFactor = _epd_getFormFactor;
	this.getModel = _epd_getModel;
	this.getProfile = _epd_getProfile;
	this.isTablet = _epd_isTablet;
	this.isMobile = _epd_isMobile;
}

function _epd_getProfile() {
	if (this.profile == null) {
		var ua = this.userAgentString;
		var profiles = new Array();
		var i = 0;
		// ----------------------- DEVICE PROFILES --------------------------------
		profiles[i++] = new EPDeviceProfile("ipad", /ipad/, "tablet", /mobile\/(\w+)/);
		profiles[i++] = new EPDeviceProfile("xoom", /xoom.*build/, "tablet", /build\/(\w+)[\s\)\;]/);
		profiles[i++] = new EPDeviceProfile("xoom", /xoom/, "tablet");
		profiles[i++] = new EPDeviceProfile("rimtablet", /rim tablet os/, "tablet");
		profiles[i++] = new EPDeviceProfile("iphone", /iphone/, "mobile", /mobile\/(\w+)/);
		profiles[i++] = new EPDeviceProfile("android", /firefox.*fennec/, "mobile", /fennec\/([\w\.]+)[\s\)\;]*/);
		profiles[i++] = new EPDeviceProfile("android", /android.*build/, "mobile", /build\/(\w+)[\s\)\;]/);
		profiles[i++] = new EPDeviceProfile("android", /android/, "mobile");
		profiles[i++] = new EPDeviceProfile("blackberry", /blackberry/, "mobile");
		profiles[i++] = new EPDeviceProfile("phone7", /os 7/, "mobile");
		profiles[i++] = new EPDeviceProfile("webos", /webos/, "mobile");
        profiles[i++] = new EPDeviceProfile("playstation", /playstation/, "console", /playstation\s\w*\;\s+([\w\.]*)[\)\s\;]+/);
		// If we get this far in the list and we haven't ID'd a smartphone or tablet...
		profiles[i++] = new EPDeviceProfile("pc", /firefox/, "default");
		profiles[i++] = new EPDeviceProfile("pc", /msie/, "default");
		profiles[i++] = new EPDeviceProfile("pc", /macintosh/, "default");
		for (var p = 0; p < i; p++) {
			var prf = profiles[p];
			if (prf.isMatch(ua)) {
				this.profile = prf;
				return this.profile;
			}
		}
		this.profile = new EPDeviceProfile("unknown", null, "unknown");
	}
	return this.profile;
}

function _epd_getIdentifier() {
	return this.getProfile().getIdentifier();
}

function _epd_getFormFactor() {
	return this.getProfile().getFormFactor();
}

function _epd_getModel() {
	return this.getProfile().getModel(this.userAgentString);
}

function _epd_isTablet() {
	return this.getFormFactor() == 'tablet';
}

function _epd_isMobile() {
	return this.getFormFactor() == 'mobile';
}


/** A Device Profile defines a specific device, how to match it in the user-agent string, how to determine it's model, and other properties. */
function EPDeviceProfile(identifier, matchPattern, formFactor, modelPattern) {
	this.identifier = identifier;
	this.matchPattern = matchPattern;
	this.formFactor = formFactor;
	this.modelPattern = modelPattern;
	this.isMatch = _epdp_isMatch;
	this.getIdentifier = _epdp_getIdentifier;
	this.getFormFactor = _epdp_getFormFactor;
	this.getModel = _epdp_getModel;
}

function _epdp_getIdentifier() {
	return this.identifier;
}

function _epdp_getFormFactor() {
	return this.formFactor;
}

function _epdp_getModel(userAgent) {
	if (this.modelPattern != null) {
		try {
			return userAgent.match(this.modelPattern)[1];
        }
		catch (e) {
		}
	}
	return "unknown";
}

function _epdp_isMatch(userAgent) {
	return (userAgent.search(this.matchPattern) != -1);
}

/** 
 * Creates an auto sniffer object for redirecting to the appropriate alternate host based on preference and device.
 * 
 * Example:
 *     new EPAutoSniffer("mysite.com", "www.mysite.com", "m.mysite.com").redirectAsNeeded();
 *   OR
 *     new EPAutoSniffer("mysite.com", "www.mysite.com", "mobile.mysite.com", "tablet.mysite.com", "console.mysite.com").redirectAsNeeded();
 */
function EPAutoRedirector(siteDomain, defaultHost, mobileHost, tabletHost, consoleHost) {
	this.siteDomain = siteDomain;
	this.defaultHost = defaultHost;
	if (typeof mobileHost != 'undefined') this.mobileHost = mobileHost;
	if (typeof tabletHost != 'undefined') this.tabletHost = tabletHost;
	if (typeof consoleHost != 'undefined') this.consoleHost = consoleHost;
	this.readCookie = _epas_readCookie;
	this.getCookiedFormFactor = _epas_getCookiedFormFactor;
	this.getSiteFormFactor = _epas_getSiteFormFactor;
	this.getHostForFormFactor = _epas_getHostForFormFactor;
	this.isHostDefined = _epas_isHostDefined;
	this.redirectForFormFactor = _epas_redirectForFormFactor;
	this.redirectAsNeeded = _epas_redirectAsNeeded;
	this.writeCookie = _epas_writeCookie;
	this.writeDomainCookie = _epas_writeDomainCookie;
}

/** Utility to read a current cookie value. */
function _epas_readCookie(name) {
	var nameEQ = name + "=";
	var ca = document.cookie.split(';');
	for(var i=0;i < ca.length;i++) {
		var c = ca[i];
		while (c.charAt(0)==' ') c = c.substring(1,c.length);
		if (c.indexOf(nameEQ) == 0) return c.substring(nameEQ.length,c.length);
	}
	return null;
}

/** Utility to write a current cookie value, to be sent on the next interaction with the server. */
function _epas_writeCookie(name, value, expires, path, domain) {
	var cookieString = name + "=" + value;
	if (typeof expires != 'undefined') cookieString += "; expires=" + expires;
	if (typeof path != 'undefined') cookieString += "; path=" + path;
	if (typeof domain != 'undefined') cookieString += "; domain=" + domain;
	// this setter has special handling to append.
	document.cookie = cookieString;
}

/** Writes a cookie for the entire domain, that by default never expires and is visible from all pages on the site. */
function _epas_writeDomainCookie(name, value, expires, path) {
    if (typeof expires == 'undefined' || expires == null) expires = "25/03/2030 00:00:00";
	if (typeof path == 'undefined' || path == null) path = "/";
	this.writeCookie(name, value, expires, path, this.siteDomain);
}

/** Returns the desired form factor of the visitor, based on the cookied preference. */
function _epas_getCookiedFormFactor() {
	var cff = this.readCookie("ff");
	if (cff == 'm') return 'mobile';
	else if (cff == 't') return 'tablet';
	else if (cff == 'c') return 'console';
	else if (cff != null) return 'default'
	else return null;
}

/** Returns the target form factor of the site being visited. */
function _epas_getSiteFormFactor() {
	var currentHost = location.href;
	try {
		currentHost = currentHost.split('?')[0];
		currentHost = currentHost.split('&')[0];
		currentHost = currentHost.match(/\w+\:\/\/(.*)/)[1].toLowerCase();
	}
	catch (ignore) {}
	if (typeof this.tabletHost != 'undefined' && currentHost.indexOf(this.tabletHost) >= 0) return 'tablet';
	else if (typeof this.mobileHost != 'undefined' && currentHost.indexOf(this.mobileHost) >= 0) return 'mobile';
	else if (typeof this.consoleHost != 'undefined' && currentHost.indexOf(this.consoleHost) >= 0) return 'console';
	else if (typeof this.defaultHost != 'undefined' && currentHost.indexOf(this.defaultHost) >= 0) return 'default';
	else {
		// This script is misconfigured for the current site, but we'll return 'default'.
		return 'default';
	}
}

/** Returns the configured host (or virtual host) for the given form factor, e.g. 'm.mysite.com' for 'mobile', or 'www.mysite.com' for default. */
function _epas_getHostForFormFactor(formFactor) {
	if (formFactor == 'mobile' && typeof this.mobileHost != 'undefined') return this.mobileHost;
	else if (formFactor == 'tablet' && typeof this.tabletHost != 'undefined') return this.tabletHost;
	else if (formFactor == 'console' && typeof this.consoleHost != 'undefined') return this.consoleHost;
	else return this.defaultHost;
}

/** Returns true if a host is defined for the given form factor. */
function _epas_isHostDefined(formFactor) {
	if (formFactor == 'mobile' && typeof this.mobileHost != 'undefined') return true;
	else if (formFactor == 'tablet' && typeof this.tabletHost != 'undefined') return true;
	else if (formFactor == 'console' && typeof this.consoleHost != 'undefined') return true;
	else if (formFactor == 'default') return true;
	else return false;
}

/** Redirects to the URL for the configured host for the given form factor, setting a cookie to stick with that form factor. */
function _epas_redirectForFormFactor(formFactor) {
	if (this.isHostDefined(formFactor)) {
		var href = location.href;
		var tokens = href.match(/(\w+\:\/{2,})([\w\.\:-]+)(.*)/);
		var protocol = tokens[1];
		var host = tokens[2];
		var suffix = tokens[3];
		var newHref = protocol + this.getHostForFormFactor(formFactor) + suffix;
		this.writeDomainCookie("ff", formFactor.substring(0,1));
		location.href = newHref;
	}
}

/** Redirects if needed to the appropriate alternate site for this visitor, based on their preference and their device. */
function _epas_redirectAsNeeded() {
	var cookiedFormFactor = this.getCookiedFormFactor();
	var siteFormFactor = this.getSiteFormFactor();
	if (siteFormFactor != cookiedFormFactor) {
		if (cookiedFormFactor != null) this.redirectForFormFactor(cookiedFormFactor);
		else {
			// determine what our current form factor is
			var deviceFormFactor = new EPSniffer().getDevice().getFormFactor();
			if (deviceFormFactor != "unknown" && siteFormFactor != deviceFormFactor) {
				this.redirectForFormFactor(deviceFormFactor);
			}
		}
	}
}

