| OLD | NEW |
| 1 // IIFE to help scripts concatenation. |
| 2 (function() { |
| 3 'use strict'; |
| 4 |
| 1 /** | 5 /** |
| 2 Use `Polymer.IronOverlayBehavior` to implement an element that can be hidden or
shown, and displays | 6 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 im
plement a variety | 7 on top of other content. It includes an optional backdrop, and can be used to im
plement a variety |
| 4 of UI controls including dialogs and drop downs. Multiple overlays may be displa
yed at once. | 8 of UI controls including dialogs and drop downs. Multiple overlays may be displa
yed at once. |
| 5 | 9 |
| 6 ### Closing and canceling | 10 ### Closing and canceling |
| 7 | 11 |
| 8 A dialog may be hidden by closing or canceling. The difference between close and
cancel is user | 12 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, | 13 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 | 14 it will cancel whenever the user taps outside it or presses the escape key. This
behavior is |
| (...skipping 83 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 94 /** | 98 /** |
| 95 * Returns the reason this dialog was last closed. | 99 * Returns the reason this dialog was last closed. |
| 96 */ | 100 */ |
| 97 closingReason: { | 101 closingReason: { |
| 98 // was a getter before, but needs to be a property so other | 102 // was a getter before, but needs to be a property so other |
| 99 // behaviors can override this. | 103 // behaviors can override this. |
| 100 type: Object | 104 type: Object |
| 101 }, | 105 }, |
| 102 | 106 |
| 103 /** | 107 /** |
| 104 * The HTMLElement that will be firing relevant KeyboardEvents. | |
| 105 * Used for capturing esc and tab. Overridden from `IronA11yKeysBehavior`. | |
| 106 */ | |
| 107 keyEventTarget: { | |
| 108 type: Object, | |
| 109 value: document | |
| 110 }, | |
| 111 | |
| 112 /** | |
| 113 * Set to true to enable restoring of focus when overlay is closed. | 108 * Set to true to enable restoring of focus when overlay is closed. |
| 114 */ | 109 */ |
| 115 restoreFocusOnClose: { | 110 restoreFocusOnClose: { |
| 116 type: Boolean, | 111 type: Boolean, |
| 117 value: false | 112 value: false |
| 118 }, | 113 }, |
| 119 | 114 |
| 115 /** |
| 116 * Set to true to keep overlay always on top. |
| 117 */ |
| 118 alwaysOnTop: { |
| 119 type: Boolean |
| 120 }, |
| 121 |
| 122 /** |
| 123 * Shortcut to access to the overlay manager. |
| 124 * @private |
| 125 * @type {Polymer.IronOverlayManagerClass} |
| 126 */ |
| 120 _manager: { | 127 _manager: { |
| 121 type: Object, | 128 type: Object, |
| 122 value: Polymer.IronOverlayManager | 129 value: Polymer.IronOverlayManager |
| 123 }, | 130 }, |
| 124 | 131 |
| 125 _boundOnCaptureClick: { | |
| 126 type: Function, | |
| 127 value: function() { | |
| 128 return this._onCaptureClick.bind(this); | |
| 129 } | |
| 130 }, | |
| 131 | |
| 132 _boundOnCaptureFocus: { | |
| 133 type: Function, | |
| 134 value: function() { | |
| 135 return this._onCaptureFocus.bind(this); | |
| 136 } | |
| 137 }, | |
| 138 | |
| 139 /** | 132 /** |
| 140 * The node being focused. | 133 * The node being focused. |
| 141 * @type {?Node} | 134 * @type {?Node} |
| 142 */ | 135 */ |
| 143 _focusedChild: { | 136 _focusedChild: { |
| 144 type: Object | 137 type: Object |
| 145 } | 138 } |
| 146 | 139 |
| 147 }, | 140 }, |
| 148 | 141 |
| 149 keyBindings: { | |
| 150 'esc': '__onEsc', | |
| 151 'tab': '__onTab' | |
| 152 }, | |
| 153 | |
| 154 listeners: { | 142 listeners: { |
| 155 'iron-resize': '_onIronResize' | 143 'iron-resize': '_onIronResize' |
| 156 }, | 144 }, |
| 157 | 145 |
| 158 /** | 146 /** |
| 159 * The backdrop element. | 147 * The backdrop element. |
| 160 * @type {Node} | 148 * @type {Element} |
| 161 */ | 149 */ |
| 162 get backdropElement() { | 150 get backdropElement() { |
| 163 return this._manager.backdropElement; | 151 return this._manager.backdropElement; |
| 164 }, | 152 }, |
| 165 | 153 |
| 166 /** | 154 /** |
| 167 * Returns the node to give focus to. | 155 * Returns the node to give focus to. |
| 168 * @type {Node} | 156 * @type {Node} |
| 169 */ | 157 */ |
| 170 get _focusNode() { | 158 get _focusNode() { |
| 171 return this._focusedChild || Polymer.dom(this).querySelector('[autofocus]'
) || this; | 159 return this._focusedChild || Polymer.dom(this).querySelector('[autofocus]'
) || this; |
| 172 }, | 160 }, |
| 173 | 161 |
| 174 /** | 162 /** |
| 175 * Array of nodes that can receive focus (overlay included), ordered by `tab
index`. | 163 * Array of nodes that can receive focus (overlay included), ordered by `tab
index`. |
| 176 * This is used to retrieve which is the first and last focusable nodes in o
rder | 164 * This is used to retrieve which is the first and last focusable nodes in o
rder |
| 177 * to wrap the focus for overlays `with-backdrop`. | 165 * to wrap the focus for overlays `with-backdrop`. |
| 178 * | 166 * |
| 179 * If you know what is your content (specifically the first and last focusab
le children), | 167 * If you know what is your content (specifically the first and last focusab
le children), |
| 180 * you can override this method to return only `[firstFocusable, lastFocusab
le];` | 168 * you can override this method to return only `[firstFocusable, lastFocusab
le];` |
| 181 * @type {[Node]} | 169 * @type {Array<Node>} |
| 182 * @protected | 170 * @protected |
| 183 */ | 171 */ |
| 184 get _focusableNodes() { | 172 get _focusableNodes() { |
| 185 // Elements that can be focused even if they have [disabled] attribute. | 173 // Elements that can be focused even if they have [disabled] attribute. |
| 186 var FOCUSABLE_WITH_DISABLED = [ | 174 var FOCUSABLE_WITH_DISABLED = [ |
| 187 'a[href]', | 175 'a[href]', |
| 188 'area[href]', | 176 'area[href]', |
| 189 'iframe', | 177 'iframe', |
| 190 '[tabindex]', | 178 '[tabindex]', |
| 191 '[contentEditable=true]' | 179 '[contentEditable=true]' |
| (...skipping 25 matching lines...) Expand all Loading... |
| 217 return 0; | 205 return 0; |
| 218 } | 206 } |
| 219 if (a.tabIndex === 0 || a.tabIndex > b.tabIndex) { | 207 if (a.tabIndex === 0 || a.tabIndex > b.tabIndex) { |
| 220 return 1; | 208 return 1; |
| 221 } | 209 } |
| 222 return -1; | 210 return -1; |
| 223 }); | 211 }); |
| 224 }, | 212 }, |
| 225 | 213 |
| 226 ready: function() { | 214 ready: function() { |
| 215 // Used to skip calls to notifyResize and refit while the overlay is anima
ting. |
| 216 this.__isAnimating = false; |
| 227 // with-backdrop needs tabindex to be set in order to trap the focus. | 217 // with-backdrop needs tabindex to be set in order to trap the focus. |
| 228 // If it is not set, IronOverlayBehavior will set it, and remove it if wit
h-backdrop = false. | 218 // If it is not set, IronOverlayBehavior will set it, and remove it if wit
h-backdrop = false. |
| 229 this.__shouldRemoveTabIndex = false; | 219 this.__shouldRemoveTabIndex = false; |
| 230 // Used for wrapping the focus on TAB / Shift+TAB. | 220 // Used for wrapping the focus on TAB / Shift+TAB. |
| 231 this.__firstFocusableNode = this.__lastFocusableNode = null; | 221 this.__firstFocusableNode = this.__lastFocusableNode = null; |
| 222 // Used for requestAnimationFrame when opened changes. |
| 223 this.__openChangedAsync = null; |
| 224 // Used for requestAnimationFrame when iron-resize is fired. |
| 225 this.__onIronResizeAsync = null; |
| 232 this._ensureSetup(); | 226 this._ensureSetup(); |
| 233 }, | 227 }, |
| 234 | 228 |
| 235 attached: function() { | 229 attached: function() { |
| 236 // Call _openedChanged here so that position can be computed correctly. | 230 // Call _openedChanged here so that position can be computed correctly. |
| 237 if (this.opened) { | 231 if (this.opened) { |
| 238 this._openedChanged(); | 232 this._openedChanged(); |
| 239 } | 233 } |
| 240 this._observer = Polymer.dom(this).observeNodes(this._onNodesChange); | 234 this._observer = Polymer.dom(this).observeNodes(this._onNodesChange); |
| 241 }, | 235 }, |
| 242 | 236 |
| 243 detached: function() { | 237 detached: function() { |
| 244 Polymer.dom(this).unobserveNodes(this._observer); | 238 Polymer.dom(this).unobserveNodes(this._observer); |
| 245 this._observer = null; | 239 this._observer = null; |
| 246 this.opened = false; | 240 this.opened = false; |
| 247 this._manager.trackBackdrop(this); | 241 if (this.withBackdrop) { |
| 248 this._manager.removeOverlay(this); | 242 // Allow user interactions right away. |
| 243 this.backdropElement.close(); |
| 244 } |
| 249 }, | 245 }, |
| 250 | 246 |
| 251 /** | 247 /** |
| 252 * Toggle the opened state of the overlay. | 248 * Toggle the opened state of the overlay. |
| 253 */ | 249 */ |
| 254 toggle: function() { | 250 toggle: function() { |
| 255 this._setCanceled(false); | 251 this._setCanceled(false); |
| 256 this.opened = !this.opened; | 252 this.opened = !this.opened; |
| 257 }, | 253 }, |
| 258 | 254 |
| 259 /** | 255 /** |
| 260 * Open the overlay. | 256 * Open the overlay. |
| 261 */ | 257 */ |
| 262 open: function() { | 258 open: function() { |
| 263 this._setCanceled(false); | 259 this._setCanceled(false); |
| 264 this.opened = true; | 260 this.opened = true; |
| 265 }, | 261 }, |
| 266 | 262 |
| 267 /** | 263 /** |
| 268 * Close the overlay. | 264 * Close the overlay. |
| 269 */ | 265 */ |
| 270 close: function() { | 266 close: function() { |
| 271 this._setCanceled(false); | 267 this._setCanceled(false); |
| 272 this.opened = false; | 268 this.opened = false; |
| 273 }, | 269 }, |
| 274 | 270 |
| 275 /** | 271 /** |
| 276 * Cancels the overlay. | 272 * Cancels the overlay. |
| 277 * @param {?Event} event The original event | 273 * @param {Event=} event The original event |
| 278 */ | 274 */ |
| 279 cancel: function(event) { | 275 cancel: function(event) { |
| 280 var cancelEvent = this.fire('iron-overlay-canceled', event, {cancelable: t
rue}); | 276 var cancelEvent = this.fire('iron-overlay-canceled', event, {cancelable: t
rue}); |
| 281 if (cancelEvent.defaultPrevented) { | 277 if (cancelEvent.defaultPrevented) { |
| 282 return; | 278 return; |
| 283 } | 279 } |
| 284 | 280 |
| 285 this._setCanceled(true); | 281 this._setCanceled(true); |
| 286 this.opened = false; | 282 this.opened = false; |
| 287 }, | 283 }, |
| (...skipping 12 matching lines...) Expand all Loading... |
| 300 this.removeAttribute('aria-hidden'); | 296 this.removeAttribute('aria-hidden'); |
| 301 } else { | 297 } else { |
| 302 this.setAttribute('aria-hidden', 'true'); | 298 this.setAttribute('aria-hidden', 'true'); |
| 303 } | 299 } |
| 304 | 300 |
| 305 // wait to call after ready only if we're initially open | 301 // wait to call after ready only if we're initially open |
| 306 if (!this._overlaySetup) { | 302 if (!this._overlaySetup) { |
| 307 return; | 303 return; |
| 308 } | 304 } |
| 309 | 305 |
| 310 this._manager.trackBackdrop(this); | 306 this._manager.addOrRemoveOverlay(this); |
| 311 | 307 |
| 308 this.__isAnimating = true; |
| 309 |
| 310 // requestAnimationFrame for non-blocking rendering |
| 311 if (this.__openChangedAsync) { |
| 312 cancelAnimationFrame(this.__openChangedAsync); |
| 313 } |
| 312 if (this.opened) { | 314 if (this.opened) { |
| 313 this._prepareRenderOpened(); | 315 if (this.withBackdrop) { |
| 316 this.backdropElement.prepare(); |
| 317 } |
| 318 this.__openChangedAsync = requestAnimationFrame(function() { |
| 319 this.__openChangedAsync = null; |
| 320 this._prepareRenderOpened(); |
| 321 this._renderOpened(); |
| 322 }.bind(this)); |
| 323 } else { |
| 324 this._renderClosed(); |
| 314 } | 325 } |
| 315 | |
| 316 if (this._openChangedAsync) { | |
| 317 this.cancelAsync(this._openChangedAsync); | |
| 318 } | |
| 319 // Async here to allow overlay layer to become visible, and to avoid | |
| 320 // listeners to immediately close via a click. | |
| 321 this._openChangedAsync = this.async(function() { | |
| 322 // overlay becomes visible here | |
| 323 this.style.display = ''; | |
| 324 // Force layout to ensure transition will go. Set offsetWidth to itself | |
| 325 // so that compilers won't remove it. | |
| 326 this.offsetWidth = this.offsetWidth; | |
| 327 if (this.opened) { | |
| 328 this._renderOpened(); | |
| 329 } else { | |
| 330 this._renderClosed(); | |
| 331 } | |
| 332 this._toggleListeners(); | |
| 333 this._openChangedAsync = null; | |
| 334 }, 1); | |
| 335 }, | 326 }, |
| 336 | 327 |
| 337 _canceledChanged: function() { | 328 _canceledChanged: function() { |
| 338 this.closingReason = this.closingReason || {}; | 329 this.closingReason = this.closingReason || {}; |
| 339 this.closingReason.canceled = this.canceled; | 330 this.closingReason.canceled = this.canceled; |
| 340 }, | 331 }, |
| 341 | 332 |
| 342 _withBackdropChanged: function() { | 333 _withBackdropChanged: function() { |
| 343 // If tabindex is already set, no need to override it. | 334 // If tabindex is already set, no need to override it. |
| 344 if (this.withBackdrop && !this.hasAttribute('tabindex')) { | 335 if (this.withBackdrop && !this.hasAttribute('tabindex')) { |
| 345 this.setAttribute('tabindex', '-1'); | 336 this.setAttribute('tabindex', '-1'); |
| 346 this.__shouldRemoveTabIndex = true; | 337 this.__shouldRemoveTabIndex = true; |
| 347 } else if (this.__shouldRemoveTabIndex) { | 338 } else if (this.__shouldRemoveTabIndex) { |
| 348 this.removeAttribute('tabindex'); | 339 this.removeAttribute('tabindex'); |
| 349 this.__shouldRemoveTabIndex = false; | 340 this.__shouldRemoveTabIndex = false; |
| 350 } | 341 } |
| 351 if (this.opened) { | 342 if (this.opened) { |
| 352 this._manager.trackBackdrop(this); | 343 this._manager.trackBackdrop(); |
| 353 if (this.withBackdrop) { | 344 if (this.withBackdrop) { |
| 354 this.backdropElement.prepare(); | 345 this.backdropElement.prepare(); |
| 355 // Give time to be added to document. | 346 // Give time to be added to document. |
| 356 this.async(function(){ | 347 this.async(function(){ |
| 357 this.backdropElement.open(); | 348 this.backdropElement.open(); |
| 358 }, 1); | 349 }, 1); |
| 359 } else { | 350 } else { |
| 360 this.backdropElement.close(); | 351 this.backdropElement.close(); |
| 361 } | 352 } |
| 362 } | 353 } |
| 363 }, | 354 }, |
| 364 | 355 |
| 365 _toggleListener: function(enable, node, event, boundListener, capture) { | 356 /** |
| 366 if (enable) { | 357 * tasks which must occur before opening; e.g. making the element visible. |
| 367 // enable document-wide tap recognizer | 358 * @protected |
| 368 if (event === 'tap') { | 359 */ |
| 369 Polymer.Gestures.add(document, 'tap', null); | |
| 370 } | |
| 371 node.addEventListener(event, boundListener, capture); | |
| 372 } else { | |
| 373 // disable document-wide tap recognizer | |
| 374 if (event === 'tap') { | |
| 375 Polymer.Gestures.remove(document, 'tap', null); | |
| 376 } | |
| 377 node.removeEventListener(event, boundListener, capture); | |
| 378 } | |
| 379 }, | |
| 380 | |
| 381 _toggleListeners: function() { | |
| 382 this._toggleListener(this.opened, document, 'tap', this._boundOnCaptureCli
ck, true); | |
| 383 this._toggleListener(this.opened, document, 'focus', this._boundOnCaptureF
ocus, true); | |
| 384 }, | |
| 385 | |
| 386 // tasks which must occur before opening; e.g. making the element visible | |
| 387 _prepareRenderOpened: function() { | 360 _prepareRenderOpened: function() { |
| 388 | 361 |
| 389 this._manager.addOverlay(this); | |
| 390 | |
| 391 // Needed to calculate the size of the overlay so that transitions on its
size | 362 // Needed to calculate the size of the overlay so that transitions on its
size |
| 392 // will have the correct starting points. | 363 // will have the correct starting points. |
| 393 this._preparePositioning(); | 364 this._preparePositioning(); |
| 394 this.fit(); | 365 this.refit(); |
| 395 this._finishPositioning(); | 366 this._finishPositioning(); |
| 396 | 367 |
| 397 if (this.withBackdrop) { | |
| 398 this.backdropElement.prepare(); | |
| 399 } | |
| 400 | |
| 401 // Safari will apply the focus to the autofocus element when displayed for
the first time, | 368 // Safari will apply the focus to the autofocus element when displayed for
the first time, |
| 402 // so we blur it. Later, _applyFocus will set the focus if necessary. | 369 // so we blur it. Later, _applyFocus will set the focus if necessary. |
| 403 if (this.noAutoFocus && document.activeElement === this._focusNode) { | 370 if (this.noAutoFocus && document.activeElement === this._focusNode) { |
| 404 this._focusNode.blur(); | 371 this._focusNode.blur(); |
| 405 } | 372 } |
| 406 }, | 373 }, |
| 407 | 374 |
| 408 // tasks which cause the overlay to actually open; typically play an | 375 /** |
| 409 // animation | 376 * Tasks which cause the overlay to actually open; typically play an animati
on. |
| 377 * @protected |
| 378 */ |
| 410 _renderOpened: function() { | 379 _renderOpened: function() { |
| 411 if (this.withBackdrop) { | 380 if (this.withBackdrop) { |
| 412 this.backdropElement.open(); | 381 this.backdropElement.open(); |
| 413 } | 382 } |
| 414 this._finishRenderOpened(); | 383 this._finishRenderOpened(); |
| 415 }, | 384 }, |
| 416 | 385 |
| 386 /** |
| 387 * Tasks which cause the overlay to actually close; typically play an animat
ion. |
| 388 * @protected |
| 389 */ |
| 417 _renderClosed: function() { | 390 _renderClosed: function() { |
| 418 if (this.withBackdrop) { | 391 if (this.withBackdrop) { |
| 419 this.backdropElement.close(); | 392 this.backdropElement.close(); |
| 420 } | 393 } |
| 421 this._finishRenderClosed(); | 394 this._finishRenderClosed(); |
| 422 }, | 395 }, |
| 423 | 396 |
| 397 /** |
| 398 * Tasks to be performed at the end of open action. Will fire `iron-overlay-
opened`. |
| 399 * @protected |
| 400 */ |
| 424 _finishRenderOpened: function() { | 401 _finishRenderOpened: function() { |
| 425 // This ensures the overlay is visible before we set the focus | |
| 426 // (by calling _onIronResize -> refit). | |
| 427 this.notifyResize(); | |
| 428 // Focus the child node with [autofocus] | 402 // Focus the child node with [autofocus] |
| 429 this._applyFocus(); | 403 this._applyFocus(); |
| 430 | 404 |
| 405 this.notifyResize(); |
| 406 this.__isAnimating = false; |
| 431 this.fire('iron-overlay-opened'); | 407 this.fire('iron-overlay-opened'); |
| 432 }, | 408 }, |
| 433 | 409 |
| 410 /** |
| 411 * Tasks to be performed at the end of close action. Will fire `iron-overlay
-closed`. |
| 412 * @protected |
| 413 */ |
| 434 _finishRenderClosed: function() { | 414 _finishRenderClosed: function() { |
| 435 // Hide the overlay and remove the backdrop. | 415 // Hide the overlay and remove the backdrop. |
| 436 this.resetFit(); | |
| 437 this.style.display = 'none'; | 416 this.style.display = 'none'; |
| 438 this._manager.removeOverlay(this); | 417 // Reset z-index only at the end of the animation. |
| 418 this.style.zIndex = ''; |
| 439 | 419 |
| 440 this._applyFocus(); | 420 this._applyFocus(); |
| 421 |
| 441 this.notifyResize(); | 422 this.notifyResize(); |
| 442 | 423 this.__isAnimating = false; |
| 443 this.fire('iron-overlay-closed', this.closingReason); | 424 this.fire('iron-overlay-closed', this.closingReason); |
| 444 }, | 425 }, |
| 445 | 426 |
| 446 _preparePositioning: function() { | 427 _preparePositioning: function() { |
| 447 this.style.transition = this.style.webkitTransition = 'none'; | 428 this.style.transition = this.style.webkitTransition = 'none'; |
| 448 this.style.transform = this.style.webkitTransform = 'none'; | 429 this.style.transform = this.style.webkitTransform = 'none'; |
| 449 this.style.display = ''; | 430 this.style.display = ''; |
| 450 }, | 431 }, |
| 451 | 432 |
| 452 _finishPositioning: function() { | 433 _finishPositioning: function() { |
| 434 // First, make it invisible & reactivate animations. |
| 453 this.style.display = 'none'; | 435 this.style.display = 'none'; |
| 436 // Force reflow before re-enabling animations so that they don't start. |
| 437 // Set scrollTop to itself so that Closure Compiler doesn't remove this. |
| 438 this.scrollTop = this.scrollTop; |
| 439 this.style.transition = this.style.webkitTransition = ''; |
| 454 this.style.transform = this.style.webkitTransform = ''; | 440 this.style.transform = this.style.webkitTransform = ''; |
| 455 // Force layout layout to avoid application of transform. | 441 // Now that animations are enabled, make it visible again |
| 456 // Set offsetWidth to itself so that compilers won't remove it. | 442 this.style.display = ''; |
| 457 this.offsetWidth = this.offsetWidth; | 443 // Force reflow, so that following animations are properly started. |
| 458 this.style.transition = this.style.webkitTransition = ''; | 444 // Set scrollTop to itself so that Closure Compiler doesn't remove this. |
| 445 this.scrollTop = this.scrollTop; |
| 459 }, | 446 }, |
| 460 | 447 |
| 448 /** |
| 449 * Applies focus according to the opened state. |
| 450 * @protected |
| 451 */ |
| 461 _applyFocus: function() { | 452 _applyFocus: function() { |
| 462 if (this.opened) { | 453 if (this.opened) { |
| 463 if (!this.noAutoFocus) { | 454 if (!this.noAutoFocus) { |
| 464 this._focusNode.focus(); | 455 this._focusNode.focus(); |
| 465 } | 456 } |
| 466 } else { | 457 } else { |
| 467 this._focusNode.blur(); | 458 this._focusNode.blur(); |
| 468 this._focusedChild = null; | 459 this._focusedChild = null; |
| 469 this._manager.focusOverlay(); | 460 this._manager.focusOverlay(); |
| 470 } | 461 } |
| 471 }, | 462 }, |
| 472 | 463 |
| 473 _onCaptureClick: function(event) { | |
| 474 if (this._manager.currentOverlay() === this && | |
| 475 Polymer.dom(event).path.indexOf(this) === -1) { | |
| 476 if (this.noCancelOnOutsideClick) { | |
| 477 this._applyFocus(); | |
| 478 } else { | |
| 479 this.cancel(event); | |
| 480 } | |
| 481 } | |
| 482 }, | |
| 483 | |
| 484 _onCaptureFocus: function (event) { | |
| 485 if (this._manager.currentOverlay() === this && this.withBackdrop) { | |
| 486 var path = Polymer.dom(event).path; | |
| 487 if (path.indexOf(this) === -1) { | |
| 488 event.stopPropagation(); | |
| 489 this._applyFocus(); | |
| 490 } else { | |
| 491 this._focusedChild = path[0]; | |
| 492 } | |
| 493 } | |
| 494 }, | |
| 495 | |
| 496 _onIronResize: function() { | |
| 497 if (this.opened) { | |
| 498 this.refit(); | |
| 499 } | |
| 500 }, | |
| 501 | |
| 502 /** | 464 /** |
| 465 * Cancels (closes) the overlay. Call when click happens outside the overlay
. |
| 466 * @param {!Event} event |
| 503 * @protected | 467 * @protected |
| 504 * Will call notifyResize if overlay is opened. | |
| 505 * Can be overridden in order to avoid multiple observers on the same node. | |
| 506 */ | 468 */ |
| 507 _onNodesChange: function() { | 469 _onCaptureClick: function(event) { |
| 508 if (this.opened) { | 470 if (!this.noCancelOnOutsideClick) { |
| 509 this.notifyResize(); | 471 this.cancel(event); |
| 510 } | 472 } |
| 511 // Store it so we don't query too much. | |
| 512 var focusableNodes = this._focusableNodes; | |
| 513 this.__firstFocusableNode = focusableNodes[0]; | |
| 514 this.__lastFocusableNode = focusableNodes[focusableNodes.length - 1]; | |
| 515 }, | 473 }, |
| 516 | 474 |
| 517 __onEsc: function(event) { | 475 /** |
| 518 // Not opened or not on top, so return. | 476 * Keeps track of the focused child. If withBackdrop, traps focus within ove
rlay. |
| 519 if (this._manager.currentOverlay() !== this) { | 477 * @param {!Event} event |
| 478 * @protected |
| 479 */ |
| 480 _onCaptureFocus: function (event) { |
| 481 if (!this.withBackdrop) { |
| 520 return; | 482 return; |
| 521 } | 483 } |
| 484 var path = Polymer.dom(event).path; |
| 485 if (path.indexOf(this) === -1) { |
| 486 event.stopPropagation(); |
| 487 this._applyFocus(); |
| 488 } else { |
| 489 this._focusedChild = path[0]; |
| 490 } |
| 491 }, |
| 492 |
| 493 /** |
| 494 * Handles the ESC key event and cancels (closes) the overlay. |
| 495 * @param {!Event} event |
| 496 * @protected |
| 497 */ |
| 498 _onCaptureEsc: function(event) { |
| 522 if (!this.noCancelOnEscKey) { | 499 if (!this.noCancelOnEscKey) { |
| 523 this.cancel(event); | 500 this.cancel(event); |
| 524 } | 501 } |
| 525 }, | 502 }, |
| 526 | 503 |
| 527 __onTab: function(event) { | 504 /** |
| 528 // Not opened or not on top, so return. | 505 * Handles TAB key events to track focus changes. |
| 529 if (this._manager.currentOverlay() !== this) { | 506 * Will wrap focus for overlays withBackdrop. |
| 530 return; | 507 * @param {!Event} event |
| 531 } | 508 * @protected |
| 509 */ |
| 510 _onCaptureTab: function(event) { |
| 532 // TAB wraps from last to first focusable. | 511 // TAB wraps from last to first focusable. |
| 533 // Shift + TAB wraps from first to last focusable. | 512 // Shift + TAB wraps from first to last focusable. |
| 534 var shift = event.detail.keyboardEvent.shiftKey; | 513 var shift = event.shiftKey; |
| 535 var nodeToCheck = shift ? this.__firstFocusableNode : this.__lastFocusable
Node; | 514 var nodeToCheck = shift ? this.__firstFocusableNode : this.__lastFocusable
Node; |
| 536 var nodeToSet = shift ? this.__lastFocusableNode : this.__firstFocusableNo
de; | 515 var nodeToSet = shift ? this.__lastFocusableNode : this.__firstFocusableNo
de; |
| 537 if (this.withBackdrop && this._focusedChild === nodeToCheck) { | 516 if (this.withBackdrop && this._focusedChild === nodeToCheck) { |
| 538 // We set here the _focusedChild so that _onCaptureFocus will handle the | 517 // We set here the _focusedChild so that _onCaptureFocus will handle the |
| 539 // wrapping of the focus (the next event after tab is focus). | 518 // wrapping of the focus (the next event after tab is focus). |
| 540 this._focusedChild = nodeToSet; | 519 this._focusedChild = nodeToSet; |
| 541 } | 520 } |
| 521 }, |
| 522 |
| 523 /** |
| 524 * Refits if the overlay is opened and not animating. |
| 525 * @protected |
| 526 */ |
| 527 _onIronResize: function() { |
| 528 if (this.__onIronResizeAsync) { |
| 529 cancelAnimationFrame(this.__onIronResizeAsync); |
| 530 this.__onIronResizeAsync = null; |
| 531 } |
| 532 if (this.opened && !this.__isAnimating) { |
| 533 this.__onIronResizeAsync = requestAnimationFrame(function() { |
| 534 this.__onIronResizeAsync = null; |
| 535 this.refit(); |
| 536 }.bind(this)); |
| 537 } |
| 538 }, |
| 539 |
| 540 /** |
| 541 * Will call notifyResize if overlay is opened. |
| 542 * Can be overridden in order to avoid multiple observers on the same node. |
| 543 * @protected |
| 544 */ |
| 545 _onNodesChange: function() { |
| 546 if (this.opened && !this.__isAnimating) { |
| 547 this.notifyResize(); |
| 548 } |
| 549 // Store it so we don't query too much. |
| 550 var focusableNodes = this._focusableNodes; |
| 551 this.__firstFocusableNode = focusableNodes[0]; |
| 552 this.__lastFocusableNode = focusableNodes[focusableNodes.length - 1]; |
| 542 } | 553 } |
| 543 }; | 554 }; |
| 544 | 555 |
| 545 /** @polymerBehavior */ | 556 /** @polymerBehavior */ |
| 546 Polymer.IronOverlayBehavior = [Polymer.IronA11yKeysBehavior, Polymer.IronFitBe
havior, Polymer.IronResizableBehavior, Polymer.IronOverlayBehaviorImpl]; | 557 Polymer.IronOverlayBehavior = [Polymer.IronFitBehavior, Polymer.IronResizableB
ehavior, Polymer.IronOverlayBehaviorImpl]; |
| 547 | 558 |
| 548 /** | 559 /** |
| 549 * Fired after the `iron-overlay` opens. | 560 * Fired after the `iron-overlay` opens. |
| 550 * @event iron-overlay-opened | 561 * @event iron-overlay-opened |
| 551 */ | 562 */ |
| 552 | 563 |
| 553 /** | 564 /** |
| 554 * Fired when the `iron-overlay` is canceled, but before it is closed. | 565 * Fired when the `iron-overlay` is canceled, but before it is closed. |
| 555 * Cancel the event to prevent the `iron-overlay` from closing. | 566 * Cancel the event to prevent the `iron-overlay` from closing. |
| 556 * @event iron-overlay-canceled | 567 * @event iron-overlay-canceled |
| 557 * @param {Event} event The closing of the `iron-overlay` can be prevented | 568 * @param {Event} event The closing of the `iron-overlay` can be prevented |
| 558 * by calling `event.preventDefault()`. The `event.detail` is the original even
t that originated | 569 * by calling `event.preventDefault()`. The `event.detail` is the original even
t that originated |
| 559 * the canceling (e.g. ESC keyboard event or click event outside the `iron-over
lay`). | 570 * the canceling (e.g. ESC keyboard event or click event outside the `iron-over
lay`). |
| 560 */ | 571 */ |
| 561 | 572 |
| 562 /** | 573 /** |
| 563 * Fired after the `iron-overlay` closes. | 574 * Fired after the `iron-overlay` closes. |
| 564 * @event iron-overlay-closed | 575 * @event iron-overlay-closed |
| 565 * @param {{canceled: (boolean|undefined)}} closingReason Contains `canceled` (
whether the overlay was canceled). | 576 * @param {{canceled: (boolean|undefined)}} closingReason Contains `canceled` (
whether the overlay was canceled). |
| 566 */ | 577 */ |
| 578 |
| 579 })(); |
| OLD | NEW |