Home | History | Annotate | Download | only in ui
      1 // Copyright (c) 2012 The Chromium Authors. All rights reserved.
      2 // Use of this source code is governed by a BSD-style license that can be
      3 // found in the LICENSE file.
      4 
      5 /**
      6  * @fileoverview Provides dialog-like behaviors for the tracing UI.
      7  */
      8 cr.define('cr.ui.overlay', function() {
      9   /**
     10    * Gets the top, visible overlay. It makes the assumption that if multiple
     11    * overlays are visible, the last in the byte order is topmost.
     12    * TODO(estade): rely on aria-visibility instead?
     13    * @return {HTMLElement} The overlay.
     14    */
     15   function getTopOverlay() {
     16     var overlays = document.querySelectorAll('.overlay:not([hidden])');
     17     return overlays[overlays.length - 1];
     18   }
     19 
     20   /**
     21    * Returns a visible default button of the overlay, if it has one. If the
     22    * overlay has more than one, the first one will be returned.
     23    *
     24    * @param {HTMLElement} overlay The .overlay.
     25    * @return {HTMLElement} The default button.
     26    */
     27   function getDefaultButton(overlay) {
     28     function isHidden(node) { return node.hidden; }
     29     var defaultButtons =
     30         overlay.querySelectorAll('.page .button-strip > .default-button');
     31     for (var i = 0; i < defaultButtons.length; i++) {
     32       if (!findAncestor(defaultButtons[i], isHidden))
     33         return defaultButtons[i];
     34     }
     35     return null;
     36   }
     37 
     38   /** @type {boolean} */
     39   var globallyInitialized = false;
     40 
     41   /**
     42    * Makes initializations which must hook at the document level.
     43    */
     44   function globalInitialization() {
     45     if (!globallyInitialized) {
     46       document.addEventListener('keydown', function(e) {
     47         var overlay = getTopOverlay();
     48         if (!overlay)
     49           return;
     50 
     51         // Close the overlay on escape.
     52         if (e.keyCode == 27)  // Escape
     53           cr.dispatchSimpleEvent(overlay, 'cancelOverlay');
     54 
     55         // Execute the overlay's default button on enter, unless focus is on an
     56         // element that has standard behavior for the enter key.
     57         var forbiddenTagNames = /^(A|BUTTON|SELECT|TEXTAREA)$/;
     58         if (e.keyIdentifier == 'Enter' &&
     59             !forbiddenTagNames.test(document.activeElement.tagName)) {
     60           var button = getDefaultButton(overlay);
     61           if (button) {
     62             button.click();
     63             // Executing the default button may result in focus moving to a
     64             // different button. Calling preventDefault is necessary to not have
     65             // that button execute as well.
     66             e.preventDefault();
     67           }
     68         }
     69       });
     70 
     71       window.addEventListener('resize', setMaxHeightAllPages);
     72       globallyInitialized = true;
     73     }
     74 
     75     setMaxHeightAllPages();
     76   }
     77 
     78   /**
     79    * Sets the max-height of all pages in all overlays, based on the window
     80    * height.
     81    */
     82   function setMaxHeightAllPages() {
     83     var pages = document.querySelectorAll('.overlay .page');
     84 
     85     var maxHeight = Math.min(0.9 * window.innerHeight, 640) + 'px';
     86     for (var i = 0; i < pages.length; i++)
     87       pages[i].style.maxHeight = maxHeight;
     88   }
     89 
     90   /**
     91    * Adds behavioral hooks for the given overlay.
     92    * @param {HTMLElement} overlay The .overlay.
     93    */
     94   function setupOverlay(overlay) {
     95     // Close the overlay on clicking any of the pages' close buttons.
     96     var closeButtons = overlay.querySelectorAll('.page > .close-button');
     97     for (var i = 0; i < closeButtons.length; i++) {
     98       closeButtons[i].addEventListener('click', function(e) {
     99         cr.dispatchSimpleEvent(overlay, 'cancelOverlay');
    100       });
    101     }
    102 
    103     // Remove the 'pulse' animation any time the overlay is hidden or shown.
    104     overlay.__defineSetter__('hidden', function(value) {
    105       this.classList.remove('pulse');
    106       if (value)
    107         this.setAttribute('hidden', true);
    108       else
    109         this.removeAttribute('hidden');
    110     });
    111     overlay.__defineGetter__('hidden', function() {
    112       return this.hasAttribute('hidden');
    113     });
    114 
    115     // Shake when the user clicks away.
    116     overlay.addEventListener('click', function(e) {
    117       // Only pulse if the overlay was the target of the click.
    118       if (this != e.target)
    119         return;
    120 
    121       // This may be null while the overlay is closing.
    122       var overlayPage = this.querySelector('.page:not([hidden])');
    123       if (overlayPage)
    124         overlayPage.classList.add('pulse');
    125     });
    126     overlay.addEventListener('webkitAnimationEnd', function(e) {
    127       e.target.classList.remove('pulse');
    128     });
    129   }
    130 
    131   return {
    132     globalInitialization: globalInitialization,
    133     setupOverlay: setupOverlay,
    134   };
    135 });
    136