| Index: third_party/polymer/v1_0/components-chromium/iron-overlay-behavior/iron-overlay-manager-extracted.js
|
| diff --git a/third_party/polymer/v1_0/components-chromium/iron-overlay-behavior/iron-overlay-manager-extracted.js b/third_party/polymer/v1_0/components-chromium/iron-overlay-behavior/iron-overlay-manager-extracted.js
|
| index b8a165b1d4ab3455c7bdf856995c93d09f407b6e..0b4df854eca328c15dd1681d32136b450989efa0 100644
|
| --- a/third_party/polymer/v1_0/components-chromium/iron-overlay-behavior/iron-overlay-manager-extracted.js
|
| +++ b/third_party/polymer/v1_0/components-chromium/iron-overlay-behavior/iron-overlay-manager-extracted.js
|
| @@ -1,195 +1,356 @@
|
| /**
|
| * @struct
|
| * @constructor
|
| + * @private
|
| */
|
| Polymer.IronOverlayManagerClass = function() {
|
| + /**
|
| + * Used to keep track of the opened overlays.
|
| + * @private {Array<Element>}
|
| + */
|
| this._overlays = [];
|
| - // Used to keep track of the last focused node before an overlay gets opened.
|
| - this._lastFocusedNodes = [];
|
|
|
| /**
|
| - * iframes have a default z-index of 100, so this default should be at least
|
| - * that.
|
| + * iframes have a default z-index of 100,
|
| + * so this default should be at least that.
|
| * @private {number}
|
| */
|
| this._minimumZ = 101;
|
|
|
| - this._backdrops = [];
|
| -
|
| + /**
|
| + * Memoized backdrop element.
|
| + * @private {Element|null}
|
| + */
|
| this._backdropElement = null;
|
| - Object.defineProperty(this, 'backdropElement', {
|
| - get: function() {
|
| - if (!this._backdropElement) {
|
| - this._backdropElement = document.createElement('iron-overlay-backdrop');
|
| - }
|
| - return this._backdropElement;
|
| - }.bind(this)
|
| - });
|
| +
|
| + // Listen to mousedown or touchstart to be sure to be the first to capture
|
| + // clicks outside the overlay.
|
| + var clickEvent = ('ontouchstart' in window) ? 'touchstart' : 'mousedown';
|
| + document.addEventListener(clickEvent, this._onCaptureClick.bind(this), true);
|
| + document.addEventListener('focus', this._onCaptureFocus.bind(this), true);
|
| + document.addEventListener('keydown', this._onCaptureKeyDown.bind(this), true);
|
| + };
|
| +
|
| + Polymer.IronOverlayManagerClass.prototype = {
|
| +
|
| + constructor: Polymer.IronOverlayManagerClass,
|
| +
|
| + /**
|
| + * The shared backdrop element.
|
| + * @type {Element} backdropElement
|
| + */
|
| + get backdropElement() {
|
| + if (!this._backdropElement) {
|
| + this._backdropElement = document.createElement('iron-overlay-backdrop');
|
| + }
|
| + return this._backdropElement;
|
| + },
|
|
|
| /**
|
| * The deepest active element.
|
| - * returns {?Node} element the active element
|
| - */
|
| - this.deepActiveElement = null;
|
| - Object.defineProperty(this, 'deepActiveElement', {
|
| - get: function() {
|
| - var active = document.activeElement;
|
| - // document.activeElement can be null
|
| - // https://developer.mozilla.org/en-US/docs/Web/API/Document/activeElement
|
| - while (active && active.root && Polymer.dom(active.root).activeElement) {
|
| - active = Polymer.dom(active.root).activeElement;
|
| - }
|
| - return active;
|
| - }.bind(this)
|
| - });
|
| - };
|
| + * @type {Element} activeElement the active element
|
| + */
|
| + get deepActiveElement() {
|
| + // document.activeElement can be null
|
| + // https://developer.mozilla.org/en-US/docs/Web/API/Document/activeElement
|
| + // In case of null, default it to document.body.
|
| + var active = document.activeElement || document.body;
|
| + while (active.root && Polymer.dom(active.root).activeElement) {
|
| + active = Polymer.dom(active.root).activeElement;
|
| + }
|
| + return active;
|
| + },
|
|
|
| - /**
|
| - * If a node is contained in an overlay.
|
| - * @private
|
| - * @param {Node} node
|
| - * @returns {Boolean}
|
| - */
|
| - Polymer.IronOverlayManagerClass.prototype._isChildOfOverlay = function(node) {
|
| - while (node && node !== document.body) {
|
| - // Use logical parentNode, or native ShadowRoot host.
|
| - node = Polymer.dom(node).parentNode || node.host;
|
| - // Check if it is an overlay.
|
| - if (node && node.behaviors && node.behaviors.indexOf(Polymer.IronOverlayBehaviorImpl) !== -1) {
|
| - return true;
|
| + /**
|
| + * Brings the overlay at the specified index to the front.
|
| + * @param {number} i
|
| + * @private
|
| + */
|
| + _bringOverlayAtIndexToFront: function(i) {
|
| + var overlay = this._overlays[i];
|
| + var lastI = this._overlays.length - 1;
|
| + // Ensure always-on-top overlay stays on top.
|
| + if (!overlay.alwaysOnTop && this._overlays[lastI].alwaysOnTop) {
|
| + lastI--;
|
| + }
|
| + // If already the top element, return.
|
| + if (!overlay || i >= lastI) {
|
| + return;
|
| + }
|
| + // Update z-index to be on top.
|
| + var minimumZ = Math.max(this.currentOverlayZ(), this._minimumZ);
|
| + if (this._getZ(overlay) <= minimumZ) {
|
| + this._applyOverlayZ(overlay, minimumZ);
|
| }
|
| - }
|
| - return false;
|
| - };
|
|
|
| - Polymer.IronOverlayManagerClass.prototype._applyOverlayZ = function(overlay, aboveZ) {
|
| - this._setZ(overlay, aboveZ + 2);
|
| - };
|
| + // Shift other overlays behind the new on top.
|
| + while (i < lastI) {
|
| + this._overlays[i] = this._overlays[i + 1];
|
| + i++;
|
| + }
|
| + this._overlays[lastI] = overlay;
|
| + },
|
|
|
| - Polymer.IronOverlayManagerClass.prototype._setZ = function(element, z) {
|
| - element.style.zIndex = z;
|
| - };
|
| + /**
|
| + * Adds the overlay and updates its z-index if it's opened, or removes it if it's closed.
|
| + * Also updates the backdrop z-index.
|
| + * @param {Element} overlay
|
| + */
|
| + addOrRemoveOverlay: function(overlay) {
|
| + if (overlay.opened) {
|
| + this.addOverlay(overlay);
|
| + } else {
|
| + this.removeOverlay(overlay);
|
| + }
|
| + this.trackBackdrop();
|
| + },
|
|
|
| - /**
|
| - * track overlays for z-index and focus managemant
|
| - */
|
| - Polymer.IronOverlayManagerClass.prototype.addOverlay = function(overlay) {
|
| - var minimumZ = Math.max(this.currentOverlayZ(), this._minimumZ);
|
| - this._overlays.push(overlay);
|
| - var newZ = this.currentOverlayZ();
|
| - if (newZ <= minimumZ) {
|
| - this._applyOverlayZ(overlay, minimumZ);
|
| - }
|
| - var element = this.deepActiveElement;
|
| - // If already in other overlay, don't reset focus there.
|
| - if (this._isChildOfOverlay(element)) {
|
| - element = null;
|
| - }
|
| - this._lastFocusedNodes.push(element);
|
| - };
|
| + /**
|
| + * Tracks overlays for z-index and focus management.
|
| + * Ensures the last added overlay with always-on-top remains on top.
|
| + * @param {Element} overlay
|
| + */
|
| + addOverlay: function(overlay) {
|
| + var i = this._overlays.indexOf(overlay);
|
| + if (i >= 0) {
|
| + this._bringOverlayAtIndexToFront(i);
|
| + return;
|
| + }
|
| + var insertionIndex = this._overlays.length;
|
| + var currentOverlay = this._overlays[insertionIndex - 1];
|
| + var minimumZ = Math.max(this._getZ(currentOverlay), this._minimumZ);
|
| + var newZ = this._getZ(overlay);
|
| +
|
| + // Ensure always-on-top overlay stays on top.
|
| + if (currentOverlay && currentOverlay.alwaysOnTop && !overlay.alwaysOnTop) {
|
| + // This bumps the z-index of +2.
|
| + this._applyOverlayZ(currentOverlay, minimumZ);
|
| + insertionIndex--;
|
| + // Update minimumZ to match previous overlay's z-index.
|
| + var previousOverlay = this._overlays[insertionIndex - 1];
|
| + minimumZ = Math.max(this._getZ(previousOverlay), this._minimumZ);
|
| + }
|
| +
|
| + // Update z-index and insert overlay.
|
| + if (newZ <= minimumZ) {
|
| + this._applyOverlayZ(overlay, minimumZ);
|
| + }
|
| + this._overlays.splice(insertionIndex, 0, overlay);
|
|
|
| - Polymer.IronOverlayManagerClass.prototype.removeOverlay = function(overlay) {
|
| - var i = this._overlays.indexOf(overlay);
|
| - if (i >= 0) {
|
| + // Get focused node.
|
| + var element = this.deepActiveElement;
|
| + overlay.restoreFocusNode = this._overlayParent(element) ? null : element;
|
| + },
|
| +
|
| + /**
|
| + * @param {Element} overlay
|
| + */
|
| + removeOverlay: function(overlay) {
|
| + var i = this._overlays.indexOf(overlay);
|
| + if (i === -1) {
|
| + return;
|
| + }
|
| this._overlays.splice(i, 1);
|
| - this._setZ(overlay, '');
|
|
|
| - var node = this._lastFocusedNodes[i];
|
| - // Focus only if still contained in document.body
|
| - if (overlay.restoreFocusOnClose && node && Polymer.dom(document.body).deepContains(node)) {
|
| + var node = overlay.restoreFocusOnClose ? overlay.restoreFocusNode : null;
|
| + overlay.restoreFocusNode = null;
|
| + // Focus back only if still contained in document.body
|
| + if (node && Polymer.dom(document.body).deepContains(node)) {
|
| node.focus();
|
| }
|
| - this._lastFocusedNodes.splice(i, 1);
|
| - }
|
| - };
|
| + },
|
|
|
| - Polymer.IronOverlayManagerClass.prototype.currentOverlay = function() {
|
| - var i = this._overlays.length - 1;
|
| - while (this._overlays[i] && !this._overlays[i].opened) {
|
| - --i;
|
| - }
|
| - return this._overlays[i];
|
| - };
|
| + /**
|
| + * Returns the current overlay.
|
| + * @return {Element|undefined}
|
| + */
|
| + currentOverlay: function() {
|
| + var i = this._overlays.length - 1;
|
| + return this._overlays[i];
|
| + },
|
|
|
| - Polymer.IronOverlayManagerClass.prototype.currentOverlayZ = function() {
|
| - return this._getOverlayZ(this.currentOverlay());
|
| - };
|
| + /**
|
| + * Returns the current overlay z-index.
|
| + * @return {number}
|
| + */
|
| + currentOverlayZ: function() {
|
| + return this._getZ(this.currentOverlay());
|
| + },
|
|
|
| - /**
|
| - * Ensures that the minimum z-index of new overlays is at least `minimumZ`.
|
| - * This does not effect the z-index of any existing overlays.
|
| - *
|
| - * @param {number} minimumZ
|
| - */
|
| - Polymer.IronOverlayManagerClass.prototype.ensureMinimumZ = function(minimumZ) {
|
| - this._minimumZ = Math.max(this._minimumZ, minimumZ);
|
| - };
|
| + /**
|
| + * Ensures that the minimum z-index of new overlays is at least `minimumZ`.
|
| + * This does not effect the z-index of any existing overlays.
|
| + * @param {number} minimumZ
|
| + */
|
| + ensureMinimumZ: function(minimumZ) {
|
| + this._minimumZ = Math.max(this._minimumZ, minimumZ);
|
| + },
|
|
|
| - Polymer.IronOverlayManagerClass.prototype.focusOverlay = function() {
|
| - var current = this.currentOverlay();
|
| - // We have to be careful to focus the next overlay _after_ any current
|
| - // transitions are complete (due to the state being toggled prior to the
|
| - // transition). Otherwise, we risk infinite recursion when a transitioning
|
| - // (closed) overlay becomes the current overlay.
|
| - //
|
| - // NOTE: We make the assumption that any overlay that completes a transition
|
| - // will call into focusOverlay to kick the process back off. Currently:
|
| - // transitionend -> _applyFocus -> focusOverlay.
|
| - if (current && !current.transitioning) {
|
| - current._applyFocus();
|
| - }
|
| - };
|
| + focusOverlay: function() {
|
| + var current = /** @type {?} */ (this.currentOverlay());
|
| + // We have to be careful to focus the next overlay _after_ any current
|
| + // transitions are complete (due to the state being toggled prior to the
|
| + // transition). Otherwise, we risk infinite recursion when a transitioning
|
| + // (closed) overlay becomes the current overlay.
|
| + //
|
| + // NOTE: We make the assumption that any overlay that completes a transition
|
| + // will call into focusOverlay to kick the process back off. Currently:
|
| + // transitionend -> _applyFocus -> focusOverlay.
|
| + if (current && !current.transitioning) {
|
| + current._applyFocus();
|
| + }
|
| + },
|
|
|
| - Polymer.IronOverlayManagerClass.prototype.trackBackdrop = function(element) {
|
| - // backdrops contains the overlays with a backdrop that are currently
|
| - // visible
|
| - var index = this._backdrops.indexOf(element);
|
| - if (element.opened && element.withBackdrop) {
|
| - // no duplicates
|
| - if (index === -1) {
|
| - this._backdrops.push(element);
|
| - }
|
| - } else if (index >= 0) {
|
| - this._backdrops.splice(index, 1);
|
| - }
|
| - };
|
| + /**
|
| + * Updates the backdrop z-index.
|
| + */
|
| + trackBackdrop: function() {
|
| + this.backdropElement.style.zIndex = this.backdropZ();
|
| + },
|
|
|
| - Polymer.IronOverlayManagerClass.prototype.getBackdrops = function() {
|
| - return this._backdrops;
|
| - };
|
| + /**
|
| + * @return {Array<Element>}
|
| + */
|
| + getBackdrops: function() {
|
| + var backdrops = [];
|
| + for (var i = 0; i < this._overlays.length; i++) {
|
| + if (this._overlays[i].withBackdrop) {
|
| + backdrops.push(this._overlays[i]);
|
| + }
|
| + }
|
| + return backdrops;
|
| + },
|
|
|
| - /**
|
| - * Returns the z-index for the backdrop.
|
| - */
|
| - Polymer.IronOverlayManagerClass.prototype.backdropZ = function() {
|
| - return this._getOverlayZ(this._overlayWithBackdrop()) - 1;
|
| - };
|
| + /**
|
| + * Returns the z-index for the backdrop.
|
| + * @return {number}
|
| + */
|
| + backdropZ: function() {
|
| + return this._getZ(this._overlayWithBackdrop()) - 1;
|
| + },
|
|
|
| - /**
|
| - * Returns the first opened overlay that has a backdrop.
|
| - */
|
| - Polymer.IronOverlayManagerClass.prototype._overlayWithBackdrop = function() {
|
| - for (var i = 0; i < this._overlays.length; i++) {
|
| - if (this._overlays[i].opened && this._overlays[i].withBackdrop) {
|
| - return this._overlays[i];
|
| + /**
|
| + * Returns the first opened overlay that has a backdrop.
|
| + * @return {Element|undefined}
|
| + * @private
|
| + */
|
| + _overlayWithBackdrop: function() {
|
| + for (var i = 0; i < this._overlays.length; i++) {
|
| + if (this._overlays[i].withBackdrop) {
|
| + return this._overlays[i];
|
| + }
|
| }
|
| - }
|
| - };
|
| + },
|
|
|
| - /**
|
| - * Calculates the minimum z-index for the overlay.
|
| - */
|
| - Polymer.IronOverlayManagerClass.prototype._getOverlayZ = function(overlay) {
|
| - var z = this._minimumZ;
|
| - if (overlay) {
|
| - var z1 = Number(window.getComputedStyle(overlay).zIndex);
|
| - // Check if is a number
|
| - // Number.isNaN not supported in IE 10+
|
| - if (z1 === z1) {
|
| - z = z1;
|
| + /**
|
| + * Calculates the minimum z-index for the overlay.
|
| + * @param {Element=} overlay
|
| + * @private
|
| + */
|
| + _getZ: function(overlay) {
|
| + var z = this._minimumZ;
|
| + if (overlay) {
|
| + var z1 = Number(overlay.style.zIndex || window.getComputedStyle(overlay).zIndex);
|
| + // Check if is a number
|
| + // Number.isNaN not supported in IE 10+
|
| + if (z1 === z1) {
|
| + z = z1;
|
| + }
|
| + }
|
| + return z;
|
| + },
|
| +
|
| + /**
|
| + * @param {Element} element
|
| + * @param {number|string} z
|
| + * @private
|
| + */
|
| + _setZ: function(element, z) {
|
| + element.style.zIndex = z;
|
| + },
|
| +
|
| + /**
|
| + * @param {Element} overlay
|
| + * @param {number} aboveZ
|
| + * @private
|
| + */
|
| + _applyOverlayZ: function(overlay, aboveZ) {
|
| + this._setZ(overlay, aboveZ + 2);
|
| + },
|
| +
|
| + /**
|
| + * Returns the overlay containing the provided node. If the node is an overlay,
|
| + * it returns the node.
|
| + * @param {Element=} node
|
| + * @return {Element|undefined}
|
| + * @private
|
| + */
|
| + _overlayParent: function(node) {
|
| + while (node && node !== document.body) {
|
| + // Check if it is an overlay.
|
| + if (node._manager === this) {
|
| + return node;
|
| + }
|
| + // Use logical parentNode, or native ShadowRoot host.
|
| + node = Polymer.dom(node).parentNode || node.host;
|
| + }
|
| + },
|
| +
|
| + /**
|
| + * Returns the deepest overlay in the path.
|
| + * @param {Array<Element>=} path
|
| + * @return {Element|undefined}
|
| + * @private
|
| + */
|
| + _overlayInPath: function(path) {
|
| + path = path || [];
|
| + for (var i = 0; i < path.length; i++) {
|
| + if (path[i]._manager === this) {
|
| + return path[i];
|
| + }
|
| + }
|
| + },
|
| +
|
| + /**
|
| + * Ensures the click event is delegated to the right overlay.
|
| + * @param {!Event} event
|
| + * @private
|
| + */
|
| + _onCaptureClick: function(event) {
|
| + var overlay = /** @type {?} */ (this.currentOverlay());
|
| + // Check if clicked outside of top overlay.
|
| + if (overlay && this._overlayInPath(Polymer.dom(event).path) !== overlay) {
|
| + overlay._onCaptureClick(event);
|
| + }
|
| + },
|
| +
|
| + /**
|
| + * Ensures the focus event is delegated to the right overlay.
|
| + * @param {!Event} event
|
| + * @private
|
| + */
|
| + _onCaptureFocus: function(event) {
|
| + var overlay = /** @type {?} */ (this.currentOverlay());
|
| + if (overlay) {
|
| + overlay._onCaptureFocus(event);
|
| + }
|
| + },
|
| +
|
| + /**
|
| + * Ensures TAB and ESC keyboard events are delegated to the right overlay.
|
| + * @param {!Event} event
|
| + * @private
|
| + */
|
| + _onCaptureKeyDown: function(event) {
|
| + var overlay = /** @type {?} */ (this.currentOverlay());
|
| + if (overlay) {
|
| + if (Polymer.IronA11yKeysBehavior.keyboardEventMatchesKeys(event, 'esc')) {
|
| + overlay._onCaptureEsc(event);
|
| + } else if (Polymer.IronA11yKeysBehavior.keyboardEventMatchesKeys(event, 'tab')) {
|
| + overlay._onCaptureTab(event);
|
| + }
|
| }
|
| }
|
| - return z;
|
| };
|
|
|
| Polymer.IronOverlayManager = new Polymer.IronOverlayManagerClass();
|
|
|