text: /**
   * PromiseWindow
   * https://github.com/amercier/promise-window
   * @ignore
   */
  (function() {
    'use strict';
  
    var root = window,
        prototype,
        html = root.document.documentElement;
  
    /**
     * Merge the contents of two or more objects together into the first object.
     *
     *     merge( target [, object1 ] [, objectN ] )
     *
     * @param {Object} target  An object that will receive the new properties if
     *                         additional objects are passed in.
     * @param {Object} object1 An object containing additional properties to merge in.
     * @param {Object} objectN An object containing additional properties to merge in.
     * @return {Object} Returns the first object.
     * @ignore
     */
    function extend() {
      var extended = arguments[0], key, i;
      for (i = 1; i < arguments.length; i++) {
        for (key in arguments[i]) {
          if (arguments[i].hasOwnProperty(key)) {
            extended[key] = arguments[i][key];
          }
        }
      }
      return extended;
    }
  
    /**
     * Generates a pseudo-unique String
     *
     * @param  {String} prefix Optional.
     * @return {String} Returns a pseudo-unique string prefixed with the given prefix, if any.
     * @ignore
     */
    function generateUniqueString(prefix) {
      return prefix + new Date().getTime() + "-" + Math.floor(10e12 * Math.random());
    }
  
    /**
     * Create a new PromiseWindow object
     *
     * During the lifecycle of this object, popup windows can be opened, closed,
     * and reopened again. However, it'
     *
     * Instanciating this prototype does not immediately opens a new popup window.
     * To open the window, use `open()` on the created object.
     *
     * @param {String}   uri                    Destination URI
     * @param {Object}   config                 Configuration object. See description below.
     * @param {Number}   config.width           Width of the popup window. Defaults to the current document width.
     * @param {Number}   config.height          Height of the popup window. Defaults to the current document height.
     * @param {Function} config.promiseProvider Promise provider. Should return a plain object containing 3 fields:
     *                                          - `promise` {Promise}  a new Promise object
     *                                          - `resolve` {Function} the method to resolve the given Promise
     *                                          - `reject`  {Function} the method to reject the given Promise
     * @param {Function} config.onPostMessage   Handler for receiving a postMessage from the opened window. Default
     *                                          implementation resolves the promise with the data passed in the post
     *                                          message, except if this data contains an `error` field. In this case,
     *                                          it rejects the Promise with the value of that field. In all cases, closes
     *                                          the popup window.
     * @param {Function} config.onPostMessage.event Event The postMessage event
     * @param {Number}   config.watcherDelay    There is no programmatic way of knowing when a popup window is closed
     *                                          (either manually or programatically). For this reason, every time
     *                                          PromiseWindow opens a popup, a new watcher is created. The watcher checks
     *                                          regularly if the window is still open. This value defines at which
     *                                          interval this check is done. Defaults to 100ms.
     * @param {String}   config.windowName      Name to be ginven to the popup window. See `window.open` references for
     *                                          details. If `null`, a random name is generated.
     * @param {Object}   config.window          Object containing window configuration settings. Scrollbars are enabled
     *                                          by default. All `window.open` ptions are accepted, but please note that
     *                                          many of them have no effect in most modern browsers. See
     *                                          https://developer.mozilla.org/en-US/docs/Web/API/Window/open for more
     *                                          details.
     * @param {Function} config.onClose         Function being called whenever the popup is being closed (either after a
     *                                          post message has been received, or window has been closed by user, or
     *                                          `.close()` method has been called. Default implementation closes the
     *                                          popup window by calling `this._window.close()`).
     * @param {RegExp} config.originRegexp      Regular expression that matches the origin part of an URI. Defaults to
     *                                          `new RegExp('^[^:/?]+://[^/]*')`. If doesn't match (ex: relative URIs),
     *                                          use `location.origin`.
     * @constructor
     */
    function PromiseWindow(uri, config) {
      this.uri = uri;
      this.config = extend({}, this.constructor.defaultConfig, config);
      this.config.windowName = this.config.windowName || generateUniqueString('promise-window-');
      this._onPostMessage = this._onPostMessage.bind(this);
    }
  
    /**
     * Create a Promise provider from a Promise/A+ constructor to be used with
     * `config.promiseProvider`.
     *
     *     new PromiseWindow(..., {
     *       ...,
     *       promiseProvider: PromiseWindow.getAPlusPromiseProvider(MyCustomPromise)
     *     });
     *
     * @param  {Function} CustomPromise Promise/A+ contructor
     * @return {Function} Returns a promise provider
     * @static
     */
    PromiseWindow.getAPlusPromiseProvider = function getAPlusPromiseProvider(CustomPromise) {
      return function promiseProvider() {
        var module = {};
        module.promise = new CustomPromise(function(resolve, reject) {
          module.resolve = resolve;
          module.reject = reject;
        });
        return module;
      };
    };
  
    /**
     * Convenience method for:
     *
     *     new PromiseWindow(uri, config).open()
     *
     * Use this method only if you never need to close the window programatically.
     * If you do, please consider using the classic way:
     *
     *     var w = new PromiseWindow(uri, config)
     *     w.open();
     *     // ...
     *     w.close();
     *
     * @return {Promise} Returns a Promise equivalent to the one returned by `open()`
     * @static
     */
    PromiseWindow.open = function open(uri, config) {
      return new PromiseWindow(uri, config).open();
    };
  
    /**
     * Default configuration
     * @type {Object}
     */
    PromiseWindow.defaultConfig = {
      width: html.clientWidth,
      height: html.clientHeight,
      window: {
        scrollbars: true
      },
      watcherDelay: 100,
      promiseProvider: null,
      onPostMessage: function onPostMessage(event) {
        if (event.data.error) {
          this._reject(event.data.error);
        } else {
          this._resolve(event.data);
        }
        this.close();
      },
      windowName: null,
      onClose: function() {
        this._window.close();
      },
      originRegexp: new RegExp('^[^:/?]+://[^/]*')
    };
  
    // Configure default Promise provider from current invironment
    if (root.Promise) {
      PromiseWindow.defaultConfig.promiseProvider = PromiseWindow.getAPlusPromiseProvider(root.Promise);
    }
    else if (root.RSVP) {
      PromiseWindow.defaultConfig.promiseProvider = PromiseWindow.getAPlusPromiseProvider(root.RSVP.Promise);
    }
    else if (root.Q) {
      PromiseWindow.defaultConfig.promiseProvider = PromiseWindow.getAPlusPromiseProvider(root.Q.Promise);
    }
    else if (root.jQuery) {
      PromiseWindow.defaultConfig.promiseProvider = function promiseProvider() {
        var deferred = root.jQuery.Deferred();
        return {
          promise: deferred.promise(),
          resolve: deferred.resolve,
          reject: deferred.reject
        };
      };
    }
    else {
      PromiseWindow.defaultConfig.promiseProvider = function() {
        throw new Error('Missing promiseProvider in PromiseWindow configuration');
      };
    }
  
    prototype = PromiseWindow.prototype;
  
    /**
     * Checks whether a value is a boolean
     * @param {*} value The value to check
     * @return {boolean} `true` if value is a boolean, `false` otherwise
     * @protected
     */
    prototype._isBoolean = function _isBoolean(value) {
      return value === true || value === false;
    };
  
    /**
     * Converts a config value into a value compatible with `window.open`.
     * If value is a boolean, convert it to 'yes' or 'no', otherwise simply
     * casts it into a string.
     * @param {*} value The value to convert
     * @return {string} The converted value
     * @protected
     */
    prototype._serializeFeatureValue = function _serializeFeatureValue(key, value) {
      if (this._isBoolean(value)) {
        return value ? 'yes' : 'no';
      }
      return '' + value;
    };
  
    /**
     * Get the left and top position in the screen for a rectangle, taking
     * dual-screen position into account
     * @param {Number} width Width of the rectangle
     * @param {Number} height Height of the rectangle
     * @return {Object} position A new object representing the position of the rectangle, centered
     * @return {Number} position.left The X coordinate of the centered rectangle
     * @return {Number} position.top The Y coordinate of the centered rectangle
     * @return {Number} position.width The width of the centered rectangle
     * @return {Number} position.height The height of the centered rectangle
     * @protected
     */
    prototype._getCenteredPosition = function _getCenteredPosition(width, height) {
      var dualScreenLeft = root.screenLeft !== undefined ? root.screenLeft : screen.left,
          dualScreenTop = root.screenTop !== undefined ? root.screenTop : screen.top,
          w = root.innerWidth || html.clientWidth || screen.width,
          h = root.innerHeight || html.clientHeight || screen.height;
  
      return {
        left: (w / 2) - (width / 2) + dualScreenLeft,
        top:  (h / 2) - (height / 2) + dualScreenTop,
        width: width,
        height: height
      };
    };
  
    /**
     * Generates window features based on the current configuration
     * @return {String} Returns window features compatible with `window.open`
     * @protected
     */
    prototype._getFeatures = function _getFeatures() {
      var config = this._getCenteredPosition(this.config.width, this.config.height);
      for (var key in this.config.window) {
        if (this.config.window.hasOwnProperty(key)) {
          config[key] = this.config.window[key];
        }
      }
  
      return Object.keys(config)
        .map(function(key) { return key + '=' + this._serializeFeatureValue(key, config[key]); }.bind(this))
        .join(',');
    };
  
    /**
     * Creates a new Promise, using `config.promiseProvider`, and save reject and
     * resolve methods for later.
     *
     * @return {Promise} Returns the new Promise object created by the configured
     *                   Promise Provider.
     * @protected
     */
    prototype._createPromise = function _createPromise() {
      var module = this.config.promiseProvider();
      this._resolve = module.resolve;
      this._reject = module.reject;
      return module.promise;
    };
  
    /**
     * Checks whether the window is alive or not
     * @return {Boolean} Returns `true` if the window is alive, `false` otherwise
     * @protected
     */
    prototype._isWindowAlive = function _isWindowAlive() {
      return this._window && !this._window.closed;
    };
  
    /**
     * Starts the popup window watcher.
     * @return {void}
     * @protected
     */
    prototype._startWatcher = function _startWatcher() {
      if (this._watcherRunning) {
        throw new Error('Watcher is already started');
      }
      this._watcher = root.setInterval(function () {
        if (this._watcherRunning && !this._isWindowAlive()) {
          this.close();
        }
      }.bind(this), this.config.watcherDelay);
      this._watcherRunning = true;
    };
  
    /**
     * Stops the popup window watcher.
     * @return {void}
     * @protected
     */
    prototype._stopWatcher = function _stopWatcher() {
      if (!this._watcherRunning) {
        throw new Error('Watcher is already stopped');
      }
      this._watcherRunning = false;
      root.clearInterval(this._watcher);
    };
  
    /**
     * Callback for post message events. If and only of the event has been
     * generated from the opened popup window, it propagates it to the configured
     * post message handler (`config.onPostMessage`).
     *
     * @param {Event} event The postMessage event
     * @return {void}
     * @protected
     */
    prototype._onPostMessage = function _onPostMessage(event) {
      var expectedOriginMatches = this.config.originRegexp.exec(this.uri);
      var expectedOrigin = expectedOriginMatches && expectedOriginMatches[0] || location.origin;
      if (this._window === event.source && event.origin === expectedOrigin) {
        this.config.onPostMessage.call(this, event);
      }
    };
  
    /**
     * Changes the URI
     * @param {String} uri The new URI
     * @throws {Error} If the window is open
     * @return {PromiseWindow} Returns this object to allow chaining
     */
    prototype.setURI = function setURI(uri) {
      if (this.isOpen()) {
        throw new Error('Cannot change the URI while the window is open');
      }
      this.uri = uri;
      return this;
    };
  
    /**
     * Opens a new popup window.
     *
     * @return {Promise} Returns a new `Promise` object. This promise will be:
     *                   - rejected with `"blocked"` message if the popup window
     *                     does not open for any reason (popup blocker, etc...)
     *                   - rejected with `"closed"` if closed either manually by
     *                     the user, or programatically
     *                   - rejected with the given error if the web page opened in
     *                     the popup sends a post message with a `error` data field.
     *                   - resolved with the given data if the web page opened in
     *                     the popup sends a post message without a `error` data
     *                     field.
     */
    prototype.open = function open() {
      if (this.isOpen()) {
        throw new Error('Window is already open');
      }
  
      this._windowOpen = true;
      var promise = this._createPromise();
      this._window = root.open(
        this.uri,
        this.config.windowName,
        this._getFeatures()
      );
      if (!this._window) {
        this._reject("blocked");
      }
      else {
        root.addEventListener("message", this._onPostMessage, true);
        this._startWatcher();
      }
      return promise;
    };
  
    /**
     * Closes the popup window.
     *
     * @return {void}
     */
    prototype.close = function close() {
      if (!this.isOpen()) {
        throw new Error('Window is already closed');
      }
      this._stopWatcher();
      root.removeEventListener("message", this._onPostMessage);
      if (this._isWindowAlive()) {
        this.config.onClose.call(this);
      }
      this._reject("closed");
      this._window = null;
      this._windowOpen = false;
    };
  
    /**
     * Checks whether the window is open or not
     * @return {Boolean} Returns `true` if the window is opened, `false` otherwise.
     */
    prototype.isOpen = function isOpen() {
      return this._windowOpen;
    };
  
    // Exports PromiseWindow to the global scope
    /* jshint ignore:start */
    if (typeof define === 'function' && define.amd) {
      define('promise-window', [], function() { return PromiseWindow });
    } else if (typeof exports === 'object') {
      module.exports = PromiseWindow;
    } else {
      root.PromiseWindow = PromiseWindow;
    }
    /* jshint ignore:end */
  
  })();
  