text: 'use strict';
  /*
   * Copyright (C) 2012 Google Inc. All rights reserved.
   *
   * Redistribution and use in source and binary forms, with or without
   * modification, are permitted provided that the following conditions
   * are met:
   * 1. Redistributions of source code must retain the above copyright
   *    notice, this list of conditions and the following disclaimer.
   * 2. Redistributions in binary form must reproduce the above copyright
   *    notice, this list of conditions and the following disclaimer in the
   *    documentation and/or other materials provided with the distribution.
   *
   * THIS SOFTWARE IS PROVIDED BY APPLE INC. AND ITS CONTRIBUTORS ``AS IS'' AND
   * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
   * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
   * ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR ITS CONTRIBUTORS BE LIABLE
   * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
   * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
   * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
   * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
   * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
   * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH
   * DAMAGE.
   */
  
  let didOpenPicker = false;
  
  /**
   * @param {!string} id
   */
  function $(id) {
    return document.getElementById(id);
  }
  
  /**
   * @param {!string} tagName
   * @param {string=} opt_class
   * @param {string=} opt_text
   * @return {!Element}
   */
  function createElement(tagName, opt_class, opt_text) {
    var element = document.createElement(tagName);
    if (opt_class)
      element.setAttribute('class', opt_class);
    if (opt_text)
      element.appendChild(document.createTextNode(opt_text));
    return element;
  }
  
  /**
   * @constructor
   * @param {!number|Rectangle|Object} xOrRect
   * @param {!number} y
   * @param {!number} width
   * @param {!number} height
   */
  function Rectangle(xOrRect, y, width, height) {
    if (typeof xOrRect === 'object') {
      y = xOrRect.y;
      width = xOrRect.width;
      height = xOrRect.height;
      xOrRect = xOrRect.x;
    }
    this.x = xOrRect;
    this.y = y;
    this.width = width;
    this.height = height;
  }
  
  Rectangle.prototype = {
    get maxX() {
      return this.x + this.width;
    },
    get maxY() {
      return this.y + this.height;
    },
    toString: function() {
      return 'Rectangle(' + this.x + ',' + this.y + ',' + this.width + ',' +
          this.height + ')';
    }
  };
  
  /**
   * @param {!Rectangle} rect1
   * @param {!Rectangle} rect2
   * @return {?Rectangle}
   */
  Rectangle.intersection = function(rect1, rect2) {
    var x = Math.max(rect1.x, rect2.x);
    var maxX = Math.min(rect1.maxX, rect2.maxX);
    var y = Math.max(rect1.y, rect2.y);
    var maxY = Math.min(rect1.maxY, rect2.maxY);
    var width = maxX - x;
    var height = maxY - y;
    if (width < 0 || height < 0)
      return null;
    return new Rectangle(x, y, width, height);
  };
  
  /**
   * @param {!number} width in CSS pixel
   * @param {!number} height in CSS pixel
   */
  function resizeWindow(width, height) {
    var zoom = global.params.zoomFactor ? global.params.zoomFactor : 1;
    setWindowRect(adjustWindowRect(
        width * zoom, height * zoom, width * zoom, height * zoom));
  }
  
  /**
   * @param {!number} width in DIP
   * @param {!number} height in DIP
   * @param {?number} minWidth in DIP
   * @param {?number} minHeight in DIP
   * @return {!Rectangle} Adjusted rectangle in DIP
   */
  function adjustWindowRect(width, height, minWidth, minHeight) {
    if (typeof minWidth !== 'number')
      minWidth = 0;
    if (typeof minHeight !== 'number')
      minHeight = 0;
  
    var windowRect = new Rectangle(0, 0, Math.ceil(width), Math.ceil(height));
  
    if (!global.params.anchorRectInScreen)
      return windowRect;
  
    var anchorRect = new Rectangle(global.params.anchorRectInScreen);
    var availRect = new Rectangle(
        window.screen.availLeft, window.screen.availTop, window.screen.availWidth,
        window.screen.availHeight);
  
    _adjustWindowRectVertically(windowRect, availRect, anchorRect, minHeight);
    _adjustWindowRectHorizontally(windowRect, availRect, anchorRect, minWidth);
  
    return windowRect;
  }
  
  /**
   * Arguments are DIPs.
   */
  function _adjustWindowRectVertically(
      windowRect, availRect, anchorRect, minHeight) {
    var availableSpaceAbove = anchorRect.y - availRect.y;
    availableSpaceAbove =
        Math.max(0, Math.min(availRect.height, availableSpaceAbove));
  
    var availableSpaceBelow = availRect.maxY - anchorRect.maxY;
    availableSpaceBelow =
        Math.max(0, Math.min(availRect.height, availableSpaceBelow));
  
    if (
        windowRect.height > availableSpaceBelow &&
        availableSpaceBelow < availableSpaceAbove) {
      windowRect.height = Math.min(windowRect.height, availableSpaceAbove);
      windowRect.height = Math.max(windowRect.height, minHeight);
      windowRect.y = anchorRect.y - windowRect.height;
    } else {
      windowRect.height = Math.min(windowRect.height, availableSpaceBelow);
      windowRect.height = Math.max(windowRect.height, minHeight);
      windowRect.y = anchorRect.maxY;
    }
  }
  
  /**
   * Arguments are DIPs.
   */
  function _adjustWindowRectHorizontally(
      windowRect, availRect, anchorRect, minWidth) {
    windowRect.width = Math.min(windowRect.width, availRect.width);
    windowRect.width = Math.max(windowRect.width, minWidth);
    windowRect.x = anchorRect.x;
    // If we are getting clipped, we want to switch alignment to the right side
    // of the anchor rect as long as doing so will make the popup not clipped.
    var rightAlignedX = windowRect.x + anchorRect.width - windowRect.width;
    if (rightAlignedX >= availRect.x &&
        (windowRect.maxX > availRect.maxX || global.params.isRTL))
      windowRect.x = rightAlignedX;
  }
  
  /**
   * @param {!Rectangle} rect Window position and size in DIP.
   */
  function setWindowRect(rect) {
    if (window.frameElement) {
      window.frameElement.style.width = rect.width + 'px';
      window.frameElement.style.height = rect.height + 'px';
    } else {
      window.pagePopupController.setWindowRect(
          rect.x, rect.y, rect.width, rect.height);
    }
  }
  
  function hideWindow() {
    setWindowRect(adjustWindowRect(1, 1, 1, 1));
  }
  
  /**
   * @return {!boolean}
   */
  function isWindowHidden() {
    // window.innerWidth and innerHeight are zoom-adjusted values.  If we call
    // setWindowRect with width=100 and the zoom-level is 2.0, innerWidth will
    // return 50.
    return window.innerWidth <= 1 && window.innerHeight <= 1;
  }
  
  window.addEventListener('resize', function() {
    if (isWindowHidden()) {
      window.dispatchEvent(new CustomEvent('didHide'));
    } else {
      window.dispatchEvent(new CustomEvent('didOpenPicker'));
      window.didOpenPicker = true;
    }
  }, false);
  
  /**
   * @return {!number}
   */
  function getScrollbarWidth() {
    if (typeof window.scrollbarWidth === 'undefined') {
      var scrollDiv = document.createElement('div');
      scrollDiv.style.opacity = '0';
      scrollDiv.style.overflow = 'scroll';
      scrollDiv.style.width = '50px';
      scrollDiv.style.height = '50px';
      document.body.appendChild(scrollDiv);
      window.scrollbarWidth = scrollDiv.offsetWidth - scrollDiv.clientWidth;
      scrollDiv.parentNode.removeChild(scrollDiv);
    }
    return window.scrollbarWidth;
  }
  
  /**
   * @param {!string} className
   * @return {?Element}
   */
  function enclosingNodeOrSelfWithClass(selfNode, className) {
    for (var node = selfNode; node && node !== selfNode.ownerDocument;
         node = node.parentNode) {
      if (node.nodeType === Node.ELEMENT_NODE &&
          node.classList.contains(className))
        return node;
    }
    return null;
  }
  
  /**
   * @constructor
   */
  function EventEmitter() {
  }
  
  /**
   * @param {!string} type
   * @param {!function({...*})} callback
   */
  EventEmitter.prototype.on = function(type, callback) {
    console.assert(callback instanceof Function);
    if (!this._callbacks)
      this._callbacks = {};
    if (!this._callbacks[type])
      this._callbacks[type] = [];
    this._callbacks[type].push(callback);
  };
  
  EventEmitter.prototype.hasListener = function(type) {
    if (!this._callbacks)
      return false;
    var callbacksForType = this._callbacks[type];
    if (!callbacksForType)
      return false;
    return callbacksForType.length > 0;
  };
  
  /**
   * @param {!string} type
   * @param {!function(Object)} callback
   */
  EventEmitter.prototype.removeListener = function(type, callback) {
    if (!this._callbacks)
      return;
    var callbacksForType = this._callbacks[type];
    if (!callbacksForType)
      return;
    callbacksForType.splice(callbacksForType.indexOf(callback), 1);
    if (callbacksForType.length === 0)
      delete this._callbacks[type];
  };
  
  /**
   * @param {!string} type
   * @param {...*} var_args
   */
  EventEmitter.prototype.dispatchEvent = function(type) {
    if (!this._callbacks)
      return;
    var callbacksForType = this._callbacks[type];
    if (!callbacksForType)
      return;
    callbacksForType = callbacksForType.slice(0);
    for (var i = 0; i < callbacksForType.length; ++i) {
      callbacksForType[i].apply(this, Array.prototype.slice.call(arguments, 1));
    }
  };
  
  /**
   * @constructor
   * @extends EventEmitter
   * @param {!Element} element
   * @param {!Object} config
   */
  function Picker(element, config) {
    this._element = element;
    this._config = config;
  }
  
  Picker.prototype = Object.create(EventEmitter.prototype);
  
  /**
   * @enum {number}
   */
  Picker.Actions = {
    SetValue: 0,
    Cancel: -1,
  };
  
  /**
   * @param {!string} value
   */
  Picker.prototype.submitValue = function(value) {
    window.pagePopupController.setValue(value);
    window.pagePopupController.closePopup();
  };
  
  Picker.prototype.handleCancel = function() {
    window.pagePopupController.closePopup();
  };
  
  Picker.prototype.cleanup = function() {};
  
  window.addEventListener('keyup', function(event) {
    // JAWS dispatches extra Alt events and unless we handle them they move the
    // focus and close the popup.
    if (event.key === 'Alt')
      event.preventDefault();
  }, true);
  