Service workers: Allow HTTPS pages arrived at via HTTP redirect to use SW
[chromium-blink-merge.git] / third_party / polymer / v1_0 / components-chromium / iron-overlay-behavior / iron-overlay-behavior-extracted.js
blobc73c13171d5f90577cf654a2dac79cb904d83dd0
1 /**
2 Use `Polymer.IronOverlayBehavior` to implement an element that can be hidden or shown, and displays
3 on top of other content. It includes an optional backdrop, and can be used to implement a variety
4 of UI controls including dialogs and drop downs. Multiple overlays may be displayed at once.
6 ### Closing and canceling
8 A dialog may be hidden by closing or canceling. The difference between close and cancel is user
9 intent. Closing generally implies that the user acknowledged the content on the overlay. By default,
10 it will cancel whenever the user taps outside it or presses the escape key. This behavior is
11 configurable with the `no-cancel-on-esc-key` and the `no-cancel-on-outside-click` properties.
12 `close()` should be called explicitly by the implementer when the user interacts with a control
13 in the overlay element.
15 ### Positioning
17 By default the element is sized and positioned to fit and centered inside the window. You can
18 position and size it manually using CSS. See `Polymer.IronFitBehavior`.
20 ### Backdrop
22 Set the `with-backdrop` attribute to display a backdrop behind the overlay. The backdrop is
23 appended to `<body>` and is of type `<iron-overlay-backdrop>`. See its doc page for styling
24 options.
26 ### Limitations
28 The element is styled to appear on top of other content by setting its `z-index` property. You
29 must ensure no element has a stacking context with a higher `z-index` than its parent stacking
30 context. You should place this element as a child of `<body>` whenever possible.
32 @demo demo/index.html
33 @polymerBehavior Polymer.IronOverlayBehavior
36   Polymer.IronOverlayBehaviorImpl = {
38     properties: {
40       /**
41        * True if the overlay is currently displayed.
42        */
43       opened: {
44         observer: '_openedChanged',
45         type: Boolean,
46         value: false,
47         notify: true
48       },
50       /**
51        * True if the overlay was canceled when it was last closed.
52        */
53       canceled: {
54         observer: '_canceledChanged',
55         readOnly: true,
56         type: Boolean,
57         value: false
58       },
60       /**
61        * Set to true to display a backdrop behind the overlay.
62        */
63       withBackdrop: {
64         type: Boolean,
65         value: false
66       },
68       /**
69        * Set to true to disable auto-focusing the overlay or child nodes with
70        * the `autofocus` attribute` when the overlay is opened.
71        */
72       noAutoFocus: {
73         type: Boolean,
74         value: false
75       },
77       /**
78        * Set to true to disable canceling the overlay with the ESC key.
79        */
80       noCancelOnEscKey: {
81         type: Boolean,
82         value: false
83       },
85       /**
86        * Set to true to disable canceling the overlay by clicking outside it.
87        */
88       noCancelOnOutsideClick: {
89         type: Boolean,
90         value: false
91       },
93       /**
94        * Returns the reason this dialog was last closed.
95        */
96       closingReason: {
97         // was a getter before, but needs to be a property so other
98         // behaviors can override this.
99         type: Object
100       },
102       _manager: {
103         type: Object,
104         value: Polymer.IronOverlayManager
105       },
107       _boundOnCaptureClick: {
108         type: Function,
109         value: function() {
110           return this._onCaptureClick.bind(this);
111         }
112       },
114       _boundOnCaptureKeydown: {
115         type: Function,
116         value: function() {
117           return this._onCaptureKeydown.bind(this);
118         }
119       }
121     },
124  * Fired after the `iron-overlay` opens.
125  * @event iron-overlay-opened
126  */
129  * Fired after the `iron-overlay` closes.
130  * @event iron-overlay-closed {{canceled: boolean}} detail -
131  *     canceled: True if the overlay was canceled.
132  */
134     listeners: {
135       'click': '_onClick',
136       'iron-resize': '_onIronResize'
137     },
139     /**
140      * The backdrop element.
141      * @type Node
142      */
143     get backdropElement() {
144       return this._backdrop;
145     },
147     get _focusNode() {
148       return Polymer.dom(this).querySelector('[autofocus]') || this;
149     },
151     registered: function() {
152       this._backdrop = document.createElement('iron-overlay-backdrop');
153     },
155     ready: function() {
156       this._ensureSetup();
157       if (this._callOpenedWhenReady) {
158         this._openedChanged();
159       }
160     },
162     detached: function() {
163       this.opened = false;
164       this._completeBackdrop();
165       this._manager.removeOverlay(this);
166     },
168     /**
169      * Toggle the opened state of the overlay.
170      */
171     toggle: function() {
172       this.opened = !this.opened;
173     },
175     /**
176      * Open the overlay.
177      */
178     open: function() {
179       this.opened = true;
180       this.closingReason = {canceled: false};
181     },
183     /**
184      * Close the overlay.
185      */
186     close: function() {
187       this.opened = false;
188       this._setCanceled(false);
189     },
191     /**
192      * Cancels the overlay.
193      */
194     cancel: function() {
195       this.opened = false,
196       this._setCanceled(true);
197     },
199     _ensureSetup: function() {
200       if (this._overlaySetup) {
201         return;
202       }
203       this._overlaySetup = true;
204       this.style.outline = 'none';
205       this.style.display = 'none';
206     },
208     _openedChanged: function() {
209       if (this.opened) {
210         this.removeAttribute('aria-hidden');
211       } else {
212         this.setAttribute('aria-hidden', 'true');
213       }
215       // wait to call after ready only if we're initially open
216       if (!this._overlaySetup) {
217         this._callOpenedWhenReady = this.opened;
218         return;
219       }
220       if (this._openChangedAsync) {
221         this.cancelAsync(this._openChangedAsync);
222       }
224       this._toggleListeners();
226       if (this.opened) {
227         this._prepareRenderOpened();
228       }
230       // async here to allow overlay layer to become visible.
231       this._openChangedAsync = this.async(function() {
232         // overlay becomes visible here
233         this.style.display = '';
234         // force layout to ensure transitions will go
235         this.offsetWidth;
236         if (this.opened) {
237           this._renderOpened();
238         } else {
239           this._renderClosed();
240         }
241         this._openChangedAsync = null;
242       });
244     },
246     _canceledChanged: function() {
247       this.closingReason = this.closingReason || {};
248       this.closingReason.canceled = this.canceled;
249     },
251     _toggleListener: function(enable, node, event, boundListener, capture) {
252       if (enable) {
253         node.addEventListener(event, boundListener, capture);
254       } else {
255         node.removeEventListener(event, boundListener, capture);
256       }
257     },
259     _toggleListeners: function() {
260       if (this._toggleListenersAsync) {
261         this.cancelAsync(this._toggleListenersAsync);
262       }
263       // async so we don't auto-close immediately via a click.
264       this._toggleListenersAsync = this.async(function() {
265         this._toggleListener(this.opened, document, 'click', this._boundOnCaptureClick, true);
266         this._toggleListener(this.opened, document, 'keydown', this._boundOnCaptureKeydown, true);
267         this._toggleListenersAsync = null;
268       });
269     },
271     // tasks which must occur before opening; e.g. making the element visible
272     _prepareRenderOpened: function() {
273       this._manager.addOverlay(this);
275       if (this.withBackdrop) {
276         this.backdropElement.prepare();
277         this._manager.trackBackdrop(this);
278       }
280       this._preparePositioning();
281       this.fit();
282       this._finishPositioning();
283     },
285     // tasks which cause the overlay to actually open; typically play an
286     // animation
287     _renderOpened: function() {
288       if (this.withBackdrop) {
289         this.backdropElement.open();
290       }
291       this._finishRenderOpened();
292     },
294     _renderClosed: function() {
295       if (this.withBackdrop) {
296         this.backdropElement.close();
297       }
298       this._finishRenderClosed();
299     },
301     _onTransitionend: function(event) {
302       // make sure this is our transition event.
303       if (event && event.target !== this) {
304         return;
305       }
306       if (this.opened) {
307         this._finishRenderOpened();
308       } else {
309         this._finishRenderClosed();
310       }
311     },
313     _finishRenderOpened: function() {
314       // focus the child node with [autofocus]
315       if (!this.noAutoFocus) {
316         this._focusNode.focus();
317       }
319       this.fire('iron-overlay-opened');
321       this._squelchNextResize = true;
322       this.async(this.notifyResize);
323     },
325     _finishRenderClosed: function() {
326       // hide the overlay and remove the backdrop
327       this.resetFit();
328       this.style.display = 'none';
329       this._completeBackdrop();
330       this._manager.removeOverlay(this);
332       this._focusNode.blur();
333       // focus the next overlay, if there is one
334       this._manager.focusOverlay();
336       this.fire('iron-overlay-closed', this.closingReason);
338       this._squelchNextResize = true;
339       this.async(this.notifyResize);
340     },
342     _completeBackdrop: function() {
343       if (this.withBackdrop) {
344         this._manager.trackBackdrop(this);
345         this.backdropElement.complete();
346       }
347     },
349     _preparePositioning: function() {
350       this.style.transition = this.style.webkitTransition = 'none';
351       this.style.transform = this.style.webkitTransform = 'none';
352       this.style.display = '';
353     },
355     _finishPositioning: function() {
356       this.style.display = 'none';
357       this.style.transform = this.style.webkitTransform = '';
358       // force layout to avoid application of transform
359       this.offsetWidth;
360       this.style.transition = this.style.webkitTransition = '';
361     },
363     _applyFocus: function() {
364       if (this.opened) {
365         if (!this.noAutoFocus) {
366           this._focusNode.focus();
367         }
368       } else {
369         this._focusNode.blur();
370         this._manager.focusOverlay();
371       }
372     },
374     _onCaptureClick: function(event) {
375       // attempt to close asynchronously and prevent the close of a tap event is immediately heard
376       // on target. This is because in shadow dom due to event retargetting event.target is not
377       // useful.
378       if (!this.noCancelOnOutsideClick && (this._manager.currentOverlay() == this)) {
379         this._cancelJob = this.async(function() {
380           this.cancel();
381         }, 10);
382       }
383     },
385     _onClick: function(event) {
386       if (this._cancelJob) {
387         this.cancelAsync(this._cancelJob);
388         this._cancelJob = null;
389       }
390     },
392     _onCaptureKeydown: function(event) {
393       var ESC = 27;
394       if (!this.noCancelOnEscKey && (event.keyCode === ESC)) {
395         this.cancel();
396         event.stopPropagation();
397       }
398     },
400     _onIronResize: function() {
401       if (this._squelchNextResize) {
402         this._squelchNextResize = false;
403         return;
404       }
405       if (this.opened) {
406         this.refit();
407       }
408     }
410   };
412   /** @polymerBehavior */
413   Polymer.IronOverlayBehavior = [Polymer.IronFitBehavior, Polymer.IronResizableBehavior, Polymer.IronOverlayBehaviorImpl];