/*  Loki Javascript API
 *  Ryan Sarver <rsarver@skyhookwireless.com>
 *
 *  This is a helper script to help you detect and gracefully handle
 *  users with Loki Plugin installed
 *
/*--------------------------------------------------------------------------*/

/////////////////////////////////////////////////////////////////////////////////////////
// Version
/////////////////////////////////////////////////////////////////////////////////////////

LokiAPI.availableVersion = "3.2.0.08";
LokiAPI.scriptRevision = "9";
LokiAPI.installedVersion = null; // Will be filled by isInstalled()

/////////////////////////////////////////////////////////////////////////////////////////
// Loki API wrapper
/////////////////////////////////////////////////////////////////////////////////////////

function LokiAPI()
{
    return Try.these(
                     function() {return new LokiPlugin()},
                     function() {return new LokiNull()}
                    ) || false;
}

LokiAPI.isInstalled = function()
{
    return LokiPlugin.isInstalled();
}

LokiAPI.isUpgradeAvailable = function()
{
    // May be isInstalled wasn't called yet. 
    // Call to ensure that LokiAPI.installedVersion is filled.
    if (!LokiAPI.installedVersion)
        LokiAPI.isInstalled(); 

    if (!LokiAPI.availableVersion)
        return false;

    if (!LokiAPI.installedVersion)
        return true;

    return (compareVersions(LokiAPI.installedVersion, LokiAPI.availableVersion) < 0);
}

LokiAPI.startUpgrade = function()
{
    loki = new LokiAPI();
    loki.tryToInstallPlugin();
}

/////////////////////////////////////////////////////////////////////////////////////////
// LokiPlugin class, common parts
/////////////////////////////////////////////////////////////////////////////////////////

function LokiPlugin()
{
    if (LokiPlugin.timer)
        clearTimeout(LokiPlugin.timer);

    LokiPlugin.attemptedInstall = false;
    LokiPlugin.failedInstall = false;
    LokiPlugin.upgradeStarted = false;
    LokiPlugin.activex = null;

    if (LokiPlugin.isInstalled())
        this.initPlugin();
    else
        this.tryToInstallPlugin();
}

LokiPlugin.isInstalled = function()
{
    switch (BrowserDetect.browser)
    {
        case "Explorer":
            return LokiPlugin.isInstalled_IE();

        case "Firefox":
            {
                try
                {
                    var lokixpcom = new Loki();
                    if (lokixpcom)
                    {
                        LokiPlugin.xpcom = lokixpcom;
                        return true;
                    }
                } catch (e) {}
            }
            /* FALLTHROUGH */
        case "Opera":
        case "Safari":
        case "Chrome":
            return LokiPlugin.isInstalled_NPAPI();

        default:
            return false;
    }
}

LokiPlugin.prototype.initPlugin = function()
{
    this.installationFinished();

    //If LokiPlugin.xpcom is set, then xpcom is installed, inited and should be used
    if (LokiPlugin.xpcom != null)
        return;

    switch (BrowserDetect.browser)
    {
        case "Explorer":
            LokiPlugin.init_IE();
            break;

        case "Firefox":
        case "Opera":
        case "Safari":
        case "Chrome":
            LokiPlugin.init_NPAPI();
            break;
    }
}

LokiPlugin.prototype.browserSupported = function()
{
    // Here is complete list of well supported platforms and browsers
    // TODO add Konqueror/Flock support

    if ((BrowserDetect.OS != "Windows" &&
         BrowserDetect.OS != "Mac") ||
        (BrowserDetect.browser != "Explorer" &&
         BrowserDetect.browser != "Firefox" && 
         BrowserDetect.browser != "Safari" &&
         BrowserDetect.browser != "Chrome" &&
         BrowserDetect.browser != "Opera"))
    {
        return false;
    }

    // IE 64bit version is not supported by plugin
    if (BrowserDetect.browser == "Explorer" &&
        window.navigator.cpuClass == "x64")
    return false;

    return true;
}

LokiPlugin.prototype.showManageDomains = function()
{
    if (LokiPlugin.xpcom != null)
        this.showManageDomains_XPCOM();
    else
    if (BrowserDetect.browser == "Explorer")
        this.showManageDomains_IE();
    else
        this.showManageDomains_NPAPI();
}

LokiPlugin.prototype.reverseGeocode = function(lat, lon, addressLookup)
{
    if (!this.browserSupported())
    {
        this.onReverseGeocodedProxy(LokiPlugin.WPS_ERROR_PLUGIN_BROWSER_NOT_SUPPORTED);
        return;
    }

    if (LokiPlugin.failedInstall)
    {
        this.onReverseGeocodedProxy(LokiPlugin.WPS_ERROR_PLUGIN_COULD_NOT_BE_INSTALLED);
        return;
    }

    if (this.operationStarted()) return;

    this.lastLatArg = lat;
    this.lastLonArg = lon;
    this.lastAddressLookupArg = addressLookup;
    
    if (LokiPlugin.xpcom != null)
        this.reverseGeocoding_XPCOM(lat, lon, addressLookup);
    else
    if (BrowserDetect.browser == "Explorer")
        this.reverseGeocoding_IE(lat, lon, addressLookup);
    else
        this.reverseGeocoding_NPAPI(lat, lon, addressLookup);
}

LokiPlugin.prototype.tuneLocation = function(lat, lon)
{
    if (!this.browserSupported())
    {
        this.onTuneCompletedProxy(LokiPlugin.WPS_ERROR_PLUGIN_BROWSER_NOT_SUPPORTED);
        return;
    }

    if (LokiPlugin.failedInstall)
    {
        this.onTuneCompletedProxy(LokiPlugin.WPS_ERROR_PLUGIN_COULD_NOT_BE_INSTALLED);
        return;
    }

    if (LokiPlugin.xpcom != null)
    {
        this.onTuneCompletedProxy(LokiPlugin.WPS_ERROR_FEATURE_NOT_SUPPORTED);
        return;
    }

    if (this.operationStarted()) return;

    if (BrowserDetect.browser == "Explorer")
        this.tuneLocation_IE(lat, lon);
    else
        this.tuneLocation_NPAPI(lat, lon);
}

LokiPlugin.prototype.requestLocation = function(latlon, addressLookup)
{
    this.requestLocationBy(false, latlon, addressLookup);
}

LokiPlugin.prototype.requestIPLocation = function(latlon, addressLookup)
{
    this.requestLocationBy(true, latlon, addressLookup);
}

LokiPlugin.prototype.requestLocationBy = function(IP, latlon, addressLookup)
{
    if (!this.browserSupported())
    {
        this.onFailureProxy(LokiPlugin.WPS_ERROR_PLUGIN_BROWSER_NOT_SUPPORTED);
        return;
    }

    if (LokiPlugin.failedInstall)
    {
        this.onFailureProxy(LokiPlugin.WPS_ERROR_PLUGIN_COULD_NOT_BE_INSTALLED);
        return;
    }

    if (LokiPlugin.pluginDisabled)
    {
        this.onFailureProxy(LokiPlugin.WPS_ERROR_PLUGIN_DISABLED);
        return;
    }

    if (latlon == undefined)
        latlon = false;
    if (addressLookup == undefined)
        addressLookup = this.NO_STREET_ADDRESS_LOOKUP;

    if (    LokiPlugin.activex == null
        && (LokiPlugin.plugin == undefined
               || LokiPlugin.plugin["asynchronousRequestLocation"] == undefined)
        &&  LokiPlugin.xpcom == null)
    {
        if (LokiPlugin.isInstalled())
            this.initPlugin();

        var self = this;

        if (LokiPlugin.timer)
            clearTimeout(LokiPlugin.timer);
        LokiPlugin.timer = setTimeout(function(){self.requestLocationBy(IP, latlon, addressLookup);}, 300);
        return;
    }

    if (this.operationStarted()) return;

    this.lastAddressLookupArg = addressLookup;
   
    if (LokiPlugin.xpcom != null)
        this.runRequestLocation_XPCOM(IP, latlon, addressLookup);
    else
    if (BrowserDetect.browser == "Explorer")
        this.runRequestLocation_IE(IP, latlon, addressLookup);
    else
        this.runRequestLocation_NPAPI(IP, latlon, addressLookup);
}

LokiPlugin.prototype.googleRevLocCB = function(addresses, callback) 
{
    if(addresses.Status.code != 200) 
    {
        var result = new Object();
        result.returnCode = -1;
        callback(result);
    }
    else 
    {
        var country_code = "";
        var country_name = "";
        var state_code = "";
        var city = "";
        var street_address = "";
        var postal_code = "";
        var newLat = "";
        var newLon = "";

        try {
            var place = addresses.Placemark[0];
            newLat = place.Point.coordinates[1];
            newLon = place.Point.coordinates[0];
            country_code = place.AddressDetails.Country.CountryNameCode;
            country_name = place.AddressDetails.Country.CountryName;
            state_code = place.AddressDetails.Country.AdministrativeArea.AdministrativeAreaName;
            city = place.AddressDetails.Country.AdministrativeArea.Locality.LocalityName;
            street_address = place.AddressDetails.Country.AdministrativeArea.Locality.Thoroughfare.ThoroughfareName;
            postal_code = place.AddressDetails.Country.AdministrativeArea.Locality.PostalCode.PostalCodeNumber;

        } catch (e) { /* drop exception, stop getting info */ };


        var result = new Object();
        result.returnCode = 0;
        result.latitude = newLat;
        result.longitude = newLon;
        result.country_code = country_code;
        result.country = country_name;
        result.region_code = state_code;
        result.city = city;
        result.street = street_address;
        result.postal_code = postal_code;

        // No info for fields:
        result.region = "";
        result.house_number = "";

        callback(result);
    }
}


LokiPlugin.prototype.fallbackToGoogleRevGeo = function(callback)
{
    var self = this;
    var geocoder = new GClientGeocoder();
    var latlng = new GLatLng(this.lastLatArg, this.lastLonArg);

    if (!geocoder || !latlng)
    {
        var result = new Object();
        result.returnCode = -1;
        callback(result);
    }

    geocoder.getLocations(latlng, function(addresses) { self.googleRevLocCB(addresses, callback) });
}

LokiPlugin.prototype.onReverseGeocodedProxy = function(result, fromFallback)
{
    this.operationFinished();
    
    if ((result && result.returnCode == 0) || fromFallback)
    {
        if (this.onReverseGeocoded != undefined)
            this.onReverseGeocoded(result);
    }
    else
    {
        var self = this;
        this.fallbackToGoogleRevGeo( function(geocoderesult) { self.onReverseGeocodedProxy(geocoderesult, true) } );
    }
}

LokiPlugin.prototype.onTuneCompletedProxy = function(errcode)
{
    this.operationFinished();

    if (this.onTuneCompleted != undefined)
        this.onTuneCompleted(errcode, LokiPlugin.returnMessages[errcode]);
}

LokiPlugin.prototype.onFailureProxy = function(errcode)
{
    this.operationFinished();

    if (this.onFailure != undefined)
        this.onFailure(errcode, LokiPlugin.returnMessages[errcode]);
}

LokiPlugin.prototype.onSuccessProxy = function(location, fromFallback)
{
    this.operationFinished();
    
    if (this.onSuccess == undefined)
        return;
    
    var shouldGetAddress = true;
    
    if (fromFallback)
        shouldGetAddress = false;
    else if (this.lastAddressLookupArg == this.NO_STREET_ADDRESS_LOOKUP)
        shouldGetAddress = false;
    else if (location.city && location.city != "")
        shouldGetAddress = false;
    
    if (shouldGetAddress)
    {
        this.lastLatArg = location.latitude;
        this.lastLonArg = location.longitude;
        
        var self = this;
        this.fallbackToGoogleRevGeo( function(geocoderesult) { self.onSuccessProxy(geocoderesult, true) } );
        
        return;
    }
    
    if (!fromFallback)
    {
        this.onSuccess(location);
        return;
    }

    // This is from fallback, no matter what retcode google rev geo returned
    location.returnCode = 0;
    
    // Restore lat/lon that was adjusted by google reverse geocoding
    location.latitude = this.lastLatArg;
    location.longitude = this.lastLonArg;
    
    // Remove unwanted information if it was not requested
    if (this.lastAddressLookupArg != this.FULL_STREET_ADDRESS_LOOKUP)
        location.street = ""; // Google reverse geo contains house numbes in street field
    
    this.onSuccess(location);
}


/////////////////////////////////////////////////////////////////////////////////////////
// LokiPlugin class, Internet Explorer specific parts
/////////////////////////////////////////////////////////////////////////////////////////

LokiPlugin.isInstalled_IE = function()
{
    if (LokiPlugin.toolbarDetected)
        return true;

    var loki;

    if (LokiPlugin.activex != null)
        loki = LokiPlugin.activex;
    else
    {
        try
        {
            loki = new ActiveXObject("Loki.LocationFinder.1");
        }
        catch (err)
        {
            return false;
        }

        if (!loki)
            return false;
    }

    LokiAPI.pluginDescription = loki.description;
    LokiAPI.installedVersion = LokiAPI.pluginDescription.substring(
        LokiAPI.pluginDescription.indexOf("v.") + 2);

    return true;
}

LokiPlugin.init_IE = function()
{
    LokiPlugin.activex = new ActiveXObject("Loki.LocationFinder.1");
}

LokiPlugin.prototype.reverseGeocoding_IE = function(lat, lon, addressLookup)
{
    try
    {
        var self = this;
        LokiPlugin.activex.onReverseGeocoded = function(arg1) { self.onReverseGeocodedProxy(arg1) };

        if ("" != this.key)
            LokiPlugin.activex.setKey(this.key);
    
        LokiPlugin.activex.reverseGeocoding(lat, lon, addressLookup);
    } catch (e)
    {
        this.onReverseGeocodedProxy(LokiPlugin.WPS_ERROR_FEATURE_NOT_SUPPORTED);
    }
}

LokiPlugin.prototype.showManageDomains_IE = function()
{
    try
    {
        LokiPlugin.activex.showManageDomains();
    } catch (e)
    {
        this.onFailureProxy(LokiPlugin.WPS_ERROR_FEATURE_NOT_SUPPORTED);
    }
}

LokiPlugin.prototype.runRequestLocation_IE = function(IP, latlon, addressLookup)
{
    var self = this;
    LokiPlugin.activex.onSuccess = function(arg1) { self.onSuccessProxy(arg1) };
    LokiPlugin.activex.onFailure = function(arg1) { self.onFailureProxy(arg1) };

    if ("" != this.key)
        LokiPlugin.activex.setKey(this.key);
    
    try
    {
        if (IP)
            LokiPlugin.activex.requestIPLocation(latlon, addressLookup);
        else
            LokiPlugin.activex.requestLocation(latlon, addressLookup);
    } catch (e)
    {
        this.onFailureProxy(LokiPlugin.WPS_ERROR_FEATURE_NOT_SUPPORTED);
    }
}

LokiPlugin.prototype.tuneLocation_IE = function(lat, lon)
{
    try
    {
        if ("" != this.key)
            LokiPlugin.activex.setKey(this.key);

        var self = this;
        LokiPlugin.activex.onTuneCompleted = function(arg1) { self.onTuneCompletedProxy(arg1) };

        LokiPlugin.activex.tuneLocation(lat, lon);
    } catch (e)
    {
        this.onTuneCompletedProxy(LokiPlugin.WPS_ERROR_FEATURE_NOT_SUPPORTED);
    }
}

/////////////////////////////////////////////////////////////////////////////////////////
// LokiPlugin class, XPCOM specific parts
/////////////////////////////////////////////////////////////////////////////////////////

LokiPlugin.prototype.reverseGeocoding_XPCOM = function(lat, lon, addressLookup)
{
    this.onFailureProxy(LokiPlugin.WPS_ERROR_FEATURE_NOT_SUPPORTED);
}

LokiPlugin.prototype.showManageDomains_XPCOM = function()
{
    this.onFailureProxy(LokiPlugin.WPS_ERROR_FEATURE_NOT_SUPPORTED);
}

LokiPlugin.prototype.runRequestLocation_XPCOM = function(IP, latlon, addressLookup)
{
    if (IP)
    {
        this.onFailureProxy(LokiPlugin.WPS_ERROR_FEATURE_NOT_SUPPORTED);
        return;
    }

    var self = this;

    LokiPlugin.xpcom.onSuccess = function(arg1) { self.onSuccessProxy(arg1) };
    LokiPlugin.xpcom.onFailure = function(arg1) { self.onFailureProxy(arg1) };

    if ("" != this.key)
        LokiPlugin.xpcom.setKey(this.key);
    
    try
    {
        LokiPlugin.xpcom.requestLocation(latlon, addressLookup);
    } catch (e)
    {
        this.onFailureProxy(LokiPlugin.WPS_ERROR_FEATURE_NOT_SUPPORTED);
    }
}

/////////////////////////////////////////////////////////////////////////////////////////
// LokiPlugin class, NPAPI specific parts
/////////////////////////////////////////////////////////////////////////////////////////

LokiPlugin.isInstalled_NPAPI = function()
{
    var deprecatedVersionIdx = -1;

    navigator.plugins.refresh(false);

    for (var i = 0; i < navigator.plugins.length; ++i)
    {
        plugin = navigator.plugins[i];

        if (plugin == undefined)
            continue;

        if (plugin.name == "Loki Plugin")
        {
            numTypes = plugin.length;
            LokiPlugin.pluginDisabled = true;
            for (j = 0; j < numTypes; j++) 
            {
                mimetype = plugin[j];
                if (mimetype.type == "application/x-loki") 
                {
                    enabledPlugin = mimetype.enabledPlugin;
                    if (enabledPlugin && (enabledPlugin.name == plugin.name))
                    {
                        LokiPlugin.pluginDisabled = false;
                        break;
                    }
                }
            }
            
            // Workaround for skip activeX plugin in Chrome. 
            // Chrome also detects activex plugin, but we should use npapi
            // TODO: Chrome can partially support activex plugin. May be we can use it 
            // if no npapi version installed.
            if (BrowserDetect.browser == "Chrome" && plugin.filename == "loki.dll")
                continue;
    
            LokiAPI.pluginDescription = plugin.description;
            LokiAPI.installedVersion = LokiAPI.pluginDescription.substring(
                LokiAPI.pluginDescription.indexOf("v.") + 2);

            return true;
        } 

        if (plugin.name == "FindMe Plugin" ||
            plugin.name == "Find Me Plugin")
        {
            deprecatedVersionIdx = i;
        }

    }

    if (-1 != deprecatedVersionIdx)
    {
        LokiAPI.pluginDescription = navigator.plugins[deprecatedVersionIdx].description;
        LokiAPI.installedVersion = LokiAPI.pluginDescription.substring(
            LokiAPI.pluginDescription.indexOf("v.") + 2);

        return true;
    }

    return false;
}

LokiPlugin.init_NPAPI = function()
{
    if (!LokiPlugin.isRunning)
    {
        var pluginAttributes =
        {
            id     : "__lokiPlugin",
            width  : "1",
            height : "1",
            type   : "application/x-loki"
        };

        var pluginDom = document.createElement("object");
        for (var x in pluginAttributes)
        {
            pluginDom.setAttribute(x, pluginAttributes[x]);
        }

        document.getElementsByTagName('body').item(0).appendChild(pluginDom);

        LokiPlugin.plugin = pluginDom;
        LokiPlugin.isRunning = true;
    }
}

LokiPlugin.prototype.showManageDomains_NPAPI = function(isRetry)
{
    if (!LokiPlugin.plugin.showManageDomains)
    {
        // Workaround for opera
        if (BrowserDetect.browser == "Opera" && !isRetry)
        {
            if (LokiPlugin.isInstalled())
            {
                this.initPlugin();
                var self = this;

                if (LokiPlugin.timer)
                    clearTimeout(LokiPlugin.timer);
                LokiPlugin.timer = setTimeout(function(){self.showManageDomains_NPAPI(true);}, 300);
                return;
            }
        }
    
        this.onFailureProxy(LokiPlugin.WPS_ERROR_FEATURE_NOT_SUPPORTED);
        return;
    }
    
    LokiPlugin.plugin.showManageDomains();
}


LokiPlugin.prototype.reverseGeocoding_NPAPI = function(lat, lon, addressLookup, isRetry)
{
    if (!LokiPlugin.plugin.asynchronousReverseGeoCode)
    {
        // Workaround for opera
        if (BrowserDetect.browser == "Opera" && !isRetry)
        {
            if (LokiPlugin.isInstalled())
            {
                this.initPlugin();
                var self = this;

                if (LokiPlugin.timer)
                    clearTimeout(LokiPlugin.timer);
                LokiPlugin.timer = setTimeout(function(){self.reverseGeocoding_NPAPI(lat, lon, addressLookup, true);}, 300);
                return;
            }
        }
    
        var result = new Object();
        result.returnCode = LokiPlugin.WPS_ERROR_FEATURE_NOT_SUPPORTED;
        this.onReverseGeocodedProxy(result);
        return;
    }

    var self = this;
    setTimeout(function()
    {
        self.runAsyncRequest(

            // Call plugin method
            function () 
            {
                LokiPlugin.plugin.asynchronousReverseGeoCode(lat, lon, addressLookup);
            },

            // Result callback
            function (tickResult) 
            {
                self.onReverseGeocodedProxy(tickResult);
            }
        );
    }, 100);
}

LokiPlugin.prototype.tuneLocation_NPAPI = function(lat, lon)
{
    if (!LokiPlugin.plugin.asynchronousTuneLocation)
    {
        this.onTuneCompletedProxy(LokiPlugin.WPS_ERROR_FEATURE_NOT_SUPPORTED);
        return;
    }

    var self = this;
    setTimeout(function()
    {
        self.runAsyncRequest(

            // Call plugin method
            function () 
            {
                LokiPlugin.plugin.asynchronousTuneLocation(lat, lon);
            },

            // Result callback
            function (tickResult) 
            {
                self.onTuneCompletedProxy(tickResult.returnCode);
            }
        );
    }, 100);
}

LokiPlugin.prototype.runRequestLocation_NPAPI = function(IP, latlon, addressLookup)
{
    var self = this;
    setTimeout(function()
    {
        self.runAsyncRequest(

            // Call plugin method
            function () 
            {
                if (IP)
                    LokiPlugin.plugin.asynchronousRequestIPLocation(self.key, latlon, addressLookup);
                else
                    LokiPlugin.plugin.asynchronousRequestLocation(self.key, latlon, addressLookup);
            },

            // Result callback
            function (tickResult) 
            {
                if (tickResult.returnCode != LokiPlugin.WPS_OK)
                {
                    self.onFailureProxy(tickResult.returnCode);
                    return;
                }
            
                self.onSuccessProxy(tickResult);
            }
        );
    }, 100);
}


LokiPlugin.prototype.runAsyncRequest = function(callPluginMethod, resultReceived)
{
    callPluginMethod();
    var tickRequest = function()
    {
        var tickResult;
        if (!LokiPlugin.plugin.tickRunHttpTuneRequest && !LokiPlugin.plugin.tickRunHttpRevGCRequest)
        {
            tickResult = LokiPlugin.plugin.tickRunHttpRequest();
        }
        else
        { 
            // Temporary workaround for supporting 3.1.1.x plugins with several tick methods
            // Should be removed when no 3.1.1.x (before .06) plugin will left in testing
            // Not needed by 3.1.0.18 and previous and by 3.1.1.06 and next

            if (callPluginMethod.toString().indexOf("asynchronousRequest") != -1)
                tickResult = LokiPlugin.plugin.tickRunHttpRequest();
            else if (callPluginMethod.toString().indexOf("asynchronousTuneLocation") != -1)
                tickResult = LokiPlugin.plugin.tickRunHttpTuneRequest();
            else if (callPluginMethod.toString().indexOf("asynchronousReverseGeoCode") != -1)
                tickResult = LokiPlugin.plugin.tickRunHttpRevGCRequest();
            else
            {
                tickResult = new Object;
                tickResult.returnCode = LokiPlugin.WPS_ERROR_FEATURE_NOT_SUPPORTED;
            }
        }
        if (!tickResult || tickResult.returnCode == undefined)
        {
            setTimeout(tickRequest, 50);
            return;
        }
        resultReceived(tickResult);
    }
    setTimeout(tickRequest, 100);
}

LokiPlugin.prototype.setKey = function(key /*string*/)
{
    if (key == undefined || key == null)
        this.key = "";
    else
        this.key = key;
}

/////////////////////////////////////////////////////////////////////////////////////////
// Install Plugin stuff
/////////////////////////////////////////////////////////////////////////////////////////


LokiPlugin.prototype.tryToInstallPlugin = function()
{
    if (!this.browserSupported())return;

    if (this.installationStarted()) return;

    if (BrowserDetect.javaAvail)
    {
        if (LokiAPI_PreloadNullapplet && !LokiPlugin.isInstalled())
        {
            if (  BrowserDetect.javaWaitingConfirmation &&
                ( BrowserDetect.browser == "Explorer" || 
                 (BrowserDetect.browser == "Safari" && BrowserDetect.OS == "Windows") ||
                 (BrowserDetect.browser == "Chrome" && BrowserDetect.OS == "Windows") ||
                 (BrowserDetect.browser == "Firefox" && LokiPlugin.javaPluginDescription.indexOf('1.4.') != -1)
                )
               )
            {
                // Internet Explorer and Safari requires confirmation that java is available. callback from nullapplet used
                var nullappletUptime = (new Date()).getTime() - BrowserDetect.javaWaitingConfirmationSince;
                if (nullappletUptime < LokiPlugin.fallbackToNativeTimeout)
                {
                    LokiPlugin.nullappletShouldRunInstaller = true;
                    setTimeout(fallbackToNativeInstaller, LokiPlugin.fallbackToNativeTimeout - nullappletUptime);
                } 
                else
                {
                    fallbackToNativeInstaller();
                }
            }
            else
            {
                LokiPlugin.startInstallApplet();
            }
        }
        else
        {
            if ( BrowserDetect.browser == "Explorer" || 
                (BrowserDetect.browser == "Safari" && BrowserDetect.OS == "Windows") ||
                (BrowserDetect.browser == "Chrome" && BrowserDetect.OS == "Windows") ||
                (BrowserDetect.browser == "Firefox" && LokiPlugin.javaPluginDescription.indexOf('1.4.') != -1)
               )
            {
                //Internet Explorer and Safari requires confirmation that java is available. callback from nullapplet used
                LokiPlugin.nullappletShouldRunInstaller = true;
                LokiPlugin.runNullapplet();
                setTimeout(fallbackToNativeInstaller, LokiPlugin.fallbackToNativeTimeout);
            }
            else
            {
                LokiPlugin.startInstallApplet();
            }
        }
    }
    else
    {
        LokiPlugin.downloadNativeInstaller();
    }
}

LokiPlugin.downloadNativeInstaller = function()
{
    switch(BrowserDetect.OS)
    {
        case "Windows":
            if (BrowserDetect.browser == "Explorer")
            {
                if (window.XMLHttpRequest) 
                {
                    // IE 7
                    document.location.href = LokiPlugin.globalURLPrefix + "loki_activex.exe";
                } else 
                {
                    // IE6
                    var downloadDiv = document.createElement("div");
                    document.getElementsByTagName('body').item(0).appendChild(downloadDiv);
                    downloadDiv.innerHTML = '<iframe src="' + LokiPlugin.globalURLPrefix + "loki_activex.exe" + '"></iframe>';
                }
            }
            else
                document.location.href = LokiPlugin.globalURLPrefix + "loki_setup.exe";
            break;

        case "Mac":
                document.location.href = LokiPlugin.globalURLPrefix + "LokiPlugin.zip";
            break;

        case "Linux":
                document.location.href = LokiPlugin.globalURLPrefix + "LokiPlugin_Installer.sh";
            break;
    }
}

function appletInstallationSuccessfull()
{
    LokiPlugin.upgradeCompletedSuccessfull = true;
}

function fallbackToNativeInstaller()
{
    if (BrowserDetect.javaWaitingConfirmation)
    {
        BrowserDetect.javaAvail = false;
        LokiPlugin.nullappletShouldRunInstaller = false;
        LokiPlugin.downloadNativeInstaller();
    }
}

function appletInstallationFailed()
{
    LokiPlugin.downloadNativeInstaller();
}

LokiPlugin.startInstallApplet = function()
{
    var appletDiv = document.createElement("div");
    var codebase_par = LokiPlugin.useGlobalURLs ?  '<PARAM NAME="archive" VALUE="' + LokiPlugin.globalURLPrefix + 'LokiApplet.jar"/>' : '';

    document.getElementsByTagName('body').item(0).appendChild(appletDiv);

    var globalUrlParameter = LokiPlugin.useGlobalURLs ?
                                 '<PARAM NAME="globalUrlPrefix" VALUE="' + LokiPlugin.globalURLPrefix + '">'
                                 : '';

    if (BrowserDetect.browser == "Explorer")
        appletDiv.innerHTML = '<OBJECT id="LokiApplet" classid="clsid:8AD9C840-044E-11D1-B3E9-00805F499D93" WIDTH="0" HEIGHT="0">'
                              + codebase_par
                              + '<PARAM NAME="CODE" VALUE="LokiApplet.class"/>'
                              + globalUrlParameter
                              + '<PARAM NAME="scriptable" VALUE="true"/></OBJECT>';
    else
        appletDiv.innerHTML = '<object classid="java:LokiApplet.class" type="application/x-java-applet" code="LokiApplet.class" archive="' + LokiPlugin.globalURLPrefix + 'LokiApplet.jar" width=0 height=0><PARAM NAME="MAYSCRIPT" VALUE="true">'
                              + globalUrlParameter
                              + '</object>';


}

/////////////////////////////////////////////////////////////////////////////////////////
// Wrappers for watching install process (allowing setting timeouts for them) and 
// operation process. 
/////////////////////////////////////////////////////////////////////////////////////////

LokiPlugin.prototype.installationTimedOut = function()
{
    LokiPlugin.installationTimer = 0;
    this.onFailureProxy(LokiPlugin.WPS_ERROR_PLUGIN_COULD_NOT_BE_INSTALLED);
    LokiPlugin.failedInstall = true;
}

LokiPlugin.prototype.installationStarted = function()
{
    if (LokiPlugin.attemptedInstall) 
        return true;

    if (LokiPlugin.installationTimer)
        clearTimeout(LokiPlugin.installationTimer);

    LokiPlugin.failedInstall = false;

    var self = this;
    LokiPlugin.installationTimer = setTimeout(function(){self.installationTimedOut();}, LokiPlugin.installationTimeout);

    LokiPlugin.attemptedInstall = true;
    return false;
}

LokiPlugin.prototype.installationFinished = function()
{
    if (LokiPlugin.installationTimer)
        clearTimeout(LokiPlugin.installationTimer);
    LokiPlugin.installationTimer = 0;
}

LokiPlugin.prototype.operationStarted = function()
{
    // Ignore all subsequent requests until current one is finished
    if (LokiPlugin.waitingRet) 
        return true;

    LokiPlugin.waitingRet = true;
    return false;
}

LokiPlugin.prototype.operationFinished = function()
{
    if (!LokiPlugin.waitingRet)
        return false;

    LokiPlugin.waitingRet = false;
    return true;
}

/////////////////////////////////////////////////////////////////////////////////////////
// Helper functions and clesses
/////////////////////////////////////////////////////////////////////////////////////////

function IsLokiToolbarInstalled()
{
    try
    {
        if (Try.these(function() {return new ActiveXObject("Loki.LokiButton.1")}) || false)
            return true;
        return false;
    } catch (e)
    {
        return false;
    }
}

// "Null" loki object to ultimately fall back to
function LokiNull(){}

LokiNull.prototype.setKey = function(){}
LokiNull.prototype.isInstalled = function(){ return false; }

LokiNull.prototype.showManageDomains = function()
{
    this.onFailureProxy(LokiPlugin.WPS_ERROR_PLUGIN_COULD_NOT_BE_INSTALLED);
}

LokiNull.prototype.tuneLocation = function()
{
    this.onTuneCompletedProxy(LokiPlugin.WPS_ERROR_PLUGIN_COULD_NOT_BE_INSTALLED);
}

LokiNull.prototype.requestLocation = function()
{
    this.onFailureProxy(LokiPlugin.WPS_ERROR_PLUGIN_COULD_NOT_BE_INSTALLED);
}

LokiNull.prototype.requestIPLocation = function()
{
    if (this.onFailure != undefined)
        this.onFailureProxy(LokiPlugin.WPS_ERROR_PLUGIN_COULD_NOT_BE_INSTALLED);
}

// Borrowed from the Prototype Library
var Try = {
  these: function() {
    var returnValue;

    for (var i = 0; i < arguments.length; i++) {
      var lambda = arguments[i];
      try {
        returnValue = lambda();
        break;
      } catch (e) {}
    }

    return returnValue;
  }
}

// Returns 0 if equal, positive if version1 > version2, negative otherwise (1.2 and 1.2.3 are equal)
function compareVersions(version1, version2)
{
    versions1 = version1.split(".");
    versions2 = version2.split(".");

    for (i = 0; i < versions1.length && i < versions2.length; i++)
    {
        if (parseInt(versions1[i], 10) > parseInt(versions2[i], 10))
            return i+1;
        if (parseInt(versions1[i], 10) < parseInt(versions2[i], 10))
            return -(i+1);
    }
    return 0;
}

var BrowserDetect = {

    init: function ()
        {
        this.browser = this.searchString(this.dataBrowser) || "Unknown browser";

        this.version = this.searchVersion(navigator.userAgent)
                       || this.searchVersion(navigator.appVersion)
                       || "Unknown version";

        this.OS = this.searchString(this.dataOS) || "Unknown OS";

        if (this.browser == "Explorer")
            this.javaAvail = true;
        else if (this.browser == "Firefox" && this.version == 3)
            this.javaAvail = this.findJava();
        else if (this.browser == "Opera")
            this.javaAvail = navigator.javaEnabled();
        else
            this.javaAvail = this.findJava() && navigator.javaEnabled();

        this.javaWaitingConfirmation = true;
        this.javaWaitingConfirmationSince = (new Date()).getTime();
    },

    findJava: function ()
        {
        if (!navigator || !navigator.plugins)
            return true;

            navigator.plugins.refresh(false);
            for (var i = 0; i < navigator.plugins.length; ++i)
            {
                if (navigator.plugins[i] == undefined)
                    continue;
                if (navigator.plugins[i].name.indexOf('Java') != -1)
                {
                    LokiPlugin.javaPluginDescription = navigator.plugins[i].description;
                    return true;
                }
            }

            return false;
        },

    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)
    {
        var index = dataString.indexOf(this.versionSearchString);
        if (index == -1)
            return;

        return parseFloat(dataString.substring(index+this.versionSearchString.length+1));
    },

    dataBrowser: [
        {
            string: navigator.userAgent,
            subString: "MSIE",
            identity: "Explorer",
            versionSearch: "MSIE"
        },
        {
            string: navigator.userAgent,
            subString: "Firefox",
            identity: "Firefox"
        },
        {
            prop: window.opera,
            identity: "Opera"
        },
        {
            string: navigator.vendor,
            subString: "Apple",
            identity: "Safari"
        },
        {
            string: navigator.vendor,
            subString: "Google",
            identity: "Chrome"
        },
        {
            string: navigator.vendor,
            subString: "KDE",
            identity: "Konqueror"
        },
        {
            string: navigator.userAgent,
            subString: "OmniWeb",
            versionSearch: "OmniWeb/",
            identity: "OmniWeb"
        },
        {
            string: navigator.vendor,
            subString: "iCab",
            identity: "iCab"
        },
        {
            string: navigator.vendor,
            subString: "Camino",
            identity: "Camino"
        },
        {        // for newer Netscapes (6+)
            string: navigator.userAgent,
            subString: "Netscape",
            identity: "Netscape"
        },
        {
            string: navigator.userAgent,
            subString: "Gecko",
            identity: "Mozilla",
            versionSearch: "rv"
        },
        {         // for older Netscapes (4-)
            string: navigator.userAgent,
            subString: "Mozilla",
            identity: "Netscape",
            versionSearch: "Mozilla"
        }
    ],

    dataOS : [
        {
            string: navigator.platform,
            subString: "Win",
            identity: "Windows"
        },
        {
            string: navigator.platform,
            subString: "Mac",
            identity: "Mac"
        },
        {
            string: navigator.platform,
            subString: "Linux",
            identity: "Linux"
        }
    ]
};

/////////////////////////////////////////////////////////////////////////////////////////
// Initialization
/////////////////////////////////////////////////////////////////////////////////////////

LokiPlugin.javaPluginDescription = "";

BrowserDetect.init();

LokiPlugin.prototype.key = "beta";

LokiPlugin.prototype.NO_STREET_ADDRESS_LOOKUP = 0;
LokiPlugin.prototype.LIMITED_STREET_ADDRESS_LOOKUP = 1;
LokiPlugin.prototype.FULL_STREET_ADDRESS_LOOKUP = 2;

LokiPlugin.WPS_OK = 0;
LokiPlugin.WPS_ERROR_SCANNER_NOT_FOUND = 1;
LokiPlugin.WPS_ERROR_WIFI_NOT_AVAILABLE = 2;
LokiPlugin.WPS_ERROR_NO_WIFI_IN_RANGE = 3;
LokiPlugin.WPS_ERROR_UNAUTHORIZED = 4;
LokiPlugin.WPS_ERROR_SERVER_UNAVAILABLE = 5;
LokiPlugin.WPS_ERROR_LOCATION_CANNOT_BE_DETERMINED = 6;
LokiPlugin.WPS_ERROR_PROXY_UNAUTHORIZED = 7;
LokiPlugin.WPS_ERROR_FILE_IO = 8;
LokiPlugin.WPS_ERROR_INVALID_FILE_FORMAT = 9;
LokiPlugin.WPS_ERROR_PLUGIN_COULD_NOT_BE_INSTALLED = 1000;
LokiPlugin.WPS_ERROR_PERMISSION_DENIED = 1001;
LokiPlugin.WPS_ERROR_PLUGIN_BROWSER_NOT_SUPPORTED = 1002;
LokiPlugin.WPS_ERROR_FEATURE_NOT_SUPPORTED = 1003;
LokiPlugin.WPS_ERROR_PLUGIN_DISABLED = 1004;
LokiPlugin.WPS_ERROR_INVALID_ARG = 1005;

LokiPlugin.returnMessages = new Object();
LokiPlugin.returnMessages[LokiPlugin.WPS_OK] = "Successfull";
LokiPlugin.returnMessages[LokiPlugin.WPS_ERROR_SCANNER_NOT_FOUND] = "Wi-Fi Scanner was not found";
LokiPlugin.returnMessages[LokiPlugin.WPS_ERROR_WIFI_NOT_AVAILABLE] = "Wi-Fi is not available";
LokiPlugin.returnMessages[LokiPlugin.WPS_ERROR_NO_WIFI_IN_RANGE] = "No Wi-Fi access points are in range";
LokiPlugin.returnMessages[LokiPlugin.WPS_ERROR_UNAUTHORIZED] = "Invalid application key, please contact the site owner";
LokiPlugin.returnMessages[LokiPlugin.WPS_ERROR_SERVER_UNAVAILABLE] = "Network error";
LokiPlugin.returnMessages[LokiPlugin.WPS_ERROR_LOCATION_CANNOT_BE_DETERMINED] = "No Wi-Fi access points were recognized";
LokiPlugin.returnMessages[LokiPlugin.WPS_ERROR_PROXY_UNAUTHORIZED] = "Proxy error";
LokiPlugin.returnMessages[LokiPlugin.WPS_ERROR_FILE_IO] = "A file I/O error was encountered";
LokiPlugin.returnMessages[LokiPlugin.WPS_ERROR_INVALID_FILE_FORMAT] = "Invalid file format";
LokiPlugin.returnMessages[LokiPlugin.WPS_ERROR_PLUGIN_COULD_NOT_BE_INSTALLED] = "Error installing plugin";
LokiPlugin.returnMessages[LokiPlugin.WPS_ERROR_PERMISSION_DENIED] = "Permission denied";
LokiPlugin.returnMessages[LokiPlugin.WPS_ERROR_PLUGIN_BROWSER_NOT_SUPPORTED] = "Browser is not supported";
LokiPlugin.returnMessages[LokiPlugin.WPS_ERROR_FEATURE_NOT_SUPPORTED] = "Feature is not supported by installed version of plugin";
LokiPlugin.returnMessages[LokiPlugin.WPS_ERROR_PLUGIN_DISABLED] = "Plugin is disabled. Check browser configuration";
LokiPlugin.returnMessages[LokiPlugin.WPS_ERROR_INVALID_ARG] = "Invalid argument to Loki Plugin call";

LokiPlugin.fallbackToNativeTimeout = 10000;
LokiPlugin.xpcom = null;
LokiPlugin.activex = null;
LokiPlugin.isRunning = false;
LokiPlugin.timer = 0;
LokiPlugin.installationTimer = 0;
LokiPlugin.installationTimeout = 90000;
LokiPlugin.attemptedInstall = false;
LokiPlugin.failedInstall = false;
LokiPlugin.upgradeCancelled = false;
LokiPlugin.upgradeStarted = false;
LokiPlugin.upgradeCompletedSuccessfull = false;
LokiPlugin.nullappletShouldRunInstaller = false;
LokiPlugin.waitingRet = false;
LokiPlugin.pluginDisabled = false;

var LokiAPI_PreloadNullapplet;
var LokiAPI_FilesLocation;

if (LokiAPI_FilesLocation == undefined)
    LokiAPI_FilesLocation = "http://loki.com/plugin/files/";

if (LokiAPI_FilesLocation.substring(7,0) != "http://")
{
    if (LokiAPI_FilesLocation.substring(1,0) == "/")
    {
        LokiAPI_FilesLocation = "http://" + location.host + LokiAPI_FilesLocation;
    } 
    else
    {
        var urlPrefix = location.href.substring(0, location.href.lastIndexOf("/") + 1);
        LokiAPI_FilesLocation = urlPrefix + LokiAPI_FilesLocation;
    }
}

if (LokiAPI_PreloadNullapplet == undefined)
    LokiAPI_PreloadNullapplet = false;

LokiPlugin.toolbarDetected = IsLokiToolbarInstalled();

LokiPlugin.useGlobalURLs = (LokiAPI_FilesLocation != undefined && LokiAPI_FilesLocation != "");
LokiPlugin.globalURLPrefix = LokiPlugin.useGlobalURLs ? LokiAPI_FilesLocation : "";

if (BrowserDetect.javaAvail && !LokiPlugin.isInstalled())
{
    if (LokiAPI_PreloadNullapplet)
    {
        // Nullapplet should be runned after scripts loads to be able to insert itself into dom
        setTimeout(function(){LokiPlugin.runNullapplet();}, 200);
    }
}

LokiPlugin.runNullapplet = function()
{
    var codebase = LokiPlugin.useGlobalURLs ?  'codebase="' + LokiPlugin.globalURLPrefix + '"' : '';
    var codebase_par = LokiPlugin.useGlobalURLs ?  '<PARAM NAME="CODEBASE" VALUE="' + LokiPlugin.globalURLPrefix + '"/>' : '';
    var appletDiv = document.createElement("div");

    document.getElementsByTagName('body').item(0).appendChild(appletDiv);

    if (!LokiAPI_PreloadNullapplet)
    {
        LokiPlugin.nullappletShouldRunInstaller = true;
    }
    
    BrowserDetect.javaWaitingConfirmationSince = (new Date()).getTime();

    if (BrowserDetect.browser == "Safari" || BrowserDetect.browser == "Chrome")
        appletDiv.innerHTML = '<object type="application/x-java-applet" code="nullapplet.class" ' + codebase + ' width=0 height=0><PARAM NAME="MAYSCRIPT" VALUE="true"><param name="JAVA_CODEBASE" value="' + LokiPlugin.globalURLPrefix + '"></object>';
    else if (BrowserDetect.browser == "Explorer")
        appletDiv.innerHTML = '<OBJECT id="nullapplet" classid="clsid:8AD9C840-044E-11D1-B3E9-00805F499D93" WIDTH="0" HEIGHT="0">' + codebase_par + '<PARAM NAME="CODE" VALUE="nullapplet.class"/><PARAM NAME="scriptable" VALUE="true"/></OBJECT>';
    else
        appletDiv.innerHTML = '<applet name="nullapplet" id="nullapplet" ' + codebase + ' code="nullapplet.class" width="0" height="0" mayscript=true><param name="mayscript" value="true"></applet>';

}

// Called from nullapplet to confirm that java available
function confirmJavaOK()
{
    BrowserDetect.javaWaitingConfirmation = false;
    if (LokiPlugin.nullappletShouldRunInstaller)
    {
        LokiPlugin.nullappletShouldRunInstaller = false;
        LokiPlugin.startInstallApplet();
    }
}
