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 */ var AutocompleteList = cr.ui.AutocompleteList; | |
7 /** @const */ var InlineEditableItem = options.InlineEditableItem; | |
8 /** @const */ var 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'] = loadTimeData.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.className = 'weakrtl'; | |
74 urlField.placeholder = loadTimeData.getString('startupPagesPlaceholder'); | |
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 |