1// Copyright (c) 2010 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
5cr.define('cr.ui', function() {
6  const Command = cr.ui.Command;
7
8  /**
9   * Creates a new menu item element.
10   * @param {Object=} opt_propertyBag Optional properties.
11   * @constructor
12   * @extends {HTMLDivElement}
13   */
14  var MenuItem = cr.ui.define('div');
15
16  /**
17   * Creates a new menu separator element.
18   * @return {cr.ui.MenuItem}
19   */
20  MenuItem.createSeparator = function() {
21    var el = cr.doc.createElement('hr');
22    MenuItem.decorate(el);
23    return el;
24  };
25
26  MenuItem.prototype = {
27    __proto__: HTMLButtonElement.prototype,
28
29    /**
30     * Initializes the menu item.
31     */
32    decorate: function() {
33      var commandId;
34      if ((commandId = this.getAttribute('command')))
35        this.command = commandId;
36
37      this.addEventListener('mouseup', this.handleMouseUp_);
38    },
39
40    /**
41     * The command associated with this menu item. If this is set to a string
42     * of the form "#element-id" then the element is looked up in the document
43     * of the command.
44     * @type {cr.ui.Command}
45     */
46    command_: null,
47    get command() {
48      return this.command_;
49    },
50    set command(command) {
51      if (this.command_) {
52        this.command_.removeEventListener('labelChange', this);
53        this.command_.removeEventListener('disabledChange', this);
54        this.command_.removeEventListener('hiddenChange', this);
55        this.command_.removeEventListener('checkedChange', this);
56      }
57
58      if (typeof command == 'string' && command[0] == '#') {
59        command = this.ownerDocument.getElementById(command.slice(1));
60        cr.ui.decorate(command, Command);
61      }
62
63      this.command_ = command;
64      if (command) {
65        if (command.id)
66          this.setAttribute('command', '#' + command.id);
67
68        this.label = command.label;
69        this.disabled = command.disabled;
70        this.hidden = command.hidden;
71
72        this.command_.addEventListener('labelChange', this);
73        this.command_.addEventListener('disabledChange', this);
74        this.command_.addEventListener('hiddenChange', this);
75        this.command_.addEventListener('checkedChange', this);
76      }
77    },
78
79    /**
80     * The text label.
81     * @type {string}
82     */
83    get label() {
84      return this.textContent;
85    },
86    set label(label) {
87      this.textContent = label;
88    },
89
90    /**
91     * @return {boolean} Whether the menu item is a separator.
92     */
93    isSeparator: function() {
94      return this.tagName == 'HR';
95    },
96
97    /**
98     * Handles mouseup events. This dispatches an active event and if there
99     * is an assiciated command then that is executed.
100     * @param {Event} The mouseup event object.
101     * @private
102     */
103    handleMouseUp_: function(e) {
104      if (!this.disabled && !this.isSeparator()) {
105        // Dispatch command event followed by executing the command object.
106        if (cr.dispatchSimpleEvent(this, 'activate', true, true)) {
107          var command = this.command;
108          if (command)
109            command.execute();
110        }
111      }
112    },
113
114    /**
115     * Handles changes to the associated command.
116     * @param {Event} e The event object.
117     */
118    handleEvent: function(e) {
119      switch (e.type) {
120        case 'disabledChange':
121          this.disabled = this.command.disabled;
122          break;
123        case 'hiddenChange':
124          this.hidden = this.command.hidden;
125          break;
126        case 'labelChange':
127          this.label = this.command.label;
128          break;
129        case 'checkedChange':
130          this.checked = this.command.checked;
131          break;
132      }
133    }
134  };
135
136  /**
137   * Whether the menu item is disabled or not.
138   * @type {boolean}
139   */
140  cr.defineProperty(MenuItem, 'disabled', cr.PropertyKind.BOOL_ATTR);
141
142  /**
143   * Whether the menu item is hidden or not.
144   * @type {boolean}
145   */
146  cr.defineProperty(MenuItem, 'hidden', cr.PropertyKind.BOOL_ATTR);
147
148  /**
149   * Whether the menu item is selected or not.
150   * @type {boolean}
151   */
152  cr.defineProperty(MenuItem, 'selected', cr.PropertyKind.BOOL_ATTR);
153
154  /**
155   * Whether the menu item is checked or not.
156   * @type {boolean}
157   */
158  cr.defineProperty(MenuItem, 'checked', cr.PropertyKind.BOOL_ATTR);
159
160  // Export
161  return {
162    MenuItem: MenuItem
163  };
164});
165