| Index: third_party/polymer/v1_0/components-chromium/iron-overlay-behavior/iron-overlay-behavior-extracted.js
|
| diff --git a/third_party/polymer/v1_0/components-chromium/iron-overlay-behavior/iron-overlay-behavior-extracted.js b/third_party/polymer/v1_0/components-chromium/iron-overlay-behavior/iron-overlay-behavior-extracted.js
|
| index ee78f3484a259cb71cb4185ff719b1d9a3b4ccef..d3fddcba28261837e79065bf42c0fd86daee09e6 100644
|
| --- a/third_party/polymer/v1_0/components-chromium/iron-overlay-behavior/iron-overlay-behavior-extracted.js
|
| +++ b/third_party/polymer/v1_0/components-chromium/iron-overlay-behavior/iron-overlay-behavior-extracted.js
|
| @@ -100,6 +100,23 @@ context. You should place this element as a child of `<body>` whenever possible.
|
| type: Object
|
| },
|
|
|
| + /**
|
| + * The HTMLElement that will be firing relevant KeyboardEvents.
|
| + * Used for capturing esc and tab. Overridden from `IronA11yKeysBehavior`.
|
| + */
|
| + keyEventTarget: {
|
| + type: Object,
|
| + value: document
|
| + },
|
| +
|
| + /**
|
| + * Set to true to enable restoring of focus when overlay is closed.
|
| + */
|
| + restoreFocusOnClose: {
|
| + type: Boolean,
|
| + value: false
|
| + },
|
| +
|
| _manager: {
|
| type: Object,
|
| value: Polymer.IronOverlayManager
|
| @@ -112,13 +129,6 @@ context. You should place this element as a child of `<body>` whenever possible.
|
| }
|
| },
|
|
|
| - _boundOnCaptureKeydown: {
|
| - type: Function,
|
| - value: function() {
|
| - return this._onCaptureKeydown.bind(this);
|
| - }
|
| - },
|
| -
|
| _boundOnCaptureFocus: {
|
| type: Function,
|
| value: function() {
|
| @@ -126,44 +136,113 @@ context. You should place this element as a child of `<body>` whenever possible.
|
| }
|
| },
|
|
|
| - /** @type {?Node} */
|
| + /**
|
| + * The node being focused.
|
| + * @type {?Node}
|
| + */
|
| _focusedChild: {
|
| type: Object
|
| }
|
|
|
| },
|
|
|
| + keyBindings: {
|
| + 'esc': '__onEsc',
|
| + 'tab': '__onTab'
|
| + },
|
| +
|
| listeners: {
|
| 'iron-resize': '_onIronResize'
|
| },
|
|
|
| /**
|
| * The backdrop element.
|
| - * @type Node
|
| + * @type {Node}
|
| */
|
| get backdropElement() {
|
| return this._manager.backdropElement;
|
| },
|
|
|
| + /**
|
| + * Returns the node to give focus to.
|
| + * @type {Node}
|
| + */
|
| get _focusNode() {
|
| return this._focusedChild || Polymer.dom(this).querySelector('[autofocus]') || this;
|
| },
|
|
|
| + /**
|
| + * Array of nodes that can receive focus (overlay included), ordered by `tabindex`.
|
| + * This is used to retrieve which is the first and last focusable nodes in order
|
| + * to wrap the focus for overlays `with-backdrop`.
|
| + *
|
| + * If you know what is your content (specifically the first and last focusable children),
|
| + * you can override this method to return only `[firstFocusable, lastFocusable];`
|
| + * @type {[Node]}
|
| + * @protected
|
| + */
|
| + get _focusableNodes() {
|
| + // Elements that can be focused even if they have [disabled] attribute.
|
| + var FOCUSABLE_WITH_DISABLED = [
|
| + 'a[href]',
|
| + 'area[href]',
|
| + 'iframe',
|
| + '[tabindex]',
|
| + '[contentEditable=true]'
|
| + ];
|
| +
|
| + // Elements that cannot be focused if they have [disabled] attribute.
|
| + var FOCUSABLE_WITHOUT_DISABLED = [
|
| + 'input',
|
| + 'select',
|
| + 'textarea',
|
| + 'button'
|
| + ];
|
| +
|
| + // Discard elements with tabindex=-1 (makes them not focusable).
|
| + var selector = FOCUSABLE_WITH_DISABLED.join(':not([tabindex="-1"]),') +
|
| + ':not([tabindex="-1"]),' +
|
| + FOCUSABLE_WITHOUT_DISABLED.join(':not([disabled]):not([tabindex="-1"]),') +
|
| + ':not([disabled]):not([tabindex="-1"])';
|
| +
|
| + var focusables = Polymer.dom(this).querySelectorAll(selector);
|
| + if (this.tabIndex >= 0) {
|
| + // Insert at the beginning because we might have all elements with tabIndex = 0,
|
| + // and the overlay should be the first of the list.
|
| + focusables.splice(0, 0, this);
|
| + }
|
| + // Sort by tabindex.
|
| + return focusables.sort(function (a, b) {
|
| + if (a.tabIndex === b.tabIndex) {
|
| + return 0;
|
| + }
|
| + if (a.tabIndex === 0 || a.tabIndex > b.tabIndex) {
|
| + return 1;
|
| + }
|
| + return -1;
|
| + });
|
| + },
|
| +
|
| ready: function() {
|
| - // with-backdrop need tabindex to be set in order to trap the focus.
|
| + // with-backdrop needs tabindex to be set in order to trap the focus.
|
| // If it is not set, IronOverlayBehavior will set it, and remove it if with-backdrop = false.
|
| this.__shouldRemoveTabIndex = false;
|
| + // Used for wrapping the focus on TAB / Shift+TAB.
|
| + this.__firstFocusableNode = this.__lastFocusableNode = null;
|
| this._ensureSetup();
|
| },
|
|
|
| attached: function() {
|
| // Call _openedChanged here so that position can be computed correctly.
|
| - if (this._callOpenedWhenReady) {
|
| + if (this.opened) {
|
| this._openedChanged();
|
| }
|
| + this._observer = Polymer.dom(this).observeNodes(this._onNodesChange);
|
| },
|
|
|
| detached: function() {
|
| + Polymer.dom(this).unobserveNodes(this._observer);
|
| + this._observer = null;
|
| this.opened = false;
|
| this._manager.trackBackdrop(this);
|
| this._manager.removeOverlay(this);
|
| @@ -195,9 +274,10 @@ context. You should place this element as a child of `<body>` whenever possible.
|
|
|
| /**
|
| * Cancels the overlay.
|
| + * @param {?Event} event The original event
|
| */
|
| - cancel: function() {
|
| - var cancelEvent = this.fire('iron-overlay-canceled', undefined, {cancelable: true});
|
| + cancel: function(event) {
|
| + var cancelEvent = this.fire('iron-overlay-canceled', event, {cancelable: true});
|
| if (cancelEvent.defaultPrevented) {
|
| return;
|
| }
|
| @@ -220,12 +300,10 @@ context. You should place this element as a child of `<body>` whenever possible.
|
| this.removeAttribute('aria-hidden');
|
| } else {
|
| this.setAttribute('aria-hidden', 'true');
|
| - Polymer.dom(this).unobserveNodes(this._observer);
|
| }
|
|
|
| // wait to call after ready only if we're initially open
|
| if (!this._overlaySetup) {
|
| - this._callOpenedWhenReady = this.opened;
|
| return;
|
| }
|
|
|
| @@ -300,16 +378,18 @@ context. You should place this element as a child of `<body>` whenever possible.
|
| }
|
| },
|
|
|
| - _toggleListeners: function () {
|
| + _toggleListeners: function() {
|
| this._toggleListener(this.opened, document, 'tap', this._boundOnCaptureClick, true);
|
| - this._toggleListener(this.opened, document, 'keydown', this._boundOnCaptureKeydown, true);
|
| this._toggleListener(this.opened, document, 'focus', this._boundOnCaptureFocus, true);
|
| },
|
|
|
| // tasks which must occur before opening; e.g. making the element visible
|
| _prepareRenderOpened: function() {
|
| +
|
| this._manager.addOverlay(this);
|
|
|
| + // Needed to calculate the size of the overlay so that transitions on its size
|
| + // will have the correct starting points.
|
| this._preparePositioning();
|
| this.fit();
|
| this._finishPositioning();
|
| @@ -317,6 +397,12 @@ context. You should place this element as a child of `<body>` whenever possible.
|
| if (this.withBackdrop) {
|
| this.backdropElement.prepare();
|
| }
|
| +
|
| + // Safari will apply the focus to the autofocus element when displayed for the first time,
|
| + // so we blur it. Later, _applyFocus will set the focus if necessary.
|
| + if (this.noAutoFocus && document.activeElement === this._focusNode) {
|
| + this._focusNode.blur();
|
| + }
|
| },
|
|
|
| // tasks which cause the overlay to actually open; typically play an
|
| @@ -336,23 +422,24 @@ context. You should place this element as a child of `<body>` whenever possible.
|
| },
|
|
|
| _finishRenderOpened: function() {
|
| - // focus the child node with [autofocus]
|
| + // This ensures the overlay is visible before we set the focus
|
| + // (by calling _onIronResize -> refit).
|
| + this.notifyResize();
|
| + // Focus the child node with [autofocus]
|
| this._applyFocus();
|
|
|
| - this._observer = Polymer.dom(this).observeNodes(this.notifyResize);
|
| this.fire('iron-overlay-opened');
|
| },
|
|
|
| _finishRenderClosed: function() {
|
| - // hide the overlay and remove the backdrop
|
| + // Hide the overlay and remove the backdrop.
|
| this.resetFit();
|
| this.style.display = 'none';
|
| this._manager.removeOverlay(this);
|
|
|
| - this._focusedChild = null;
|
| this._applyFocus();
|
| -
|
| this.notifyResize();
|
| +
|
| this.fire('iron-overlay-closed', this.closingReason);
|
| },
|
|
|
| @@ -365,8 +452,9 @@ context. You should place this element as a child of `<body>` whenever possible.
|
| _finishPositioning: function() {
|
| this.style.display = 'none';
|
| this.style.transform = this.style.webkitTransform = '';
|
| - // force layout to avoid application of transform
|
| - /** @suppress {suspiciousCode} */ this.offsetWidth;
|
| + // Force layout layout to avoid application of transform.
|
| + // Set offsetWidth to itself so that compilers won't remove it.
|
| + this.offsetWidth = this.offsetWidth;
|
| this.style.transition = this.style.webkitTransition = '';
|
| },
|
|
|
| @@ -377,6 +465,7 @@ context. You should place this element as a child of `<body>` whenever possible.
|
| }
|
| } else {
|
| this._focusNode.blur();
|
| + this._focusedChild = null;
|
| this._manager.focusOverlay();
|
| }
|
| },
|
| @@ -387,23 +476,13 @@ context. You should place this element as a child of `<body>` whenever possible.
|
| if (this.noCancelOnOutsideClick) {
|
| this._applyFocus();
|
| } else {
|
| - this.cancel();
|
| + this.cancel(event);
|
| }
|
| }
|
| },
|
|
|
| - _onCaptureKeydown: function(event) {
|
| - var ESC = 27;
|
| - if (this._manager.currentOverlay() === this &&
|
| - !this.noCancelOnEscKey &&
|
| - event.keyCode === ESC) {
|
| - this.cancel();
|
| - }
|
| - },
|
| -
|
| _onCaptureFocus: function (event) {
|
| - if (this._manager.currentOverlay() === this &&
|
| - this.withBackdrop) {
|
| + if (this._manager.currentOverlay() === this && this.withBackdrop) {
|
| var path = Polymer.dom(event).path;
|
| if (path.indexOf(this) === -1) {
|
| event.stopPropagation();
|
| @@ -418,25 +497,70 @@ context. You should place this element as a child of `<body>` whenever possible.
|
| if (this.opened) {
|
| this.refit();
|
| }
|
| - }
|
| + },
|
|
|
| -/**
|
| - * Fired after the `iron-overlay` opens.
|
| - * @event iron-overlay-opened
|
| - */
|
| + /**
|
| + * @protected
|
| + * Will call notifyResize if overlay is opened.
|
| + * Can be overridden in order to avoid multiple observers on the same node.
|
| + */
|
| + _onNodesChange: function() {
|
| + if (this.opened) {
|
| + this.notifyResize();
|
| + }
|
| + // Store it so we don't query too much.
|
| + var focusableNodes = this._focusableNodes;
|
| + this.__firstFocusableNode = focusableNodes[0];
|
| + this.__lastFocusableNode = focusableNodes[focusableNodes.length - 1];
|
| + },
|
|
|
| -/**
|
| - * Fired when the `iron-overlay` is canceled, but before it is closed.
|
| - * Cancel the event to prevent the `iron-overlay` from closing.
|
| - * @event iron-overlay-canceled
|
| - */
|
| + __onEsc: function(event) {
|
| + // Not opened or not on top, so return.
|
| + if (this._manager.currentOverlay() !== this) {
|
| + return;
|
| + }
|
| + if (!this.noCancelOnEscKey) {
|
| + this.cancel(event);
|
| + }
|
| + },
|
|
|
| -/**
|
| - * Fired after the `iron-overlay` closes.
|
| - * @event iron-overlay-closed
|
| - * @param {{canceled: (boolean|undefined)}} set to the `closingReason` attribute
|
| - */
|
| + __onTab: function(event) {
|
| + // Not opened or not on top, so return.
|
| + if (this._manager.currentOverlay() !== this) {
|
| + return;
|
| + }
|
| + // TAB wraps from last to first focusable.
|
| + // Shift + TAB wraps from first to last focusable.
|
| + var shift = event.detail.keyboardEvent.shiftKey;
|
| + var nodeToCheck = shift ? this.__firstFocusableNode : this.__lastFocusableNode;
|
| + var nodeToSet = shift ? this.__lastFocusableNode : this.__firstFocusableNode;
|
| + if (this.withBackdrop && this._focusedChild === nodeToCheck) {
|
| + // We set here the _focusedChild so that _onCaptureFocus will handle the
|
| + // wrapping of the focus (the next event after tab is focus).
|
| + this._focusedChild = nodeToSet;
|
| + }
|
| + }
|
| };
|
|
|
| /** @polymerBehavior */
|
| - Polymer.IronOverlayBehavior = [Polymer.IronFitBehavior, Polymer.IronResizableBehavior, Polymer.IronOverlayBehaviorImpl];
|
| + Polymer.IronOverlayBehavior = [Polymer.IronA11yKeysBehavior, Polymer.IronFitBehavior, Polymer.IronResizableBehavior, Polymer.IronOverlayBehaviorImpl];
|
| +
|
| + /**
|
| + * Fired after the `iron-overlay` opens.
|
| + * @event iron-overlay-opened
|
| + */
|
| +
|
| + /**
|
| + * Fired when the `iron-overlay` is canceled, but before it is closed.
|
| + * Cancel the event to prevent the `iron-overlay` from closing.
|
| + * @event iron-overlay-canceled
|
| + * @param {Event} event The closing of the `iron-overlay` can be prevented
|
| + * by calling `event.preventDefault()`. The `event.detail` is the original event that originated
|
| + * the canceling (e.g. ESC keyboard event or click event outside the `iron-overlay`).
|
| + */
|
| +
|
| + /**
|
| + * Fired after the `iron-overlay` closes.
|
| + * @event iron-overlay-closed
|
| + * @param {{canceled: (boolean|undefined)}} closingReason Contains `canceled` (whether the overlay was canceled).
|
| + */
|
|
|