/**

	sniffler.js summary:
	
	    EntriqSniffler 1.1: A easy-to-use, non-invasive method for getting your browser and plugin settings
	    By Pete McBride, Entriq.
	    
	    When loaded as a script on your webpage, Sniffler will quickly determine the browser's
	    settings and capabilities. To be as non-invasive as possible, (e.g. to avoid script collisions,
	    method dependencies, and unnecessary checks) it stores its findings in two "safe" locations - 
	    in the HTML-tag's CSS class attribute, so CSS can take advantage of the info, and in a small
	    public global object (which you arbitrarily name) so other scripts can read the info
	    Sniffler gleans.
	    
	    At this writing, Sniffler can tell you:
	    
	        Browser Type (11 types, easy to add more) and Version. Build number also, in case of Safari.
	        OS (3 Types, with subtypes where applicable)
	        Common Plugins (5 types), if installed, if active, version, and if it meets a minimum version
	        Javascript active (by not setting a CSS class)
	        Cookies available
	        ActiveX active (where applicable)
	    
		Example:
		
		<HTML class="snf_isWin_true snf_js_true snf_IE snf_Win snf_IE7_Win snf_fl_true snf_wmp_true snf_qt_err_version" >
		
		Those headers tell us, among other things: that we're running in an IE 7 or later browser;  
		that we're on Windows; that JavaScript is on and working; that Flash and wmp are installed 
		with compatible versions; and that quicktime is installed but doesn't meet our minimum version.
		This same information could be gotten from a script looking at properties of window["Sniffler"].
		
	DEPLOYMENT:
		
	    THIS VERSION REQUIRES DOJO!!! Download at www.dojo.org
	    
	    Sniffler is designed to be fire-and-forget: Just include it in your header. It fires, writes out
	    what it finds, and commits suicide, erasing itself from memory. You'll never need to instantiate
	    it, call methods in it, or worry that it's going to step on someone elses code, because 
	    it executes entirely within an anonymous namespace. Its public output is completely configurable
	    using strings, to give you further conflict-avoidance and control.
			        
	
	CONFIGURATION
	
	    Dojo (www.dojo.org) is required for three functions: isIE, isOpera, and addClass. These
	    can be replaced with your own preferred methods, if desired.
    	
	    Apart from that, configuration consists largely of deciding what you want to look for and 
	    how you wish to treat it. Almost everything you'll want to play with is in initSniffler().
	
	        window[Labels.NameSpace] (or pns) is a small global object used to remember the settings that
	        Sniffler is able to figure out. It has no dependencies or methods and exists soley so that
	        other javascripts can use it to get the information.
    	    
	        addClass(className) is a straight passthrough to the dojo addClass method, except that
	        classes ALWAYS go into the HTML tag (which is difficult in some browsers, which is why 
	        I'm using Dojo). It's up to you to decide what information is going to be usefull to 
	        you, although the default classes created in initSniffler should suffice for most common
	        browsers.

            getPluginInfo(plugin, cssLabel, minVersion) handles plugin detection. Each call adds
            exactly one css class to HTML, telling you the state of that particular plugin.
        
        Make up your own classes, depending on your needs. for instance, if you need a special spacer div to
        appear, but ONLY in Firefox on Mac you could make one that reports browser and OS, and then only have
        the div appear when snf_ff_mac appears, and hide it otherwise.
        
    PLUGIN/ACTIVEX STATES
        
        Most plugins/AX controls will return with one of the following states:
        
            OK - plugin is installed, activated, and of the correct version.
            
            ERROR - something is wrong. This is further divided into:
            
                INSTALL - plugin is not installed or otherwise deactivated.
                ACTIVEX - ActiveX is turned off, making detection impossible.
                VERSION - plugin is installed, but did not meet the minimum version.
                WARN - plugin appears to be installed, but version could not be determined.
        
        Actual state-names are set by the Labels vars in the root.
	
	    Detection is implemented in two parts. BrowserDetect goes first, followed by PluginDetect.
	    Both Detectors should work fine as they are, if you keep in mind the following:
	
	    BrowserDetect is pretty straightforward and generally requires no configuration. The exception
	    is if you're tracking Safari Builds, in which case you'll want to update the safari builds table
	    (dataSafariBuild) with latest data from the Apple nightly safari build site. 
    	
	    PluginDetect, may require some work depending on what you are trying to detect. It's currently 
	    configured for QuickTime, Flash, Shockwave, Director, and Windows Media Player (WMP), all of which
	    add up to about 10k in this file. You can remove up to 6k of this by eliminating unneeded detectors.
	    
	    LoaderCheck assumes you have manually created a class called "snf_loaded_false", referring to
	    whether or not the page has fully loaded. One the page fully loads, this will change to 
	    "snf_loaded_true"
	    
	SCRIPT TOO BIG?
	    Sniffler was designed to be butchered! Feel free to remove chunks of code related to specific
	    plugins that you're not concerned with, Browser/build data you don't care about, etc. 
	    Sniffler is very forgiving.
	    
	    Tried so far: http://www.creativyst.com/Prod/3/, Dojo ShrinkSafe and Dean Edward's tool do NOT work.
	    Do it by hand.
	    
	CREDITS
	    Eric Gerds (http://www.pinlady.net/PluginDetect) did most of the work in plugin detect; I merely 
	    modified it to fit within Sniffler. You should be able to cut and paste most of his code (if he
	    updates) within the space provided, so long as you remove the initial "if !PluginDetect"
	    wrapper... Sniffler will handle the rest.
	    
	    Peter-Paul Koch (http://www.quirksmode.org/js/detect.html) did the basics of BrowserDetect,
	    although I heavily modified it to calculate the Safari build-to-version conversion, so it
	    now accurately reflects the real-world Safari version number instead of using Apples internal
	    build number.
	    
	    Peter McBride (mailto:snaux@cox.net) (that's me) merged it all together, and did the bit
	    of storing the information as HTML class-tags and a read-only global rather than calling the
	    methods over and over again, not to mention sticking it all in a "case hardened" protected
	    workspace.  I work on huge projects, and code-clobbering is a major concern with us!
	    
	    
	USAGE (SUGGESTED)
	        
        
        Error handling is managed by comparing two classes, one is the root and the other on the error
        handler div(s). When the root error shows, the handler div displays. Pretty straightforward.

        sniffler.js will post specific errors as classes in the <HTML> tag. When these "error condition classes"
        are present (or in some cases, when absent), you turn on the display:block attribute of the specific 
        div class you want to show. Like magic the error div for the condition will appear:
        
            .error_mac,
            .error_flash,
            .error_os
            {
                 display:none;
            }

            .eq_flash_false .error_flash,
            .eq_mac .error_mac,
            .eq_isWin_false .error_os
            {
                display:block;
            }


        By convention, all error condition classes start with "snf" (for "sniffler") followed by additional
        params relating to the specific error. The param labels and specific errors can be configured 
        within sniffler.js.

        In general, multiple errors will stack on top of each other in this particular interface, so it's
        important that you place 100% fatal errors AFTER less critical ones in the HTML, so that they get 
        a higher z-index... there's no point telling the user he needs to upgrade Flash, for instance, if
        he's running on a Mac, and no point warnng him he's on a mac if he's in africa and can't be a 
        customer anyways.
 
        You can always assign the z-index via css, of course, but I find stacking easier to follow in the 
        code. Just a preference.
        
        
        
        
	
	*/



(function() {

    // tests take time and may or may not trigger the ActiveX yellow alert bar.
    // Use this section to configure which tests to run the moment Sniffler loads.
    // Tests can still be run using the .initScript() command on the global object
    // if you need to, thus window["Sniffler"].wmp.initScript() will populate the
    // window["Sniffler"].wmp.version (et all) properties.

    // SETUP PLUGIN CHECKERS:
    // minVersion is the minimum version you need, or false if you want to skip the check.
    // Set minVersion to "0" if you merely want to verify that ANY version exists.
    // label is the label that will be used in both css and the public global to
    // identify the plugin.
    var Flash = { autoDetect: true, minVersion: "9,0,114,0", label: "fl" };
    var WMP = { autoDetect: true, minVersion: "11,0,0,0", label: "wmp" };

    // SETUP LABELS:
    // Labels will become the actual CSS names, eg class="snf_fl_err_vers" indicates
    // flash is installed but it out-of-date. Edit existing values, or add your own.
    // Labels.NameSpace will become your global variable that other scripts may use to
    // access Sniffler data in a safe context.
    // Labels.Sniffler (a namespace id) prepends everything, 
    // always, and is there to prevent your classes from being stepped on by other
    // processes, such as Yahoo and Dojo, that use similar systems to this.
    var Labels = {
        NameSpace: "Sniffler",
        Sniffler: "snf",
        Passed: "pass",
        Failed: "fail",
        Error: "err",
        Warn: "warn",
        Version: "vers",
        Install: "inst",
        ActiveX: "actx",
        True: "true",
        False: "false",
        Delimiter: "_"
    }

    // This script goes away after being run once. When it goes away, we'll leave this
    // public object behind so javascripters can harvest the information it gathered.
    // Thus, window["Sniffler"].wmp.Version might return "11.0.23.99932"
    window[Labels.NameSpace] = new Object();

    // Internal Reference to the public namespace, to save space in here:
    var pns = window[Labels.NameSpace];


    var d = dojo;
    var ie = d.isIE;
    var opera = d.isOpera;
    var maj = Math.floor;


    var initSniffler = function() {

        //debugger;
        // Harvest all the info, which get placed in the pns/window[Labels.NameSpace] Object:
        initData();


        // -------------------------------------------------------------------------------
        // USER FUNCTIONS GO HERE
        // -------------------------------------------------------------------------------

        // Boolean Classes... feel free to add your own!
        //addClass("os" , passedStr(pns.Browser.os=="Win"));
        addClass("os", passedStr(pns.Browser.os == "Windows 7" || pns.Browser.os == "Windows Vista" || pns.Browser.os == "Windows XP"));

        // Ths one checks for IE7 or FF2 or greater... no old browsers!!!

        var okIE = (pns.Browser.browser == "IE" && pns.Browser.version.major >= 7)
        var okFF = (pns.Browser.browser == "FF" && pns.Browser.version.major >= 2)

        //alert(okIE||okFF)
        addClass("browser", passedStr(okIE || okFF));
        addClass("online", passedStr(pns.Browser.online));
        addClass("cookies", passedStr(pns.Browser.cookies));

        // Informative Classes
        addClass(pns.Browser.os);
        addClass(pns.Browser.browser);
        addClass(pns.Browser.browser + pns.Browser.version.major);
        addClass("lang", pns.Browser.language);


        // Rat out some plugin versions:
        addClass(Flash.label, pns.Flash.version.major);
        addClass(Flash.label, passedStr(pns.Flash.status.isOK()));

        // Did they pass inspection???
        addClass(WMP.label, pns.WMP.version.major);
        addClass(WMP.label, passedStr(pns.WMP.status.isOK()));

        // Security check!
        //    addClass(DRM.label, passedStr(pns.DRM.clientSecurityVersion.meetsOrExceeds("3.5.0.0")));

        // Download Manager! 
        //    addClass(DLManager.label, passedStr(pns.DownloadManager.status==1));



    }

    var randName = function() {
        var trueName = '';
        for (var i = 0; i < 16; i++) {
            trueName += String.fromCharCode(Math.floor(Math.random() * 26) + 97);
        }
        return trueName;
    }

    // Given a true/false expression, returns the css label you've specified:
    var boolStr = function(tf) {
        if (tf) return Labels.True;
        return Labels.False;
    }

    var passedStr = function(tf) {
        if (tf) return Labels.Passed;
        return Labels.Failed;
    }

    function quoteIfString(obj) {
        if (typeof (obj) == "string") {
            return '"' + obj + '"';
        }
        return obj
    }

    // publish will make references of all public vars (those preceded with $) in the
    // public variable, using the namespace provided 
    var publish = function(detectorObject, publishClassName) {
        for (var i in detectorObject) {

            if (i.indexOf("$") == 0) {
                if (pns[publishClassName] == undefined) {
                    pns[publishClassName] = new Object()
                }
                var nameProp = i.substring(1);
                pns[publishClassName][nameProp] = detectorObject[i]

            }
        }
    }

    var invoke = function(detectorObject, publishClassName) {
        detectorObject.initScript();
        if (publishClassName != undefined) {
            publish(detectorObject, publishClassName)
        }
    }

    var defer = function(detectorObject, publishClassName) {
        // Opens deferred-execution detection of certain plugins (those that would
        // trigger an ActiveX warning) to the global object. Adds the "invoke"
        // method to the Sniffler.plugName.invoke(), which triggers the ActiveX.
        detectorObject.$status = new StatusObject(detectorObject);
        if (pns[publishClassName] == undefined) {
            pns[publishClassName] = new Object()
        }
        pns[publishClassName].invoke = detectorObject.initScript;
        publish(detectorObject, publishClassName)
    }


    var ActiveXWarningInvoke = function() {
        // These methods *may* trigger the ActiveX Yellow Bar because they are not
        // pre-approved controls. Once approved by the user, they will always work, though.
        // untl IE is fully restarted to factory-default values.
        //defer(DownloadManagerDetect, "DownloadManager");
        //defer(DRMDetect, "DRM");
    }

    var initData = function() {

        // Init the scripts:
        invoke(JavaScriptDetect, "JavaScript");
        invoke(BrowserDetect, "Browser");
        invoke(LoaderDetect, "Loader");
        invoke(CookieDetect);
        invoke(PluginDetect);
        invoke(FlashDetect, "Flash");
        invoke(WMPDetect, "WMP");

        // Defer any non-preapproved ActiveXs so they don't trigger an alert until we need them:
        defer(DownloadManagerDetect, "DownloadManager");
        defer(DRMDetect, "DRM");


        // A little sidestepping to combine cookie Detector into Browser Detector where it belongs:
        pns["Browser"].cookies = CookieDetect.CookiesWork;

        // Expose these functions so you can roll your own classes:
        pns.Methods = new Object();
        pns.Methods.addClass = addClass;
        pns.Methods.removeClass = removeClass;
        pns.Methods.booleanString = boolStr;

        // Other Javascripts may be interested in what the css labels are:
        pns.Labels = Labels;


        // Output entire contents of the public object in html format
        // for demonstration and diagnosis. Pobably shoul dmake this a self-ref function
        // eventually...
        pns.toString = function() {
            var str = ""
            for (obj in pns) {
                var nso = "\nwindow[\"" + Labels.NameSpace + "\"]." + obj;
                if (typeof (pns[obj]) == "function") {
                    str += nso + " = [function]<br />";
                } else {
                    str += "<br />" + "<br /><b>" + nso + " = " + quoteIfString(pns[obj]) + "</b><br />";
                }

                if (pns.hasOwnProperty(obj)) {
                    if (typeof (pns[obj]) == "function") { }
                    if (typeof (pns[obj]) == "object") {
                        for (obj2 in pns[obj]) {
                            if (pns[obj].hasOwnProperty(obj2)) {
                                if (typeof (pns[obj][obj2]) == "function") {
                                    str += nso + "." + obj2 + " = [function]<br />";
                                } else {
                                    str += nso + "." + obj2 + " = " + quoteIfString(pns[obj][obj2]) + "<br />";
                                }
                                if (typeof (pns[obj][obj2]) == "object") {
                                    for (obj3 in pns[obj][obj2]) {
                                        if (pns[obj][obj2].hasOwnProperty(obj3)) {
                                            if (typeof (pns[obj][obj2][obj3]) == "function") {
                                                str += nso + "." + obj2 + "." + obj3 + "() = [function]<br />";
                                            } else {
                                                str += nso + "." + obj2 + "." + obj3 + " = " + quoteIfString(pns[obj][obj2][obj3]) + "<br />";
                                            }
                                        }
                                    }
                                }
                            }
                        }
                    }
                }
            }

            return str;
        }

        // window[Labels.NameSpace] (called "pns") will also have properties related
        // to each of the plugins that you specifically check. These are accessed using 
        // the name of the plugin as used in the first param of the getPluginInfo
        // function, eg window["Sniffler"].WindowsMediaPlayer or window["Sniffler"].Flash
        // These objects will contain: .minVersion, .version, .state where 1 is OK, 
        // 0 is undetermined/not checked,  -1 is version error, -2 is "inactive" and -3 is because
        // all activeX/plugins are turned off.

    }

    /* SNIFFLER METHODS */

    var buildClassName = function(args) {
        var classname = "";
        for (var i = 0; i < args.length; i++) {
            classname += Labels.Delimiter + args[i];
        }
        return classname.toLowerCase();
    }

    var addClass = function() {
        dojo.addClass(document.documentElement, Labels.Sniffler + buildClassName(arguments));
    }

    var removeClass = function() {
        dojo.removeClass(document.documentElement, Labels.Sniffler + buildClassName(arguments));
    }

    var pageLoaded = function() {
        removeClass("snf_loaded_false");
        addClass("snf_loaded_true");
    }


    // ----------------------------------------------------------
    // STATUS CLASS OBJECT
    var StatusObject = function(obj) {
        this.description = "Uninitialized";
        this.number = 0;
        this.error = false;
        // initially set to the Detect Object, errors will change "obj" into some kind of error object:
        this.data = obj;

        // allows js folks to externally invoke the object:
        this.invoke = obj.initScript;
    }
    StatusObject.prototype.isOK = function() {
        return (this.number == 1);
    }

    StatusObject.prototype.toString = function() {
        return (this.error ? "Error: " + this.number + ": " : "") + this.description + "\ndata: " + this.data;
    }
    StatusObject.prototype.SetStatus = function(n, e) {
        var d;
        this.number = n;
        this.error = true;
        if (e != undefined) {
            this.data = e;
            this.errorDetail = e;
            //alert(this.parent)
            //window["Sniffler"].err = e
            this.errorDetail.toString = function() {
                var dataStr = "{"
                var errObj
                for (errObj in e) {
                    dataStr += errObj + ": " + quoteIfString(e[errObj]) + ", "
                }
                dataStr += "}"
                return dataStr
            }
        }

        switch (this.number) {
            case 1: d = "OK"; this.error = false; break;
            case 0: d = "Uninitialized"; break;
            case -1: d = "Unable to Instantiate"; break;
            case -2: d = "Unable to Invoke"; break;
            case -3: d = "Browser not supported"; break;
            case -4: d = "Version Error"; break;
            case -5: d = "ActiveX Error"; break;
            case -6: d = "Unable to Determine"; break;
            default: d = "Unknown Error"; break;
        }
        this.description = d
    }

    // ----------------------------------------------------------
    // VERSION CLASS OBJECT
    // All items in here will use the version class as a base class for version numbers, and as
    // such will respond to version, version.major, version.minor, version.revision, and version.release.
    // If any of those cannot be calculated for any reason, they will be set to -1.
    // Version Object contains several helper methods and properties: 
    //
    //      .major = 3
    //      .minor = 5
    //      .revision = 0
    //      .release = 0
    //      .toString() = "3.5"
    //      .toFullString() = "3.5.0.0"
    //      .meetsOrExceeds("3.4") = false

    var VersionObject = function() {
        this.major = 0;
        this.minor = 0;

        // These two can't always be determined, depending on each browser.
        // Thus, if they are -1, it means "unknown"
        this.revision = -1;
        this.release = -1;

    }
    VersionObject.prototype.toString = function() {
        var str = this.major;
        if (this.minor == -1) { return str } else { str += "." + this.minor; }
        if (this.revision == -1) { return str } else { str += "." + this.revision; }
        if (this.release == -1) { return str } else { str += "." + this.release; }
        return str
    }
    VersionObject.prototype.toFullString = function() {
        var str = this.major;
        if (this.minor == -1) { str += ".0" } else { str += "." + this.minor; }
        if (this.revision == -1) { str += ".0" } else { str += "." + this.revision; }
        if (this.release == -1) { str += ".0" } else { str += "." + this.release; }
        return str
    }

    VersionObject.prototype.meetsOrExceeds = function(check) {
        var aVIn = check.split(".");
        var thisVers = this.toFullString().split(".")
        // loops if equal, otherwise fails or passes test
        for (var i in thisVers) {
            var mV = parseInt(thisVers[i])
            var cV = parseInt(aVIn[i])
            if (mV > cV) { return true }
            if (mV < cV) { return false }
        }
        // totally equal: pass!
        return true;
    }

    var versionParser = function(dataString, curIndex) {
        if (dataString == undefined) {
            dataString = "-1.0.0.0";
        }
        if (curIndex == undefined) {
            curIndex = 0;
        }

        var vers = new VersionObject();
        var test;
        var subs = "major,minor,revision,release".split(",")
        for (v in subs) {
            test = parseInt(dataString.substring(curIndex));
            if (isNaN(test)) { return vers; }
            vers[subs[v]] = test
            curIndex += String(test).length + 1;
        }
        return vers;
    }




    /* DETECTION SCRIPTS */



    var DRMDetect = {

        initScript: function() {

            this.$status = new StatusObject(this);
            this.$version = versionParser();
            this.$securityVersion = versionParser();
            this.$clientSecurityVersion = versionParser();
            this._id = randName();
            if (ie) {
                this.DRMObject = this.invoke();
                try {
                    this.$version = versionParser(this.DRMObject.GetDRMVersion());
                    this.$securityVersion = versionParser(this.DRMObject.GetDRMSecurityVersion());
                    this.$clientSecurityVersion = versionParser((this.GetClientDRMVersionInfo(this.DRMObject.GetSystemInfo())));
                    this.$status.SetStatus(1);
                } catch (e) {
                    this.$status.SetStatus(-2, e);
                }
                // try to remove the object, we don't need it anymore:
                try {
                    //this.DRMObject.parentNode.parentNode.removeChild(this.DRMObject.parentNode)
                } catch (e) { }

            } else {
                this.$status.SetStatus(-3);
            }
        },


        invoke: function() {
            var oe = "OBJECT";
            var params = "id='" + this._id + "'" + " classid=\"clsid:A9FC132B-096D-460B-B7D5-1DB0FAE0C062\" name=\"" + this._id + "\" VIEWASTEXT";
            var emb = "<embed MAYSCRIPT type=\"application/x-drm-v2\" hidden=\"true\"></embed>";
            var plugInstance = PluginDetect.instantiate(oe, params, emb);
            return plugInstance[0].firstChild
        },

        GetClientDRMVersionInfo: function(sysInfo) {
            var machString = sysInfo.substring(sysInfo.indexOf("<MACHINECERTIFICATE>") + 20, sysInfo.indexOf("</MACHINECERTIFICATE>"));
            machString = machString.replace(/!/g, "+");
            machString = machString.replace(/\*/g, "/");
            var decodeString = this.decode(machString);
            //extract the <c:SecurityVersion> info from the decoded string                
            var verInfo = decodeString.substring(decodeString.indexOf("<c:SecurityVersion>") + 19, decodeString.indexOf("</c:SecurityVersion>"));
            return verInfo;
        },

        decode: function(input) {
            var output = "";
            var _keyStr = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/=";
            var chr1, chr2, chr3;
            var enc1, enc2, enc3, enc4;
            var i = 0;
            input = input.replace(/[^A-Za-z0-9\+\/\=]/g, "");
            while (i < input.length) {
                enc1 = _keyStr.indexOf(input.charAt(i++));
                enc2 = _keyStr.indexOf(input.charAt(i++));
                enc3 = _keyStr.indexOf(input.charAt(i++));
                enc4 = _keyStr.indexOf(input.charAt(i++));
                chr1 = (enc1 << 2) | (enc2 >> 4);
                chr2 = ((enc2 & 15) << 4) | (enc3 >> 2);
                chr3 = ((enc3 & 3) << 6) | enc4;
                output = output + String.fromCharCode(chr1);
                if (enc3 != 64) { output = output + String.fromCharCode(chr2); }
                if (enc4 != 64) { output = output + String.fromCharCode(chr3); }
            }
            return this.utf8(output)
        },

        utf8: function(utftext) {
            var string = "";
            var i = 0;
            var c = c1 = c2 = 0;
            while (i < utftext.length) {
                c = utftext.charCodeAt(i);
                if (c < 128) {
                    string += String.fromCharCode(c);
                    i++;
                }
                else if ((c > 191) && (c < 224)) {
                    c2 = utftext.charCodeAt(i + 1);
                    string += String.fromCharCode(((c & 31) << 6) | (c2 & 63));
                    i += 2;
                }
                else {
                    c2 = utftext.charCodeAt(i + 1);
                    c3 = utftext.charCodeAt(i + 2);
                    string += String.fromCharCode(((c & 15) << 12) | ((c2 & 63) << 6) | (c3 & 63));
                    i += 3;
                }
            }
            return string;
        }
    }



    var DownloadManagerDetect = {

        initScript: function() {
            this.$status = new StatusObject(this);
            this.$coreVersion = versionParser()
            this.$clientVersion = versionParser()
            this._id = randName();
            this._invoke();
        },

        _invoke: function() {
            var t, d = document;

            if (!dojo.isIE && !dojo.isFF) {
                // Bad Browser
                this.$status.SetStatus(-3);
                return;
            }
            if (dojo.isIE) {
                t = "<OBJECT id=\"" + this._id + "\" width=\"1\" height=\"1\" style=\"display:none\" classid=\"clsid:FC7F24C5-7DC3-48DF-A00D-F57DE89EADC5\" name=\"" + this._id + "\" VIEWASTEXT></OBJECT>"
            } else {
                t = "<embed name=\"" + this._id + "\" id=\"" + this._id + "\" type=\"application/entriq-version-check-npruntime\"  hidden></embed>"
            }
            this.$status.SetStatus(1); // OK until proven otherwise!



            var body = (d.getElementsByTagName("body")[0] || d.body);
            var div = d.createElement("div");

            if (body) {
                body.appendChild(div)
            } else {
                try {
                    d.write("<div>o</div><div>" + t + "</div>");
                    body = (d.getElementsByTagName("body")[0] || d.body);
                    body.removeChild(body.firstChild);
                    div = body.firstChild
                } catch (e) {
                    try {
                        body = d.createElement("body");
                        d.getElementsByTagName("html")[0].appendChild(body);
                        body.appendChild(div);
                        div.innerHTML = t;
                    } catch (e) {
                        // Unable to create element
                        this.$status.SetStatus(-1, e);
                        return;
                    }
                }
            }
            this._versionChecker = div.firstChild;

            var _coreCode = "CORE";
            var _clientCode = "CONFIG";
            var _core;
            var _client;
            var _custID = "sfanytime";
            var _delim = "=";
            try {
                // Assume we're OK until proven otherwise:
                this.$status.SetStatus(1);
                _core = this._versionChecker.GetDMExtVersion(_coreCode, _custID).split(_delim);
                _client = this._versionChecker.GetDMExtVersion(_clientCode, _custID).split(_delim);
                this.$coreVersion = versionParser(_core[1]);
                this.$clientVersion = versionParser(_client[1]);
            } catch (e) {
                // general failure - the control probably isn't there, or is disabled:
                this.$status.SetStatus(-2, e);
            }
            try {
                // this._versionChecker.parentNode.parentNode.removeChild(this._versionChecker.parentNode)
                this._versionChecker.parentNode.removeChild(this._versionChecker)
            } catch (e) {
                // something went horribly wrong in the config and we cannot remove what we made! Oh well!
            }
        }
    }

    // Obviously, you can't test for JavaScript using JavaScript! But what you CAN do is
    // manually set a "No Javascript" class in the HTML CSS, and have javascript remove 
    // it if JS is actually running. That way, if the class still exists, CSS will know
    // that JS is off and can take appropriate action.
    // BUGBUG Future: Get JavaScript Version can we do this without HTML?
    var JavaScriptDetect = {
        initScript: function() {
            this.$status = new StatusObject(this);
            this.$version = new VersionObject(this);
            removeClass("js", Labels.False);
            addClass("js", Labels.True);
            this.$status.SetStatus(1);
        }
    }

    var LoaderDetect = {
        initScript: function() {
            this.$status = new StatusObject(this);
            if (typeof window.addEventListener !== 'undefined') {
                window.addEventListener('load', function() { pageLoaded() }, false);
            } else if (typeof window.attachEvent !== 'undefined') {
                window.attachEvent('onload', function() { pageLoaded() });
            }
        }

    }

    var CookieDetect = { initScript: function() { var tmpcookie = new Date(); chkcookie = (tmpcookie.getTime() + ''); document.cookie = "chkcookie=" + chkcookie + "; path=/"; this.CookiesWork = !(document.cookie.indexOf(chkcookie, 0) < 0) } }

    var BrowserDetect = {
        initScript: function() {
            //alert("foo")
            // debugger;

            this.$status = new StatusObject(this);
            this.$build = 0
            this.$language = "err";
            //platform differs from os. Platform is more specific: Win32, Win64; OS is generic: Win.
            this.$platform = navigator.platform;
            this.$online = navigator.onLine;

            this.$browser = this.searchString(this.dataBrowser) || 0;
            this.$version = this.searchVersion(navigator.userAgent) || this.searchVersion(navigator.appVersion) || 0;
            //this.$os=this.searchString(this.dataOS) || 0;
            try {
                this.$os = (/Windows NT 6\.2/.test(navigator.userAgent)) ? 'Windows Storage Server'
				: (/Windows NT 6\.1/.test(navigator.userAgent)) ? 'Windows 7'
				: (/Windows NT 6\.0/.test(navigator.userAgent)) ? 'Windows Vista'
				: (/Windows NT 5\.2/.test(navigator.userAgent)) ? 'Windows Server 2003'
				: (/Windows NT 5\.1/.test(navigator.userAgent)) ? 'Windows XP'
				: (/Windows NT 5\.0/.test(navigator.userAgent)) ? 'Windows 2000'
				: (/Windows NT 4\.0/.test(navigator.userAgent)) ? 'Windows NT 4.0'
				: (/Windows 98/.test(navigator.userAgent)) ? 'Windows 98'
				: (/Windows 95/.test(navigator.userAgent)) ? 'Windows 95'
				: (/Windows CE/.test(navigator.userAgent)) ? 'Windows CE'
				: this.searchString(this.dataOS)
            } catch (e) {
                this.$os = 0;
            }

            // quirks:
            if (this.$browser == "Safari") {
                this.$build = this.$version;
                var sBuild = eval(this.$version.major + "." + this.$version.minor)
                this.$version = this.searchSafariBuilds(sBuild, this.dataSafariBuild)
            } else if (this.$browser == "FF") {
                this.$build = navigator.buildID
                this.$language = navigator.language.replace(/-/g, "")
            } else if (this.$browser == "IE") {
                this.$build = 0
                this.$language = navigator.userLanguage.replace(/-/g, "")
            } else {
                this.$language = navigator.language.replace(/-/g, "")
            }
            this.$status.SetStatus(1)
        },

        searchString: function(data) {
            for (var i = 0; i < data.length; i++) {
                var dataString = data[i].string;
                var dataProp = data[i].prop;
                this.versionSearchString = data[i].versionSearch || data[i].identity;
                if (dataString) {
                    if (dataString.indexOf(data[i].subString) != -1)
                        return data[i].identity;
                }
                else if (dataProp)
                    return data[i].identity;
            }
        },


        searchVersion: function(dataString) {
            // i.e. look for "MSIE" which is followed by "7.0"
            // handles up to n.n.n.n format, using any delimiter. Gives up 
            // when it finds a number it can't parse.
            var index = dataString.indexOf(this.versionSearchString);
            if (index == -1) return; // not found.
            var curIndex = index + this.versionSearchString.length + 1;
            var vers = versionParser(dataString, curIndex);
            return vers;
        },


        searchSafariBuilds: function(vers, data) {
            /* Safari uses internal "build number" instead of a release number, 
            so this translates for us non-apple-employees. Grrrrr. */
            var svers = new VersionObject();
            for (var i = 0; i < data.length; i++) {
                if (vers >= data[i].sf) {
                    var vdata = data[i].v.split(".")[0];
                    svers.major = vdata[0];
                    svers.minor = vdata[1];
                    svers.revision = vdata[2];
                } else {
                    break;
                }
            }
            return svers;
        },
        /* identity is how we will see the browser name in the CSS, eg eq_IE, eq_Safari, etc.
        string is the navigator object to look at, and subString is the name that should appear
        there for a match to be valid. Note that for Netscape and some other browsers, position
        in this list is important. */
        dataBrowser: [
		    { string: navigator.userAgent,
		        subString: "OmniWeb",
		        versionSearch: "OmniWeb/",
		        identity: "OmniWeb"
		    },
		    {
		        string: navigator.vendor,
		        subString: "Apple",
		        identity: "Safari"
		    },
		    {
		        prop: window.opera,
		        identity: "Opera"
		    },
		    {
		        string: navigator.vendor,
		        subString: "iCab",
		        identity: "iCab"
		    },
		    {
		        string: navigator.vendor,
		        subString: "KDE",
		        identity: "Konqueror"
		    },
		    {
		        string: navigator.userAgent,
		        subString: "Firefox",
		        versionSearch: "Firefox",
		        identity: "FF"
		    },
		    {
		        string: navigator.vendor,
		        subString: "Camino",
		        identity: "Camino"
		    },
		    {		// for newer Netscapes (6+)
		        string: navigator.userAgent,
		        subString: "Netscape",
		        identity: "Netscape"
		    },
		    {
		        string: navigator.userAgent,
		        subString: "MSIE",
		        identity: "IE",
		        versionSearch: "MSIE"
		    },
		    {
		        string: navigator.userAgent,
		        subString: "Gecko",
		        identity: "Moz",
		        versionSearch: "rv"
		    },
		    { 		// for older Netscapes (4-)
		        string: navigator.userAgent,
		        subString: "Mozilla",
		        identity: "NS",
		        versionSearch: "Mozilla"
		    }
	    ],
        dataOS: [
		    {
		        string: navigator.platform,
		        subString: "Win",
		        identity: "Win"
		    },
		    {
		        string: navigator.platform,
		        subString: "Mac",
		        identity: "Mac"
		    },
		    {
		        string: navigator.platform,
		        subString: "Linux",
		        identity: "Lin"
		    }
	    ],

        dataSafariBuild: [
        /* Apple uses build numbers and not "real" version numbers; this table corrects this problem.
        but of course this means you need to maintain it... the safari build number will be scanned
        from lowest to highest; the number that matches or at least doesn't exceed the check number 
        reveals the correct version number, e.g. anything from 412.0 to 412.49 is 2.0.0 Webkit builds
        are included here for reference only and are not used curently, unless I can figure out a way
        to differentiate between webkit and safari. */
        /* v: real-world version; wk: webkit nightly build number; sf: safari nightly build number */
            {v: "1.0.0", wk: 85.7, sf: 85.5 },
            { v: "1.0.2", wk: 85.7, sf: 85.7 },
            { v: "1.0.3", wk: 85.8, sf: 85.8 },
            { v: "1.1.0", wk: 100, sf: 100 },
            { v: "1.1.1", wk: 100, sf: 100.1 },
            { v: "1.2.2", wk: 125.2, sf: 125.7 },
            { v: "1.2.4", wk: 125.5, sf: 125.11 },
            { v: "1.3.0", wk: 312.1, sf: 312 },
            { v: "1.3.1", wk: 312.5, sf: 312.3 },
            { v: "1.3.2", wk: 312.8, sf: 312.5 },
            { v: "2.0.0", wk: 412, sf: 412 },
            { v: "2.0.1", wk: 412.7, sf: 412.5 },
            { v: "2.0.2", wk: 416.11, sf: 416.12 },
            { v: "2.0.2", wk: 416.12, sf: 416.13 },
            { v: "2.0.3", wk: 417.9, sf: 417.8 },
            { v: "2.0.4", wk: 418.8, sf: 419.3 },
            { v: "3.0.4", wk: 523.1, sf: 523.1 },
            { v: "3.0.4", wk: 523.1, sf: 523.2 }


	    ]

} /* End Browserdetect() */



        /* PluginDetect v0.4.9 (10.0k QT Shockwave Flash WMP Silverlight ) by Eric Gerds www.pinlady.net/PluginDetect */
        /* Modified by Pete so it fits within this namespace */


        var PluginDetect = {
            getNum: function(A, _2) { if (!this.num(A)) { return null } var m; if (typeof _2 == "undefined") { m = /[\d][\d\.\_,-]*/.exec(A) } else { m = (new RegExp(_2)).exec(A) } return m ? m[0].replace(/[\.\_-]/g, ",") : null },
            hasMimeType: function(_4) { var s, t, z, M = _4.constructor == String ? [_4] : _4; for (z = 0; z < M.length; z++) { s = navigator.mimeTypes[M[z]]; if (s && s.enabledPlugin) { t = s.enabledPlugin; if (t.name || t.description) { return s } } } return null },
            findNavPlugin: function(N, _7) { var _8 = N.constructor == String ? N : N.join(".*"), numS = _7 === false ? "" : "\\d"; var i, re = new RegExp(_8 + ".*" + numS + "|" + numS + ".*" + _8, "i"); var _a = navigator.plugins; for (i = 0; i < _a.length; i++) { if (re.test(_a[i].description) || re.test(_a[i].name)) { return _a[i] } } return null },
            getAXO: function(_b) { var _c, e; try { _c = new ActiveXObject(_b); return _c } catch (e) { } return null },
            num: function(A) { return (typeof A != "string" ? false : (/\d/).test(A)) },
            compareNums: function(_e, _f) { if (!this.num(_e) || !this.num(_f)) { return 0 } if (this.plugin && this.plugin.compareNums) { return this.plugin.compareNums(_e, _f) } var m1 = _e.split(","), m2 = _f.split(","), x, p = parseInt; for (x = 0; x < Math.min(m1.length, m2.length); x++) { if (p(m1[x], 10) > p(m2[x], 10)) { return 1 } if (p(m1[x], 10) < p(m2[x], 10)) { return -1 } } return 0 },
            formatNum: function(num) { if (!this.num(num)) { return null } var x, n = num.replace(/\s/g, "").replace(/[\.\_]/g, ",").split(",").concat(["0", "0", "0", "0"]); for (x = 0; x < 4; x++) { if (/^(0+)(.+)$/.test(n[x])) { n[x] = RegExp.$2 } } return n[0] + "," + n[1] + "," + n[2] + "," + n[3] },
            initScript: function() { var $ = this, IE; $.isIE = (/*@cc_on!@*/false); $.IEver = -1; $.ActiveXEnabled = false; if ($.isIE) { IE = (/msie\s*\d\.{0,1}\d*/i).exec(navigator.userAgent); if (IE) { $.IEver = parseFloat((/\d.{0,1}\d*/i).exec(IE[0]), 10) } var _14, x; _14 = ["ShockwaveFlash.ShockwaveFlash", "Msxml2.XMLHTTP", "Microsoft.XMLDOM", "Msxml2.DOMDocument", "TDCCtl.TDCCtl", "Shell.UIHelper", "Scripting.Dictionary", "wmplayer.ocx"]; for (x = 0; x < _14.length; x++) { if ($.getAXO(_14[x])) { $.ActiveXEnabled = true; break } } } if ($.isIE) { $.head = typeof document.getElementsByTagName != "undefined" ? document.getElementsByTagName("head")[0] : null } },
            init: function(_15) { if (typeof _15 != "string") { return -3 } _15 = _15.toLowerCase().replace(/\s/g, ""); var $ = this, IE, p; if (typeof $[_15] == "undefined") { return -3 } p = $[_15]; $.plugin = p; if (typeof p.installed == "undefined") { p.minversion = {}; p.installed = null; p.version = null; p.getVersionDone = null } $.garbage = false; if ($.isIE && !$.ActiveXEnabled) { return -2 } return 1 },
            isMinVersion: function(_17, _18, _19) { var $ = PluginDetect, i = $.init(_17); if (i < 0) { return i } if (typeof _18 == "undefined" || _18 == null) { _18 = "0" } if (typeof _18 == "number") { _18 = _18.toString() } if (!$.num(_18)) { return -3 } _18 = $.formatNum(_18); if (typeof _19 == "undefined") { _19 = null } var p = $.plugin, m = p.minversion; if (typeof m["a" + _18] == "undefined") { if (p.getVersionDone == null) { var tmp, x; for (x in m) { tmp = $.compareNums(_18, x.substring(1, x.length)); if (m[x] == 1 && tmp <= 0) { return 1 } if (m[x] == -1 && tmp >= 0) { return -1 } }; p.getVersion(_18, _19) } if (typeof m["a" + _18] != "undefined") { } else { if (p.version != null || p.installed != null) { p.getVersionDone = 1; m["a" + _18] = (p.installed == -1 ? -1 : (p.version == null ? 0 : ($.compareNums(p.version, _18) >= 0 ? 1 : -1))) } else { m["a" + _18] = -1 } } } $.cleanup(); return m["a" + _18]; return -3 },
            getVersion: function(_1d, _1e) { var $ = PluginDetect, i = $.init(_1d); if (i < 0) { return null } var p = $.plugin; if (typeof _1e == "undefined") { _1e = null } if (p.getVersionDone == null) { p.getVersion(null, _1e); p.getVersionDone = 1 } $.cleanup(); return p.version; return null },
            cleanup: function() { var $ = this; if ($.garbage && typeof window.CollectGarbage != "undefined") { window.CollectGarbage() } },
            isActiveXObject: function(_22) { var $ = this, result, e, s = "<object width=\"1\" height=\"1\" " + "style=\"display:none\" " + $.plugin.getCodeBaseVersion(_22) + ">" + $.plugin.HTML + "</object>"; if ($.head.firstChild) { $.head.insertBefore(document.createElement("object"), $.head.firstChild) } else { $.head.appendChild(document.createElement("object")) } $.head.firstChild.outerHTML = s; try { $.head.firstChild.classid = $.plugin.classID } catch (e) { } result = false; try { if ($.head.firstChild.object) { result = true } } catch (e) { } try { if (result && $.head.firstChild.readyState < 4) { $.garbage = true } } catch (e) { } $.head.removeChild($.head.firstChild); return result },
            codebaseSearch: function(min) { var $ = this; if (typeof min != "undefined") { return $.isActiveXObject(min) }; var _26 = [0, 0, 0, 0], x, y, A = $.plugin.digits, t = function(x, y) { var _29 = (x == 0 ? y : _26[0]) + "," + (x == 1 ? y : _26[1]) + "," + (x == 2 ? y : _26[2]) + "," + (x == 3 ? y : _26[3]); return $.isActiveXObject(_29) }; var _2a, tmp; var _2b = false; for (x = 0; x < A.length; x++) { _2a = A[x] * 2; _26[x] = 0; for (y = 0; y < 20; y++) { if (_2a == 1 && x > 0 && _2b) { break } if (_2a - _26[x] > 1) { tmp = Math.round((_2a + _26[x]) / 2); if (t(x, tmp)) { _26[x] = tmp; _2b = true } else { _2a = tmp } } else { if (_2a - _26[x] == 1) { _2a--; if (!_2b && t(x, _2a)) { _2b = true } break } else { if (!_2b && t(x, _2a)) { _2b = true } break } } } if (!_2b) { return null } } return _26.join(",") },
            dummy1: 0
        }

        PluginDetect.instantiate = function(_63, _64, _65) { var e, d = document, tag1 = "<" + _63 + " width=\"1\" height=\"1\" " + _64 + ">" + _65 + "</" + _63 + ">", body = (d.getElementsByTagName("body")[0] || d.body), div = d.createElement("div"); if (body) { body.appendChild(div) } else { try { d.write("<div>o</div><div>" + tag1 + "</div>"); body = (d.getElementsByTagName("body")[0] || d.body); body.removeChild(body.firstChild); div = body.firstChild } catch (e) { try { body = d.createElement("body"); d.getElementsByTagName("html")[0].appendChild(body); body.appendChild(div); div.innerHTML = tag1; return [div, body] } catch (e) { } } return [div, div] } if (div && div.parentNode) { try { div.innerHTML = tag1 } catch (e) { } } return [div, div] };

        /* You can remove any of these that you don't need (to save space) */

        /* END PluginDetect v0.4.9 */



        function convertPluginDetectCode(obj, plugName, minVersion) {

            // Get the actual version number of the installed plugin:
            obj.$version = versionParser(PluginDetect.getVersion(plugName), 0);

            // If not acceptable, tell us why in the status:
            var PD = PluginDetect.isMinVersion(plugName, minVersion);
            if (PD == 1) {
                // everything's just fine: installed, usable, right version!
                obj.$status.SetStatus(1)
            } else if (PD == 0) {
                // unable to determine version, but apparently installed:
                obj.$status.SetStatus(-6)
            } else if (PD == -1) {
                if (PluginDetect.isMinVersion(plugName, '0') >= 0) {
                    // Wrong min version:
                    obj.$status.SetStatus(-4)
                } else {
                    // Not installed or turned off:
                    obj.$status.SetStatus(-2)
                }
            } else if (PD == -2) {
                // activeX appears to be off:
                obj.$status.SetStatus(-5)
            };
        }


        var FlashDetect = {
            initScript: function() {
                this.$status = new StatusObject(this);
                this.$version = versionParser();
                PluginDetect.flash = { mimeType: ["application/x-shockwave-flash", "application/futuresplash"], progID: "ShockwaveFlash.ShockwaveFlash", classID: "clsid:D27CDB6E-AE6D-11CF-96B8-444553540000", getVersion: function() { var _5e = function(A) { if (!A) { return null } var m = /[\d][\d\,\.\s]*[rRdD]{0,1}[\d\,]*/.exec(A); return m ? m[0].replace(/[rRdD\.]/g, ",").replace(/\s/g, "") : null }; var p, $ = PluginDetect, e, i, version = null, AXO = null, majV = null; if (!$.isIE) { p = $.findNavPlugin("Flash"); if (p && p.description && $.hasMimeType(this.mimeType)) { version = _5e(p.description) } } else { for (i = 15; i > 2; i--) { AXO = $.getAXO(this.progID + "." + i); if (AXO) { majV = i.toString(); break } } if (majV == "6") { try { AXO.AllowScriptAccess = "always" } catch (e) { return "6,0,21,0" } } try { version = _5e(AXO.GetVariable("$version")) } catch (e) { } if (!version && majV) { version = majV } } this.installed = version ? 1 : -1; this.version = $.formatNum(version); return true } };
                convertPluginDetectCode(this, "flash", Flash.minVersion)
            }
        }

        var WMPDetect = {
            initScript: function() {
                this.$status = new StatusObject(this);
                this.$version = versionParser();
                PluginDetect.windowsmediaplayer = { mimeType: ["application/x-mplayer2", "application/asx"], progID: "wmplayer.ocx", classID: "clsid:6BF52A52-394A-11D3-B153-00C04F79FAA6", getVersion: function() { var _67 = null, $ = PluginDetect, tmp = null; this.installed = -1; if (!$.isIE) { if ($.hasMimeType(this.mimeType)) { if ($.findNavPlugin(["Windows", "Media", "(Plug-in|Plugin)"], false) || $.findNavPlugin(["Flip4Mac", "Windows", "Media"], false)) { this.installed = 0 } var q = (/rv\:(1\.[0-7]|0).*Gecko/).test(navigator.userAgent); if (!q && $.findNavPlugin(["Windows", "Media", "Firefox Plugin"], false)) { var _69 = $.instantiate("object", "type=\"" + this.mimeType[0] + "\"", ""); if (_69[0] && _69[1]) { if (_69[0].firstChild) { _67 = _69[0].firstChild.versionInfo } if (_69[1].parentNode) { _69[1].parentNode.removeChild(_69[1]) } } } } } else { tmp = $.getAXO(this.progID); if (tmp) { _67 = tmp.versionInfo } } if (_67) { this.installed = 1 } this.version = $.formatNum(_67) } };
                convertPluginDetectCode(this, "windowsmediaplayer", WMP.minVersion)

            }
        }
        initSniffler();


    } /* End function() */)/* End (function()) */()/* call the closure (function())() */;



