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
7 * Class representing a menu button and its associated menu items.
8 */
9
10'use strict';
11
12/** @suppress {duplicate} */
13var remoting = remoting || {};
14
15/**
16 * @constructor
17 * @param {Element} container The element containing the <button> and <ul>
18 *     elements comprising the menu. It should have the "menu-button" class.
19 * @param {function():void=} opt_onShow Optional callback invoked before the
20 *     menu is shown.
21 * @param {function():void=} opt_onHide Optional callback after before the
22 *     menu is hidden.
23 */
24remoting.MenuButton = function(container, opt_onShow, opt_onHide) {
25  /**
26   * @type {HTMLElement}
27   * @private
28   */
29  this.button_ = /** @type {HTMLElement} */
30      (container.querySelector('button,.menu-button-activator'));
31
32  /**
33   * @type {HTMLElement}
34   * @private
35   */
36  this.menu_ = /** @type {HTMLElement} */ (container.querySelector('ul'));
37
38  /**
39   * @type {undefined|function():void}
40   * @private
41   */
42  this.onShow_ = opt_onShow;
43
44  /**
45   * @type {undefined|function():void}
46   * @private
47   */
48  this.onHide_ = opt_onHide;
49
50  /**
51   * Create a "click-trap" div covering the entire document, but below the
52   * menu in the z-order. This ensures the the menu can be closed by clicking
53   * anywhere. Note that adding this event handler to <body> is not enough,
54   * because elements can prevent event propagation; specifically, the client
55   * plugin element does this.
56   *
57   * @type {HTMLElement}
58   * @private
59   */
60  this.clickTrap_ = /** @type {HTMLElement} */ (document.createElement('div'));
61  this.clickTrap_.classList.add('menu-button-click-trap');
62
63  /** @type {remoting.MenuButton} */
64  var that = this;
65
66  var closeHandler = function() {
67    that.button_.classList.remove(remoting.MenuButton.BUTTON_ACTIVE_CLASS_);
68    container.removeChild(that.clickTrap_);
69    if (that.onHide_) {
70      that.onHide_();
71    }
72  };
73
74  var onClick = function() {
75    if (that.onShow_) {
76      that.onShow_();
77    }
78    that.button_.classList.add(remoting.MenuButton.BUTTON_ACTIVE_CLASS_);
79    container.appendChild(that.clickTrap_);
80  };
81
82  this.button_.addEventListener('click', onClick, false);
83  this.clickTrap_.addEventListener('click', closeHandler, false);
84  this.menu_.addEventListener('click', closeHandler, false);
85};
86
87/**
88 * @return {HTMLElement} The button that activates the menu.
89 */
90remoting.MenuButton.prototype.button = function() {
91  return this.button_;
92};
93
94/**
95 * @return {HTMLElement} The menu.
96 */
97remoting.MenuButton.prototype.menu = function() {
98  return this.menu_;
99};
100
101/**
102 * Set or unset the selected state of an <li> menu item.
103 * @param {Element} item The menu item to update.
104 * @param {boolean} selected True to select the item, false to deselect it.
105 * @return {void} Nothing.
106 */
107remoting.MenuButton.select = function(item, selected) {
108  if (selected) {
109    /** @type {DOMTokenList} */(item.classList).add('selected');
110  } else {
111    /** @type {DOMTokenList} */(item.classList).remove('selected');
112  }
113};
114
115/** @const @private */
116remoting.MenuButton.BUTTON_ACTIVE_CLASS_ = 'active';
117