| OLD | NEW | 
 | (Empty) | 
|    1 <!-- |  | 
|    2 @license |  | 
|    3 Copyright (c) 2015 The Polymer Project Authors. All rights reserved. |  | 
|    4 This code may only be used under the BSD style license found at http://polymer.g
     ithub.io/LICENSE.txt |  | 
|    5 The complete set of authors may be found at http://polymer.github.io/AUTHORS.txt |  | 
|    6 The complete set of contributors may be found at http://polymer.github.io/CONTRI
     BUTORS.txt |  | 
|    7 Code distributed by Google as part of the polymer project is also |  | 
|    8 subject to an additional IP rights grant found at http://polymer.github.io/PATEN
     TS.txt |  | 
|    9 --> |  | 
|   10  |  | 
|   11 <link rel="import" href="../polymer/polymer.html"> |  | 
|   12 <link rel="import" href="../iron-fit-behavior/iron-fit-behavior.html"> |  | 
|   13 <link rel="import" href="../iron-resizable-behavior/iron-resizable-behavior.html
     "> |  | 
|   14 <link rel="import" href="iron-overlay-backdrop.html"> |  | 
|   15 <link rel="import" href="iron-overlay-manager.html"> |  | 
|   16  |  | 
|   17 <script> |  | 
|   18  |  | 
|   19 /** |  | 
|   20 Use `Polymer.IronOverlayBehavior` to implement an element that can be hidden or 
     shown, and displays |  | 
|   21 on top of other content. It includes an optional backdrop, and can be used to im
     plement a variety |  | 
|   22 of UI controls including dialogs and drop downs. Multiple overlays may be displa
     yed at once. |  | 
|   23  |  | 
|   24 ### Closing and canceling |  | 
|   25  |  | 
|   26 A dialog may be hidden by closing or canceling. The difference between close and
      cancel is user |  | 
|   27 intent. Closing generally implies that the user acknowledged the content on the 
     overlay. By default, |  | 
|   28 it will cancel whenever the user taps outside it or presses the escape key. This
      behavior is |  | 
|   29 configurable with the `no-cancel-on-esc-key` and the `no-cancel-on-outside-click
     ` properties. |  | 
|   30 `close()` should be called explicitly by the implementer when the user interacts
      with a control |  | 
|   31 in the overlay element. |  | 
|   32  |  | 
|   33 ### Positioning |  | 
|   34  |  | 
|   35 By default the element is sized and positioned to fit and centered inside the wi
     ndow. You can |  | 
|   36 position and size it manually using CSS. See `Polymer.IronFitBehavior`. |  | 
|   37  |  | 
|   38 ### Backdrop |  | 
|   39  |  | 
|   40 Set the `with-backdrop` attribute to display a backdrop behind the overlay. The 
     backdrop is |  | 
|   41 appended to `<body>` and is of type `<iron-overlay-backdrop>`. See its doc page 
     for styling |  | 
|   42 options. |  | 
|   43  |  | 
|   44 ### Limitations |  | 
|   45  |  | 
|   46 The element is styled to appear on top of other content by setting its `z-index`
      property. You |  | 
|   47 must ensure no element has a stacking context with a higher `z-index` than its p
     arent stacking |  | 
|   48 context. You should place this element as a child of `<body>` whenever possible. |  | 
|   49  |  | 
|   50 @demo demo/index.html |  | 
|   51 @polymerBehavior Polymer.IronOverlayBehavior |  | 
|   52 */ |  | 
|   53  |  | 
|   54   Polymer.IronOverlayBehaviorImpl = { |  | 
|   55  |  | 
|   56     properties: { |  | 
|   57  |  | 
|   58       /** |  | 
|   59        * True if the overlay is currently displayed. |  | 
|   60        */ |  | 
|   61       opened: { |  | 
|   62         observer: '_openedChanged', |  | 
|   63         type: Boolean, |  | 
|   64         value: false, |  | 
|   65         notify: true |  | 
|   66       }, |  | 
|   67  |  | 
|   68       /** |  | 
|   69        * True if the overlay was canceled when it was last closed. |  | 
|   70        */ |  | 
|   71       canceled: { |  | 
|   72         observer: '_canceledChanged', |  | 
|   73         readOnly: true, |  | 
|   74         type: Boolean, |  | 
|   75         value: false |  | 
|   76       }, |  | 
|   77  |  | 
|   78       /** |  | 
|   79        * Set to true to display a backdrop behind the overlay. |  | 
|   80        */ |  | 
|   81       withBackdrop: { |  | 
|   82         type: Boolean, |  | 
|   83         value: false |  | 
|   84       }, |  | 
|   85  |  | 
|   86       /** |  | 
|   87        * Set to true to disable auto-focusing the overlay or child nodes with |  | 
|   88        * the `autofocus` attribute` when the overlay is opened. |  | 
|   89        */ |  | 
|   90       noAutoFocus: { |  | 
|   91         type: Boolean, |  | 
|   92         value: false |  | 
|   93       }, |  | 
|   94  |  | 
|   95       /** |  | 
|   96        * Set to true to disable canceling the overlay with the ESC key. |  | 
|   97        */ |  | 
|   98       noCancelOnEscKey: { |  | 
|   99         type: Boolean, |  | 
|  100         value: false |  | 
|  101       }, |  | 
|  102  |  | 
|  103       /** |  | 
|  104        * Set to true to disable canceling the overlay by clicking outside it. |  | 
|  105        */ |  | 
|  106       noCancelOnOutsideClick: { |  | 
|  107         type: Boolean, |  | 
|  108         value: false |  | 
|  109       }, |  | 
|  110  |  | 
|  111       /** |  | 
|  112        * Returns the reason this dialog was last closed. |  | 
|  113        */ |  | 
|  114       closingReason: { |  | 
|  115         // was a getter before, but needs to be a property so other |  | 
|  116         // behaviors can override this. |  | 
|  117         type: Object |  | 
|  118       }, |  | 
|  119  |  | 
|  120       _manager: { |  | 
|  121         type: Object, |  | 
|  122         value: Polymer.IronOverlayManager |  | 
|  123       }, |  | 
|  124  |  | 
|  125       _boundOnCaptureClick: { |  | 
|  126         type: Function, |  | 
|  127         value: function() { |  | 
|  128           return this._onCaptureClick.bind(this); |  | 
|  129         } |  | 
|  130       }, |  | 
|  131  |  | 
|  132       _boundOnCaptureKeydown: { |  | 
|  133         type: Function, |  | 
|  134         value: function() { |  | 
|  135           return this._onCaptureKeydown.bind(this); |  | 
|  136         } |  | 
|  137       } |  | 
|  138  |  | 
|  139     }, |  | 
|  140  |  | 
|  141 /** |  | 
|  142  * Fired after the `iron-overlay` opens. |  | 
|  143  * @event iron-overlay-opened |  | 
|  144  */ |  | 
|  145  |  | 
|  146 /** |  | 
|  147  * Fired after the `iron-overlay` closes. |  | 
|  148  * @event iron-overlay-closed {{canceled: boolean}} detail - |  | 
|  149  *     canceled: True if the overlay was canceled. |  | 
|  150  */ |  | 
|  151  |  | 
|  152     listeners: { |  | 
|  153       'click': '_onClick', |  | 
|  154       'iron-resize': '_onIronResize' |  | 
|  155     }, |  | 
|  156  |  | 
|  157     /** |  | 
|  158      * The backdrop element. |  | 
|  159      * @type Node |  | 
|  160      */ |  | 
|  161     get backdropElement() { |  | 
|  162       return this._backdrop; |  | 
|  163     }, |  | 
|  164  |  | 
|  165     get _focusNode() { |  | 
|  166       return Polymer.dom(this).querySelector('[autofocus]') || this; |  | 
|  167     }, |  | 
|  168  |  | 
|  169     registered: function() { |  | 
|  170       this._backdrop = document.createElement('iron-overlay-backdrop'); |  | 
|  171     }, |  | 
|  172  |  | 
|  173     ready: function() { |  | 
|  174       this._ensureSetup(); |  | 
|  175       if (this._callOpenedWhenReady) { |  | 
|  176         this._openedChanged(); |  | 
|  177       } |  | 
|  178     }, |  | 
|  179  |  | 
|  180     detached: function() { |  | 
|  181       this.opened = false; |  | 
|  182       this._completeBackdrop(); |  | 
|  183       this._manager.removeOverlay(this); |  | 
|  184     }, |  | 
|  185  |  | 
|  186     /** |  | 
|  187      * Toggle the opened state of the overlay. |  | 
|  188      */ |  | 
|  189     toggle: function() { |  | 
|  190       this.opened = !this.opened; |  | 
|  191     }, |  | 
|  192  |  | 
|  193     /** |  | 
|  194      * Open the overlay. |  | 
|  195      */ |  | 
|  196     open: function() { |  | 
|  197       this.opened = true; |  | 
|  198       this.closingReason = {canceled: false}; |  | 
|  199     }, |  | 
|  200  |  | 
|  201     /** |  | 
|  202      * Close the overlay. |  | 
|  203      */ |  | 
|  204     close: function() { |  | 
|  205       this.opened = false; |  | 
|  206       this._setCanceled(false); |  | 
|  207     }, |  | 
|  208  |  | 
|  209     /** |  | 
|  210      * Cancels the overlay. |  | 
|  211      */ |  | 
|  212     cancel: function() { |  | 
|  213       this.opened = false, |  | 
|  214       this._setCanceled(true); |  | 
|  215     }, |  | 
|  216  |  | 
|  217     _ensureSetup: function() { |  | 
|  218       if (this._overlaySetup) { |  | 
|  219         return; |  | 
|  220       } |  | 
|  221       this._overlaySetup = true; |  | 
|  222       this.style.outline = 'none'; |  | 
|  223       this.style.display = 'none'; |  | 
|  224     }, |  | 
|  225  |  | 
|  226     _openedChanged: function() { |  | 
|  227       if (this.opened) { |  | 
|  228         this.removeAttribute('aria-hidden'); |  | 
|  229       } else { |  | 
|  230         this.setAttribute('aria-hidden', 'true'); |  | 
|  231       } |  | 
|  232  |  | 
|  233       // wait to call after ready only if we're initially open |  | 
|  234       if (!this._overlaySetup) { |  | 
|  235         this._callOpenedWhenReady = this.opened; |  | 
|  236         return; |  | 
|  237       } |  | 
|  238       if (this._openChangedAsync) { |  | 
|  239         this.cancelAsync(this._openChangedAsync); |  | 
|  240       } |  | 
|  241  |  | 
|  242       this._toggleListeners(); |  | 
|  243  |  | 
|  244       if (this.opened) { |  | 
|  245         this._prepareRenderOpened(); |  | 
|  246       } |  | 
|  247  |  | 
|  248       // async here to allow overlay layer to become visible. |  | 
|  249       this._openChangedAsync = this.async(function() { |  | 
|  250         // overlay becomes visible here |  | 
|  251         this.style.display = ''; |  | 
|  252         // force layout to ensure transitions will go |  | 
|  253         this.offsetWidth; |  | 
|  254         if (this.opened) { |  | 
|  255           this._renderOpened(); |  | 
|  256         } else { |  | 
|  257           this._renderClosed(); |  | 
|  258         } |  | 
|  259         this._openChangedAsync = null; |  | 
|  260       }); |  | 
|  261  |  | 
|  262     }, |  | 
|  263  |  | 
|  264     _canceledChanged: function() { |  | 
|  265       this.closingReason = this.closingReason || {}; |  | 
|  266       this.closingReason.canceled = this.canceled; |  | 
|  267     }, |  | 
|  268  |  | 
|  269     _toggleListener: function(enable, node, event, boundListener, capture) { |  | 
|  270       if (enable) { |  | 
|  271         node.addEventListener(event, boundListener, capture); |  | 
|  272       } else { |  | 
|  273         node.removeEventListener(event, boundListener, capture); |  | 
|  274       } |  | 
|  275     }, |  | 
|  276  |  | 
|  277     _toggleListeners: function() { |  | 
|  278       if (this._toggleListenersAsync) { |  | 
|  279         this.cancelAsync(this._toggleListenersAsync); |  | 
|  280       } |  | 
|  281       // async so we don't auto-close immediately via a click. |  | 
|  282       this._toggleListenersAsync = this.async(function() { |  | 
|  283         this._toggleListener(this.opened, document, 'click', this._boundOnCaptur
     eClick, true); |  | 
|  284         this._toggleListener(this.opened, document, 'keydown', this._boundOnCapt
     ureKeydown, true); |  | 
|  285         this._toggleListenersAsync = null; |  | 
|  286       }); |  | 
|  287     }, |  | 
|  288  |  | 
|  289     // tasks which must occur before opening; e.g. making the element visible |  | 
|  290     _prepareRenderOpened: function() { |  | 
|  291       this._manager.addOverlay(this); |  | 
|  292  |  | 
|  293       if (this.withBackdrop) { |  | 
|  294         this.backdropElement.prepare(); |  | 
|  295         this._manager.trackBackdrop(this); |  | 
|  296       } |  | 
|  297  |  | 
|  298       this._preparePositioning(); |  | 
|  299       this.fit(); |  | 
|  300       this._finishPositioning(); |  | 
|  301     }, |  | 
|  302  |  | 
|  303     // tasks which cause the overlay to actually open; typically play an |  | 
|  304     // animation |  | 
|  305     _renderOpened: function() { |  | 
|  306       if (this.withBackdrop) { |  | 
|  307         this.backdropElement.open(); |  | 
|  308       } |  | 
|  309       this._finishRenderOpened(); |  | 
|  310     }, |  | 
|  311  |  | 
|  312     _renderClosed: function() { |  | 
|  313       if (this.withBackdrop) { |  | 
|  314         this.backdropElement.close(); |  | 
|  315       } |  | 
|  316       this._finishRenderClosed(); |  | 
|  317     }, |  | 
|  318  |  | 
|  319     _onTransitionend: function(event) { |  | 
|  320       // make sure this is our transition event. |  | 
|  321       if (event && event.target !== this) { |  | 
|  322         return; |  | 
|  323       } |  | 
|  324       if (this.opened) { |  | 
|  325         this._finishRenderOpened(); |  | 
|  326       } else { |  | 
|  327         this._finishRenderClosed(); |  | 
|  328       } |  | 
|  329     }, |  | 
|  330  |  | 
|  331     _finishRenderOpened: function() { |  | 
|  332       // focus the child node with [autofocus] |  | 
|  333       if (!this.noAutoFocus) { |  | 
|  334         this._focusNode.focus(); |  | 
|  335       } |  | 
|  336  |  | 
|  337       this.fire('iron-overlay-opened'); |  | 
|  338  |  | 
|  339       this._squelchNextResize = true; |  | 
|  340       this.async(this.notifyResize); |  | 
|  341     }, |  | 
|  342  |  | 
|  343     _finishRenderClosed: function() { |  | 
|  344       // hide the overlay and remove the backdrop |  | 
|  345       this.resetFit(); |  | 
|  346       this.style.display = 'none'; |  | 
|  347       this._completeBackdrop(); |  | 
|  348       this._manager.removeOverlay(this); |  | 
|  349  |  | 
|  350       this._focusNode.blur(); |  | 
|  351       // focus the next overlay, if there is one |  | 
|  352       this._manager.focusOverlay(); |  | 
|  353  |  | 
|  354       this.fire('iron-overlay-closed', this.closingReason); |  | 
|  355  |  | 
|  356       this._squelchNextResize = true; |  | 
|  357       this.async(this.notifyResize); |  | 
|  358     }, |  | 
|  359  |  | 
|  360     _completeBackdrop: function() { |  | 
|  361       if (this.withBackdrop) { |  | 
|  362         this._manager.trackBackdrop(this); |  | 
|  363         this.backdropElement.complete(); |  | 
|  364       } |  | 
|  365     }, |  | 
|  366  |  | 
|  367     _preparePositioning: function() { |  | 
|  368       this.style.transition = this.style.webkitTransition = 'none'; |  | 
|  369       this.style.transform = this.style.webkitTransform = 'none'; |  | 
|  370       this.style.display = ''; |  | 
|  371     }, |  | 
|  372  |  | 
|  373     _finishPositioning: function() { |  | 
|  374       this.style.display = 'none'; |  | 
|  375       this.style.transform = this.style.webkitTransform = ''; |  | 
|  376       // force layout to avoid application of transform |  | 
|  377       this.offsetWidth; |  | 
|  378       this.style.transition = this.style.webkitTransition = ''; |  | 
|  379     }, |  | 
|  380  |  | 
|  381     _applyFocus: function() { |  | 
|  382       if (this.opened) { |  | 
|  383         if (!this.noAutoFocus) { |  | 
|  384           this._focusNode.focus(); |  | 
|  385         } |  | 
|  386       } else { |  | 
|  387         this._focusNode.blur(); |  | 
|  388         this._manager.focusOverlay(); |  | 
|  389       } |  | 
|  390     }, |  | 
|  391  |  | 
|  392     _onCaptureClick: function(event) { |  | 
|  393       // attempt to close asynchronously and prevent the close of a tap event is
      immediately heard |  | 
|  394       // on target. This is because in shadow dom due to event retargetting even
     t.target is not |  | 
|  395       // useful. |  | 
|  396       if (!this.noCancelOnOutsideClick && (this._manager.currentOverlay() == thi
     s)) { |  | 
|  397         this._cancelJob = this.async(function() { |  | 
|  398           this.cancel(); |  | 
|  399         }, 10); |  | 
|  400       } |  | 
|  401     }, |  | 
|  402  |  | 
|  403     _onClick: function(event) { |  | 
|  404       if (this._cancelJob) { |  | 
|  405         this.cancelAsync(this._cancelJob); |  | 
|  406         this._cancelJob = null; |  | 
|  407       } |  | 
|  408     }, |  | 
|  409  |  | 
|  410     _onCaptureKeydown: function(event) { |  | 
|  411       var ESC = 27; |  | 
|  412       if (!this.noCancelOnEscKey && (event.keyCode === ESC)) { |  | 
|  413         this.cancel(); |  | 
|  414         event.stopPropagation(); |  | 
|  415       } |  | 
|  416     }, |  | 
|  417  |  | 
|  418     _onIronResize: function() { |  | 
|  419       if (this._squelchNextResize) { |  | 
|  420         this._squelchNextResize = false; |  | 
|  421         return; |  | 
|  422       } |  | 
|  423       if (this.opened) { |  | 
|  424         this.refit(); |  | 
|  425       } |  | 
|  426     } |  | 
|  427  |  | 
|  428   }; |  | 
|  429  |  | 
|  430   /** @polymerBehavior */ |  | 
|  431   Polymer.IronOverlayBehavior = [Polymer.IronFitBehavior, Polymer.IronResizableB
     ehavior, Polymer.IronOverlayBehaviorImpl]; |  | 
|  432  |  | 
|  433  |  | 
|  434 </script> |  | 
| OLD | NEW |