• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 */
8cr.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(
84        '.overlay .page:not(.not-resizable)');
85
86    var maxHeight = Math.min(0.9 * window.innerHeight, 640) + 'px';
87    for (var i = 0; i < pages.length; i++)
88      pages[i].style.maxHeight = maxHeight;
89  }
90
91  /**
92   * Adds behavioral hooks for the given overlay.
93   * @param {HTMLElement} overlay The .overlay.
94   */
95  function setupOverlay(overlay) {
96    // Close the overlay on clicking any of the pages' close buttons.
97    var closeButtons = overlay.querySelectorAll('.page > .close-button');
98    for (var i = 0; i < closeButtons.length; i++) {
99      closeButtons[i].addEventListener('click', function(e) {
100        cr.dispatchSimpleEvent(overlay, 'cancelOverlay');
101      });
102    }
103
104    // Remove the 'pulse' animation any time the overlay is hidden or shown.
105    overlay.__defineSetter__('hidden', function(value) {
106      this.classList.remove('pulse');
107      if (value)
108        this.setAttribute('hidden', true);
109      else
110        this.removeAttribute('hidden');
111    });
112    overlay.__defineGetter__('hidden', function() {
113      return this.hasAttribute('hidden');
114    });
115
116    // Shake when the user clicks away.
117    overlay.addEventListener('click', function(e) {
118      // Only pulse if the overlay was the target of the click.
119      if (this != e.target)
120        return;
121
122      // This may be null while the overlay is closing.
123      var overlayPage = this.querySelector('.page:not([hidden])');
124      if (overlayPage)
125        overlayPage.classList.add('pulse');
126    });
127    overlay.addEventListener('webkitAnimationEnd', function(e) {
128      e.target.classList.remove('pulse');
129    });
130  }
131
132  return {
133    globalInitialization: globalInitialization,
134    setupOverlay: setupOverlay,
135  };
136});
137