OLD | NEW |
1 // Copyright (c) 2012 The Chromium Authors. All rights reserved. | 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 | 2 // Use of this source code is governed by a BSD-style license that can be |
3 // found in the LICENSE file. | 3 // found in the LICENSE file. |
4 | 4 |
5 // TODO(arv): Now that this is driven by a data model, implement a data model | 5 // TODO(arv): Now that this is driven by a data model, implement a data model |
6 // that handles the loading and the events from the bookmark backend. | 6 // that handles the loading and the events from the bookmark backend. |
7 | 7 |
8 /** | 8 /** |
9 * @typedef {{childIds: Array<string>}} | 9 * @typedef {{childIds: Array<string>}} |
10 * | 10 * |
(...skipping 133 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
144 cr.dispatchSimpleEvent(this, 'invalidId'); | 144 cr.dispatchSimpleEvent(this, 'invalidId'); |
145 return; | 145 return; |
146 } | 146 } |
147 | 147 |
148 this.dataModel = new BookmarksArrayDataModel(items); | 148 this.dataModel = new BookmarksArrayDataModel(items); |
149 | 149 |
150 this.fixWidth_(); | 150 this.fixWidth_(); |
151 cr.dispatchSimpleEvent(this, 'load'); | 151 cr.dispatchSimpleEvent(this, 'load'); |
152 | 152 |
153 // Use the same histogram configuration as UMA_HISTOGRAM_COUNTS_1000(). | 153 // Use the same histogram configuration as UMA_HISTOGRAM_COUNTS_1000(). |
154 chrome.metricsPrivate.recordValue({ | 154 chrome.metricsPrivate.recordValue( |
155 'metricName': 'Bookmarks.BookmarksInFolder', | 155 { |
156 'type': chrome.metricsPrivate.MetricTypeType.HISTOGRAM_LOG, | 156 'metricName': 'Bookmarks.BookmarksInFolder', |
157 'min': 1, | 157 'type': chrome.metricsPrivate.MetricTypeType.HISTOGRAM_LOG, |
158 'max': 1000, | 158 'min': 1, |
159 'buckets': 50 | 159 'max': 1000, |
160 }, this.dataModel.length); | 160 'buckets': 50 |
| 161 }, |
| 162 this.dataModel.length); |
161 }, | 163 }, |
162 | 164 |
163 /** | 165 /** |
164 * The bookmark node that the list is currently displaying. If we are | 166 * The bookmark node that the list is currently displaying. If we are |
165 * currently displaying search this returns null. | 167 * currently displaying search this returns null. |
166 * @type {BookmarkTreeNode} | 168 * @type {BookmarkTreeNode} |
167 */ | 169 */ |
168 get bookmarkNode() { | 170 get bookmarkNode() { |
169 if (this.isSearch()) | 171 if (this.isSearch()) |
170 return null; | 172 return null; |
(...skipping 25 matching lines...) Expand all Loading... |
196 * lazily. | 198 * lazily. |
197 * @private | 199 * @private |
198 * @param {!Event} e The mouseover event object. | 200 * @param {!Event} e The mouseover event object. |
199 */ | 201 */ |
200 handleMouseOver_: function(e) { | 202 handleMouseOver_: function(e) { |
201 var el = e.target; | 203 var el = e.target; |
202 while (el && el.parentNode != this) { | 204 while (el && el.parentNode != this) { |
203 el = el.parentNode; | 205 el = el.parentNode; |
204 } | 206 } |
205 | 207 |
206 if (el && el.parentNode == this && | 208 if (el && el.parentNode == this && !el.editing && |
207 !el.editing && | |
208 !(el.lastChild instanceof ContextMenuButton)) { | 209 !(el.lastChild instanceof ContextMenuButton)) { |
209 el.appendChild(new ContextMenuButton); | 210 el.appendChild(new ContextMenuButton); |
210 } | 211 } |
211 }, | 212 }, |
212 | 213 |
213 /** | 214 /** |
214 * Dispatches an urlClicked event which is used to open URLs in new | 215 * Dispatches an urlClicked event which is used to open URLs in new |
215 * tabs etc. | 216 * tabs etc. |
216 * @private | 217 * @private |
217 * @param {string} url The URL that was clicked. | 218 * @param {string} url The URL that was clicked. |
218 * @param {!Event} originalEvent The original click event object. | 219 * @param {!Event} originalEvent The original click event object. |
219 */ | 220 */ |
220 dispatchUrlClickedEvent_: function(url, originalEvent) { | 221 dispatchUrlClickedEvent_: function(url, originalEvent) { |
221 var event = new Event('urlClicked', {bubbles: true}); | 222 var event = new Event('urlClicked', {bubbles: true}); |
222 event.url = url; | 223 event.url = url; |
223 event.originalEvent = originalEvent; | 224 event.originalEvent = originalEvent; |
224 this.dispatchEvent(event); | 225 this.dispatchEvent(event); |
225 }, | 226 }, |
226 | 227 |
227 /** | 228 /** |
228 * Handles mousedown events so that we can prevent the auto scroll as | 229 * Handles mousedown events so that we can prevent the auto scroll as |
229 * necessary. | 230 * necessary. |
230 * @private | 231 * @private |
231 * @param {!Event} e The mousedown event object. | 232 * @param {!Event} e The mousedown event object. |
232 */ | 233 */ |
233 handleMouseDown_: function(e) { | 234 handleMouseDown_: function(e) { |
234 e = /** @type {!MouseEvent} */(e); | 235 e = /** @type {!MouseEvent} */ (e); |
235 if (e.button == 1) { | 236 if (e.button == 1) { |
236 // WebKit no longer fires click events for middle clicks so we manually | 237 // WebKit no longer fires click events for middle clicks so we manually |
237 // listen to mouse up to dispatch a click event. | 238 // listen to mouse up to dispatch a click event. |
238 this.addEventListener('mouseup', this.handleMiddleMouseUp_); | 239 this.addEventListener('mouseup', this.handleMiddleMouseUp_); |
239 | 240 |
240 // When the user does a middle click we need to prevent the auto scroll | 241 // When the user does a middle click we need to prevent the auto scroll |
241 // in case the user is trying to middle click to open a bookmark in a | 242 // in case the user is trying to middle click to open a bookmark in a |
242 // background tab. | 243 // background tab. |
243 // We do not do this in case the target is an input since middle click | 244 // We do not do this in case the target is an input since middle click |
244 // is also paste on Linux and we don't want to break that. | 245 // is also paste on Linux and we don't want to break that. |
245 if (e.target.tagName != 'INPUT') | 246 if (e.target.tagName != 'INPUT') |
246 e.preventDefault(); | 247 e.preventDefault(); |
247 } | 248 } |
248 }, | 249 }, |
249 | 250 |
250 /** | 251 /** |
251 * WebKit no longer dispatches click events for middle clicks so we need | 252 * WebKit no longer dispatches click events for middle clicks so we need |
252 * to emulate it. | 253 * to emulate it. |
253 * @private | 254 * @private |
254 * @param {!Event} e The mouse up event object. | 255 * @param {!Event} e The mouse up event object. |
255 */ | 256 */ |
256 handleMiddleMouseUp_: function(e) { | 257 handleMiddleMouseUp_: function(e) { |
257 e = /** @type {!MouseEvent} */(e); | 258 e = /** @type {!MouseEvent} */ (e); |
258 this.removeEventListener('mouseup', this.handleMiddleMouseUp_); | 259 this.removeEventListener('mouseup', this.handleMiddleMouseUp_); |
259 if (e.button == 1) { | 260 if (e.button == 1) { |
260 var el = e.target; | 261 var el = e.target; |
261 while (el.parentNode != this) { | 262 while (el.parentNode != this) { |
262 el = el.parentNode; | 263 el = el.parentNode; |
263 } | 264 } |
264 var node = el.bookmarkNode; | 265 var node = el.bookmarkNode; |
265 if (node && !bmm.isFolder(node)) | 266 if (node && !bmm.isFolder(node)) |
266 this.dispatchUrlClickedEvent_(node.url, e); | 267 this.dispatchUrlClickedEvent_(node.url, e); |
267 } | 268 } |
(...skipping 42 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
310 this.dataModel.splice(bookmarkNode.index, 0, bookmarkNode); | 311 this.dataModel.splice(bookmarkNode.index, 0, bookmarkNode); |
311 }, | 312 }, |
312 | 313 |
313 /** | 314 /** |
314 * @param {string} id | 315 * @param {string} id |
315 * @param {MoveInfo} moveInfo | 316 * @param {MoveInfo} moveInfo |
316 */ | 317 */ |
317 handleMoved: function(id, moveInfo) { | 318 handleMoved: function(id, moveInfo) { |
318 if (moveInfo.parentId == this.parentId || | 319 if (moveInfo.parentId == this.parentId || |
319 moveInfo.oldParentId == this.parentId) { | 320 moveInfo.oldParentId == this.parentId) { |
320 | |
321 var dataModel = this.dataModel; | 321 var dataModel = this.dataModel; |
322 | 322 |
323 if (moveInfo.oldParentId == moveInfo.parentId) { | 323 if (moveInfo.oldParentId == moveInfo.parentId) { |
324 // Reorder within this folder | 324 // Reorder within this folder |
325 | 325 |
326 this.startBatchUpdates(); | 326 this.startBatchUpdates(); |
327 | 327 |
328 var bookmarkNode = this.dataModel.item(moveInfo.oldIndex); | 328 var bookmarkNode = this.dataModel.item(moveInfo.oldIndex); |
329 this.dataModel.splice(moveInfo.oldIndex, 1); | 329 this.dataModel.splice(moveInfo.oldIndex, 1); |
330 this.dataModel.splice(moveInfo.index, 0, bookmarkNode); | 330 this.dataModel.splice(moveInfo.index, 0, bookmarkNode); |
(...skipping 31 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
362 * Workaround for http://crbug.com/40902 | 362 * Workaround for http://crbug.com/40902 |
363 * @private | 363 * @private |
364 */ | 364 */ |
365 fixWidth_: function() { | 365 fixWidth_: function() { |
366 var list = bmm.list; | 366 var list = bmm.list; |
367 if (this.loadCount_ || !list) | 367 if (this.loadCount_ || !list) |
368 return; | 368 return; |
369 | 369 |
370 // The width of the list is wrong after its content has changed. | 370 // The width of the list is wrong after its content has changed. |
371 // Fortunately the reported offsetWidth is correct so we can detect the | 371 // Fortunately the reported offsetWidth is correct so we can detect the |
372 //incorrect width. | 372 // incorrect width. |
373 if (list.offsetWidth != list.parentNode.clientWidth - list.offsetLeft) { | 373 if (list.offsetWidth != list.parentNode.clientWidth - list.offsetLeft) { |
374 // Set the width to the correct size. This causes the relayout. | 374 // Set the width to the correct size. This causes the relayout. |
375 list.style.width = list.parentNode.clientWidth - list.offsetLeft + 'px'; | 375 list.style.width = list.parentNode.clientWidth - list.offsetLeft + 'px'; |
376 // Remove the temporary style.width in a timeout. Once the timer fires | 376 // Remove the temporary style.width in a timeout. Once the timer fires |
377 // the size should not change since we already fixed the width. | 377 // the size should not change since we already fixed the width. |
378 window.setTimeout(function() { | 378 window.setTimeout(function() { |
379 list.style.width = ''; | 379 list.style.width = ''; |
380 }, 0); | 380 }, 0); |
381 } | 381 } |
382 } | 382 } |
383 }; | 383 }; |
384 | 384 |
385 /** | 385 /** |
386 * The ID of the bookmark folder we are displaying. | 386 * The ID of the bookmark folder we are displaying. |
387 */ | 387 */ |
388 cr.defineProperty(BookmarkList, 'parentId', cr.PropertyKind.JS, | 388 cr.defineProperty(BookmarkList, 'parentId', cr.PropertyKind.JS, function() { |
389 function() { | 389 this.reload(); |
390 this.reload(); | 390 }); |
391 }); | |
392 | 391 |
393 /** | 392 /** |
394 * The contextMenu property. | 393 * The contextMenu property. |
395 */ | 394 */ |
396 cr.ui.contextMenuHandler.addContextMenuProperty(BookmarkList); | 395 cr.ui.contextMenuHandler.addContextMenuProperty(BookmarkList); |
397 /** @type {cr.ui.Menu} */ | 396 /** @type {cr.ui.Menu} */ |
398 BookmarkList.prototype.contextMenu; | 397 BookmarkList.prototype.contextMenu; |
399 | 398 |
400 /** | 399 /** |
401 * Creates a new bookmark list item. | 400 * Creates a new bookmark list item. |
(...skipping 37 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
439 labelText.className = 'label-text'; | 438 labelText.className = 'label-text'; |
440 labelText.textContent = bookmarkNode.title; | 439 labelText.textContent = bookmarkNode.title; |
441 | 440 |
442 var urlEl = this.ownerDocument.createElement('div'); | 441 var urlEl = this.ownerDocument.createElement('div'); |
443 urlEl.className = 'url'; | 442 urlEl.className = 'url'; |
444 | 443 |
445 if (bmm.isFolder(bookmarkNode)) { | 444 if (bmm.isFolder(bookmarkNode)) { |
446 this.className = 'folder'; | 445 this.className = 'folder'; |
447 // TODO(pkasting): Condense folder icon resources together. | 446 // TODO(pkasting): Condense folder icon resources together. |
448 labelImg.style.content = cr.icon.getImage( | 447 labelImg.style.content = cr.icon.getImage( |
449 cr.isMac ? | 448 cr.isMac ? 'chrome://theme/IDR_BOOKMARK_BAR_FOLDER' : |
450 'chrome://theme/IDR_BOOKMARK_BAR_FOLDER' : | 449 'chrome://theme/IDR_FOLDER_CLOSED'); |
451 'chrome://theme/IDR_FOLDER_CLOSED'); | |
452 } else { | 450 } else { |
453 labelImg.style.content = cr.icon.getFavicon(bookmarkNode.url); | 451 labelImg.style.content = cr.icon.getFavicon(bookmarkNode.url); |
454 urlEl.textContent = bookmarkNode.url; | 452 urlEl.textContent = bookmarkNode.url; |
455 } | 453 } |
456 | 454 |
457 labelImgWrapper.appendChild(labelImg); | 455 labelImgWrapper.appendChild(labelImg); |
458 labelEl.appendChild(labelImgWrapper); | 456 labelEl.appendChild(labelImgWrapper); |
459 labelEl.appendChild(labelText); | 457 labelEl.appendChild(labelText); |
460 this.appendChild(labelEl); | 458 this.appendChild(labelEl); |
461 this.appendChild(urlEl); | 459 this.appendChild(urlEl); |
(...skipping 90 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
552 }, 50); | 550 }, 50); |
553 } | 551 } |
554 | 552 |
555 var doc = this.ownerDocument; | 553 var doc = this.ownerDocument; |
556 var labelTextEl = queryRequiredElement('.label-text', this); | 554 var labelTextEl = queryRequiredElement('.label-text', this); |
557 var urlEl = queryRequiredElement('.url', this); | 555 var urlEl = queryRequiredElement('.url', this); |
558 if (editing) { | 556 if (editing) { |
559 this.setAttribute('editing', ''); | 557 this.setAttribute('editing', ''); |
560 this.draggable = false; | 558 this.draggable = false; |
561 | 559 |
562 labelInput = /** @type {HTMLElement} */(doc.createElement('input')); | 560 labelInput = /** @type {HTMLElement} */ (doc.createElement('input')); |
563 labelInput.placeholder = | 561 labelInput.placeholder = |
564 loadTimeData.getString('name_input_placeholder'); | 562 loadTimeData.getString('name_input_placeholder'); |
565 replaceAllChildren(labelTextEl, labelInput); | 563 replaceAllChildren(labelTextEl, labelInput); |
566 labelInput.value = title; | 564 labelInput.value = title; |
567 | 565 |
568 if (!isFolder) { | 566 if (!isFolder) { |
569 urlInput = /** @type {HTMLElement} */(doc.createElement('input')); | 567 urlInput = /** @type {HTMLElement} */ (doc.createElement('input')); |
570 urlInput.type = 'url'; | 568 urlInput.type = 'url'; |
571 urlInput.required = true; | 569 urlInput.required = true; |
572 urlInput.placeholder = | 570 urlInput.placeholder = |
573 loadTimeData.getString('url_input_placeholder'); | 571 loadTimeData.getString('url_input_placeholder'); |
574 | 572 |
575 // We also need a name for the input for the CSS to work. | 573 // We also need a name for the input for the CSS to work. |
576 urlInput.name = '-url-input-' + cr.createUid(); | 574 urlInput.name = '-url-input-' + cr.createUid(); |
577 replaceAllChildren(assert(urlEl), urlInput); | 575 replaceAllChildren(assert(urlEl), urlInput); |
578 urlInput.value = url; | 576 urlInput.value = url; |
579 } | 577 } |
(...skipping 66 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
646 } | 644 } |
647 } else if (newLabel != title || newUrl != url) { | 645 } else if (newLabel != title || newUrl != url) { |
648 cr.dispatchSimpleEvent(this, 'edit', true); | 646 cr.dispatchSimpleEvent(this, 'edit', true); |
649 } | 647 } |
650 } | 648 } |
651 } | 649 } |
652 }; | 650 }; |
653 | 651 |
654 return { | 652 return { |
655 BookmarkList: BookmarkList, | 653 BookmarkList: BookmarkList, |
656 list: /** @type {Element} */(null), // Set when decorated. | 654 list: /** @type {Element} */ (null), // Set when decorated. |
657 }; | 655 }; |
658 }); | 656 }); |
OLD | NEW |