summaryrefslogtreecommitdiff
path: root/chromium/ui/webui/resources/js/cr/ui/overlay.js
blob: 56f339f1b1d3e07c5c74d73b5f778d2458fa92a9 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
// Copyright (c) 2012 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

/**
 * @fileoverview Provides dialog-like behaviors for the tracing UI.
 */
cr.define('cr.ui.overlay', function() {
  /**
   * Gets the top, visible overlay. It makes the assumption that if multiple
   * overlays are visible, the last in the byte order is topmost.
   * TODO(estade): rely on aria-visibility instead?
   * @return {HTMLElement} The overlay.
   */
  function getTopOverlay() {
    var overlays = document.querySelectorAll('.overlay:not([hidden])');
    return overlays[overlays.length - 1];
  }

  /**
   * Returns a visible default button of the overlay, if it has one. If the
   * overlay has more than one, the first one will be returned.
   *
   * @param {HTMLElement} overlay The .overlay.
   * @return {HTMLElement} The default button.
   */
  function getDefaultButton(overlay) {
    function isHidden(node) { return node.hidden; }
    var defaultButtons =
        overlay.querySelectorAll('.page .button-strip > .default-button');
    for (var i = 0; i < defaultButtons.length; i++) {
      if (!findAncestor(defaultButtons[i], isHidden))
        return defaultButtons[i];
    }
    return null;
  }

  /** @type {boolean} */
  var globallyInitialized = false;

  /**
   * Makes initializations which must hook at the document level.
   */
  function globalInitialization() {
    if (!globallyInitialized) {
      document.addEventListener('keydown', function(e) {
        var overlay = getTopOverlay();
        if (!overlay)
          return;

        // Close the overlay on escape.
        if (e.keyIdentifier == 'U+001B')
          cr.dispatchSimpleEvent(overlay, 'cancelOverlay');

        // Execute the overlay's default button on enter, unless focus is on an
        // element that has standard behavior for the enter key.
        var forbiddenTagNames = /^(A|BUTTON|SELECT|TEXTAREA)$/;
        if (e.keyIdentifier == 'Enter' &&
            !forbiddenTagNames.test(document.activeElement.tagName)) {
          var button = getDefaultButton(overlay);
          if (button) {
            button.click();
            // Executing the default button may result in focus moving to a
            // different button. Calling preventDefault is necessary to not have
            // that button execute as well.
            e.preventDefault();
          }
        }
      });

      window.addEventListener('resize', setMaxHeightAllPages);
      globallyInitialized = true;
    }

    setMaxHeightAllPages();
  }

  /**
   * Sets the max-height of all pages in all overlays, based on the window
   * height.
   */
  function setMaxHeightAllPages() {
    var pages = document.querySelectorAll(
        '.overlay .page:not(.not-resizable)');

    var maxHeight = Math.min(0.9 * window.innerHeight, 640) + 'px';
    for (var i = 0; i < pages.length; i++)
      pages[i].style.maxHeight = maxHeight;
  }

  /**
   * Adds behavioral hooks for the given overlay.
   * @param {HTMLElement} overlay The .overlay.
   */
  function setupOverlay(overlay) {
    // Close the overlay on clicking any of the pages' close buttons.
    var closeButtons = overlay.querySelectorAll('.page > .close-button');
    for (var i = 0; i < closeButtons.length; i++) {
      closeButtons[i].addEventListener('click', function(e) {
        if (cr.ui.FocusOutlineManager)
          cr.ui.FocusOutlineManager.forDocument(document).updateVisibility();
        cr.dispatchSimpleEvent(overlay, 'cancelOverlay');
      });
    }

    // Remove the 'pulse' animation any time the overlay is hidden or shown.
    overlay.__defineSetter__('hidden', function(value) {
      this.classList.remove('pulse');
      if (value)
        this.setAttribute('hidden', true);
      else
        this.removeAttribute('hidden');
    });
    overlay.__defineGetter__('hidden', function() {
      return this.hasAttribute('hidden');
    });

    // Shake when the user clicks away.
    overlay.addEventListener('click', function(e) {
      // Only pulse if the overlay was the target of the click.
      if (this != e.target)
        return;

      // This may be null while the overlay is closing.
      var overlayPage = this.querySelector('.page:not([hidden])');
      if (overlayPage)
        overlayPage.classList.add('pulse');
    });
    overlay.addEventListener('webkitAnimationEnd', function(e) {
      e.target.classList.remove('pulse');
    });
  }

  return {
    getDefaultButton: getDefaultButton,
    globalInitialization: globalInitialization,
    setupOverlay: setupOverlay,
  };
});