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