• 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
5cr.define('options', function() {
6  /** @const */ var List = cr.ui.List;
7  /** @const */ var ListItem = cr.ui.ListItem;
8
9  /**
10   * Creates a deletable list item, which has a button that will trigger a call
11   * to deleteItemAtIndex(index) in the list.
12   */
13  var DeletableItem = cr.ui.define('li');
14
15  DeletableItem.prototype = {
16    __proto__: ListItem.prototype,
17
18    /**
19     * The element subclasses should populate with content.
20     * @type {HTMLElement}
21     * @private
22     */
23    contentElement_: null,
24
25    /**
26     * The close button element.
27     * @type {HTMLElement}
28     * @private
29     */
30    closeButtonElement_: null,
31
32    /**
33     * Whether or not this item can be deleted.
34     * @type {boolean}
35     * @private
36     */
37    deletable_: true,
38
39    /** @override */
40    decorate: function() {
41      ListItem.prototype.decorate.call(this);
42
43      this.classList.add('deletable-item');
44
45      this.contentElement_ = this.ownerDocument.createElement('div');
46      this.appendChild(this.contentElement_);
47
48      this.closeButtonElement_ = this.ownerDocument.createElement('button');
49      this.closeButtonElement_.className =
50          'raw-button row-delete-button custom-appearance';
51      this.closeButtonElement_.addEventListener('mousedown',
52                                                this.handleMouseDownUpOnClose_);
53      this.closeButtonElement_.addEventListener('mouseup',
54                                                this.handleMouseDownUpOnClose_);
55      this.closeButtonElement_.addEventListener('focus',
56                                                this.handleFocus_.bind(this));
57      this.closeButtonElement_.title =
58          loadTimeData.getString('deletableItemDeleteButtonTitle');
59      this.appendChild(this.closeButtonElement_);
60    },
61
62    /**
63     * Returns the element subclasses should add content to.
64     * @return {HTMLElement} The element subclasses should popuplate.
65     */
66    get contentElement() {
67      return this.contentElement_;
68    },
69
70    /**
71     * Returns the close button element.
72     * @return {HTMLElement} The close |<button>| element.
73     */
74    get closeButtonElement() {
75      return this.closeButtonElement_;
76    },
77
78    /* Gets/sets the deletable property. An item that is not deletable doesn't
79     * show the delete button (although space is still reserved for it).
80     */
81    get deletable() {
82      return this.deletable_;
83    },
84    set deletable(value) {
85      this.deletable_ = value;
86      this.closeButtonElement_.disabled = !value;
87    },
88
89    /**
90     * Called when a focusable child element receives focus. Selects this item
91     * in the list selection model.
92     * @private
93     */
94    handleFocus_: function() {
95      var list = this.parentNode;
96      var index = list.getIndexOfListItem(this);
97      list.selectionModel.selectedIndex = index;
98      list.selectionModel.anchorIndex = index;
99    },
100
101    /**
102     * Don't let the list have a crack at the event. We don't want clicking the
103     * close button to change the selection of the list or to focus on the close
104     * button.
105     * @param {Event} e The mouse down/up event object.
106     * @private
107     */
108    handleMouseDownUpOnClose_: function(e) {
109      if (e.target.disabled)
110        return;
111      e.stopPropagation();
112      e.preventDefault();
113    },
114  };
115
116  var DeletableItemList = cr.ui.define('list');
117
118  DeletableItemList.prototype = {
119    __proto__: List.prototype,
120
121    /** @override */
122    decorate: function() {
123      List.prototype.decorate.call(this);
124      this.addEventListener('click', this.handleClick_);
125      this.addEventListener('keydown', this.handleKeyDown_);
126    },
127
128    /**
129     * Callback for onclick events.
130     * @param {Event} e The click event object.
131     * @private
132     */
133    handleClick_: function(e) {
134      if (this.disabled)
135        return;
136
137      var target = e.target;
138      if (target.classList.contains('row-delete-button')) {
139        var listItem = this.getListItemAncestor(target);
140        var selected = this.selectionModel.selectedIndexes;
141
142        // Check if the list item that contains the close button being clicked
143        // is not in the list of selected items. Only delete this item in that
144        // case.
145        var idx = this.getIndexOfListItem(listItem);
146        if (selected.indexOf(idx) == -1) {
147          this.deleteItemAtIndex(idx);
148        } else {
149          this.deleteSelectedItems_();
150        }
151      }
152    },
153
154    /**
155     * Callback for keydown events.
156     * @param {Event} e The keydown event object.
157     * @private
158     */
159    handleKeyDown_: function(e) {
160      // Map delete (and backspace on Mac) to item deletion (unless focus is
161      // in an input field, where it's intended for text editing).
162      if ((e.keyCode == 46 || (e.keyCode == 8 && cr.isMac)) &&
163          e.target.tagName != 'INPUT') {
164        this.deleteSelectedItems_();
165        // Prevent the browser from going back.
166        e.preventDefault();
167      }
168    },
169
170    /**
171     * Deletes all the currently selected items that are deletable.
172     * @private
173     */
174    deleteSelectedItems_: function() {
175      var selected = this.selectionModel.selectedIndexes;
176      // Reverse through the list of selected indexes to maintain the
177      // correct index values after deletion.
178      for (var j = selected.length - 1; j >= 0; j--) {
179        var index = selected[j];
180        if (this.getListItemByIndex(index).deletable)
181          this.deleteItemAtIndex(index);
182      }
183    },
184
185    /**
186     * Called when an item should be deleted; subclasses are responsible for
187     * implementing.
188     * @param {number} index The index of the item that is being deleted.
189     */
190    deleteItemAtIndex: function(index) {
191    },
192  };
193
194  return {
195    DeletableItemList: DeletableItemList,
196    DeletableItem: DeletableItem,
197  };
198});
199