alykoshin/inline-install

View on GitHub
index.js

Summary

Maintainability
C
1 day
Test Coverage
/**
 * Created by alykoshin on 9/30/14.
 */

/* globals chrome */

'use strict';


if (typeof module !== 'undefined' && typeof require !== 'undefined') {
  var Emitter = require('mini-emitter');
}


(function() {

  /**
   *
   * @param options
   * @param {string} options.itemId                  - itemId of the Extension in Chrome Web Store
   * @param {boolean} [options.checkInstalled=true]  - Check if the extension is already installed in the browser
   * @param {boolean} [options.reloadOnSuccess=true] - Reload page on success
   * @param {boolean} [options.prompt.enabled=true]  - Text to show to the user
   * @param {string} [options.prompt.text='This site requires Chrome Extension to be installed. Proceed with the installation?] - Text to show to the user
   * @param {string} [options.prompt.ok='OK]         - OK button text
   * @param {string} [options.prompt.cancel='Cancel] - Cancel button text
   * @returns {InlineInstall}
   * @constructor
   */
  var InlineInstall = function (options) {
    var self = this;
    Emitter(self);

    if (!options || !options.itemId) {
      throw 'InlineInstall: execute(): options and options.id are mandatory.';
    }
    var baseStoreUrl      = 'https://chrome.google.com/webstore/detail/';
    var extensionUrl      = baseStoreUrl + options.itemId;
    options.prompt        = options.prompt || {};
    if (typeof options.prompt.enabled !== 'boolean') options.prompt.enabled = true;
    options.prompt.text   = options.prompt.text   || 'This site requires Chrome Extension to be installed. Proceed with the installation?';
    options.prompt.ok     = options.prompt.ok     || 'OK';
    options.prompt.cancel = options.prompt.cancel || 'Cancel';
    if (typeof options.checkInstalled === 'undefined') {
      options.checkInstalled = true;
    }
    if (typeof options.reloadOnSuccess === 'undefined') {
      options.reloadOnSuccess = true;
    }

    var h = 40; // Prompt height

    var showPrompt = function (text, okText, cancelText, onClickCb) {
      var w = window;

      var i                   = document.createElement('iframe');
      i.style.position        = 'fixed';
      i.style.top             = '-' + (h + 1) + 'px';
      i.style.left            = 0;
      i.style.right           = 0;
      i.style.width           = '100%';
      i.style.height          = h + 'px';
      i.style.backgroundColor = '#ffffe0';
      i.style.border          = 'none';
      i.style.borderBottom    = '1px solid #888888';
      i.style.zIndex          = '9999999';
      if (typeof i.style.webkitTransition === 'string') {
        i.style.webkitTransition = 'all .25s ease-out';
      } else if (typeof i.style.transition === 'string') {
        i.style.transition = 'all .25s ease-out';
      }
      document.body.appendChild(i);
      var c = (i.contentWindow) ? i.contentWindow :
              (i.contentDocument.document) ? i.contentDocument.document : i.contentDocument;
      c.document.open();
      c.document.write(
        '<span style="' +
        '  font-family: Helvetica, Arial, sans-serif; ' +
        '  font-size: .9rem; ' +
        '  padding: 7px; ' +
        '  vertical-align: middle; ' +
        '  cursor: default;' +
        '">' +
        text +
        '</span>');

      if (okText && onClickCb) {
        c.document.write(
          '<button id="okay">' + okText + '</button>' +
          '&nbsp;' +
          '<button>' + cancelText + '</button>');
        c.document.close();

        c.document.getElementById('okay').addEventListener('click', function (e) {
          // window.open(buttonLink, '_top');

          if (onClickCb) {
            onClickCb();
          }

          e.preventDefault();
          try {
            event.cancelBubble = true;
          } catch (error) {
            // Mute the exception
          }
        });
      } else {
        c.document.close();
      }

      c.document.addEventListener('click', function () {
        w.document.body.removeChild(i);
      });

      setTimeout(function () {
        if (typeof i.style.webkitTransform === 'string') {
          i.style.webkitTransform = 'translateY(' + h + 'px)';
        } else if (typeof i.style.transform === 'string') {
          i.style.transform = 'translateY(' + h + 'px)';
        } else {
          i.style.top = '0px';
        }
      }, 300);

    };

    var successCallback = function () {
      chrome.webstore.onInstallStageChanged.removeListener(onInstallStageChanged);
      chrome.webstore.onDownloadProgress.removeListener(onDownloadProgress);
      self.emit('success');
      if (options.reloadOnSuccess) {
        location.reload();
      }
    };

    var failureCallback = function (failureDetailString, errorCode) {
      chrome.webstore.onInstallStageChanged.addListener(onInstallStageChanged);
      chrome.webstore.onDownloadProgress.addListener(onDownloadProgress);
      self.emit('error', failureDetailString, errorCode);
    };

    var addLink = function (url) {
      var a  = document.createElement('link');
      a.href = url;
      a.rel  = 'chrome-webstore-item';
      document.head.appendChild(a);
    };

    var onInstallStageChanged = function (installStage) {
      self.emit('installstagechanged', installStage);
    };

    var onDownloadProgress = function (percentDownloaded) {
      self.emit('downloadprogress', percentDownloaded);
    };

    var checkInstalled = function (responseCb) {
      chrome.runtime.sendMessage(
        options.itemId,
        { message: 'inline-install-check' },
        function (reply) {
          var isInstalled = reply && reply.installed;
          responseCb(isInstalled);
        });
    };

    var doInstall = function () {
      addLink(extensionUrl);
      try {
        chrome.webstore.onInstallStageChanged.addListener(onInstallStageChanged);
        chrome.webstore.onDownloadProgress.addListener(onDownloadProgress);

        return chrome.webstore.install(
          extensionUrl,
          successCallback,
          failureCallback
        );
      } catch (e) {
        failureCallback(e);
        return false;
      }
    };

    self.execute = function () {
      if (!window.chrome) {
        self.emit('error', 'You must use Chrome browser to enable Inline Installation for the extensions from Chrome Web Store');
      } else if (!chrome.webstore || !chrome.webstore.install) {
        self.emit('error', 'Your version of Chrome does not support Inline Installation for the extensions from Chrome Web Store');
      } else {
        if (options.checkInstalled) {
          checkInstalled(function (isInstalled) {
            if (isInstalled) {
              self.emit('success');
            } else {
              if (options.prompt.enabled)
                showPrompt(options.prompt.text, options.prompt.ok, options.prompt.cancel, doInstall);
            }
          });
        } else {
          if (options.prompt.enabled)
            showPrompt(options.prompt.text, options.prompt.ok, options.prompt.cancel, doInstall);
        }
      }
    };

    return self;
  };


  if (typeof module !== 'undefined') {
    module.exports = InlineInstall;
  }

  if (typeof window !== 'undefined') {
    window.InlineInstall = function (options) {
      console.log('window.InlineInstall: deprecated. Use require() instead');
      return InlineInstall.call(this, options);
    };
  }

}());