OLD | NEW |
(Empty) | |
| 1 <!-- |
| 2 Copyright (c) 2014 The Polymer Project Authors. All rights reserved. |
| 3 This code may only be used under the BSD style license found at http://polymer.g
ithub.io/LICENSE.txt |
| 4 The complete set of authors may be found at http://polymer.github.io/AUTHORS.txt |
| 5 The complete set of contributors may be found at http://polymer.github.io/CONTRI
BUTORS.txt |
| 6 Code distributed by Google as part of the polymer project is also |
| 7 subject to an additional IP rights grant found at http://polymer.github.io/PATEN
TS.txt |
| 8 --> |
| 9 |
| 10 <link rel="import" href="../polymer/polymer.html"> |
| 11 <link rel="import" href="../core-transition/core-transition.html"> |
| 12 <link rel="import" href="core-key-helper.html"> |
| 13 <link rel="import" href="core-overlay-layer.html"> |
| 14 |
| 15 <!-- |
| 16 The `core-overlay` element displays overlayed on top of other content. It starts |
| 17 out hidden and is displayed by setting its `opened` property to true. |
| 18 A `core-overlay's` opened state can be toggled by calling the `toggle` |
| 19 method. |
| 20 |
| 21 The `core-overlay` will, by default, show/hide itself when it's opened. The |
| 22 `target` property may be set to another element to cause that element to |
| 23 be shown when the overlay is opened. |
| 24 |
| 25 It's common to want a `core-overlay` to animate to its opened |
| 26 position. The `core-overlay` element uses a `core-transition` to handle |
| 27 animation. The default transition is `core-transition-fade` which |
| 28 causes the overlay to fade in when displayed. See |
| 29 <a href="../core-transition/">`core-transition`</a> for more |
| 30 information about customizing a `core-overlay's` opening animation. The |
| 31 `backdrop` property can be set to true to show a backdrop behind the overlay |
| 32 that will darken the rest of the window. |
| 33 |
| 34 An element that should close the `core-overlay` will automatically |
| 35 do so if it's given the `core-overlay-toggle` attribute. This attribute |
| 36 can be customized with the `closeAttribute` property. You can also use |
| 37 `closeSelector` if more general matching is needed. |
| 38 |
| 39 By default `core-overlay` will close whenever the user taps outside it or |
| 40 presses the escape key. This behavior can be turned off via the |
| 41 `autoCloseDisabled` property. |
| 42 |
| 43 <core-overlay> |
| 44 <h2>Dialog</h2> |
| 45 <input placeholder="say something..." autofocus> |
| 46 <div>I agree with this wholeheartedly.</div> |
| 47 <button core-overlay-toggle>OK</button> |
| 48 </core-overlay> |
| 49 |
| 50 `core-overlay` will automatically size and position itself according to the |
| 51 following rules. If the target's style.top and style.left are unset, the |
| 52 target will be centered. The size of the target is constrained to be no larger |
| 53 than the window dimensions. The `margin` property specifies the extra amount |
| 54 of space that should be reserved around the overlay. This can be used to ensure |
| 55 that, for example, a drop shadow is always visible around the overlay. |
| 56 |
| 57 @group Core Elements |
| 58 @element core-overlay |
| 59 @homepage github.io |
| 60 --> |
| 61 <!-- |
| 62 Fired when the `core-overlay`'s `opened` property changes. |
| 63 |
| 64 @event core-overlay-open |
| 65 @param {Object} detail |
| 66 @param {Object} detail.opened the opened state |
| 67 --> |
| 68 |
| 69 <style> |
| 70 .core-overlay-backdrop { |
| 71 position: fixed; |
| 72 top: 0; |
| 73 left: 0; |
| 74 width: 100vw; |
| 75 height: 100vh; |
| 76 background-color: black; |
| 77 opacity: 0; |
| 78 transition: opacity 0.2s; |
| 79 } |
| 80 |
| 81 .core-overlay-backdrop.core-opened { |
| 82 opacity: 0.6; |
| 83 } |
| 84 </style> |
| 85 |
| 86 <polymer-element name="core-overlay"> |
| 87 <script> |
| 88 (function() { |
| 89 |
| 90 Polymer('core-overlay', { |
| 91 |
| 92 publish: { |
| 93 /** |
| 94 * The target element that will be shown when the overlay is |
| 95 * opened. If unspecified, the core-overlay itself is the target. |
| 96 * |
| 97 * @attribute target |
| 98 * @type Object |
| 99 * @default the overlay element |
| 100 */ |
| 101 target: null, |
| 102 |
| 103 |
| 104 /** |
| 105 * A `core-overlay`'s size is guaranteed to be |
| 106 * constrained to the window size. To achieve this, the sizingElement |
| 107 * is sized with a max-height/width. By default this element is the |
| 108 * target element, but it can be specifically set to a specific element |
| 109 * inside the target if that is more appropriate. This is useful, for |
| 110 * example, when a region inside the overlay should scroll if needed. |
| 111 * |
| 112 * @attribute sizingTarget |
| 113 * @type Object |
| 114 * @default the target element |
| 115 */ |
| 116 sizingTarget: null, |
| 117 |
| 118 /** |
| 119 * Set opened to true to show an overlay and to false to hide it. |
| 120 * A `core-overlay` may be made initially opened by setting its |
| 121 * `opened` attribute. |
| 122 * @attribute opened |
| 123 * @type boolean |
| 124 * @default false |
| 125 */ |
| 126 opened: false, |
| 127 |
| 128 /** |
| 129 * If true, the overlay has a backdrop darkening the rest of the screen. |
| 130 * The backdrop element is attached to the document body and may be styled |
| 131 * with the class `core-overlay-backdrop`. When opened the `core-opened` |
| 132 * class is applied. |
| 133 * |
| 134 * @attribute backdrop |
| 135 * @type boolean |
| 136 * @default false |
| 137 */ |
| 138 backdrop: false, |
| 139 |
| 140 /** |
| 141 * If true, the overlay is guaranteed to display above page content. |
| 142 * |
| 143 * @attribute layered |
| 144 * @type boolean |
| 145 * @default false |
| 146 */ |
| 147 layered: false, |
| 148 |
| 149 /** |
| 150 * By default an overlay will close automatically if the user |
| 151 * taps outside it or presses the escape key. Disable this |
| 152 * behavior by setting the `autoCloseDisabled` property to true. |
| 153 * @attribute autoCloseDisabled |
| 154 * @type boolean |
| 155 * @default false |
| 156 */ |
| 157 autoCloseDisabled: false, |
| 158 |
| 159 /** |
| 160 * This property specifies an attribute on elements that should |
| 161 * close the overlay on tap. Should not set `closeSelector` if this |
| 162 * is set. |
| 163 * |
| 164 * @attribute closeAttribute |
| 165 * @type string |
| 166 * @default "core-overlay-toggle" |
| 167 */ |
| 168 closeAttribute: 'core-overlay-toggle', |
| 169 |
| 170 /** |
| 171 * This property specifies a selector matching elements that should |
| 172 * close the overlay on tap. Should not set `closeAttribute` if this |
| 173 * is set. |
| 174 * |
| 175 * @attribute closeSelector |
| 176 * @type string |
| 177 * @default "" |
| 178 */ |
| 179 closeSelector: '', |
| 180 |
| 181 /** |
| 182 * A `core-overlay` target's size is constrained to the window size. |
| 183 * The `margin` property specifies a pixel amount around the overlay |
| 184 * that will be reserved. It's useful for ensuring that, for example, |
| 185 * a shadow displayed outside the target will always be visible. |
| 186 * |
| 187 * @attribute margin |
| 188 * @type number |
| 189 * @default 0 |
| 190 */ |
| 191 margin: 0, |
| 192 |
| 193 /** |
| 194 * The transition property specifies a string which identifies a |
| 195 * <a href="../core-transition/">`core-transition`</a> element that |
| 196 * will be used to help the overlay open and close. The default |
| 197 * `core-transition-fade` will cause the overlay to fade in and out. |
| 198 * |
| 199 * @attribute transition |
| 200 * @type string |
| 201 * @default 'core-transition-fade' |
| 202 */ |
| 203 transition: 'core-transition-fade' |
| 204 |
| 205 }, |
| 206 |
| 207 captureEventName: 'tap', |
| 208 targetListeners: { |
| 209 'tap': 'tapHandler', |
| 210 'keydown': 'keydownHandler', |
| 211 'core-transitionend': 'transitionend' |
| 212 }, |
| 213 |
| 214 registerCallback: function(element) { |
| 215 this.layer = document.createElement('core-overlay-layer'); |
| 216 this.keyHelper = document.createElement('core-key-helper'); |
| 217 this.meta = document.createElement('core-transition'); |
| 218 this.scrim = document.createElement('div'); |
| 219 this.scrim.className = 'core-overlay-backdrop'; |
| 220 }, |
| 221 |
| 222 ready: function() { |
| 223 this.target = this.target || this; |
| 224 // flush to ensure styles are installed before paint |
| 225 Platform.flush(); |
| 226 }, |
| 227 |
| 228 /** |
| 229 * Toggle the opened state of the overlay. |
| 230 * @method toggle |
| 231 */ |
| 232 toggle: function() { |
| 233 this.opened = !this.opened; |
| 234 }, |
| 235 |
| 236 /** |
| 237 * Open the overlay. This is equivalent to setting the `opened` |
| 238 * property to true. |
| 239 * @method open |
| 240 */ |
| 241 open: function() { |
| 242 this.opened = true; |
| 243 }, |
| 244 |
| 245 /** |
| 246 * Close the overlay. This is equivalent to setting the `opened` |
| 247 * property to false. |
| 248 * @method close |
| 249 */ |
| 250 close: function() { |
| 251 this.opened = false; |
| 252 }, |
| 253 |
| 254 domReady: function() { |
| 255 this.ensureTargetSetup(); |
| 256 }, |
| 257 |
| 258 targetChanged: function(old) { |
| 259 if (this.target) { |
| 260 // really make sure tabIndex is set |
| 261 if (this.target.tabIndex < 0) { |
| 262 this.target.tabIndex = -1; |
| 263 } |
| 264 this.addElementListenerList(this.target, this.targetListeners); |
| 265 this.target.style.display = 'none'; |
| 266 } |
| 267 if (old) { |
| 268 this.removeElementListenerList(old, this.targetListeners); |
| 269 var transition = this.getTransition(); |
| 270 if (transition) { |
| 271 transition.teardown(old); |
| 272 } else { |
| 273 old.style.position = ''; |
| 274 old.style.outline = ''; |
| 275 } |
| 276 old.style.display = ''; |
| 277 } |
| 278 }, |
| 279 |
| 280 // NOTE: wait to call this until we're as sure as possible that target |
| 281 // is styled. |
| 282 ensureTargetSetup: function() { |
| 283 if (!this.target || this.target.__overlaySetup) { |
| 284 return; |
| 285 } |
| 286 this.target.__overlaySetup = true; |
| 287 this.target.style.display = ''; |
| 288 var transition = this.getTransition(); |
| 289 if (transition) { |
| 290 transition.setup(this.target); |
| 291 } |
| 292 var computed = getComputedStyle(this.target); |
| 293 this.targetStyle = { |
| 294 position: computed.position === 'static' ? 'fixed' : |
| 295 computed.position |
| 296 } |
| 297 if (!transition) { |
| 298 this.target.style.position = this.targetStyle.position; |
| 299 this.target.style.outline = 'none'; |
| 300 } |
| 301 this.target.style.display = 'none'; |
| 302 }, |
| 303 |
| 304 openedChanged: function() { |
| 305 this.transitioning = true; |
| 306 this.ensureTargetSetup(); |
| 307 this.prepareRenderOpened(); |
| 308 // continue styling after delay so display state can change |
| 309 // without aborting transitions |
| 310 // note: we wait a full frame so that transition changes executed |
| 311 // during measuring do not cause transition |
| 312 this.async(function() { |
| 313 this.target.style.display = ''; |
| 314 this.async('renderOpened'); |
| 315 }); |
| 316 this.fire('core-overlay-open', this.opened); |
| 317 }, |
| 318 |
| 319 // tasks which must occur before opening; e.g. making the element visible |
| 320 prepareRenderOpened: function() { |
| 321 if (this.opened) { |
| 322 addOverlay(this); |
| 323 } |
| 324 this.prepareBackdrop(); |
| 325 // async so we don't auto-close immediately via a click. |
| 326 this.async(function() { |
| 327 if (!this.autoCloseDisabled) { |
| 328 this.enableElementListener(this.opened, document, |
| 329 this.captureEventName, 'captureHandler', true); |
| 330 } |
| 331 }); |
| 332 this.enableElementListener(this.opened, window, 'resize', |
| 333 'resizeHandler'); |
| 334 |
| 335 if (this.opened) { |
| 336 // TODO(sorvell): force SD Polyfill to render |
| 337 forcePolyfillRender(this.target); |
| 338 if (!this._shouldPosition) { |
| 339 this.target.style.position = 'absolute'; |
| 340 var computed = getComputedStyle(this.target); |
| 341 var t = (computed.top === 'auto' && computed.bottom === 'auto'); |
| 342 var l = (computed.left === 'auto' && computed.right === 'auto'); |
| 343 this.target.style.position = this.targetStyle.position; |
| 344 this._shouldPosition = {top: t, left: l}; |
| 345 } |
| 346 // if we are showing, then take care when measuring |
| 347 this.prepareMeasure(this.target); |
| 348 this.updateTargetDimensions(); |
| 349 this.finishMeasure(this.target); |
| 350 if (this.layered) { |
| 351 this.layer.addElement(this.target); |
| 352 this.layer.opened = this.opened; |
| 353 } |
| 354 } |
| 355 }, |
| 356 |
| 357 // tasks which cause the overlay to actually open; typically play an |
| 358 // animation |
| 359 renderOpened: function() { |
| 360 var transition = this.getTransition(); |
| 361 if (transition) { |
| 362 transition.go(this.target, {opened: this.opened}); |
| 363 } else { |
| 364 this.transitionend(); |
| 365 } |
| 366 this.renderBackdropOpened(); |
| 367 }, |
| 368 |
| 369 // finishing tasks; typically called via a transition |
| 370 transitionend: function(e) { |
| 371 // make sure this is our transition event. |
| 372 if (e && e.target !== this.target) { |
| 373 return; |
| 374 } |
| 375 this.transitioning = false; |
| 376 if (!this.opened) { |
| 377 this.resetTargetDimensions(); |
| 378 this.target.style.display = 'none'; |
| 379 this.completeBackdrop(); |
| 380 removeOverlay(this); |
| 381 if (this.layered) { |
| 382 if (!currentOverlay()) { |
| 383 this.layer.opened = this.opened; |
| 384 } |
| 385 this.layer.removeElement(this.target); |
| 386 } |
| 387 } |
| 388 this.applyFocus(); |
| 389 }, |
| 390 |
| 391 prepareBackdrop: function() { |
| 392 if (this.backdrop && this.opened) { |
| 393 if (!this.scrim.parentNode) { |
| 394 document.body.appendChild(this.scrim); |
| 395 this.scrim.style.zIndex = currentOverlayZ() - 1; |
| 396 } |
| 397 trackBackdrop(this); |
| 398 } |
| 399 }, |
| 400 |
| 401 renderBackdropOpened: function() { |
| 402 if (this.backdrop && getBackdrops().length < 2) { |
| 403 this.scrim.classList.toggle('core-opened', this.opened); |
| 404 } |
| 405 }, |
| 406 |
| 407 completeBackdrop: function() { |
| 408 if (this.backdrop) { |
| 409 trackBackdrop(this); |
| 410 if (getBackdrops().length === 0) { |
| 411 this.scrim.parentNode.removeChild(this.scrim); |
| 412 } |
| 413 } |
| 414 }, |
| 415 |
| 416 prepareMeasure: function(target) { |
| 417 target.style.transition = target.style.webkitTransition = 'none'; |
| 418 target.style.transform = target.style.webkitTransform = 'none'; |
| 419 target.style.display = ''; |
| 420 }, |
| 421 |
| 422 finishMeasure: function(target) { |
| 423 target.style.display = 'none'; |
| 424 target.style.transform = target.style.webkitTransform = ''; |
| 425 target.style.transition = target.style.webkitTransition = ''; |
| 426 }, |
| 427 |
| 428 getTransition: function() { |
| 429 return this.meta.byId(this.transition); |
| 430 }, |
| 431 |
| 432 getFocusNode: function() { |
| 433 return this.target.querySelector('[autofocus]') || this.target; |
| 434 }, |
| 435 |
| 436 applyFocus: function() { |
| 437 var focusNode = this.getFocusNode(); |
| 438 if (this.opened) { |
| 439 focusNode.focus(); |
| 440 } else { |
| 441 focusNode.blur(); |
| 442 if (currentOverlay() == this) { |
| 443 console.warn('Current core-overlay is attempting to focus itself as ne
xt! (bug)'); |
| 444 } else { |
| 445 focusOverlay(); |
| 446 } |
| 447 } |
| 448 }, |
| 449 |
| 450 updateTargetDimensions: function() { |
| 451 this.positionTarget(); |
| 452 this.sizeTarget(); |
| 453 // |
| 454 if (this.layered) { |
| 455 var rect = this.target.getBoundingClientRect(); |
| 456 this.target.style.top = rect.top + 'px'; |
| 457 this.target.style.left = rect.left + 'px'; |
| 458 this.target.style.right = this.target.style.bottom = 'auto'; |
| 459 } |
| 460 }, |
| 461 |
| 462 sizeTarget: function() { |
| 463 var sizer = this.sizingTarget || this.target; |
| 464 var rect = sizer.getBoundingClientRect(); |
| 465 var mt = rect.top === this.margin ? this.margin : this.margin * 2; |
| 466 var ml = rect.left === this.margin ? this.margin : this.margin * 2; |
| 467 var h = window.innerHeight - rect.top - mt; |
| 468 var w = window.innerWidth - rect.left - ml; |
| 469 sizer.style.maxHeight = h + 'px'; |
| 470 sizer.style.maxWidth = w + 'px'; |
| 471 sizer.style.boxSizing = 'border-box'; |
| 472 }, |
| 473 |
| 474 positionTarget: function() { |
| 475 // vertically and horizontally center if not positioned |
| 476 if (this._shouldPosition.top) { |
| 477 var t = Math.max((window.innerHeight - |
| 478 this.target.offsetHeight - this.margin*2) / 2, this.margin); |
| 479 this.target.style.top = t + 'px'; |
| 480 } |
| 481 if (this._shouldPosition.left) { |
| 482 var l = Math.max((window.innerWidth - |
| 483 this.target.offsetWidth - this.margin*2) / 2, this.margin); |
| 484 this.target.style.left = l + 'px'; |
| 485 } |
| 486 }, |
| 487 |
| 488 resetTargetDimensions: function() { |
| 489 this.target.style.top = this.target.style.left = ''; |
| 490 this.target.style.right = this.target.style.bottom = ''; |
| 491 this.target.style.width = this.target.style.height = ''; |
| 492 this._shouldPosition = null; |
| 493 }, |
| 494 |
| 495 tapHandler: function(e) { |
| 496 // closeSelector takes precedence since closeAttribute has a default non-n
ull value. |
| 497 if (e.target && |
| 498 (this.closeSelector && e.target.matches(this.closeSelector)) || |
| 499 (this.closeAttribute && e.target.hasAttribute(this.closeAttribute))) { |
| 500 this.toggle(); |
| 501 } else { |
| 502 if (this.autoCloseJob) { |
| 503 this.autoCloseJob.stop(); |
| 504 this.autoCloseJob = null; |
| 505 } |
| 506 } |
| 507 }, |
| 508 |
| 509 // We use the traditional approach of capturing events on document |
| 510 // to to determine if the overlay needs to close. However, due to |
| 511 // ShadowDOM event retargeting, the event target is not useful. Instead |
| 512 // of using it, we attempt to close asynchronously and prevent the close |
| 513 // if a tap event is immediately heard on the target. |
| 514 // TODO(sorvell): This approach will not work with modal. For |
| 515 // this we need a scrim. |
| 516 captureHandler: function(e) { |
| 517 if (!this.autoCloseDisabled && (currentOverlay() == this)) { |
| 518 this.autoCloseJob = this.job(this.autoCloseJob, function() { |
| 519 this.close(); |
| 520 }); |
| 521 } |
| 522 }, |
| 523 |
| 524 keydownHandler: function(e) { |
| 525 if (!this.autoCloseDisabled && (e.keyCode == this.keyHelper.ESCAPE_KEY)) { |
| 526 this.close(); |
| 527 e.stopPropagation(); |
| 528 } |
| 529 }, |
| 530 |
| 531 /** |
| 532 * Extensions of core-overlay should implement the `resizeHandler` |
| 533 * method to adjust the size and position of the overlay when the |
| 534 * browser window resizes. |
| 535 * @method resizeHandler |
| 536 */ |
| 537 resizeHandler: function() { |
| 538 this.updateTargetDimensions(); |
| 539 }, |
| 540 |
| 541 // TODO(sorvell): these utility methods should not be here. |
| 542 addElementListenerList: function(node, events) { |
| 543 for (var i in events) { |
| 544 this.addElementListener(node, i, events[i]); |
| 545 } |
| 546 }, |
| 547 |
| 548 removeElementListenerList: function(node, events) { |
| 549 for (var i in events) { |
| 550 this.removeElementListener(node, i, events[i]); |
| 551 } |
| 552 }, |
| 553 |
| 554 enableElementListener: function(enable, node, event, methodName, capture) { |
| 555 if (enable) { |
| 556 this.addElementListener(node, event, methodName, capture); |
| 557 } else { |
| 558 this.removeElementListener(node, event, methodName, capture); |
| 559 } |
| 560 }, |
| 561 |
| 562 addElementListener: function(node, event, methodName, capture) { |
| 563 var fn = this._makeBoundListener(methodName); |
| 564 if (node && fn) { |
| 565 Polymer.addEventListener(node, event, fn, capture); |
| 566 } |
| 567 }, |
| 568 |
| 569 removeElementListener: function(node, event, methodName, capture) { |
| 570 var fn = this._makeBoundListener(methodName); |
| 571 if (node && fn) { |
| 572 Polymer.removeEventListener(node, event, fn, capture); |
| 573 } |
| 574 }, |
| 575 |
| 576 _makeBoundListener: function(methodName) { |
| 577 var self = this, method = this[methodName]; |
| 578 if (!method) { |
| 579 return; |
| 580 } |
| 581 var bound = '_bound' + methodName; |
| 582 if (!this[bound]) { |
| 583 this[bound] = function(e) { |
| 584 method.call(self, e); |
| 585 } |
| 586 } |
| 587 return this[bound]; |
| 588 }, |
| 589 }); |
| 590 |
| 591 function forcePolyfillRender(target) { |
| 592 if (window.ShadowDOMPolyfill) { |
| 593 target.offsetHeight; |
| 594 } |
| 595 } |
| 596 |
| 597 // TODO(sorvell): This should be an element with private state so it can |
| 598 // be independent of overlay. |
| 599 // track overlays for z-index and focus managemant |
| 600 var overlays = []; |
| 601 function addOverlay(overlay) { |
| 602 var z0 = currentOverlayZ(); |
| 603 overlays.push(overlay); |
| 604 var z1 = currentOverlayZ(); |
| 605 if (z1 <= z0) { |
| 606 applyOverlayZ(overlay, z0); |
| 607 } |
| 608 } |
| 609 |
| 610 function removeOverlay(overlay) { |
| 611 var i = overlays.indexOf(overlay); |
| 612 if (i >= 0) { |
| 613 overlays.splice(i, 1); |
| 614 setZ(overlay, ''); |
| 615 } |
| 616 } |
| 617 |
| 618 function applyOverlayZ(overlay, aboveZ) { |
| 619 setZ(overlay.target, aboveZ + 2); |
| 620 } |
| 621 |
| 622 function setZ(element, z) { |
| 623 element.style.zIndex = z; |
| 624 } |
| 625 |
| 626 function currentOverlay() { |
| 627 return overlays[overlays.length-1]; |
| 628 } |
| 629 |
| 630 var DEFAULT_Z = 10; |
| 631 |
| 632 function currentOverlayZ() { |
| 633 var z; |
| 634 var current = currentOverlay(); |
| 635 if (current) { |
| 636 var z1 = window.getComputedStyle(current.target).zIndex; |
| 637 if (!isNaN(z1)) { |
| 638 z = Number(z1); |
| 639 } |
| 640 } |
| 641 return z || DEFAULT_Z; |
| 642 } |
| 643 |
| 644 function focusOverlay() { |
| 645 var current = currentOverlay(); |
| 646 // We have to be careful to focus the next overlay _after_ any current |
| 647 // transitions are complete (due to the state being toggled prior to the |
| 648 // transition). Otherwise, we risk infinite recursion when a transitioning |
| 649 // (closed) overlay becomes the current overlay. |
| 650 // |
| 651 // NOTE: We make the assumption that any overlay that completes a transition |
| 652 // will call into focusOverlay to kick the process back off. Currently: |
| 653 // transitionend -> applyFocus -> focusOverlay. |
| 654 if (current && !current.transitioning) { |
| 655 current.applyFocus(); |
| 656 } |
| 657 } |
| 658 |
| 659 var backdrops = []; |
| 660 function trackBackdrop(element) { |
| 661 if (element.opened) { |
| 662 backdrops.push(element); |
| 663 } else { |
| 664 var i = backdrops.indexOf(element); |
| 665 if (i >= 0) { |
| 666 backdrops.splice(i, 1); |
| 667 } |
| 668 } |
| 669 } |
| 670 |
| 671 function getBackdrops() { |
| 672 return backdrops; |
| 673 } |
| 674 })(); |
| 675 </script> |
| 676 </polymer-element> |
OLD | NEW |