| 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. The overlay's size is constrained such that it does not |  | 
|   52 overflow the screen. This is done by setting maxHeight/maxWidth on the  |  | 
|   53 `sizingTarget`. If the `sizingTarget` already has a setting for one of these |  | 
|   54 properties, it will not be overridden. The overlay should |  | 
|   55 be positioned via css or imperatively using the `core-overlay-position` event. |  | 
|   56 If the overlay is not positioned vertically via setting `top` or `bottom`, it |  | 
|   57 will be centered vertically. The same is true horizontally via a setting to  |  | 
|   58 `left` or `right`. In addition, css `margin` can be used to provide some space |  | 
|   59 around the overlay. This can be used to ensure |  | 
|   60 that, for example, a drop shadow is always visible around the overlay. |  | 
|   61  |  | 
|   62 @group Core Elements |  | 
|   63 @element core-overlay |  | 
|   64 @homepage github.io |  | 
|   65 --> |  | 
|   66 <!-- |  | 
|   67 Fired when the `core-overlay`'s `opened` property changes. |  | 
|   68  |  | 
|   69 @event core-overlay-open |  | 
|   70 @param {Object} detail |  | 
|   71 @param {Object} detail.opened the opened state |  | 
|   72 --> |  | 
|   73 <!-- |  | 
|   74 Fired when the `core-overlay` has completely opened. |  | 
|   75  |  | 
|   76 @event core-overlay-open-completed |  | 
|   77 --> |  | 
|   78 <!-- |  | 
|   79 Fired when the `core-overlay` has completely closed. |  | 
|   80  |  | 
|   81 @event core-overlay-close-completed |  | 
|   82 --> |  | 
|   83 <!-- |  | 
|   84 Fired when the `core-overlay` needs to position itself. Optionally, implement |  | 
|   85 in order to position an overlay via code. If the overlay was not otherwise |  | 
|   86 positioned, it's important to indicate how the overlay has been positioned by |  | 
|   87 setting the `dimensions.position` object. For example, if the overlay has been |  | 
|   88 positioned via setting `right` and `top`, set dimensions.position to an |  | 
|   89 object like this: `{v: 'top', h: 'right'}`. |  | 
|   90  |  | 
|   91 @event core-overlay-position |  | 
|   92 @param {Object} detail |  | 
|   93 @param {Object} detail.target the overlay target |  | 
|   94 @param {Object} detail.sizingTarget the overlay sizing target |  | 
|   95 @param {Object} detail.opened the opened state |  | 
|   96 --> |  | 
|   97 <style> |  | 
|   98   .core-overlay-backdrop { |  | 
|   99     position: fixed; |  | 
|  100     top: 0; |  | 
|  101     left: 0; |  | 
|  102     width: 100vw; |  | 
|  103     height: 100vh; |  | 
|  104     background-color: black; |  | 
|  105     opacity: 0; |  | 
|  106     transition: opacity 0.2s; |  | 
|  107   } |  | 
|  108  |  | 
|  109   .core-overlay-backdrop.core-opened { |  | 
|  110     opacity: 0.6; |  | 
|  111   } |  | 
|  112 </style> |  | 
|  113  |  | 
|  114 <polymer-element name="core-overlay"> |  | 
|  115 <script> |  | 
|  116 (function() { |  | 
|  117  |  | 
|  118   Polymer('core-overlay', { |  | 
|  119  |  | 
|  120     publish: { |  | 
|  121       /** |  | 
|  122        * The target element that will be shown when the overlay is  |  | 
|  123        * opened. If unspecified, the core-overlay itself is the target. |  | 
|  124        * |  | 
|  125        * @attribute target |  | 
|  126        * @type Object |  | 
|  127        * @default the overlay element |  | 
|  128        */ |  | 
|  129       target: null, |  | 
|  130  |  | 
|  131  |  | 
|  132       /** |  | 
|  133        * A `core-overlay`'s size is guaranteed to be  |  | 
|  134        * constrained to the window size. To achieve this, the sizingElement |  | 
|  135        * is sized with a max-height/width. By default this element is the  |  | 
|  136        * target element, but it can be specifically set to a specific element |  | 
|  137        * inside the target if that is more appropriate. This is useful, for  |  | 
|  138        * example, when a region inside the overlay should scroll if needed. |  | 
|  139        * |  | 
|  140        * @attribute sizingTarget |  | 
|  141        * @type Object |  | 
|  142        * @default the target element |  | 
|  143        */ |  | 
|  144       sizingTarget: null, |  | 
|  145      |  | 
|  146       /** |  | 
|  147        * Set opened to true to show an overlay and to false to hide it. |  | 
|  148        * A `core-overlay` may be made initially opened by setting its |  | 
|  149        * `opened` attribute. |  | 
|  150        * @attribute opened |  | 
|  151        * @type boolean |  | 
|  152        * @default false |  | 
|  153        */ |  | 
|  154       opened: false, |  | 
|  155  |  | 
|  156       /** |  | 
|  157        * If true, the overlay has a backdrop darkening the rest of the screen. |  | 
|  158        * The backdrop element is attached to the document body and may be styled |  | 
|  159        * with the class `core-overlay-backdrop`. When opened the `core-opened` |  | 
|  160        * class is applied. |  | 
|  161        * |  | 
|  162        * @attribute backdrop |  | 
|  163        * @type boolean |  | 
|  164        * @default false |  | 
|  165        */     |  | 
|  166       backdrop: false, |  | 
|  167  |  | 
|  168       /** |  | 
|  169        * If true, the overlay is guaranteed to display above page content. |  | 
|  170        * |  | 
|  171        * @attribute layered |  | 
|  172        * @type boolean |  | 
|  173        * @default false |  | 
|  174       */ |  | 
|  175       layered: false, |  | 
|  176      |  | 
|  177       /** |  | 
|  178        * By default an overlay will close automatically if the user |  | 
|  179        * taps outside it or presses the escape key. Disable this |  | 
|  180        * behavior by setting the `autoCloseDisabled` property to true. |  | 
|  181        * @attribute autoCloseDisabled |  | 
|  182        * @type boolean |  | 
|  183        * @default false |  | 
|  184        */ |  | 
|  185       autoCloseDisabled: false, |  | 
|  186  |  | 
|  187       /** |  | 
|  188        * By default an overlay will focus its target or an element inside |  | 
|  189        * it with the `autoFocus` attribute. Disable this |  | 
|  190        * behavior by setting the `autoFocusDisabled` property to true. |  | 
|  191        * @attribute autoFocusDisabled |  | 
|  192        * @type boolean |  | 
|  193        * @default false |  | 
|  194        */ |  | 
|  195       autoFocusDisabled: false, |  | 
|  196  |  | 
|  197       /** |  | 
|  198        * This property specifies an attribute on elements that should |  | 
|  199        * close the overlay on tap. Should not set `closeSelector` if this |  | 
|  200        * is set. |  | 
|  201        * |  | 
|  202        * @attribute closeAttribute |  | 
|  203        * @type string |  | 
|  204        * @default "core-overlay-toggle" |  | 
|  205        */ |  | 
|  206       closeAttribute: 'core-overlay-toggle', |  | 
|  207  |  | 
|  208       /** |  | 
|  209        * This property specifies a selector matching elements that should |  | 
|  210        * close the overlay on tap. Should not set `closeAttribute` if this |  | 
|  211        * is set. |  | 
|  212        * |  | 
|  213        * @attribute closeSelector |  | 
|  214        * @type string |  | 
|  215        * @default "" |  | 
|  216        */ |  | 
|  217       closeSelector: '', |  | 
|  218  |  | 
|  219       /** |  | 
|  220        * The transition property specifies a string which identifies a  |  | 
|  221        * <a href="../core-transition/">`core-transition`</a> element that  |  | 
|  222        * will be used to help the overlay open and close. The default |  | 
|  223        * `core-transition-fade` will cause the overlay to fade in and out. |  | 
|  224        * |  | 
|  225        * @attribute transition |  | 
|  226        * @type string |  | 
|  227        * @default 'core-transition-fade' |  | 
|  228        */ |  | 
|  229       transition: 'core-transition-fade' |  | 
|  230  |  | 
|  231     }, |  | 
|  232  |  | 
|  233     captureEventName: 'tap', |  | 
|  234     targetListeners: { |  | 
|  235       'tap': 'tapHandler', |  | 
|  236       'keydown': 'keydownHandler', |  | 
|  237       'core-transitionend': 'transitionend' |  | 
|  238     }, |  | 
|  239      |  | 
|  240     registerCallback: function(element) { |  | 
|  241       this.layer = document.createElement('core-overlay-layer'); |  | 
|  242       this.keyHelper = document.createElement('core-key-helper'); |  | 
|  243       this.meta = document.createElement('core-transition'); |  | 
|  244       this.scrim = document.createElement('div'); |  | 
|  245       this.scrim.className = 'core-overlay-backdrop'; |  | 
|  246     }, |  | 
|  247  |  | 
|  248     ready: function() { |  | 
|  249       this.target = this.target || this; |  | 
|  250       // flush to ensure styles are installed before paint |  | 
|  251       Platform.flush(); |  | 
|  252     }, |  | 
|  253  |  | 
|  254     /**  |  | 
|  255      * Toggle the opened state of the overlay. |  | 
|  256      * @method toggle |  | 
|  257      */ |  | 
|  258     toggle: function() { |  | 
|  259       this.opened = !this.opened; |  | 
|  260     }, |  | 
|  261  |  | 
|  262     /**  |  | 
|  263      * Open the overlay. This is equivalent to setting the `opened` |  | 
|  264      * property to true. |  | 
|  265      * @method open |  | 
|  266      */ |  | 
|  267     open: function() { |  | 
|  268       this.opened = true; |  | 
|  269     }, |  | 
|  270  |  | 
|  271     /**  |  | 
|  272      * Close the overlay. This is equivalent to setting the `opened`  |  | 
|  273      * property to false. |  | 
|  274      * @method close |  | 
|  275      */ |  | 
|  276     close: function() { |  | 
|  277       this.opened = false; |  | 
|  278     }, |  | 
|  279  |  | 
|  280     domReady: function() { |  | 
|  281       this.ensureTargetSetup(); |  | 
|  282     }, |  | 
|  283  |  | 
|  284     targetChanged: function(old) { |  | 
|  285       if (this.target) { |  | 
|  286         // really make sure tabIndex is set |  | 
|  287         if (this.target.tabIndex < 0) { |  | 
|  288           this.target.tabIndex = -1; |  | 
|  289         } |  | 
|  290         this.addElementListenerList(this.target, this.targetListeners); |  | 
|  291         this.target.style.display = 'none'; |  | 
|  292         this.target.__overlaySetup = false; |  | 
|  293       } |  | 
|  294       if (old) { |  | 
|  295         this.removeElementListenerList(old, this.targetListeners); |  | 
|  296         var transition = this.getTransition(); |  | 
|  297         if (transition) { |  | 
|  298           transition.teardown(old); |  | 
|  299         } else { |  | 
|  300           old.style.position = ''; |  | 
|  301           old.style.outline = ''; |  | 
|  302         } |  | 
|  303         old.style.display = ''; |  | 
|  304       } |  | 
|  305     }, |  | 
|  306  |  | 
|  307     transitionChanged: function(old) { |  | 
|  308       if (!this.target) { |  | 
|  309         return; |  | 
|  310       } |  | 
|  311       if (old) { |  | 
|  312         this.getTransition(old).teardown(this.target); |  | 
|  313       } |  | 
|  314       this.target.__overlaySetup = false; |  | 
|  315     }, |  | 
|  316  |  | 
|  317     // NOTE: wait to call this until we're as sure as possible that target |  | 
|  318     // is styled. |  | 
|  319     ensureTargetSetup: function() { |  | 
|  320       if (!this.target || this.target.__overlaySetup) { |  | 
|  321         return; |  | 
|  322       } |  | 
|  323       if (!this.sizingTarget) { |  | 
|  324         this.sizingTarget = this.target; |  | 
|  325       } |  | 
|  326       this.target.__overlaySetup = true; |  | 
|  327       this.target.style.display = ''; |  | 
|  328       var transition = this.getTransition(); |  | 
|  329       if (transition) { |  | 
|  330         transition.setup(this.target); |  | 
|  331       } |  | 
|  332       var style = this.target.style; |  | 
|  333       var computed = getComputedStyle(this.target); |  | 
|  334       if (computed.position === 'static') { |  | 
|  335         style.position = 'fixed'; |  | 
|  336       } |  | 
|  337       style.outline = 'none'; |  | 
|  338       style.display = 'none'; |  | 
|  339     }, |  | 
|  340  |  | 
|  341     openedChanged: function() { |  | 
|  342       this.transitioning = true; |  | 
|  343       this.ensureTargetSetup(); |  | 
|  344       this.prepareRenderOpened(); |  | 
|  345       // async here to allow overlay layer to become visible. |  | 
|  346       this.async(function() { |  | 
|  347         this.target.style.display = ''; |  | 
|  348         // force layout to ensure transitions will go |  | 
|  349         this.target.offsetWidth; |  | 
|  350         this.renderOpened(); |  | 
|  351       }); |  | 
|  352       this.fire('core-overlay-open', this.opened); |  | 
|  353     }, |  | 
|  354  |  | 
|  355     // tasks which must occur before opening; e.g. making the element visible |  | 
|  356     prepareRenderOpened: function() { |  | 
|  357       if (this.opened) { |  | 
|  358         addOverlay(this); |  | 
|  359       } |  | 
|  360       this.prepareBackdrop(); |  | 
|  361       // async so we don't auto-close immediately via a click. |  | 
|  362       this.async(function() { |  | 
|  363         if (!this.autoCloseDisabled) { |  | 
|  364           this.enableElementListener(this.opened, document, |  | 
|  365               this.captureEventName, 'captureHandler', true); |  | 
|  366         } |  | 
|  367       }); |  | 
|  368       this.enableElementListener(this.opened, window, 'resize', |  | 
|  369           'resizeHandler'); |  | 
|  370  |  | 
|  371       if (this.opened) { |  | 
|  372         // force layout so SD Polyfill renders |  | 
|  373         this.target.offsetHeight; |  | 
|  374         this.discoverDimensions(); |  | 
|  375         // if we are showing, then take care when positioning |  | 
|  376         this.preparePositioning(); |  | 
|  377         this.positionTarget(); |  | 
|  378         this.updateTargetDimensions(); |  | 
|  379         this.finishPositioning(); |  | 
|  380         if (this.layered) { |  | 
|  381           this.layer.addElement(this.target); |  | 
|  382           this.layer.opened = this.opened; |  | 
|  383         } |  | 
|  384       } |  | 
|  385     }, |  | 
|  386  |  | 
|  387     // tasks which cause the overlay to actually open; typically play an |  | 
|  388     // animation |  | 
|  389     renderOpened: function() { |  | 
|  390       var transition = this.getTransition(); |  | 
|  391       if (transition) { |  | 
|  392         transition.go(this.target, {opened: this.opened}); |  | 
|  393       } else { |  | 
|  394         this.transitionend(); |  | 
|  395       } |  | 
|  396       this.renderBackdropOpened(); |  | 
|  397     }, |  | 
|  398  |  | 
|  399     // finishing tasks; typically called via a transition |  | 
|  400     transitionend: function(e) { |  | 
|  401       // make sure this is our transition event. |  | 
|  402       if (e && e.target !== this.target) { |  | 
|  403         return; |  | 
|  404       } |  | 
|  405       this.transitioning = false; |  | 
|  406       if (!this.opened) { |  | 
|  407         this.resetTargetDimensions(); |  | 
|  408         this.target.style.display = 'none'; |  | 
|  409         this.completeBackdrop(); |  | 
|  410         removeOverlay(this); |  | 
|  411         if (this.layered) { |  | 
|  412           if (!currentOverlay()) { |  | 
|  413             this.layer.opened = this.opened; |  | 
|  414           } |  | 
|  415           this.layer.removeElement(this.target); |  | 
|  416         } |  | 
|  417       } |  | 
|  418       this.fire('core-overlay-' + (this.opened ? 'open' : 'close') +  |  | 
|  419           '-completed'); |  | 
|  420       this.applyFocus(); |  | 
|  421     }, |  | 
|  422  |  | 
|  423     prepareBackdrop: function() { |  | 
|  424       if (this.backdrop && this.opened) { |  | 
|  425         if (!this.scrim.parentNode) { |  | 
|  426           document.body.appendChild(this.scrim); |  | 
|  427           this.scrim.style.zIndex = currentOverlayZ() - 1; |  | 
|  428         } |  | 
|  429         trackBackdrop(this); |  | 
|  430       } |  | 
|  431     }, |  | 
|  432  |  | 
|  433     renderBackdropOpened: function() { |  | 
|  434       if (this.backdrop && getBackdrops().length < 2) { |  | 
|  435         this.scrim.classList.toggle('core-opened', this.opened); |  | 
|  436       } |  | 
|  437     }, |  | 
|  438  |  | 
|  439     completeBackdrop: function() { |  | 
|  440       if (this.backdrop) { |  | 
|  441         trackBackdrop(this); |  | 
|  442         if (getBackdrops().length === 0) { |  | 
|  443           this.scrim.parentNode.removeChild(this.scrim); |  | 
|  444         } |  | 
|  445       } |  | 
|  446     }, |  | 
|  447  |  | 
|  448     preparePositioning: function() { |  | 
|  449       this.target.style.transition = this.target.style.webkitTransition = 'none'
     ; |  | 
|  450       this.target.style.transform = this.target.style.webkitTransform = 'none'; |  | 
|  451       this.target.style.display = ''; |  | 
|  452     }, |  | 
|  453  |  | 
|  454     discoverDimensions: function() { |  | 
|  455       if (this.dimensions) { |  | 
|  456         return; |  | 
|  457       } |  | 
|  458       var target = getComputedStyle(this.target); |  | 
|  459       var sizer = getComputedStyle(this.sizingTarget); |  | 
|  460       this.dimensions = { |  | 
|  461         position: { |  | 
|  462           v: target.top !== 'auto' ? 'top' : (target.bottom !== 'auto' ? |  | 
|  463             'bottom' : null), |  | 
|  464           h: target.left !== 'auto' ? 'left' : (target.right !== 'auto' ? |  | 
|  465             'right' : null), |  | 
|  466           css: target.position |  | 
|  467         }, |  | 
|  468         size: { |  | 
|  469           v: sizer.maxHeight !== 'none', |  | 
|  470           h: sizer.maxWidth !== 'none' |  | 
|  471         }, |  | 
|  472         margin: { |  | 
|  473           top: parseInt(target.marginTop) || 0, |  | 
|  474           right: parseInt(target.marginRight) || 0, |  | 
|  475           bottom: parseInt(target.marginBottom) || 0, |  | 
|  476           left: parseInt(target.marginLeft) || 0 |  | 
|  477         } |  | 
|  478       }; |  | 
|  479     }, |  | 
|  480  |  | 
|  481     finishPositioning: function(target) { |  | 
|  482       this.target.style.display = 'none'; |  | 
|  483       this.target.style.transform = this.target.style.webkitTransform = ''; |  | 
|  484       // force layout to avoid application of transform |  | 
|  485       this.target.offsetWidth; |  | 
|  486       this.target.style.transition = this.target.style.webkitTransition = ''; |  | 
|  487     }, |  | 
|  488  |  | 
|  489     getTransition: function(name) { |  | 
|  490       return this.meta.byId(name || this.transition); |  | 
|  491     }, |  | 
|  492  |  | 
|  493     getFocusNode: function() { |  | 
|  494       return this.target.querySelector('[autofocus]') || this.target; |  | 
|  495     }, |  | 
|  496  |  | 
|  497     applyFocus: function() { |  | 
|  498       var focusNode = this.getFocusNode(); |  | 
|  499       if (this.opened) { |  | 
|  500         if (!this.autoFocusDisabled) { |  | 
|  501           focusNode.focus(); |  | 
|  502         } |  | 
|  503       } else { |  | 
|  504         focusNode.blur(); |  | 
|  505         if (currentOverlay() == this) { |  | 
|  506           console.warn('Current core-overlay is attempting to focus itself as ne
     xt! (bug)'); |  | 
|  507         } else { |  | 
|  508           focusOverlay(); |  | 
|  509         } |  | 
|  510       } |  | 
|  511     }, |  | 
|  512  |  | 
|  513     positionTarget: function() { |  | 
|  514       // fire positioning event |  | 
|  515       this.fire('core-overlay-position', {target: this.target, |  | 
|  516           sizingTarget: this.sizingTarget, opened: this.opened}); |  | 
|  517       if (!this.dimensions.position.v) { |  | 
|  518         this.target.style.top = '0px'; |  | 
|  519       } |  | 
|  520       if (!this.dimensions.position.h) { |  | 
|  521         this.target.style.left = '0px'; |  | 
|  522       } |  | 
|  523     }, |  | 
|  524  |  | 
|  525     updateTargetDimensions: function() { |  | 
|  526       this.sizeTarget(); |  | 
|  527       this.repositionTarget(); |  | 
|  528     }, |  | 
|  529  |  | 
|  530     sizeTarget: function() { |  | 
|  531       this.sizingTarget.style.boxSizing = 'border-box'; |  | 
|  532       var dims = this.dimensions; |  | 
|  533       var rect = this.target.getBoundingClientRect(); |  | 
|  534       if (!dims.size.v) { |  | 
|  535         this.sizeDimension(rect, dims.position.v, 'top', 'bottom', 'Height'); |  | 
|  536       } |  | 
|  537       if (!dims.size.h) { |  | 
|  538         this.sizeDimension(rect, dims.position.h, 'left', 'right', 'Width'); |  | 
|  539       } |  | 
|  540     }, |  | 
|  541  |  | 
|  542     sizeDimension: function(rect, positionedBy, start, end, extent) { |  | 
|  543       var dims = this.dimensions; |  | 
|  544       var flip = (positionedBy === end); |  | 
|  545       var m = flip ? start : end; |  | 
|  546       var ws = window['inner' + extent]; |  | 
|  547       var o = dims.margin[m] + (flip ? ws - rect[end] :  |  | 
|  548           rect[start]); |  | 
|  549       var offset = 'offset' + extent; |  | 
|  550       var o2 = this.target[offset] - this.sizingTarget[offset]; |  | 
|  551       this.sizingTarget.style['max' + extent] = (ws - o - o2) + 'px'; |  | 
|  552     }, |  | 
|  553  |  | 
|  554     // vertically and horizontally center if not positioned |  | 
|  555     repositionTarget: function() { |  | 
|  556       // only center if position fixed.       |  | 
|  557       if (this.dimensions.position.css !== 'fixed') { |  | 
|  558         return;  |  | 
|  559       } |  | 
|  560       if (!this.dimensions.position.v) { |  | 
|  561         var t = (window.innerHeight - this.target.offsetHeight) / 2; |  | 
|  562         t -= this.dimensions.margin.top; |  | 
|  563         this.target.style.top = t + 'px'; |  | 
|  564       } |  | 
|  565  |  | 
|  566       if (!this.dimensions.position.h) { |  | 
|  567         var l = (window.innerWidth - this.target.offsetWidth) / 2; |  | 
|  568         l -= this.dimensions.margin.left; |  | 
|  569         this.target.style.left = l + 'px'; |  | 
|  570       } |  | 
|  571     }, |  | 
|  572  |  | 
|  573     resetTargetDimensions: function() { |  | 
|  574       if (!this.dimensions.size.v) { |  | 
|  575         this.sizingTarget.style.maxHeight = '';   |  | 
|  576       } |  | 
|  577       if (!this.dimensions.size.h) { |  | 
|  578         this.sizingTarget.style.maxWidth = '';   |  | 
|  579       } |  | 
|  580       this.dimensions = null; |  | 
|  581     }, |  | 
|  582  |  | 
|  583     tapHandler: function(e) { |  | 
|  584       // closeSelector takes precedence since closeAttribute has a default non-n
     ull value. |  | 
|  585       if (e.target && |  | 
|  586           (this.closeSelector && e.target.matches(this.closeSelector)) || |  | 
|  587           (this.closeAttribute && e.target.hasAttribute(this.closeAttribute))) { |  | 
|  588         this.toggle(); |  | 
|  589       } else { |  | 
|  590         if (this.autoCloseJob) { |  | 
|  591           this.autoCloseJob.stop(); |  | 
|  592           this.autoCloseJob = null; |  | 
|  593         } |  | 
|  594       } |  | 
|  595     }, |  | 
|  596      |  | 
|  597     // We use the traditional approach of capturing events on document |  | 
|  598     // to to determine if the overlay needs to close. However, due to  |  | 
|  599     // ShadowDOM event retargeting, the event target is not useful. Instead |  | 
|  600     // of using it, we attempt to close asynchronously and prevent the close |  | 
|  601     // if a tap event is immediately heard on the target. |  | 
|  602     // TODO(sorvell): This approach will not work with modal. For |  | 
|  603     // this we need a scrim. |  | 
|  604     captureHandler: function(e) { |  | 
|  605       if (!this.autoCloseDisabled && (currentOverlay() == this)) { |  | 
|  606         this.autoCloseJob = this.job(this.autoCloseJob, function() { |  | 
|  607           this.close(); |  | 
|  608         }); |  | 
|  609       } |  | 
|  610     }, |  | 
|  611  |  | 
|  612     keydownHandler: function(e) { |  | 
|  613       if (!this.autoCloseDisabled && (e.keyCode == this.keyHelper.ESCAPE_KEY)) { |  | 
|  614         this.close(); |  | 
|  615         e.stopPropagation(); |  | 
|  616       } |  | 
|  617     }, |  | 
|  618  |  | 
|  619     /** |  | 
|  620      * Extensions of core-overlay should implement the `resizeHandler` |  | 
|  621      * method to adjust the size and position of the overlay when the  |  | 
|  622      * browser window resizes. |  | 
|  623      * @method resizeHandler |  | 
|  624      */ |  | 
|  625     resizeHandler: function() { |  | 
|  626       this.updateTargetDimensions(); |  | 
|  627     }, |  | 
|  628  |  | 
|  629     // TODO(sorvell): these utility methods should not be here. |  | 
|  630     addElementListenerList: function(node, events) { |  | 
|  631       for (var i in events) { |  | 
|  632         this.addElementListener(node, i, events[i]); |  | 
|  633       } |  | 
|  634     }, |  | 
|  635  |  | 
|  636     removeElementListenerList: function(node, events) { |  | 
|  637       for (var i in events) { |  | 
|  638         this.removeElementListener(node, i, events[i]); |  | 
|  639       } |  | 
|  640     }, |  | 
|  641  |  | 
|  642     enableElementListener: function(enable, node, event, methodName, capture) { |  | 
|  643       if (enable) { |  | 
|  644         this.addElementListener(node, event, methodName, capture); |  | 
|  645       } else { |  | 
|  646         this.removeElementListener(node, event, methodName, capture); |  | 
|  647       } |  | 
|  648     }, |  | 
|  649  |  | 
|  650     addElementListener: function(node, event, methodName, capture) { |  | 
|  651       var fn = this._makeBoundListener(methodName); |  | 
|  652       if (node && fn) { |  | 
|  653         Polymer.addEventListener(node, event, fn, capture); |  | 
|  654       } |  | 
|  655     }, |  | 
|  656  |  | 
|  657     removeElementListener: function(node, event, methodName, capture) { |  | 
|  658       var fn = this._makeBoundListener(methodName); |  | 
|  659       if (node && fn) { |  | 
|  660         Polymer.removeEventListener(node, event, fn, capture); |  | 
|  661       } |  | 
|  662     }, |  | 
|  663  |  | 
|  664     _makeBoundListener: function(methodName) { |  | 
|  665       var self = this, method = this[methodName]; |  | 
|  666       if (!method) { |  | 
|  667         return; |  | 
|  668       } |  | 
|  669       var bound = '_bound' + methodName; |  | 
|  670       if (!this[bound]) { |  | 
|  671         this[bound] = function(e) { |  | 
|  672           method.call(self, e); |  | 
|  673         }; |  | 
|  674       } |  | 
|  675       return this[bound]; |  | 
|  676     }, |  | 
|  677   }); |  | 
|  678  |  | 
|  679   // TODO(sorvell): This should be an element with private state so it can |  | 
|  680   // be independent of overlay. |  | 
|  681   // track overlays for z-index and focus managemant |  | 
|  682   var overlays = []; |  | 
|  683   function addOverlay(overlay) { |  | 
|  684     var z0 = currentOverlayZ(); |  | 
|  685     overlays.push(overlay); |  | 
|  686     var z1 = currentOverlayZ(); |  | 
|  687     if (z1 <= z0) { |  | 
|  688       applyOverlayZ(overlay, z0); |  | 
|  689     } |  | 
|  690   } |  | 
|  691  |  | 
|  692   function removeOverlay(overlay) { |  | 
|  693     var i = overlays.indexOf(overlay); |  | 
|  694     if (i >= 0) { |  | 
|  695       overlays.splice(i, 1); |  | 
|  696       setZ(overlay, ''); |  | 
|  697     } |  | 
|  698   } |  | 
|  699    |  | 
|  700   function applyOverlayZ(overlay, aboveZ) { |  | 
|  701     setZ(overlay.target, aboveZ + 2); |  | 
|  702   } |  | 
|  703    |  | 
|  704   function setZ(element, z) { |  | 
|  705     element.style.zIndex = z; |  | 
|  706   } |  | 
|  707  |  | 
|  708   function currentOverlay() { |  | 
|  709     return overlays[overlays.length-1]; |  | 
|  710   } |  | 
|  711    |  | 
|  712   var DEFAULT_Z = 10; |  | 
|  713    |  | 
|  714   function currentOverlayZ() { |  | 
|  715     var z; |  | 
|  716     var current = currentOverlay(); |  | 
|  717     if (current) { |  | 
|  718       var z1 = window.getComputedStyle(current.target).zIndex; |  | 
|  719       if (!isNaN(z1)) { |  | 
|  720         z = Number(z1); |  | 
|  721       } |  | 
|  722     } |  | 
|  723     return z || DEFAULT_Z; |  | 
|  724   } |  | 
|  725    |  | 
|  726   function focusOverlay() { |  | 
|  727     var current = currentOverlay(); |  | 
|  728     // We have to be careful to focus the next overlay _after_ any current |  | 
|  729     // transitions are complete (due to the state being toggled prior to the |  | 
|  730     // transition). Otherwise, we risk infinite recursion when a transitioning |  | 
|  731     // (closed) overlay becomes the current overlay. |  | 
|  732     // |  | 
|  733     // NOTE: We make the assumption that any overlay that completes a transition |  | 
|  734     // will call into focusOverlay to kick the process back off. Currently: |  | 
|  735     // transitionend -> applyFocus -> focusOverlay. |  | 
|  736     if (current && !current.transitioning) { |  | 
|  737       current.applyFocus(); |  | 
|  738     } |  | 
|  739   } |  | 
|  740  |  | 
|  741   var backdrops = []; |  | 
|  742   function trackBackdrop(element) { |  | 
|  743     if (element.opened) { |  | 
|  744       backdrops.push(element); |  | 
|  745     } else { |  | 
|  746       var i = backdrops.indexOf(element); |  | 
|  747       if (i >= 0) { |  | 
|  748         backdrops.splice(i, 1); |  | 
|  749       } |  | 
|  750     } |  | 
|  751   } |  | 
|  752  |  | 
|  753   function getBackdrops() { |  | 
|  754     return backdrops; |  | 
|  755   } |  | 
|  756 })(); |  | 
|  757 </script> |  | 
|  758 </polymer-element> |  | 
| OLD | NEW |