// qmpinstall.js - Routines for installing/upgrading the Quantum Media Player
// Copyright (c) 2003-2005 Move Networks
// Assumes movenetworks.js has been loaded too

/* QMPInstall contains all the functions for checking install state, installing, and upgrading

// Simplest usage:
function AllDone(allOk)
{
    log('all done. allok?', allOk);
}

MN.QMPInstall.CreatePlayer('player', AllDone, 400, 300);

The above creates a player inside the div with id 'player' and gives the player the ID 'qmp'.
The player is 400x300 pixels wide. Once created, AllDone(true) is called. If the player is
not installed, installation is attempted. If an older player is present, an upgrade is attempted.
If AllDone(false) is called it means that, for whatever reason, installtion or upgrade failed
and the player was not created.
*/

MN.QMPInstall =
{
    useActiveX : ((document.all && document.attachEvent) ? true : false ), // true if we're using the IE version

    // The various states the user's machine may be in
    'ST_UP_TO_DATE':0, // player is installed and up to date
    'ST_HAVE_OLD_VERSION':1, // player is installed but is old
    'ST_LOADED_OLD_VERSION':2, // player is installed and up to date, but older version is loaded
    'ST_CAN_INSTALL':3, // not installed but platform is one we support
    'ST_BAD_PLATFORM':4, // not installed and platform is not one we support

    // Human-readable messages during platform-check, installation, and upgrading
    'MSG_NOTWIN':"We're sorry, but only Microsoft Windows 98SE or later is supported at this time. Please check back soon for support for other operating systems.",
    'MSG_NOTIE':"We're sorry, but only Internet Explorer 5.5 or later is supported at this time. Please check back soon for support for other browsers.",
    'MSG_LOADEDOLD':"You have the current version of the player installed, but your web browser has loaded an older version (this is common after upgrading). Please completely close all web browser windows and load this page again.",
    'MSG_NEEDUPGRADE':'A free upgrade of the player is needed to view this content, and upgrading will take only a few seconds. Click <a href="javascript:MN.QMPInstall.StartUpgrade()">here</a> to proceed.',
    'MSG_UPGRADING':'Please wait while the upgrade completes...<br>&nbsp;&nbsp;&nbsp;',
    'MSG_UPGRADE_ERROR':'Upgrade failed. Please try again later.',
    'MSG_UPGRADE_START_ERROR':'Could not initiate the upgrade. Please try again later.',
    'MSG_CANINSTALL':'You do not yet have the free player. Click <a href="javascript:MN.QMPInstall.StartInstall()">here</a> to install it - installation is fast and easy.',
    'MSG_ACTIVEXPROMPT':"Installation is starting. If you are asked to allow the installation, click 'Yes' or 'Install' to continue.<br><br>",
    'MSG_INSTALLTIMEOUT':'Installation timed out. Be sure your Internet connection is working and press the Refresh button in your browser to try again.',
    'MSG_ACTIVEXUNSAFE':'Your browser security settings are too high and have blocked installation of the player.<ol><li>On the Internet Explorer menu, click Tools --> Internet Options --> Security and then click the Default Level button.</li><li>Click Ok</li><li>Press the Refresh button in your browser to reload this page and try again.</li></ol>',
    'MSG_ACTIVEXBLOCKED':'The installation has been blocked or canceled. If you accidentally canceled installation, press the Refresh button in your browser to try again.<br><br>If installation was automatically blocked, there may be a banner at the top of this page informing you of this. If so, click the banner and choose "Install ActiveX control..." and then repeat the installation process.',

    cantInstallReason: '', // After calling GetState, a message indicating why installation can't happen if needed

    // stuff related to the hidden version test instance of the player that we create (and use for upgrading)
    _objectStrActiveX : '<OBJECT ID="%s" width="%s" height="%s" CLASSID="CLSID:e3e02f12-2adb-478c-8742-5f0819f9f0f4"%s>%s</OBJECT>',
    _vtID : '_qmpVerTest',
    _vtDiv : null,
    _CreateVersionTest : function()
    {
        if (MN.QMPInstall._vtDiv != null)
            return; // assume it's already done
        var td = MN.QMPInstall._vtDiv = document.createElement('div');
        document.body.appendChild(td);
        td.innerHTML = MN.QMPInstall._objectStrActiveX.format(MN.QMPInstall._vtID, 0, 0, '', '<PARAM name="VersionTest" value="1">');
    },

    _DestroyVersionTest : function()
    {
        if (MN.QMPInstall._vtDiv == null)
            return;
        MN.QMPInstall._vtDiv.innerHTML = '';
        document.body.removeChild(MN.QMPInstall._vtDiv);
        MN.QMPInstall._vtDiv = null;
    },

    // Returns the URL to the cab file to install
    _MakeCabURL : function()
    {
        // _libBase comes from movenetworks.js - i.e. we assume the .cab file lives next to the .js files
        return MN.URL.Join(MN._libBase, 'qsp2ie' + MN.QMPInstall.QMP_LATEST_VER_COMPARABLE + '.cab');
    },

    // Returns a STATE value reflective of user's installation state. Also sets MN.QMPInstall.cantInstallReason
    // if installation cannot happen. If leaveVTForUpgrade is true and the player is installed but needs to
    // be upgraded, the version test instance is not destroyed (we need it to perform the upgrade for us)
    GetState : function(leaveVTForUpgrade)
    {
        // If we've already loaded the player without problems on this page, subsequent loads can skip the checks
        if (MN.QMPInstall._checksPassed)
            return MN.QMPInstall.ST_UP_TO_DATE;

        MN.QMPInstall.cantInstallReason = '';

        // TODO: add moz support

        // Add a hidden instance of the player to test for its presence and version
        MN.QMPInstall._CreateVersionTest();

        var installed = false;
        var regVer = null;
        var loadVer = null;
        var testPlayer = $(MN.QMPInstall._vtID);
        if (testPlayer && 'Play' in testPlayer)
        {   // *something* is installed
            installed = true;
            regVer = testPlayer.RegistryVersion;
            loadVer = testPlayer.Version;
        }

        // see if it's installed and what version
        if (installed && regVer && loadVer)
        {
            if (regVer >= MN.QMPInstall.QMP_LATEST_VER_COMPARABLE)
            {
                MN.QMPInstall._DestroyVersionTest();
                if (loadVer >= MN.QMPInstall.QMP_LATEST_VER_COMPARABLE)
                {
                    MN.QMPInstall._checksPassed = true;
                    return MN.QMPInstall.ST_UP_TO_DATE;
                }
                return MN.QMPInstall.ST_LOADED_OLD_VERSION;
            }
            else
            {
                if (!leaveVTForUpgrade)
                    MN.QMPInstall._DestroyVersionTest();
                return MN.QMPInstall.ST_HAVE_OLD_VERSION;
            }
        }

        MN.QMPInstall._DestroyVersionTest();

        // nothing is installed - *could* we install?
        var ua = navigator.userAgent.toLowerCase();

        // Initial OS checks
        if (ua.indexOf('win') == -1 || ua.indexOf('16bit') != -1)
        {
            MN.QMPInstall.cantInstallReason = MN.QMPInstall.MSG_NOTWIN;
            return MN.QMPInstall.ST_BAD_PLATFORM;
        }

        // check the browser
        var major = parseInt(navigator.appVersion);
        if ((ua.indexOf('opera') != -1 || ua.indexOf('msie') == -1) ||
            (major < 4 || ua.indexOf('msie 4') != -1 || ua.indexOf('msie 5.0') != -1))
        {
            MN.QMPInstall.cantInstallReason = MN.QMPInstall.MSG_NOTIE;
            return MN.QMPInstall.ST_BAD_PLATFORM;
        }

        return MN.QMPInstall.ST_CAN_INSTALL; // huzzah!
    },

    _createParams : {}, // copy of the parameters passed to CreatePlayer so it can be called again later
    _checksPassed : false, // true if we've already done all the install checks and everything is up to date

    // Utility func to display a user message and then call the create doneCB with failure
    _CreateFail : function(msg)
    {
        var cp = MN.QMPInstall._createParams;
        $(cp.parentID).innerHTML = msg;
        return cp.DoneCB(false);
    },

    MakePlayerID : function(parentID) { return parentID + '_QMP'; },

    // Utility function to emit a new player object into the given parent element. Use this if
    // you already know for sure that the installation is ok (this is also called by CreatePlayer).
    // w and h are optional and default to 480x360. Returns the player's HTML element ID.
    EmitObj : function (parentID, w, h, playerID)
    {
        if (playerID == null)
            playerID = MN.QMPInstall.MakePlayerID(parentID);

        w = w || 480;
        h = h || 360;
        var s = MN.QMPInstall._objectStrActiveX;
        if (!MN.QMPInstall.useActiveX)
            s = '<object id="%s" width="%s" height="%s" type="application/x-vnd.movenetworks.qm"%s>%s</object>';

        $(parentID).innerHTML = s.format(playerID, w, h, '', '');
        $(playerID).style.width = w;
        $(playerID).style.height = h;
        return playerID;
    },

    // Utility function to create a player if all the install checks pass. Otherwise, helps
    // the user through the install or upgrade process if possible. Performs platform/browser
    // checks too, informing users on unsupported platforms appropriately. DoneCB is called
    // after no more is to be done, it takes a single bool parameter 'playerCreated' to
    // indicate the outcome.
    CreatePlayer : function (parentID, DoneCB, w, h, playerID)
    {   // TODO: This needs to be refactored so it doesn't fail if multiple players are created at the "same" time
        if (playerID == null)
            playerID = MN.QMPInstall.MakePlayerID(parentID);
        MN.QMPInstall._createParams = {'parentID':parentID, 'playerID':playerID, 'DoneCB':DoneCB, 'w':w, 'h':h};
        log('create params', MN.QMPInstall._createParams, MN.QMPInstall._createParams.parentID, parentID);

        var st = MN.QMPInstall.GetState(true); // tell it to leave the version test instance if an upgrade is needed
        log('MN.QMPInstall.CreatePlayer install state:', st);
        if (st == MN.QMPInstall.ST_UP_TO_DATE)
        {
            MN.QMPInstall.EmitObj(parentID, w, h);
            return DoneCB(true);
        }
        else if (st == MN.QMPInstall.ST_LOADED_OLD_VERSION)
            return MN.QMPInstall._CreateFail(MN.QMPInstall.MSG_LOADEDOLD);
        else if (st == MN.QMPInstall.ST_BAD_PLATFORM)
            return MN.QMPInstall._CreateFail(MN.QMPInstall.cantInstallReason);
        else if (st == MN.QMPInstall.ST_HAVE_OLD_VERSION)
        {
            $(parentID).innerHTML = MN.QMPInstall.MSG_NEEDUPGRADE;
            // user sees a link to start the upgrade process
        }
        else if (st == MN.QMPInstall.ST_CAN_INSTALL)
        {
            $(parentID).innerHTML = MN.QMPInstall.MSG_CANINSTALL;
            // user sees a link to start the installation process
        }
    },

    // Called when user clicks link to start the installation. After installation, calls
    // CreatePlayer again.
    StartInstall : function()
    {
        // Convert latest version string to the format used in OCX URLs
        var ver = MN.QMPInstall.QMP_LATEST_VER_COMPARABLE;
        var vy = parseInt(ver.substr(0,2));
        var vm = parseInt(ver.substr(2,2));
        var vd = parseInt(ver.substr(4,2));
        var vb = parseInt(ver.substr(6,2));
        var ocxVer = '%s,%s,%s,%s'.format(vy, vm, vd, vb);

        var url = MN.QMPInstall._MakeCabURL() + '#Version=' + ocxVer;
        log('starting install from', url);
        $(MN.QMPInstall._createParams.parentID).innerHTML = MN.QMPInstall.MSG_ACTIVEXPROMPT;

        // By adding the OBJECT HTML to the body, Internet Explorer will download and install it
        window.status = 'Loading...';
        var codebase = ' codebase="%s"'.format(url);
        var spew = MN.QMPInstall._objectStrActiveX.format(MN.QMPInstall._vtID, 0, 0, codebase, '');
        document.body.insertAdjacentHTML('beforeEnd', spew);
        MN.QMPInstall._MonitorInstallProgress(0);
    },

    // Called to monitor completion (or abort) of the install
    _MonitorInstallProgress : function(elapsedTime)
    {
        var vt = $(MN.QMPInstall._vtID);
        if (vt && vt.readyState >= 4) // 4 means loaded and ready
        {
            // installation has stopped. See if it stopped w/ errors
            try
            {
                if (vt.object && vt.RegistryVersion)
                    log('installed in registry:', vt.RegistryVersion);

                if (vt.object && vt.RegistryVersion && vt.RegistryVersion >= MN.QMPInstall.QMP_LATEST_VER_COMPARABLE)
                {   // woohoo!
                    var cp = MN.QMPInstall._createParams;
                    window.status = 'Installation complete.';
                    MN.QMPInstall.CreatePlayer(cp.parentID, cp.DoneCB, cp.w, cp.h); // retry the whole thing
                    return;
                }
            }
            catch(e)
            {
                log('install error:', e.message);
                window.status = 'Installation blocked by security settings.';
                return MN.QMPInstall._CreateFail(MN.QMPInstall.MSG_ACTIVEXUNSAFE + '<br>(' + e.message + ')');
            }

            window.status = 'Installation blocked or canceled by user.';
            return MN.QMPInstall._CreateFail(MN.QMPInstall.MSG_ACTIVEXBLOCKED);
        }

        if (elapsedTime > MN.QMPInstall.installTimeoutSec)
        {
            window.status = MN.QMPInstall.MSG_INSTALLTIMEOUT;
            return MN.QMPInstall._CreateFail(MN.QMPInstall.MSG_INSTALLTIMEOUT);
        }

        try { MN.QMPInstall.OnInstallUpdateUI(elapsedTime); } catch(e) { log('Install update UI error:', e.message); }

        setTimeout('MN.QMPInstall._MonitorInstallProgress(%s)'.format(elapsedTime+0.5), 500);
    },

    // called periodically during install to update the UI so user doesn't worry that nothing is happening
    // elapsedTime is roughly the number of seconds that have elapsed since the install started.
    OnInstallUpdateUI : function(elapsedTime)
    {
        var msgDiv = $(MN.QMPInstall._createParams.parentID);
        if (msgDiv)
            msgDiv.innerHTML = msgDiv.innerHTML + '.';
    },

    // Called when user clicks link to start the upgrade process. Updates the UI as progress
    // is made, after which the DoneCB originally passed to CreatePlayer is called.
    StartUpgrade : function()
    {
        var url = MN.QMPInstall._MakeCabURL();
        log('starting upgrade from', url);
        MN.QMPInstall._CreateVersionTest(); // creates it only if needed
        var vt = $(MN.QMPInstall._vtID);
        try
        {
            vt.Upgrade(url);
        }
        catch (e)
        {
            log('vt.Upgrade failed:', e.message);
            return MN.QMPInstall._CreateFail(MN.QMPInstall.MSG_UPGRADE_START_ERROR);
        }

        MN.Event.Observe(vt, 'UpgradeProgress', MN.QMPInstall._OnUpgradeProgress);
        MN.QMPInstall._monitorID = setInterval(MN.QMPInstall._MonitorUpgradeProgress, 250);
    },

    // Callback for reporting progress of an upgrade
    _OnUpgradeProgress : function(error, msg, done)
    {
        if (error)
        {
            var msg = '%s<br>&nbsp;(%s)'.format(MN.QMPInstall.MSG_UPGRADE_ERROR, msg);
            return MN.QMPInstall._CreateFail(msg);
        }
        else
            $(MN.QMPInstall._createParams.parentID).innerHTML = MN.QMPInstall.MSG_UPGRADING + msg;
    },

    // Callback to monitor the completion of the upgrade (bleh)
    _monitorID : 0,
    _MonitorUpgradeProgress : function()
    {
        var vt = $(MN.QMPInstall._vtID);
        if (vt && vt.RegistryVersion >= MN.QMPInstall.QMP_LATEST_VER_COMPARABLE)
        {   // yay, it's done
            log('Upgrade done successfully, will retry player creation');
            MN.Event.StopObserving(vt, 'UpgradeProgress', MN.QMPInstall._OnUpgradeProgress);
            clearInterval(MN.QMPInstall._monitorID);
            MN.QMPInstall._DestroyVersionTest(); // maybe we'll get lucky and be able to load the new version
            var cp = MN.QMPInstall._createParams;
            MN.QMPInstall.CreatePlayer(cp.parentID, cp.DoneCB, cp.w, cp.h); // retry the whole thing
        }
    },

    // This line is auto-generated; do not modify
    QMP_LATEST_VER_COMPARABLE : '06041001'
};


