| OLD | NEW |
| (Empty) |
| 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 | |
| 5 cr.define('options.browser_options', function() { | |
| 6 const AutocompleteList = cr.ui.AutocompleteList; | |
| 7 const InlineEditableItem = options.InlineEditableItem; | |
| 8 const InlineEditableItemList = options.InlineEditableItemList; | |
| 9 | |
| 10 /** | |
| 11 * Creates a new startup page list item. | |
| 12 * @param {Object} pageInfo The page this item represents. | |
| 13 * @constructor | |
| 14 * @extends {cr.ui.ListItem} | |
| 15 */ | |
| 16 function StartupPageListItem(pageInfo) { | |
| 17 var el = cr.doc.createElement('div'); | |
| 18 el.pageInfo_ = pageInfo; | |
| 19 StartupPageListItem.decorate(el); | |
| 20 return el; | |
| 21 } | |
| 22 | |
| 23 /** | |
| 24 * Decorates an element as a startup page list item. | |
| 25 * @param {!HTMLElement} el The element to decorate. | |
| 26 */ | |
| 27 StartupPageListItem.decorate = function(el) { | |
| 28 el.__proto__ = StartupPageListItem.prototype; | |
| 29 el.decorate(); | |
| 30 }; | |
| 31 | |
| 32 StartupPageListItem.prototype = { | |
| 33 __proto__: InlineEditableItem.prototype, | |
| 34 | |
| 35 /** | |
| 36 * Input field for editing the page url. | |
| 37 * @type {HTMLElement} | |
| 38 * @private | |
| 39 */ | |
| 40 urlField_: null, | |
| 41 | |
| 42 /** @inheritDoc */ | |
| 43 decorate: function() { | |
| 44 InlineEditableItem.prototype.decorate.call(this); | |
| 45 | |
| 46 var pageInfo = this.pageInfo_; | |
| 47 | |
| 48 if (pageInfo['modelIndex'] == '-1') { | |
| 49 this.isPlaceholder = true; | |
| 50 pageInfo['title'] = localStrings.getString('startupAddLabel'); | |
| 51 pageInfo['url'] = ''; | |
| 52 } | |
| 53 | |
| 54 var titleEl = this.ownerDocument.createElement('div'); | |
| 55 titleEl.className = 'title'; | |
| 56 titleEl.classList.add('favicon-cell'); | |
| 57 titleEl.classList.add('weakrtl'); | |
| 58 titleEl.textContent = pageInfo['title']; | |
| 59 if (!this.isPlaceholder) { | |
| 60 titleEl.style.backgroundImage = url('chrome://favicon/' + | |
| 61 pageInfo['url']); | |
| 62 titleEl.title = pageInfo['tooltip']; | |
| 63 } | |
| 64 | |
| 65 this.contentElement.appendChild(titleEl); | |
| 66 | |
| 67 var urlEl = this.createEditableTextCell(pageInfo['url']); | |
| 68 urlEl.className = 'url'; | |
| 69 urlEl.classList.add('weakrtl'); | |
| 70 this.contentElement.appendChild(urlEl); | |
| 71 | |
| 72 var urlField = urlEl.querySelector('input') | |
| 73 urlField.required = true; | |
| 74 urlField.className = 'weakrtl'; | |
| 75 this.urlField_ = urlField; | |
| 76 | |
| 77 this.addEventListener('commitedit', this.onEditCommitted_); | |
| 78 | |
| 79 var self = this; | |
| 80 urlField.addEventListener('focus', function(event) { | |
| 81 self.parentNode.autocompleteList.attachToInput(urlField); | |
| 82 }); | |
| 83 urlField.addEventListener('blur', function(event) { | |
| 84 self.parentNode.autocompleteList.detach(); | |
| 85 }); | |
| 86 | |
| 87 if (!this.isPlaceholder) | |
| 88 this.draggable = true; | |
| 89 }, | |
| 90 | |
| 91 /** @inheritDoc */ | |
| 92 get currentInputIsValid() { | |
| 93 return this.urlField_.validity.valid; | |
| 94 }, | |
| 95 | |
| 96 /** @inheritDoc */ | |
| 97 get hasBeenEdited() { | |
| 98 return this.urlField_.value != this.pageInfo_['url']; | |
| 99 }, | |
| 100 | |
| 101 /** | |
| 102 * Called when committing an edit; updates the model. | |
| 103 * @param {Event} e The end event. | |
| 104 * @private | |
| 105 */ | |
| 106 onEditCommitted_: function(e) { | |
| 107 var url = this.urlField_.value; | |
| 108 if (this.isPlaceholder) | |
| 109 chrome.send('addStartupPage', [url]); | |
| 110 else | |
| 111 chrome.send('editStartupPage', [this.pageInfo_['modelIndex'], url]); | |
| 112 }, | |
| 113 }; | |
| 114 | |
| 115 var StartupPageList = cr.ui.define('list'); | |
| 116 | |
| 117 StartupPageList.prototype = { | |
| 118 __proto__: InlineEditableItemList.prototype, | |
| 119 | |
| 120 /** | |
| 121 * An autocomplete suggestion list for URL editing. | |
| 122 * @type {AutocompleteList} | |
| 123 */ | |
| 124 autocompleteList: null, | |
| 125 | |
| 126 /** | |
| 127 * The drop position information: "below" or "above". | |
| 128 */ | |
| 129 dropPos: null, | |
| 130 | |
| 131 /** @inheritDoc */ | |
| 132 decorate: function() { | |
| 133 InlineEditableItemList.prototype.decorate.call(this); | |
| 134 | |
| 135 // Listen to drag and drop events. | |
| 136 this.addEventListener('dragstart', this.handleDragStart_.bind(this)); | |
| 137 this.addEventListener('dragenter', this.handleDragEnter_.bind(this)); | |
| 138 this.addEventListener('dragover', this.handleDragOver_.bind(this)); | |
| 139 this.addEventListener('drop', this.handleDrop_.bind(this)); | |
| 140 this.addEventListener('dragleave', this.handleDragLeave_.bind(this)); | |
| 141 this.addEventListener('dragend', this.handleDragEnd_.bind(this)); | |
| 142 }, | |
| 143 | |
| 144 /** @inheritDoc */ | |
| 145 createItem: function(pageInfo) { | |
| 146 var item = new StartupPageListItem(pageInfo); | |
| 147 item.urlField_.disabled = this.disabled; | |
| 148 return item; | |
| 149 }, | |
| 150 | |
| 151 /** @inheritDoc */ | |
| 152 deleteItemAtIndex: function(index) { | |
| 153 chrome.send('removeStartupPages', [String(index)]); | |
| 154 }, | |
| 155 | |
| 156 /* | |
| 157 * Computes the target item of drop event. | |
| 158 * @param {Event} e The drop or dragover event. | |
| 159 * @private | |
| 160 */ | |
| 161 getTargetFromDropEvent_ : function(e) { | |
| 162 var target = e.target; | |
| 163 // e.target may be an inner element of the list item | |
| 164 while (target != null && !(target instanceof StartupPageListItem)) { | |
| 165 target = target.parentNode; | |
| 166 } | |
| 167 return target; | |
| 168 }, | |
| 169 | |
| 170 /* | |
| 171 * Handles the dragstart event. | |
| 172 * @param {Event} e The dragstart event. | |
| 173 * @private | |
| 174 */ | |
| 175 handleDragStart_: function(e) { | |
| 176 // Prevent dragging if the list is disabled. | |
| 177 if (this.disabled) { | |
| 178 e.preventDefault(); | |
| 179 return false; | |
| 180 } | |
| 181 | |
| 182 var target = e.target; | |
| 183 // StartupPageListItem should be the only draggable element type in the | |
| 184 // page but let's make sure. | |
| 185 if (target instanceof StartupPageListItem) { | |
| 186 this.draggedItem = target; | |
| 187 this.draggedItem.editable = false; | |
| 188 e.dataTransfer.effectAllowed = 'move'; | |
| 189 // We need to put some kind of data in the drag or it will be | |
| 190 // ignored. Use the URL in case the user drags to a text field or the | |
| 191 // desktop. | |
| 192 e.dataTransfer.setData('text/plain', target.urlField_.value); | |
| 193 } | |
| 194 }, | |
| 195 | |
| 196 /* | |
| 197 * Handles the dragenter event. | |
| 198 * @param {Event} e The dragenter event. | |
| 199 * @private | |
| 200 */ | |
| 201 handleDragEnter_: function(e) { | |
| 202 e.preventDefault(); | |
| 203 }, | |
| 204 | |
| 205 /* | |
| 206 * Handles the dragover event. | |
| 207 * @param {Event} e The dragover event. | |
| 208 * @private | |
| 209 */ | |
| 210 handleDragOver_: function(e) { | |
| 211 var dropTarget = this.getTargetFromDropEvent_(e); | |
| 212 // Determines whether the drop target is to accept the drop. | |
| 213 // The drop is only successful on another StartupPageListItem. | |
| 214 if (!(dropTarget instanceof StartupPageListItem) || | |
| 215 dropTarget == this.draggedItem || dropTarget.isPlaceholder) { | |
| 216 this.hideDropMarker_(); | |
| 217 return; | |
| 218 } | |
| 219 // Compute the drop postion. Should we move the dragged item to | |
| 220 // below or above the drop target? | |
| 221 var rect = dropTarget.getBoundingClientRect(); | |
| 222 var dy = e.clientY - rect.top; | |
| 223 var yRatio = dy / rect.height; | |
| 224 var dropPos = yRatio <= .5 ? 'above' : 'below'; | |
| 225 this.dropPos = dropPos; | |
| 226 this.showDropMarker_(dropTarget, dropPos); | |
| 227 e.preventDefault(); | |
| 228 }, | |
| 229 | |
| 230 /* | |
| 231 * Handles the drop event. | |
| 232 * @param {Event} e The drop event. | |
| 233 * @private | |
| 234 */ | |
| 235 handleDrop_: function(e) { | |
| 236 var dropTarget = this.getTargetFromDropEvent_(e); | |
| 237 this.hideDropMarker_(); | |
| 238 | |
| 239 // Insert the selection at the new position. | |
| 240 var newIndex = this.dataModel.indexOf(dropTarget.pageInfo_); | |
| 241 if (this.dropPos == 'below') | |
| 242 newIndex += 1; | |
| 243 | |
| 244 var selected = this.selectionModel.selectedIndexes; | |
| 245 var stringized_selected = []; | |
| 246 for (var j = 0; j < selected.length; j++) | |
| 247 stringized_selected.push(String(selected[j])); | |
| 248 | |
| 249 chrome.send('dragDropStartupPage', | |
| 250 [String(newIndex), stringized_selected] ); | |
| 251 }, | |
| 252 | |
| 253 /* | |
| 254 * Handles the dragleave event. | |
| 255 * @param {Event} e The dragleave event | |
| 256 * @private | |
| 257 */ | |
| 258 handleDragLeave_: function(e) { | |
| 259 this.hideDropMarker_(); | |
| 260 }, | |
| 261 | |
| 262 /** | |
| 263 * Handles the dragend event. | |
| 264 * @param {Event} e The dragend event | |
| 265 * @private | |
| 266 */ | |
| 267 handleDragEnd_: function(e) { | |
| 268 this.draggedItem.editable = true; | |
| 269 this.draggedItem.updateEditState(); | |
| 270 }, | |
| 271 | |
| 272 /* | |
| 273 * Shows and positions the marker to indicate the drop target. | |
| 274 * @param {HTMLElement} target The current target list item of drop | |
| 275 * @param {string} pos 'below' or 'above' | |
| 276 * @private | |
| 277 */ | |
| 278 showDropMarker_ : function(target, pos) { | |
| 279 window.clearTimeout(this.hideDropMarkerTimer_); | |
| 280 var marker = $('startupPagesListDropmarker'); | |
| 281 var rect = target.getBoundingClientRect(); | |
| 282 var markerHeight = 6; | |
| 283 if (pos == 'above') { | |
| 284 marker.style.top = (rect.top - markerHeight/2) + 'px'; | |
| 285 } else { | |
| 286 marker.style.top = (rect.bottom - markerHeight/2) + 'px'; | |
| 287 } | |
| 288 marker.style.width = rect.width + 'px'; | |
| 289 marker.style.left = rect.left + 'px'; | |
| 290 marker.style.display = 'block'; | |
| 291 }, | |
| 292 | |
| 293 /* | |
| 294 * Hides the drop marker. | |
| 295 * @private | |
| 296 */ | |
| 297 hideDropMarker_ : function() { | |
| 298 // Hide the marker in a timeout to reduce flickering as we move between | |
| 299 // valid drop targets. | |
| 300 window.clearTimeout(this.hideDropMarkerTimer_); | |
| 301 this.hideDropMarkerTimer_ = window.setTimeout(function() { | |
| 302 $('startupPagesListDropmarker').style.display = ''; | |
| 303 }, 100); | |
| 304 }, | |
| 305 }; | |
| 306 | |
| 307 return { | |
| 308 StartupPageList: StartupPageList | |
| 309 }; | |
| 310 }); | |
| OLD | NEW |