1c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch// Copyright (c) 2010 The Chromium Authors. All rights reserved.
2c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch// Use of this source code is governed by a BSD-style license that can be
3c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch// found in the LICENSE file.
4c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch
5c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch/**
6c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch * @fileoverview A command is an abstraction of an action a user can do in the
7c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch * UI.
8c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch *
9c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch * When the focus changes in the document for each command a canExecute event
10c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch * is dispatched on the active element. By listening to this event you can
11c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch * enable and disable the command by setting the event.canExecute property.
12c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch *
13c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch * When a command is executed a command event is dispatched on the active
14c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch * element. Note that you should stop the propagation after you have handled the
15c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch * command if there might be other command listeners higher up in the DOM tree.
16c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch */
17c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch
18c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdochcr.define('cr.ui', function() {
19c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch
20c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  /**
21c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch   * This is used to identify keyboard shortcuts.
22c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch   * @param {string} shortcut The text used to describe the keys for this
23c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch   *     keyboard shortcut.
24c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch   * @constructor
25c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch   */
26c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  function KeyboardShortcut(shortcut) {
27c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch    var mods = {};
28c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch    var ident = '';
29c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch    shortcut.split('-').forEach(function(part) {
30c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch      var partLc = part.toLowerCase();
31c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch      switch (partLc) {
32c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch        case 'alt':
33c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch        case 'ctrl':
34c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch        case 'meta':
35c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch        case 'shift':
36c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch          mods[partLc + 'Key'] = true;
37c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch          break;
38c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch        default:
39c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch          if (ident)
40c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch            throw Error('Invalid shortcut');
41c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch          ident = part;
42c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch      }
43c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch    });
44c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch
45c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch    this.ident_ = ident;
46c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch    this.mods_ = mods;
47c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  }
48c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch
49c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  KeyboardShortcut.prototype = {
50c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch    /**
51c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch     * Wether the keyboard shortcut object mathes a keyboard event.
52c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch     * @param {!Event} e The keyboard event object.
53c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch     * @return {boolean} Whether we found a match or not.
54c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch     */
55c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch    matchesEvent: function(e) {
56c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch      if (e.keyIdentifier == this.ident_) {
57c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch        // All keyboard modifiers needs to match.
58c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch        var mods = this.mods_;
59c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch        return ['altKey', 'ctrlKey', 'metaKey', 'shiftKey'].every(function(k) {
60c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch          return e[k] == !!mods[k];
61c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch        });
62c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch      }
63c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch      return false;
64c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch    }
65c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  };
66c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch
67c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  /**
68c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch   * Creates a new command element.
69c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch   * @constructor
70c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch   * @extends {HTMLElement}
71c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch   */
72c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  var Command = cr.ui.define('command');
73c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch
74c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  Command.prototype = {
75c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch    __proto__: HTMLElement.prototype,
76c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch
77c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch    /**
78c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch     * Initializes the command.
79c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch     */
80c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch    decorate: function() {
81c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch      CommandManager.init(this.ownerDocument);
82c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch    },
83c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch
84c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch    /**
85c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch     * Executes the command. This dispatches a command event on the active
86c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch     * element. If the command is {@code disabled} this does nothing.
87c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch     */
88c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch    execute: function() {
89c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch      if (this.disabled)
90c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch        return;
91c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch      var doc = this.ownerDocument;
92c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch      if (doc.activeElement) {
93c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch        var e = new cr.Event('command', true, false);
94c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch        e.command = this;
95c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch        doc.activeElement.dispatchEvent(e);
96c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch      }
97c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch    },
98c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch
99c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch    /**
100c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch     * Call this when there have been changes that might change whether the
101c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch     * command can be executed or not.
102c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch     */
103c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch    canExecuteChange: function() {
104c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch      dispatchCanExecuteEvent(this, this.ownerDocument.activeElement);
105c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch    },
106c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch
107c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch    /**
108c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch     * The keyboard shortcut that triggers the command. This is a string
109c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch     * consisting of a keyIdentifier (as reported by WebKit in keydown) as
110c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch     * well as optional key modifiers joinded with a '-'.
111c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch     *
112c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch     * Multiple keyboard shortcuts can be provided by separating them by
113c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch     * whitespace.
114c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch     *
115c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch     * For example:
116c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch     *   "F1"
117c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch     *   "U+0008-Meta" for Apple command backspace.
118c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch     *   "U+0041-Ctrl" for Control A
119c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch     *   "U+007F U+0008-Meta" for Delete and Command Backspace
120c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch     *
121c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch     * @type {string}
122c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch     */
123c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch    shortcut_: '',
124c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch    get shortcut() {
125c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch      return this.shortcut_;
126c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch    },
127c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch    set shortcut(shortcut) {
128c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch      var oldShortcut = this.shortcut_;
129c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch      if (shortcut !== oldShortcut) {
130c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch        this.keyboardShortcuts_ = shortcut.split(/\s+/).map(function(shortcut) {
131c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch          return new KeyboardShortcut(shortcut);
132c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch        });
133c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch
134c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch        // Set this after the keyboardShortcuts_ since that might throw.
135c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch        this.shortcut_ = shortcut;
136c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch        cr.dispatchPropertyChange(this, 'shortcut', this.shortcut_,
137c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch                                  oldShortcut);
138c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch      }
139c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch    },
140c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch
141c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch    /**
142c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch     * Whether the event object matches the shortcut for this command.
143c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch     * @param {!Event} e The key event object.
144c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch     * @return {boolean} Whether it matched or not.
145c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch     */
146c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch    matchesEvent: function(e) {
147c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch      if (!this.keyboardShortcuts_)
148c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch        return false;
149c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch
150c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch      return this.keyboardShortcuts_.some(function(keyboardShortcut) {
151c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch        return keyboardShortcut.matchesEvent(e);
152c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch        });
153c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch      }
154c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  };
155c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch
156c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  /**
157c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch   * The label of the command.
158c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch   * @type {string}
159c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch   */
160c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  cr.defineProperty(Command, 'label', cr.PropertyKind.ATTR);
161c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch
162c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  /**
163c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch   * Whether the command is disabled or not.
164c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch   * @type {boolean}
165c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch   */
166c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  cr.defineProperty(Command, 'disabled', cr.PropertyKind.BOOL_ATTR);
167c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch
168c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  /**
169c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch   * Whether the command is hidden or not.
170c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch   * @type {boolean}
171c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch   */
172c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  cr.defineProperty(Command, 'hidden', cr.PropertyKind.BOOL_ATTR);
173c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch
174c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  /**
1753345a6884c488ff3a535c2c9acdd33d74b37e311Iain Merrick   * Whether the command is checked or not.
1763345a6884c488ff3a535c2c9acdd33d74b37e311Iain Merrick   * @type {boolean}
1773345a6884c488ff3a535c2c9acdd33d74b37e311Iain Merrick   */
1783345a6884c488ff3a535c2c9acdd33d74b37e311Iain Merrick  cr.defineProperty(Command, 'checked', cr.PropertyKind.BOOL_ATTR);
1793345a6884c488ff3a535c2c9acdd33d74b37e311Iain Merrick
1803345a6884c488ff3a535c2c9acdd33d74b37e311Iain Merrick  /**
181c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch   * Dispatches a canExecute event on the target.
182c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch   * @param {cr.ui.Command} command The command that we are testing for.
183c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch   * @param {Element} target The target element to dispatch the event on.
184c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch   */
185c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  function dispatchCanExecuteEvent(command, target) {
186c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch    var e = new CanExecuteEvent(command, true)
187c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch    target.dispatchEvent(e);
188c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch    command.disabled = !e.canExecute;
189c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  }
190c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch
191c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  /**
192c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch   * The command managers for different documents.
193c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch   */
194c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  var commandManagers = {};
195c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch
196c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  /**
197c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch   * Keeps track of the focused element and updates the commands when the focus
198c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch   * changes.
199c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch   * @param {!Document} doc The document that we are managing the commands for.
200c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch   * @constructor
201c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch   */
202c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  function CommandManager(doc) {
2033345a6884c488ff3a535c2c9acdd33d74b37e311Iain Merrick    doc.addEventListener('focus', this.handleFocus_.bind(this), true);
204c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch    // Make sure we add the listener to the bubbling phase so that elements can
205c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch    // prevent the command.
2063345a6884c488ff3a535c2c9acdd33d74b37e311Iain Merrick    doc.addEventListener('keydown', this.handleKeyDown_.bind(this), false);
207c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  }
208c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch
209c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  /**
210c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch   * Initializes a command manager for the document as needed.
211c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch   * @param {!Document} doc The document to manage the commands for.
212c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch   */
213c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  CommandManager.init = function(doc) {
214c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch    var uid = cr.getUid(doc);
215c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch    if (!(uid in commandManagers)) {
216c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch      commandManagers[uid] = new CommandManager(doc);
217c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch    }
218c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  },
219c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch
220c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  CommandManager.prototype = {
221c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch
222c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch    /**
223c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch     * Handles focus changes on the document.
224c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch     * @param {Event} e The focus event object.
225c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch     * @private
226c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch     */
227c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch    handleFocus_: function(e) {
228c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch      var target = e.target;
229c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch      var commands = Array.prototype.slice.call(
230c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch          target.ownerDocument.querySelectorAll('command'));
231c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch
232c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch      commands.forEach(function(command) {
233c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch        dispatchCanExecuteEvent(command, target);
234c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch      });
235c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch    },
236c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch
237c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch    /**
238c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch     * Handles the keydown event and routes it to the right command.
239c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch     * @param {!Event} e The keydown event.
240c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch     */
241c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch    handleKeyDown_: function(e) {
242c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch      var target = e.target;
243c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch      var commands = Array.prototype.slice.call(
244c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch          target.ownerDocument.querySelectorAll('command'));
245c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch
246c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch      for (var i = 0, command; command = commands[i]; i++) {
247c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch        if (!command.disabled && command.matchesEvent(e)) {
248c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch          e.preventDefault();
249c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch          // We do not want any other element to handle this.
250c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch          e.stopPropagation();
251c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch
252c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch          command.execute();
253c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch          return;
254c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch        }
255c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch      }
256c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch    }
257c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  };
258c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch
259c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  /**
260c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch   * The event type used for canExecute events.
261c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch   * @param {!cr.ui.Command} command The command that we are evaluating.
262c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch   * @extends {Event}
263c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch   */
264c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  function CanExecuteEvent(command) {
265c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch    var e = command.ownerDocument.createEvent('Event');
266c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch    e.initEvent('canExecute', true, false);
267c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch    e.__proto__ = CanExecuteEvent.prototype;
268c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch    e.command = command;
269c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch    return e;
270c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  }
271c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch
272c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  CanExecuteEvent.prototype = {
273c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch    __proto__: Event.prototype,
274c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch
275c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch    /**
276c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch     * The current command
277c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch     * @type {cr.ui.Command}
278c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch     */
279c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch    command: null,
280c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch
281c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch    /**
282c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch     * Whether the target can execute the command. Setting this also stops the
283c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch     * propagation.
284c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch     * @type {boolean}
285c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch     */
286c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch    canExecute_: false,
287c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch    get canExecute() {
288c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch      return this.canExecute_;
289c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch    },
290c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch    set canExecute(canExecute) {
2913345a6884c488ff3a535c2c9acdd33d74b37e311Iain Merrick      this.canExecute_ = !!canExecute;
292c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch      this.stopPropagation();
293c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch    }
294c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  };
295c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch
296c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  // Export
297c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  return {
298c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch    Command: Command,
299c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch    CanExecuteEvent: CanExecuteEvent
300c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  };
301c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch});
302