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 /** | |
9 * @typedef {{childIds: Array.<string>}} | |
10 * | |
11 * @see chrome/common/extensions/api/bookmarks.json | |
12 */ | |
13 var ReorderInfo; | |
14 | |
15 /** | |
16 * @typedef {{parentId: string, index: number, oldParentId: string, | |
17 * oldIndex: number}} | |
18 * | |
19 * @see chrome/common/extensions/api/bookmarks.json | |
20 */ | |
21 var MoveInfo; | |
22 | |
8 cr.define('bmm', function() { | 23 cr.define('bmm', function() { |
9 var List = cr.ui.List; | 24 var List = cr.ui.List; |
10 var ListItem = cr.ui.ListItem; | 25 var ListItem = cr.ui.ListItem; |
11 var ArrayDataModel = cr.ui.ArrayDataModel; | 26 var ArrayDataModel = cr.ui.ArrayDataModel; |
12 var ContextMenuButton = cr.ui.ContextMenuButton; | 27 var ContextMenuButton = cr.ui.ContextMenuButton; |
13 | 28 |
14 var list; | |
15 | |
16 /** | 29 /** |
17 * Basic array data model for use with bookmarks. | 30 * Basic array data model for use with bookmarks. |
18 * @param {!Array.<!BookmarkTreeNode>} items The bookmark items. | 31 * @param {!Array.<!BookmarkTreeNode>} items The bookmark items. |
19 * @constructor | 32 * @constructor |
20 * @extends {ArrayDataModel} | 33 * @extends {ArrayDataModel} |
21 */ | 34 */ |
22 function BookmarksArrayDataModel(items) { | 35 function BookmarksArrayDataModel(items) { |
23 ArrayDataModel.call(this, items); | 36 ArrayDataModel.call(this, items); |
24 } | 37 } |
25 | 38 |
(...skipping 24 matching lines...) Expand all Loading... | |
50 while ((n = parent.lastChild)) { | 63 while ((n = parent.lastChild)) { |
51 parent.removeChild(n); | 64 parent.removeChild(n); |
52 } | 65 } |
53 parent.appendChild(newChild); | 66 parent.appendChild(newChild); |
54 } | 67 } |
55 | 68 |
56 /** | 69 /** |
57 * Creates a new bookmark list. | 70 * Creates a new bookmark list. |
58 * @param {Object=} opt_propertyBag Optional properties. | 71 * @param {Object=} opt_propertyBag Optional properties. |
59 * @constructor | 72 * @constructor |
60 * @extends {HTMLButtonElement} | 73 * @extends {cr.ui.List} |
61 */ | 74 */ |
62 var BookmarkList = cr.ui.define('list'); | 75 var BookmarkList = cr.ui.define('list'); |
63 | 76 |
64 BookmarkList.prototype = { | 77 BookmarkList.prototype = { |
65 __proto__: List.prototype, | 78 __proto__: List.prototype, |
66 | 79 |
67 /** @override */ | 80 /** @override */ |
68 decorate: function() { | 81 decorate: function() { |
69 List.prototype.decorate.call(this); | 82 List.prototype.decorate.call(this); |
70 this.addEventListener('mousedown', this.handleMouseDown_); | 83 this.addEventListener('mousedown', this.handleMouseDown_); |
71 | 84 |
72 // HACK(arv): http://crbug.com/40902 | 85 // HACK(arv): http://crbug.com/40902 |
73 window.addEventListener('resize', this.redraw.bind(this)); | 86 window.addEventListener('resize', this.redraw.bind(this)); |
74 | 87 |
75 // We could add the ContextMenuButton in the BookmarkListItem but it slows | 88 // We could add the ContextMenuButton in the BookmarkListItem but it slows |
76 // down redraws a lot so we do this on mouseovers instead. | 89 // down redraws a lot so we do this on mouseovers instead. |
77 this.addEventListener('mouseover', this.handleMouseOver_.bind(this)); | 90 this.addEventListener('mouseover', this.handleMouseOver_.bind(this)); |
78 | 91 |
79 bmm.list = this; | 92 bmm.list = this; |
Dan Beam
2014/09/25 04:27:18
wait, this is already set?
Vitaly Pavlenko
2014/09/25 15:37:15
Ok, I reset this assignment.
| |
80 }, | 93 }, |
81 | 94 |
95 /** | |
96 * @param {!BookmarkTreeNode} bookmarkNode | |
97 * @override | |
98 */ | |
82 createItem: function(bookmarkNode) { | 99 createItem: function(bookmarkNode) { |
83 return new BookmarkListItem(bookmarkNode); | 100 return new BookmarkListItem(bookmarkNode); |
84 }, | 101 }, |
85 | 102 |
86 /** @private {string} */ | 103 /** @private {string} */ |
87 parentId_: '', | 104 parentId_: '', |
88 | 105 |
89 /** @private {number} */ | 106 /** @private {number} */ |
90 loadCount_: 0, | 107 loadCount_: 0, |
91 | 108 |
(...skipping 99 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
191 var event = new Event('urlClicked', {bubbles: true}); | 208 var event = new Event('urlClicked', {bubbles: true}); |
192 event.url = url; | 209 event.url = url; |
193 event.originalEvent = originalEvent; | 210 event.originalEvent = originalEvent; |
194 this.dispatchEvent(event); | 211 this.dispatchEvent(event); |
195 }, | 212 }, |
196 | 213 |
197 /** | 214 /** |
198 * Handles mousedown events so that we can prevent the auto scroll as | 215 * Handles mousedown events so that we can prevent the auto scroll as |
199 * necessary. | 216 * necessary. |
200 * @private | 217 * @private |
201 * @param {!MouseEvent} e The mousedown event object. | 218 * @param {!Event} e The mousedown event object. |
202 */ | 219 */ |
203 handleMouseDown_: function(e) { | 220 handleMouseDown_: function(e) { |
221 e = /** @type {!MouseEvent} */(e); | |
204 if (e.button == 1) { | 222 if (e.button == 1) { |
205 // WebKit no longer fires click events for middle clicks so we manually | 223 // WebKit no longer fires click events for middle clicks so we manually |
206 // listen to mouse up to dispatch a click event. | 224 // listen to mouse up to dispatch a click event. |
207 this.addEventListener('mouseup', this.handleMiddleMouseUp_); | 225 this.addEventListener('mouseup', this.handleMiddleMouseUp_); |
208 | 226 |
209 // When the user does a middle click we need to prevent the auto scroll | 227 // When the user does a middle click we need to prevent the auto scroll |
210 // in case the user is trying to middle click to open a bookmark in a | 228 // in case the user is trying to middle click to open a bookmark in a |
211 // background tab. | 229 // background tab. |
212 // We do not do this in case the target is an input since middle click | 230 // We do not do this in case the target is an input since middle click |
213 // is also paste on Linux and we don't want to break that. | 231 // is also paste on Linux and we don't want to break that. |
214 if (e.target.tagName != 'INPUT') | 232 if (e.target.tagName != 'INPUT') |
215 e.preventDefault(); | 233 e.preventDefault(); |
216 } | 234 } |
217 }, | 235 }, |
218 | 236 |
219 /** | 237 /** |
220 * WebKit no longer dispatches click events for middle clicks so we need | 238 * WebKit no longer dispatches click events for middle clicks so we need |
221 * to emulate it. | 239 * to emulate it. |
222 * @private | 240 * @private |
223 * @param {!MouseEvent} e The mouse up event object. | 241 * @param {!Event} e The mouse up event object. |
224 */ | 242 */ |
225 handleMiddleMouseUp_: function(e) { | 243 handleMiddleMouseUp_: function(e) { |
244 e = /** @type {!MouseEvent} */(e); | |
226 this.removeEventListener('mouseup', this.handleMiddleMouseUp_); | 245 this.removeEventListener('mouseup', this.handleMiddleMouseUp_); |
227 if (e.button == 1) { | 246 if (e.button == 1) { |
228 var el = e.target; | 247 var el = e.target; |
229 while (el.parentNode != this) { | 248 while (el.parentNode != this) { |
230 el = el.parentNode; | 249 el = el.parentNode; |
231 } | 250 } |
232 var node = el.bookmarkNode; | 251 var node = el.bookmarkNode; |
233 if (node && !bmm.isFolder(node)) | 252 if (node && !bmm.isFolder(node)) |
234 this.dispatchUrlClickedEvent_(node.url, e); | 253 this.dispatchUrlClickedEvent_(node.url, e); |
235 } | 254 } |
236 e.preventDefault(); | 255 e.preventDefault(); |
237 }, | 256 }, |
238 | 257 |
239 // Bookmark model update callbacks | 258 // Bookmark model update callbacks |
240 handleBookmarkChanged: function(id, changeInfo) { | 259 handleBookmarkChanged: function(id, changeInfo) { |
241 var dataModel = this.dataModel; | 260 var dataModel = this.dataModel; |
242 var index = dataModel.findIndexById(id); | 261 var index = dataModel.findIndexById(id); |
243 if (index != -1) { | 262 if (index != -1) { |
244 var bookmarkNode = this.dataModel.item(index); | 263 var bookmarkNode = this.dataModel.item(index); |
245 bookmarkNode.title = changeInfo.title; | 264 bookmarkNode.title = changeInfo.title; |
246 if ('url' in changeInfo) | 265 if ('url' in changeInfo) |
247 bookmarkNode.url = changeInfo['url']; | 266 bookmarkNode.url = changeInfo['url']; |
248 | 267 |
249 dataModel.updateIndex(index); | 268 dataModel.updateIndex(index); |
250 } | 269 } |
251 }, | 270 }, |
252 | 271 |
272 /** | |
273 * @param {string} id | |
274 * @param {ReorderInfo} reorderInfo | |
275 */ | |
253 handleChildrenReordered: function(id, reorderInfo) { | 276 handleChildrenReordered: function(id, reorderInfo) { |
254 if (this.parentId == id) { | 277 if (this.parentId == id) { |
255 // We create a new data model with updated items in the right order. | 278 // We create a new data model with updated items in the right order. |
256 var dataModel = this.dataModel; | 279 var dataModel = this.dataModel; |
257 var items = {}; | 280 var items = {}; |
258 for (var i = this.dataModel.length - 1; i >= 0; i--) { | 281 for (var i = this.dataModel.length - 1; i >= 0; i--) { |
259 var bookmarkNode = dataModel.item(i); | 282 var bookmarkNode = dataModel.item(i); |
260 items[bookmarkNode.id] = bookmarkNode; | 283 items[bookmarkNode.id] = bookmarkNode; |
261 } | 284 } |
262 var newArray = []; | 285 var newArray = []; |
263 for (var i = 0; i < reorderInfo.childIds.length; i++) { | 286 for (var i = 0; i < reorderInfo.childIds.length; i++) { |
264 newArray[i] = items[reorderInfo.childIds[i]]; | 287 newArray[i] = items[reorderInfo.childIds[i]]; |
265 newArray[i].index = i; | 288 newArray[i].index = i; |
266 } | 289 } |
267 | 290 |
268 this.dataModel = new BookmarksArrayDataModel(newArray); | 291 this.dataModel = new BookmarksArrayDataModel(newArray); |
269 } | 292 } |
270 }, | 293 }, |
271 | 294 |
272 handleCreated: function(id, bookmarkNode) { | 295 handleCreated: function(id, bookmarkNode) { |
273 if (this.parentId == bookmarkNode.parentId) | 296 if (this.parentId == bookmarkNode.parentId) |
274 this.dataModel.splice(bookmarkNode.index, 0, bookmarkNode); | 297 this.dataModel.splice(bookmarkNode.index, 0, bookmarkNode); |
275 }, | 298 }, |
276 | 299 |
300 /** | |
301 * @param {string} id | |
302 * @param {MoveInfo} moveInfo | |
303 */ | |
277 handleMoved: function(id, moveInfo) { | 304 handleMoved: function(id, moveInfo) { |
278 if (moveInfo.parentId == this.parentId || | 305 if (moveInfo.parentId == this.parentId || |
279 moveInfo.oldParentId == this.parentId) { | 306 moveInfo.oldParentId == this.parentId) { |
280 | 307 |
281 var dataModel = this.dataModel; | 308 var dataModel = this.dataModel; |
282 | 309 |
283 if (moveInfo.oldParentId == moveInfo.parentId) { | 310 if (moveInfo.oldParentId == moveInfo.parentId) { |
284 // Reorder within this folder | 311 // Reorder within this folder |
285 | 312 |
286 this.startBatchUpdates(); | 313 this.startBatchUpdates(); |
(...skipping 50 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
337 // the size should not change since we already fixed the width. | 364 // the size should not change since we already fixed the width. |
338 window.setTimeout(function() { | 365 window.setTimeout(function() { |
339 list.style.width = ''; | 366 list.style.width = ''; |
340 }, 0); | 367 }, 0); |
341 } | 368 } |
342 } | 369 } |
343 }; | 370 }; |
344 | 371 |
345 /** | 372 /** |
346 * The ID of the bookmark folder we are displaying. | 373 * The ID of the bookmark folder we are displaying. |
347 * @type {string} | |
348 */ | 374 */ |
349 cr.defineProperty(BookmarkList, 'parentId', cr.PropertyKind.JS, | 375 cr.defineProperty(BookmarkList, 'parentId', cr.PropertyKind.JS, |
350 function() { | 376 function() { |
351 this.reload(); | 377 this.reload(); |
352 }); | 378 }); |
353 | 379 |
354 /** | 380 /** |
355 * The contextMenu property. | 381 * The contextMenu property. |
356 * @type {cr.ui.Menu} | |
357 */ | 382 */ |
358 cr.ui.contextMenuHandler.addContextMenuProperty(BookmarkList); | 383 cr.ui.contextMenuHandler.addContextMenuProperty(BookmarkList); |
384 /** @type {cr.ui.Menu} */ | |
385 BookmarkList.prototype.contextMenu; | |
359 | 386 |
360 /** | 387 /** |
361 * Creates a new bookmark list item. | 388 * Creates a new bookmark list item. |
362 * @param {!BookmarkTreeNode} bookmarkNode The bookmark node this represents. | 389 * @param {!BookmarkTreeNode} bookmarkNode The bookmark node this represents. |
363 * @constructor | 390 * @constructor |
364 * @extends {cr.ui.ListItem} | 391 * @extends {cr.ui.ListItem} |
365 */ | 392 */ |
366 function BookmarkListItem(bookmarkNode) { | 393 function BookmarkListItem(bookmarkNode) { |
367 var el = cr.doc.createElement('div'); | 394 var el = cr.doc.createElement('div'); |
368 el.bookmarkNode = bookmarkNode; | 395 el.bookmarkNode = bookmarkNode; |
(...skipping 131 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
500 listItem.editing = false; | 527 listItem.editing = false; |
501 } | 528 } |
502 }, 50); | 529 }, 50); |
503 } | 530 } |
504 | 531 |
505 var doc = this.ownerDocument; | 532 var doc = this.ownerDocument; |
506 if (editing) { | 533 if (editing) { |
507 this.setAttribute('editing', ''); | 534 this.setAttribute('editing', ''); |
508 this.draggable = false; | 535 this.draggable = false; |
509 | 536 |
510 labelInput = doc.createElement('input'); | 537 labelInput = /** @type {HTMLElement} */(doc.createElement('input')); |
511 labelInput.placeholder = | 538 labelInput.placeholder = |
512 loadTimeData.getString('name_input_placeholder'); | 539 loadTimeData.getString('name_input_placeholder'); |
513 replaceAllChildren(labelEl, labelInput); | 540 replaceAllChildren(labelEl, labelInput); |
514 labelInput.value = title; | 541 labelInput.value = title; |
515 | 542 |
516 if (!isFolder) { | 543 if (!isFolder) { |
517 urlInput = doc.createElement('input'); | 544 urlInput = /** @type {HTMLElement} */(doc.createElement('input')); |
518 urlInput.type = 'url'; | 545 urlInput.type = 'url'; |
519 urlInput.required = true; | 546 urlInput.required = true; |
520 urlInput.placeholder = | 547 urlInput.placeholder = |
521 loadTimeData.getString('url_input_placeholder'); | 548 loadTimeData.getString('url_input_placeholder'); |
522 | 549 |
523 // We also need a name for the input for the CSS to work. | 550 // We also need a name for the input for the CSS to work. |
524 urlInput.name = '-url-input-' + cr.createUid(); | 551 urlInput.name = '-url-input-' + cr.createUid(); |
525 replaceAllChildren(urlEl, urlInput); | 552 replaceAllChildren(assert(urlEl), urlInput); |
526 urlInput.value = url; | 553 urlInput.value = url; |
527 } | 554 } |
528 | 555 |
529 function stopPropagation(e) { | 556 var stopPropagation = function(e) { |
530 e.stopPropagation(); | 557 e.stopPropagation(); |
531 } | 558 }; |
532 | 559 |
533 var eventsToStop = | 560 var eventsToStop = |
534 ['mousedown', 'mouseup', 'contextmenu', 'dblclick', 'paste']; | 561 ['mousedown', 'mouseup', 'contextmenu', 'dblclick', 'paste']; |
535 eventsToStop.forEach(function(type) { | 562 eventsToStop.forEach(function(type) { |
536 labelInput.addEventListener(type, stopPropagation); | 563 labelInput.addEventListener(type, stopPropagation); |
537 }); | 564 }); |
538 labelInput.addEventListener('keydown', handleKeydown); | 565 labelInput.addEventListener('keydown', handleKeydown); |
539 labelInput.addEventListener('blur', handleBlur); | 566 labelInput.addEventListener('blur', handleBlur); |
540 cr.ui.limitInputWidth(labelInput, this, 100, 0.5); | 567 cr.ui.limitInputWidth(labelInput, this, 100, 0.5); |
541 labelInput.focus(); | 568 labelInput.focus(); |
(...skipping 52 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
594 } | 621 } |
595 } else if (newLabel != title || newUrl != url) { | 622 } else if (newLabel != title || newUrl != url) { |
596 cr.dispatchSimpleEvent(this, 'edit', true); | 623 cr.dispatchSimpleEvent(this, 'edit', true); |
597 } | 624 } |
598 } | 625 } |
599 } | 626 } |
600 }; | 627 }; |
601 | 628 |
602 return { | 629 return { |
603 BookmarkList: BookmarkList, | 630 BookmarkList: BookmarkList, |
604 list: list | 631 list: $('list'), |
Dan Beam
2014/09/24 21:38:58
either:
get list() { return $('list'); },
or
Vitaly Pavlenko
2014/09/24 22:18:23
Done.
| |
605 }; | 632 }; |
606 }); | 633 }); |
OLD | NEW |