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