// Copyright (c) 2011 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

/**
 * @fileoverview Implements an in-site integration of the Chrome Frame
 * installation flow by loading its implementation on demand.
 **/

goog.provide('google.cf.installer.Prompt');

goog.require('google.cf.installer.InteractionDelegate');

/**
 * Constructs a prompt flow that loads its implementation from an external
 * Javascript file.
 * @constructor
 * @param {Window=} opt_windowInstance The window in which to load the external
 *     script. Defaults to the global window object.
 */
google.cf.installer.Prompt = function(opt_windowInstance) {
  this.window_ = opt_windowInstance || window;
};

/**
 * An optional adapter to customize the display of the installation flow.
 * @type {!google.cf.installer.InteractionDelegate|undefined}
 * @private
 */
google.cf.installer.Prompt.prototype.customInteractionDelegate_ = undefined;

/**
 * An optional URL to a resource in the same domain as the calling site that
 * will be used to transport installation results back to the client.
 */
google.cf.installer.Prompt.prototype.
    manuallySpecifiedSameDomainResourceUri_ = undefined;

/**
 * Customizes the display of the prompt via the given adapter.
 * @param {!google.cf.installer.InteractionDelegate} interactionDelegate The
 *     adapter.
 */
google.cf.installer.Prompt.prototype.setInteractionDelegate =
    function(interactionDelegate) {
  this.customInteractionDelegate_ = interactionDelegate;
};

/**
 * Specifies a resource in the same domain as the calling site that will be used
 * to carry installation results across domain boundaries.
 * @param {string} uri The resource URI to use.
 */
google.cf.installer.Prompt.prototype.setSameDomainResourceUri = function(uri) {
  this.manuallySpecifiedSameDomainResourceUri_ = uri;
};

/**
 * Holds the loaded installation flow execution method. See
 * CrossDomainInstall.execute (crossdomaininstall.js).
 * @type {?function(function(), function()=, string=,
 *                  google.cf.installer.InteractionDelegate=, string=)}
 * @private
 **/
google.cf.installer.Prompt.prototype.installProc_ = null;

/**
 * @define {string} Defines the default implementation location.
 **/
google.cf.installer.Prompt.DEFAULT_IMPLEMENTATION_URL =
    '//ajax.googleapis.com/ajax/libs/chrome-frame/2/CFInstall.impl.min.js';

/**
 * Defines the URL from which the full prompting logic will be retrieved.
 * @type {string}
 * @private
 **/
google.cf.installer.Prompt.prototype.implementationUrl_ =
    google.cf.installer.Prompt.DEFAULT_IMPLEMENTATION_URL;

/**
 * Defines a custom URL for the Chrome Frame download page.
 * @type {string|undefined}
 * @private
 */
google.cf.installer.Prompt.prototype.downloadPageUrl_ = undefined;

/**
 * Overrides the default URL for loading the implementation.
 * @param {string} url The custom URL.
 */
google.cf.installer.Prompt.prototype.setImplementationUrl = function(url) {
  this.implementationUrl_ = url;
};

/**
 * Overrides the default URL for the download page.
 * @param {string} url The custom URL.
 */
google.cf.installer.Prompt.prototype.setDownloadPageUrl = function(url) {
  this.downloadPageUrl_ = url;
};

/**
 * Initiates loading of the full implementation if not already initiated.
 * @param {function()=} opt_loadCompleteHandler A callback that will be notified
 *     when the loading of the script has completed, possibly unsuccessfully.
 **/
google.cf.installer.Prompt.prototype.loadInstallProc_ =
    function(opt_loadCompleteHandler) {
  var scriptParent = this.window_.document.getElementsByTagName('head')[0] ||
      this.window_.document.documentElement;
  var scriptEl = this.window_.document.createElement('script');
  scriptEl.src = this.implementationUrl_;
  scriptEl.type = 'text/javascript';

  if (opt_loadCompleteHandler) {
    var loadHandler = goog.bind(function() {
      scriptEl.onreadystatechange = null;
      scriptEl.onerror = null;
      scriptEl.onload = null;
      loadHandler = function(){};
      opt_loadCompleteHandler();
    }, this);

    scriptEl.onload = loadHandler;
    scriptEl.onerror = loadHandler;
    scriptEl.onreadystatechange = function() {
      if (scriptEl.readyState == 'loaded')
        loadHandler();
    };
  }

  scriptParent.appendChild(scriptEl);
};

/**
 * Invokes the installation procedure with our configured parameters and
 * the provided callbacks.
 * @param {function()} successHandler The method to invoke upon installation
 *     success.
 * @param {function()} opt_failureHandler The method to invoke upon installation
 *     failure.
 */
google.cf.installer.Prompt.prototype.invokeProc_ =
    function(successHandler, opt_failureHandler) {
  this.installProc_(successHandler,
                    opt_failureHandler,
                    this.downloadPageUrl_,
                    this.customInteractionDelegate_,
                    this.manuallySpecifiedSameDomainResourceUri_);
}

/**
 * Receives a handle to the flow implementation method and invokes it.
 * @param {function(function(), function()=, string=,
 *                  google.cf.installer.InteractionDelegate=, string=)}
 *     installProc The install execution method. See
 *     CrossDomainInstall.execute().
 * @param {function()} successHandler The method to invoke upon installation
 *     success.
 * @param {function()} opt_failureHandler The method to invoke upon installation
 *     failure.
 */
google.cf.installer.Prompt.prototype.onProcLoaded_ =
    function(installProc, successHandler, opt_failureHandler) {
  this.window_['CF_google_cf_xd_install_stub'] = undefined;
  this.installProc_ = installProc;
  this.invokeProc_(successHandler, opt_failureHandler);
};

/**
 * Loads and executes the in-line Google Chrome Frame installation flow.
 * Will typically execute asynchronously (as the flow behaviour is not yet
 * loaded).
 *
 * @param {function()} successHandler A function to invoke once Google
 *     Chrome Frame is successfully installed. Overrides the default
 *     behaviour, which is to reload the current page.
 * @param {function()=} opt_failureHandler A function to invoke if the
 *     installation fails. The default behaviour is to do nothing.
 */
google.cf.installer.Prompt.prototype.open = function(successHandler,
                                                     opt_failureHandler) {
  if (this.installProc_) {
    this.invokeProc_(successHandler, opt_failureHandler);
    return;
  }

  if (this.window_['CF_google_cf_xd_install_stub']) {
    if (opt_failureHandler)
      opt_failureHandler();
    return;
  }

  this.window_['CF_google_cf_xd_install_stub'] = goog.bind(
    function(installProc) {
      this.onProcLoaded_(installProc, successHandler, opt_failureHandler);
    }, this);

  var loadCompleteHandler = undefined;
  if (opt_failureHandler) {
    loadCompleteHandler = goog.bind(function() {
      if (!this.installProc_)
        opt_failureHandler();
    }, this);
  }

  this.loadInstallProc_(loadCompleteHandler);
};