Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(107)

Side by Side Diff: resources/bookmark_manager/main.html

Issue 853002: Updating the Chromium reference build for Windows. The continuous... (Closed) Base URL: svn://chrome-svn/chrome/trunk/deps/reference_builds/chrome/
Patch Set: Added the symbol files back. Created 10 years, 9 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch | Annotate | Revision Log
« no previous file with comments | « resources/bookmark_manager/js/util.js ('k') | resources/bookmark_manager/manifest.json » ('j') | no next file with comments »
Toggle Intra-line Diffs ('i') | Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
OLDNEW
(Empty)
1 <!DOCTYPE html>
2 <html i18n-values="dir:textdirection">
3 <!--
4
5 Copyright (c) 2010 The Chromium Authors. All rights reserved.
6 Use of this source code is governed by a BSD-style license that can be
7 found in the LICENSE file.
8
9
10 This is work in progress:
11
12 Favicons: chrome-extension: is not allowed to access chrome://favicon. We need
13 to whitelist it or expose a way to get the data URI for the favicon (slow and
14 sucky).
15
16 Favicon of bmm does not work. No icon is showed.
17
18 -->
19 <head>
20 <title i18n-content="title"></title>
21
22 <link rel="stylesheet" href="css/list.css">
23 <link rel="stylesheet" href="css/tree.css">
24 <link rel="stylesheet" href="css/menu.css">
25 <link rel="stylesheet" href="css/bmm.css">
26
27 <script src="css/tree.css.js"></script>
28 <script src="css/bmm.css.js"></script>
29
30 <script src="js/cr.js"></script>
31 <script src="js/cr/event.js"></script>
32 <script src="js/cr/eventtarget.js"></script>
33 <script src="js/cr/promise.js"></script>
34 <script src="js/cr/ui.js"></script>
35 <script src="js/cr/ui/listselectionmodel.js"></script>
36 <script src="js/cr/ui/listitem.js"></script>
37 <script src="js/cr/ui/list.js"></script>
38 <script src="js/cr/ui/tree.js"></script>
39 <script src="js/cr/ui/command.js"></script>
40 <script src="js/cr/ui/menuitem.js"></script>
41 <script src="js/cr/ui/menu.js"></script>
42 <script src="js/cr/ui/menubutton.js"></script>
43 <script src="js/cr/ui/contextmenuhandler.js"></script>
44
45 <script src="js/util.js"></script>
46 <script src="js/localstrings.js"></script>
47 <script src="js/i18ntemplate.js"></script>
48
49 <script src="js/bmm/treeiterator.js"></script>
50 <script src="js/bmm.js"></script>
51 <script src="js/bmm/bookmarklist.js"></script>
52 <script src="js/bmm/bookmarktree.js"></script>
53
54 <script>
55
56 // Sometimes the extension API is not initialized.
57 if (!chrome.bookmarks)
58 window.location.reload();
59
60 // Allow platform specific CSS rules.
61 if (cr.isMac)
62 document.documentElement.setAttribute('os', 'mac');
63
64 </script>
65 </head>
66 <body i18n-values=".style.fontFamily:fontfamily;.style.fontSize:fontsize">
67
68 <div class="header">
69 <button onclick="resetSearch()" class="logo" tabindex=3></button>
70 <div>
71 <form onsubmit="setSearch(this.term.value); return false;"
72 class="form">
73 <input type="text" id="term" tabindex=1 autofocus>
74 <input type="submit" i18n-values=".value:search_button" tabindex=1>
75 </form>
76 <div class=toolbar>
77 <button menu="#organize-menu" tabindex="-1" i18n-content="organize_menu">< /button>
78 <button menu="#tools-menu" tabindex="-1" i18n-content="tools_menu"></butto n>
79 </div>
80 </div>
81 </div>
82
83 <div class=main>
84 <div id=tree-container>
85 <tree id=tree tabindex=2></tree>
86 </div>
87 <list id=list tabindex=2></list>
88 </div>
89
90
91 <script>
92
93 const BookmarkList = bmm.BookmarkList;
94 const BookmarkTree = bmm.BookmarkTree;
95 const ListItem = cr.ui.ListItem;
96 const TreeItem = cr.ui.TreeItem;
97
98 /**
99 * The id of the bookmark root.
100 * @type {number}
101 */
102 const ROOT_ID = '0';
103
104 var bookmarkCache = {
105 /**
106 * This returns a reference to the bookmark node that is cached by the tree
107 * or list. Use this funciton when we need to update the local cachea after
108 * changes. It only returns bookmarks that are used by the tree and/or the
109 * list.
110 * @param {string} The ID of the bookmark that we want to get.
111 * @return {BookmarkTreeNode}
112 */
113 getById: function(id) {
114 var el = bmm.treeLookup[id] || bmm.listLookup[id];
115 return el && el.bookmarkNode;
116 },
117
118 /**
119 * Removes the cached item from both the list and tree lookups.
120 */
121 remove: function(id) {
122 delete bmm.listLookup[id];
123
124 var treeItem = bmm.treeLookup[id];
125 if (treeItem) {
126 var items = treeItem.items; // is an HTMLCollection
127 for (var i = 0, item; item = items[i]; i++) {
128 var bookmarkNode = item.bookmarkNode;
129 delete bmm.treeLookup[bookmarkNode.id];
130 }
131 delete bmm.treeLookup[id];
132 }
133 },
134
135 /**
136 * Updates the underlying bookmark node for the tree items and list items by
137 * querying the bookmark backend.
138 * @param {string} id The id of the node to update the children for.
139 * @param {Function=} opt_f A funciton to call when done.
140 */
141 updateChildren: function(id, opt_f) {
142 function updateItem(bookmarkNode) {
143 var treeItem = bmm.treeLookup[bookmarkNode.id];
144 if (treeItem) {
145 treeItem.bookmarkNode = bookmarkNode;
146 }
147 var listItem = bmm.listLookup[bookmarkNode.id];
148 if (listItem) {
149 listItem.bookmarkNode = bookmarkNode;
150 }
151 }
152
153 chrome.bookmarks.getChildren(id, function(children) {
154 children.forEach(updateItem);
155
156 if (opt_f)
157 opt_f(children);
158 });
159 }
160 };
161
162 </script>
163 <script>
164
165 BookmarkList.decorate(list);
166
167 var searchTreeItem = new TreeItem({
168 label: 'Search',
169 icon: 'images/bookmark_manager_search.png',
170 bookmarkId: 'q='
171 });
172 bmm.treeLookup[searchTreeItem.bookmarkId] = searchTreeItem;
173
174 var recentTreeItem = new TreeItem({
175 label: 'Recent',
176 icon: 'images/bookmark_manager_recent.png',
177 bookmarkId: 'recent'
178 });
179 bmm.treeLookup[recentTreeItem.bookmarkId] = recentTreeItem;
180
181 BookmarkTree.decorate(tree);
182
183 tree.addEventListener('change', function() {
184 navigateTo(tree.selectedItem.bookmarkId);
185 });
186
187 /**
188 * Navigates to a bookmark ID.
189 * @param {string} id The ID to navigate to.
190 */
191 function navigateTo(id) {
192 console.info('navigateTo', window.location.hash, id);
193 // Update the location hash using a timer to prevent reentrancy. This is how
194 // often we add history entries and the time here is a bit arbitrary but was
195 // picked as the smallest time a human perceives as instant.
196 clearTimeout(navigateTo.timer_);
197 navigateTo.timer_ = setTimeout(function() {
198 window.location.hash = tree.selectedItem.bookmarkId;
199 }, 300);
200 updateParentId(id);
201 }
202
203 /**
204 * Updates the parent ID of the bookmark list and selects the correct tree item.
205 * @param {string} id The id.
206 */
207 function updateParentId(id) {
208 list.parentId = id;
209 if (id in bmm.treeLookup)
210 tree.selectedItem = bmm.treeLookup[id];
211 }
212
213 // We listen to hashchange so that we can update the currently shown folder when
214 // the user goes back and forward in the history.
215 window.onhashchange = function(e) {
216 var id = window.location.hash.slice(1);
217
218 var valid = false;
219
220 // In case we got a search hash update the text input and the bmm.treeLookup
221 // to use the new id.
222 if (/^q=/.test(id)) {
223 delete bmm.treeLookup[searchTreeItem.bookmarkId];
224 $('term').value = id.slice(2);
225 searchTreeItem.bookmarkId = id;
226 bmm.treeLookup[id] = searchTreeItem;
227 valid = true;
228 } else if (id == 'recent') {
229 valid = true;
230 }
231
232 if (valid) {
233 updateParentId(id);
234 } else {
235 // We need to verify that this is a correct ID.
236 chrome.bookmarks.get(id, function(items) {
237 if (items && items.length == 1)
238 updateParentId(id);
239 });
240 }
241 };
242
243 list.addEventListener('activate', function(e) {
244 var bookmarkNodes = getSelectedBookmarkNodes();
245
246 // If we double clicked or pressed enter on a single folder navigate to it.
247 if (bookmarkNodes.length == 1 && bmm.isFolder(bookmarkNodes[0])) {
248 navigateTo(bookmarkNodes[0].id);
249 } else {
250 var command = $('open-in-new-tab-command');
251 command.execute();
252 }
253 });
254
255 // The list dispatches an event when the user clicks on the URL or the Show in
256 // folder part.
257 list.addEventListener('urlClicked', function(e) {
258 openUrls([e.url], e.kind);
259 });
260
261 /**
262 * Timer id used for delaying find-as-you-type
263 */
264 var inputDelayTimer;
265
266 // Capture input changes to the search term input element and delay searching
267 // for 250ms to reduce flicker.
268 $('term').oninput = function(e) {
269 clearTimeout(inputDelayTimer);
270 inputDelayTimer = setTimeout(function() {
271 setSearch($('term').value);
272 }, 250);
273 };
274
275 /**
276 * Navigates to the search results for the search text.
277 * @para {string} searchText The text to search for.
278 */
279 function setSearch(searchText) {
280 navigateTo('q=' + searchText);
281 }
282
283 /**
284 * Clears the search.
285 */
286 function resetSearch() {
287 $('term').value = '';
288 setSearch('');
289 $('term').focus();
290 }
291
292 /**
293 * Called when the title of a bookmark changes.
294 * @param {string} id
295 * @param {!Object} changeInfo
296 */
297 function handleBookmarkChanged(id, changeInfo) {
298 // console.log('handleBookmarkChanged', id, changeInfo);
299 list.handleBookmarkChanged(id, changeInfo);
300 tree.handleBookmarkChanged(id, changeInfo);
301 }
302
303 /**
304 * Callback for when the user reorders by title.
305 * @param {string} id The id of the bookmark folder that was reordered.
306 * @param {!Object} reorderInfo The information about how the items where
307 * reordered.
308 */
309 function handleChildrenReordered(id, reorderInfo) {
310 // console.info('handleChildrenReordered', id, reorderInfo);
311 list.handleChildrenReordered(id, reorderInfo);
312 tree.handleChildrenReordered(id, reorderInfo);
313 bookmarkCache.updateChildren(id);
314 }
315
316 /**
317 * Callback for when a bookmark node is created.
318 * @param {string} id The id of the newly created bookmark node.
319 * @param {!Object} bookmarkNode The new bookmark node.
320 */
321 function handleCreated(id, bookmarkNode) {
322 // console.info('handleCreated', id, bookmarkNode);
323 list.handleCreated(id, bookmarkNode);
324 tree.handleCreated(id, bookmarkNode);
325 bookmarkCache.updateChildren(bookmarkNode.parentId);
326 }
327
328 function handleMoved(id, moveInfo) {
329 // console.info('handleMoved', id, moveInfo);
330 list.handleMoved(id, moveInfo);
331 tree.handleMoved(id, moveInfo);
332
333 bookmarkCache.updateChildren(moveInfo.parentId);
334 if (moveInfo.parentId != moveInfo.oldParentId)
335 bookmarkCache.updateChildren(moveInfo.oldParentId);
336 }
337
338 function handleRemoved(id, removeInfo) {
339 // console.info('handleRemoved', id, removeInfo);
340 list.handleRemoved(id, removeInfo);
341 tree.handleRemoved(id, removeInfo);
342
343 bookmarkCache.updateChildren(removeInfo.parentId);
344 bookmarkCache.remove(id);
345 }
346
347 function handleImportBegan() {
348 chrome.bookmarks.onCreated.removeListener(handleCreated);
349 }
350
351 function handleImportEnded() {
352 chrome.bookmarks.onCreated.addListener(handleCreated);
353 var p = bmm.loadTree();
354 p.addListener(function(node) {
355 var otherBookmarks = node.children[1].children;
356 var importedFolder = otherBookmarks[otherBookmarks.length - 1];
357 var importId = importedFolder.id;
358 tree.insertSubtree(importedFolder);
359 navigateTo(importId)
360 });
361 }
362
363 /**
364 * Adds the listeners for the bookmark model change events.
365 */
366 function addBookmarkModelListeners() {
367 chrome.bookmarks.onChanged.addListener(handleBookmarkChanged);
368 chrome.bookmarks.onChildrenReordered.addListener(handleChildrenReordered);
369 chrome.bookmarks.onCreated.addListener(handleCreated);
370 chrome.bookmarks.onMoved.addListener(handleMoved);
371 chrome.bookmarks.onRemoved.addListener(handleRemoved);
372 chrome.experimental.bookmarkManager.onImportBegan.addListener(
373 handleImportBegan);
374 chrome.experimental.bookmarkManager.onImportEnded.addListener(
375 handleImportEnded);
376 }
377
378 /**
379 * This returns the user visible path to the folder where the bookmark is
380 * located.
381 * @param {number} parentId The ID of the parent folder.
382 * @return {string} The path to the the bookmark,
383 */
384 function getFolder(parentId) {
385 var parentNode = tree.getBookmarkNodeById(parentId);
386 if (parentNode) {
387 var s = parentNode.title;
388 if (parentNode.parentId != ROOT_ID) {
389 return getFolder(parentNode.parentId) + '/' + s;
390 }
391 return s;
392 }
393 }
394
395 tree.addEventListener('load', function(e) {
396 // Add hard coded tree items
397 tree.add(recentTreeItem);
398 tree.add(searchTreeItem);
399
400 // Now we can select a tree item.
401 var hash = window.location.hash.slice(1);
402 if (!hash) {
403 // If we do not have a hash select first item in the tree.
404 hash = tree.items[0].bookmarkId;
405 }
406
407 if (/^q=/.test(hash))
408 $('term').value = hash.slice(2);
409 navigateTo(hash);
410 });
411
412 tree.buildTree();
413 addBookmarkModelListeners();
414
415 var dnd = {
416 DND_EFFECT_COPY: 'copy',
417 DND_EFFECT_MOVE: cr.isMac ? 'move' : 'copy', // http://crbug.com/14654
418
419 dragData: null,
420
421 getBookmarkElement: function(el) {
422 while (el && !el.bookmarkNode) {
423 el = el.parentNode;
424 }
425 return el;
426 },
427
428 // If we are over the list and the list is showing recent or search result
429 // we cannot drop.
430 isOverRecentOrSearch: function(overElement) {
431 return (list.isRecent() || list.isSearch()) && list.contains(overElement);
432 },
433
434 checkEvery_: function(f, overBookmarkNode, overElement) {
435 return this.dragData.elements.every(function(element) {
436 return f.call(this, element, overBookmarkNode, overElement);
437 }, this);
438 },
439
440 /**
441 * @return {boolean} Whether we are currently dragging any folders.
442 */
443 isDraggingFolders: function() {
444 return !!this.dragData && this.dragData.elements.some(function(node) {
445 return !node.url;
446 });
447 },
448
449 /**
450 * This is a first pass wether we can drop the dragged items.
451 *
452 * @param {!BookmarkTreeNode} overBookmarkNode The bookmark that we are
453 * currently dragging over.
454 * @param {!HTMLElement} overElement The element that we are currently
455 * dragging over.
456 * @return {boolean} If this returns false then we know we should not drop
457 * the items. If it returns true we still have to call canDropOn,
458 * canDropAbove and canDropBelow.
459 */
460 canDrop: function(overBookmarkNode, overElement) {
461 var dragData = this.dragData;
462 if (!dragData)
463 return false;
464
465 if (this.isOverRecentOrSearch(overElement))
466 return false;
467
468 if (!dragData.sameProfile)
469 return true;
470
471 return this.checkEvery_(this.canDrop_, overBookmarkNode, overElement);
472 },
473
474 /**
475 * Helper for canDrop that only checks one bookmark node.
476 * @private
477 */
478 canDrop_: function(dragNode, overBookmarkNode, overElement) {
479 var dragId = dragNode.id;
480
481 if (overBookmarkNode.id == dragId)
482 return false;
483
484 // If we are dragging a folder we cannot drop it on any of its descendants
485 var dragBookmarkItem = bmm.treeLookup[dragId];
486 var dragBookmarkNode = dragBookmarkItem && dragBookmarkItem.bookmarkNode;
487 if (dragBookmarkNode && bmm.contains(dragBookmarkNode, overBookmarkNode)) {
488 return false;
489 }
490
491 return true;
492 },
493
494 /**
495 * Whether we can drop the dragged items above the drop target.
496 *
497 * @param {!BookmarkTreeNode} overBookmarkNode The bookmark that we are
498 * currently dragging over.
499 * @param {!HTMLElement} overElement The element that we are currently
500 * dragging over.
501 * @return {boolean} Whether we can drop the dragged items above the drop
502 * target.
503 */
504 canDropAbove: function(overBookmarkNode, overElement) {
505 if (overElement instanceof BookmarkList)
506 return false;
507
508 // We cannot drop between Bookmarks bar and Other bookmarks
509 if (overBookmarkNode.parentId == ROOT_ID)
510 return false;
511
512 var isOverTreeItem = overElement instanceof TreeItem;
513
514 // We can only drop between items in the tree if we have any folders.
515 if (isOverTreeItem && !this.isDraggingFolders())
516 return false;
517
518 if (!this.dragData.sameProfile)
519 return this.isDraggingFolders() || !isOverTreeItem;
520
521 return this.checkEvery_(this.canDropAbove_, overBookmarkNode, overElement);
522 },
523
524 /**
525 * Helper for canDropAbove that only checks one bookmark node.
526 * @private
527 */
528 canDropAbove_: function(dragNode, overBookmarkNode, overElement) {
529 var dragId = dragNode.id;
530
531 // We cannot drop above if the item below is already in the drag source
532 var previousElement = overElement.previousElementSibling;
533 if (previousElement &&
534 previousElement.bookmarkId == dragId)
535 return false;
536
537 return true;
538 },
539
540 /**
541 * Whether we can drop the dragged items below the drop target.
542 *
543 * @param {!BookmarkTreeNode} overBookmarkNode The bookmark that we are
544 * currently dragging over.
545 * @param {!HTMLElement} overElement The element that we are currently
546 * dragging over.
547 * @return {boolean} Whether we can drop the dragged items below the drop
548 * target.
549 */
550 canDropBelow: function(overBookmarkNode, overElement) {
551 if (overElement instanceof BookmarkList)
552 return false;
553
554 // We cannot drop between Bookmarks bar and Other bookmarks
555 if (overBookmarkNode.parentId == ROOT_ID)
556 return false;
557
558 // We can only drop between items in the tree if we have any folders.
559 if (!this.isDraggingFolders() && overElement instanceof TreeItem)
560 return false;
561
562 var isOverTreeItem = overElement instanceof TreeItem;
563
564 // Don't allow dropping below an expanded tree item since it is confusing
565 // to the user anyway.
566 if (isOverTreeItem && overElement.expanded)
567 return false;
568
569 if (!this.dragData.sameProfile)
570 return this.isDraggingFolders() || !isOverTreeItem;
571
572 return this.checkEvery_(this.canDropBelow_, overBookmarkNode, overElement);
573 },
574
575 /**
576 * Helper for canDropBelow that only checks one bookmark node.
577 * @private
578 */
579 canDropBelow_: function(dragNode, overBookmarkNode, overElement) {
580 var dragId = dragNode.id;
581
582 // We cannot drop below if the item below is already in the drag source
583 var nextElement = overElement.nextElementSibling;
584 if (nextElement &&
585 nextElement.bookmarkId == dragId)
586 return false;
587
588 return true;
589 },
590
591 /**
592 * Whether we can drop the dragged items on the drop target.
593 *
594 * @param {!BookmarkTreeNode} overBookmarkNode The bookmark that we are
595 * currently dragging over.
596 * @param {!HTMLElement} overElement The element that we are currently
597 * dragging over.
598 * @return {boolean} Whether we can drop the dragged items on the drop
599 * target.
600 */
601 canDropOn: function(overBookmarkNode, overElement) {
602 // We can only drop on a folder.
603 if (!bmm.isFolder(overBookmarkNode))
604 return false;
605
606 if (!this.dragData.sameProfile)
607 return true;
608
609 return this.checkEvery_(this.canDropOn_, overBookmarkNode, overElement);
610 },
611
612 /**
613 * Helper for canDropOn that only checks one bookmark node.
614 * @private
615 */
616 canDropOn_: function(dragNode, overBookmarkNode, overElement) {
617 var dragId = dragNode.id;
618
619 if (overElement instanceof BookmarkList) {
620 // We are trying to drop an item after the last item in the list. This
621 // is allowed if the item is different from the last item in the list
622 var listItems = list.items;
623 var len = listItems.length;
624 if (len == 0 ||
625 listItems[len - 1].bookmarkId != dragId) {
626 return true;
627 }
628 }
629
630 // Cannot drop on current parent.
631 if (overBookmarkNode.id == dragNode.parentId)
632 return false;
633
634 return true;
635 },
636
637 /**
638 * Callback for the dragstart event.
639 * @param {Event} e The dragstart event.
640 */
641 handleDragStart: function(e) {
642 // console.log(e.type);
643
644 // Determine the selected bookmarks.
645 var target = e.target;
646 var draggedItems = [];
647 if (target instanceof ListItem) {
648 // Use selected items.
649 draggedItems = target.parentNode.selectedItems;
650 } else if (target instanceof TreeItem) {
651 draggedItems.push(target);
652 }
653
654 // We manage starting the drag by using the extension API.
655 e.preventDefault();
656
657 if (draggedItems.length) {
658 // If we are dragging a single link we can do the *Link* effect, otherwise
659 // we only allow copy and move.
660 var effectAllowed;
661 if (draggedItems.length == 1 &&
662 !bmm.isFolder(draggedItems[0].bookmarkNode)) {
663 effectAllowed = 'copyMoveLink';
664 } else {
665 effectAllowed = 'copyMove';
666 }
667 e.dataTransfer.effectAllowed = effectAllowed;
668
669 var ids = draggedItems.map(function(el) {
670 return el.bookmarkId;
671 });
672
673 chrome.experimental.bookmarkManager.startDrag(ids);
674 }
675 },
676
677 handleDragEnter: function(e) {
678 // console.log(e.type);
679
680 e.preventDefault();
681 },
682
683 /**
684 * Calback for the dragover event.
685 * @param {Event} e The dragover event.
686 */
687 handleDragOver: function(e) {
688 // console.log(e.type);
689
690 // The default operation is to allow dropping links etc to do navigation.
691 // We never want to do that for the bookmark manager.
692 e.preventDefault();
693
694 if (!this.dragData)
695 return;
696
697 var overElement = this.getBookmarkElement(e.target);
698 if (!overElement && e.target == list)
699 overElement = list;
700
701 if (!overElement)
702 return;
703
704 var overBookmarkNode = overElement.bookmarkNode;
705
706 if (!this.canDrop(overBookmarkNode, overElement))
707 return;
708
709 var bookmarkNode = overElement.bookmarkNode;
710
711 var canDropAbove = this.canDropAbove(overBookmarkNode, overElement);
712 var canDropOn = this.canDropOn(overBookmarkNode, overElement);
713 var canDropBelow = this.canDropBelow(overBookmarkNode, overElement);
714
715 if (!canDropAbove && !canDropOn && !canDropBelow)
716 return;
717
718 // Now we know that we can drop. Determine if we will drop above, on or
719 // below based on mouse position etc.
720
721 var dropPos;
722
723 e.dataTransfer.dropEffect = this.dragData.sameProfile ?
724 this.DND_EFFECT_MOVE : this.DND_EFFECT_COPY;
725
726 var rect;
727 if (overElement instanceof TreeItem) {
728 // We only want the rect of the row representing the item and not
729 // its children
730 rect = overElement.rowElement.getBoundingClientRect();
731 } else {
732 rect = overElement.getBoundingClientRect();
733 }
734
735 var dy = e.clientY - rect.top;
736 var yRatio = dy / rect.height;
737
738 // above
739 if (canDropAbove &&
740 (yRatio <= .25 || yRatio <= .5 && !(canDropBelow && canDropOn))) {
741 dropPos = 'above';
742
743 // below
744 } else if (canDropBelow &&
745 (yRatio > .75 || yRatio > .5 && !(canDropAbove && canDropOn))) {
746 dropPos = 'below';
747
748 // on
749 } else if (canDropOn) {
750 dropPos = 'on';
751
752 // none
753 } else {
754 // No drop can happen. Exit now.
755 e.dataTransfer.dropEffect = 'none';
756 return;
757 }
758
759 function cloneClientRect(rect) {
760 var newRect = {};
761 for (var key in rect) {
762 newRect[key] = rect[key];
763 }
764 return newRect;
765 }
766
767 // If we are dropping above or below a tree item adjust the width so
768 // that it is clearer where the item will be dropped.
769 if ((dropPos == 'above' || dropPos == 'below') &&
770 overElement instanceof TreeItem) {
771 // ClientRect is read only so clone in into a read-write object.
772 rect = cloneClientRect(rect);
773 var rtl = getComputedStyle(overElement).direction == 'rtl';
774 var labelElement = overElement.labelElement;
775 var labelRect = labelElement.getBoundingClientRect();
776 if (rtl) {
777 rect.width = labelRect.left + labelRect.width - rect.left;
778 } else {
779 rect.left = labelRect.left;
780 rect.width -= rect.left
781 }
782 }
783
784 var overlayType = dropPos;
785
786 // If we are dropping on a list we want to show a overlay drop line after
787 // the last element
788 if (overElement instanceof BookmarkList) {
789 overlayType = 'below';
790
791 // Get the rect of the last list item.
792 var items = overElement.items;
793 var length = items.length;
794 if (length) {
795 dropPos = 'below';
796 overElement = items[length - 1];
797 rect = overElement.getBoundingClientRect();
798 } else {
799 // If there are no items, collapse the height of the rect
800 rect = cloneClientRect(rect);
801 rect.height = 0;
802 // We do not use bottom so we don't care to adjust it.
803 }
804 }
805
806 this.showDropOverlay_(rect, overlayType);
807
808 this.dropDestination = {
809 dropPos: dropPos,
810 relatedNode: overElement.bookmarkNode
811 };
812 },
813
814 /**
815 * Shows and positions the drop marker overlay.
816 * @param {ClientRect} targetRect The drop target rect
817 * @param {string} overlayType The position relative to the target rect.
818 * @private
819 */
820 showDropOverlay_: function(targetRect, overlayType) {
821 window.clearTimeout(this.hideDropOverlayTimer_);
822 var overlay = $('drop-overlay');
823 if (overlayType == 'on') {
824 overlay.className = '';
825 overlay.style.top = targetRect.top + 'px';
826 overlay.style.height = targetRect.height + 'px';
827 } else {
828 overlay.className = 'line';
829 overlay.style.height = '';
830 }
831 overlay.style.width = targetRect.width + 'px';
832 overlay.style.left = targetRect.left + 'px';
833 overlay.style.display = 'block';
834
835 if (overlayType != 'on') {
836 var overlayRect = overlay.getBoundingClientRect();
837 if (overlayType == 'above') {
838 overlay.style.top = targetRect.top - overlayRect.height / 2 + 'px';
839 } else {
840 overlay.style.top = targetRect.top + targetRect.height -
841 overlayRect.height / 2 + 'px';
842 }
843 }
844 },
845
846 /**
847 * Hides the drop overlay element.
848 * @private
849 */
850 hideDropOverlay_: function() {
851 // Hide the overlay in a timeout to reduce flickering as we move between
852 // valid drop targets.
853 window.clearTimeout(this.hideDropOverlayTimer_);
854 this.hideDropOverlayTimer_ = window.setTimeout(function() {
855 $('drop-overlay').style.display = '';
856 }, 100);
857 },
858
859 handleDragLeave: function(e) {
860 // console.log(e.type);
861
862 this.hideDropOverlay_();
863 },
864
865 handleDrop: function(e) {
866 // console.log(e.type);
867
868 if (this.dropDestination && this.dragData) {
869 var dropPos = this.dropDestination.dropPos;
870 var relatedNode = this.dropDestination.relatedNode;
871 var parentId = dropPos == 'on' ? relatedNode.id : relatedNode.parentId;
872
873 var index;
874 if (dropPos == 'above')
875 index = relatedNode.index;
876 else if (dropPos == 'below')
877 index = relatedNode.index + 1;
878
879 if (index != undefined)
880 chrome.experimental.bookmarkManager.drop(parentId, index);
881 else
882 chrome.experimental.bookmarkManager.drop(parentId);
883
884 // TODO(arv): Select the newly dropped items.
885 }
886 this.dropDestination = null;
887 this.hideDropOverlay_();
888 },
889
890 handleDrag: function(e) {
891 // console.log(e.type);
892 },
893
894 handleDragEnd: function(e) {
895 // console.log(e.type);
896
897 var self = this;
898 // Chromium Win incorrectly fires the dragend event before the drop event.
899 // http://code.google.com/p/chromium/issues/detail?id=31292
900 window.setTimeout(function() {
901 self.dragData = null;
902 }, 1)
903 },
904
905 handleChromeDragEnter: function(dragData) {
906 this.dragData = dragData;
907 },
908
909 init: function() {
910 document.addEventListener('dragstart', cr.bind(this.handleDragStart, this));
911 document.addEventListener('dragenter', cr.bind(this.handleDragEnter, this));
912 document.addEventListener('dragover', cr.bind(this.handleDragOver, this));
913 document.addEventListener('dragleave', cr.bind(this.handleDragLeave, this));
914 document.addEventListener('drop', cr.bind(this.handleDrop, this));
915 document.addEventListener('dragend', cr.bind(this.handleDragEnd, this));
916 document.addEventListener('drag', cr.bind(this.handleDrag, this));
917
918 chrome.experimental.bookmarkManager.onDragEnter.addListener(cr.bind(
919 this.handleChromeDragEnter, this));
920 }
921
922 };
923
924 dnd.init();
925
926 </script>
927
928 <!-- Organize menu -->
929 <command i18n-values=".label:rename_folder" id="rename-folder-command"></command >
930 <command i18n-values=".label:edit" id="edit-command"></command>
931 <command i18n-values=".label:delete" id="delete-command"></command>
932 <command i18n-values=".label:show_in_folder" id="show-in-folder-command"></comma nd>
933 <command i18n-values=".label:cut" id="cut-command"></command>
934 <command i18n-values=".label:copy" id="copy-command"></command>
935 <command i18n-values=".label:paste" id="paste-command"></command>
936 <command i18n-values=".label:sort" id="sort-command"></command>
937 <command i18n-values=".label:add_new_bookmark" id="add-new-bookmark-command"></c ommand>
938 <command i18n-values=".label:new_folder" id="new-folder-command"></command>
939
940 <!-- Tools menu -->
941 <command i18n-values=".label:import_menu" id="import-menu-command"></command>
942 <command i18n-values=".label:export_menu" id="export-menu-command"></command>
943
944 <!-- open * are handled in canExecute handler -->
945 <command id="open-in-new-tab-command"></command>
946 <command id="open-in-new-window-command"></command>
947 <command id="open-incognito-window-command"></command>
948
949 <!-- TODO(arv): I think the commands might be better created in code? -->
950
951 <menu id="organize-menu">
952 <button command="#rename-folder-command"></button>
953 <button command="#edit-command"></button>
954 <button command="#delete-command"></button>
955 <button command="#show-in-folder-command"></button>
956 <hr>
957 <button command="#cut-command"></button>
958 <button command="#copy-command"></button>
959 <button command="#paste-command"></button>
960 <hr>
961 <button command="#sort-command"></button>
962 <hr>
963 <button command="#add-new-bookmark-command"></button>
964 <button command="#new-folder-command"></button>
965 </menu>
966
967 <menu id="tools-menu">
968 <button command="#import-menu-command"></button>
969 <button command="#export-menu-command"></button>
970 </menu>
971
972 <menu id="context-menu">
973 <button command="#open-in-new-tab-command"></button>
974 <button command="#open-in-new-window-command"></button>
975 <button command="#open-incognito-window-command"></button>
976 <hr>
977 <button command="#rename-folder-command"></button>
978 <button command="#edit-command"></button>
979 <button command="#delete-command"></button>
980 <button command="#show-in-folder-command"></button>
981 <hr>
982 <button command="#cut-command"></button>
983 <button command="#copy-command"></button>
984 <button command="#paste-command"></button>
985 <hr>
986 <button command="#add-new-bookmark-command"></button>
987 <button command="#new-folder-command"></button>
988 </menu>
989
990 <script>
991
992 // Commands
993
994 const Command = cr.ui.Command;
995 const CommandBinding = cr.ui.CommandBinding;
996 const Menu = cr.ui.Menu;
997 const MenuButton = cr.ui.MenuButton;
998
999 cr.ui.decorate('menu', Menu);
1000 cr.ui.decorate('button[menu]', MenuButton);
1001 cr.ui.decorate('command', Command);
1002
1003 cr.ui.contextMenuHandler.addContextMenuProperty(tree);
1004 list.contextMenu = $('context-menu');
1005 tree.contextMenu = $('context-menu');
1006
1007 /**
1008 * Helper function that updates the canExecute and labels for the open like
1009 * commands.
1010 * @param {!cr.ui.CanExecuteEvent} e The event fired by the command system.
1011 * @param {!cr.ui.Command} command The command we are currently precessing.
1012 */
1013 function updateOpenCommands(e, command) {
1014 var selectedItem = e.target.selectedItem;
1015 var selectionCount;
1016 if (e.target == tree)
1017 selectionCount = selectedItem ? 1 : 0;
1018 else
1019 selectionCount = e.target.selectedItems.length;
1020
1021 var isFolder = selectionCount == 1 &&
1022 selectedItem.bookmarkNode &&
1023 bmm.isFolder(selectedItem.bookmarkNode);
1024 var multiple = selectionCount != 1 || isFolder;
1025
1026 function hasBookmarks(node) {
1027 var it = new bmm.TreeIterator(node);
1028 while (it.moveNext()) {
1029 if (!bmm.isFolder(it.current))
1030 return true;
1031 }
1032 return false;
1033 }
1034
1035 switch (command.id) {
1036 case 'open-in-new-tab-command':
1037 command.label = localStrings.getString(multiple ?
1038 'open_all' : 'open_in_new_tab');
1039 break;
1040
1041 case 'open-in-new-window-command':
1042 command.label = localStrings.getString(multiple ?
1043 'open_all_new_window' : 'open_in_new_window');
1044 break;
1045 case 'open-incognito-window-command':
1046 command.label = localStrings.getString(multiple ?
1047 'open_all_incognito' : 'open_incognito');
1048 break;
1049 }
1050 e.canExecute = selectionCount > 0 && !!selectedItem.bookmarkNode;
1051 if (isFolder && e.canExecute) {
1052 // We need to get all the bookmark items in this tree. If the tree does not
1053 // contain any non-folders we need to disable the command.
1054 var p = bmm.loadSubtree(selectedItem.bookmarkId);
1055 p.addListener(function(node) {
1056 command.disabled = !node || !hasBookmarks(node);
1057 });
1058 }
1059 }
1060
1061 /**
1062 * Calls the backend to figure out if we can paste the clipboard into the active
1063 * folder.
1064 * @param {Function=} opt_f Function to call after the state has been
1065 * updated.
1066 */
1067 function updatePasteCommand(opt_f) {
1068 function update(canPaste) {
1069 var command = $('paste-command');
1070 command.disabled = !canPaste;
1071 if (opt_f)
1072 opt_f();
1073 }
1074 // We cannot paste into search and recent view.
1075 if (list.isSearch() || list.isRecent()) {
1076 update(false);
1077 } else {
1078 chrome.experimental.bookmarkManager.canPaste(list.parentId, update);
1079 }
1080 }
1081
1082 // We can always execute the import-menu and export-menu commands.
1083 document.addEventListener('canExecute', function(e) {
1084 var command = e.command;
1085 var commandId = command.id;
1086 if (commandId == 'import-menu-command' || commandId == 'export-menu-command') {
1087 e.canExecute = true;
1088 }
1089 });
1090
1091 // Update canExecute for the commands when the list is the active element.
1092 list.addEventListener('canExecute', function(e) {
1093 if (e.target != list) return;
1094
1095 var command = e.command;
1096 var commandId = command.id;
1097
1098 function hasSelected() {
1099 return !!e.target.selectedItem;
1100 }
1101
1102 function hasSingleSelected() {
1103 return e.target.selectedItems.length == 1;
1104 }
1105
1106 function isRecentOrSearch() {
1107 return list.isRecent() || list.isSearch();
1108 }
1109
1110 switch (commandId) {
1111 case 'rename-folder-command':
1112 // Show rename if a single folder is selected
1113 var items = e.target.selectedItems;
1114 if (items.length != 1) {
1115 e.canExecute = false;
1116 command.hidden = true;
1117 } else {
1118 var isFolder = bmm.isFolder(items[0].bookmarkNode);
1119 e.canExecute = isFolder;
1120 command.hidden = !isFolder;
1121 }
1122 break;
1123
1124 case 'edit-command':
1125 // Show the edit command if not a folder
1126 var items = e.target.selectedItems;
1127 if (items.length != 1) {
1128 e.canExecute = false;
1129 command.hidden = false;
1130 } else {
1131 var isFolder = bmm.isFolder(items[0].bookmarkNode);
1132 e.canExecute = !isFolder;
1133 command.hidden = isFolder;
1134 }
1135 break;
1136
1137 case 'show-in-folder-command':
1138 e.canExecute = isRecentOrSearch() && hasSingleSelected();
1139 break;
1140
1141 case 'delete-command':
1142 case 'cut-command':
1143 case 'copy-command':
1144 e.canExecute = hasSelected();
1145 break;
1146
1147 case 'paste-command':
1148 updatePasteCommand();
1149 break;
1150
1151 case 'sort-command':
1152 case 'add-new-bookmark-command':
1153 case 'new-folder-command':
1154 e.canExecute = !isRecentOrSearch();
1155 break;
1156
1157 case 'open-in-new-tab-command':
1158 case 'open-in-new-window-command':
1159 case 'open-incognito-window-command':
1160 updateOpenCommands(e, command);
1161 break;
1162 }
1163 });
1164
1165 // Update canExecute for the commands when the tree is the active element.
1166 tree.addEventListener('canExecute', function(e) {
1167 if (e.target != tree) return;
1168
1169 var command = e.command;
1170 var commandId = command.id;
1171
1172 function hasSelected() {
1173 return !!e.target.selectedItem;
1174 }
1175
1176 function isRecentOrSearch() {
1177 var item = e.target.selectedItem;
1178 return item == recentTreeItem || item == searchTreeItem;
1179 }
1180
1181 function isTopLevelItem() {
1182 return e.target.selectedItem.parentNode == tree;
1183 }
1184
1185 switch (commandId) {
1186 case 'rename-folder-command':
1187 command.hidden = false;
1188 e.canExecute = hasSelected() && !isTopLevelItem();
1189 break;
1190
1191 case 'edit-command':
1192 command.hidden = true;
1193 e.canExecute = false;
1194 break;
1195
1196 case 'delete-command':
1197 case 'cut-command':
1198 case 'copy-command':
1199 e.canExecute = hasSelected() && !isTopLevelItem();
1200 break;
1201
1202 case 'paste-command':
1203 updatePasteCommand();
1204 break;
1205
1206 case 'sort-command':
1207 case 'add-new-bookmark-command':
1208 case 'new-folder-command':
1209 e.canExecute = !isRecentOrSearch();
1210 break;
1211
1212 case 'open-in-new-tab-command':
1213 case 'open-in-new-window-command':
1214 case 'open-incognito-window-command':
1215 updateOpenCommands(e, command);
1216 break;
1217 }
1218 });
1219
1220 /**
1221 * Update the canExecute state of the commands when the selection changes.
1222 * @param {Event} e The change event object.
1223 */
1224 function updateCommandsBasedOnSelection(e) {
1225 if (e.target == document.activeElement) {
1226 // Paste only needs to updated when the tree selection changes.
1227 var commandNames = ['copy', 'cut', 'delete', 'rename-folder', 'edit',
1228 'add-new-bookmark', 'new-folder', 'open-in-new-tab',
1229 'open-in-new-window', 'open-incognito-window'];
1230
1231 if (e.target == tree) {
1232 commandNames.push('paste', 'show-in-folder', 'sort');
1233 }
1234
1235 commandNames.forEach(function(baseId) {
1236 $(baseId + '-command').canExecuteChange();
1237 });
1238 }
1239 }
1240
1241 list.addEventListener('change', updateCommandsBasedOnSelection);
1242 tree.addEventListener('change', updateCommandsBasedOnSelection);
1243
1244 document.addEventListener('command', function(e) {
1245 var command = e.command;
1246 var commandId = command.id;
1247 console.log(command.id, 'executed', 'on', e.target);
1248 if (commandId == 'import-menu-command') {
1249 chrome.experimental.bookmarkManager.import();
1250 } else if (command.id == 'export-menu-command') {
1251 chrome.experimental.bookmarkManager.export();
1252 }
1253 });
1254
1255 function handleRename(e) {
1256 var item = e.target;
1257 var bookmarkNode = item.bookmarkNode;
1258 chrome.bookmarks.update(bookmarkNode.id, {title: item.label});
1259 }
1260
1261 tree.addEventListener('rename', handleRename);
1262 list.addEventListener('rename', handleRename);
1263
1264 list.addEventListener('edit', function(e) {
1265 var item = e.target;
1266 var bookmarkNode = item.bookmarkNode;
1267 var context = {
1268 title: bookmarkNode.title
1269 };
1270 if (!bmm.isFolder(bookmarkNode))
1271 context.url = bookmarkNode.url;
1272
1273 if (bookmarkNode.id == 'new') {
1274 // New page
1275 context.parentId = bookmarkNode.parentId;
1276 chrome.bookmarks.create(context, function(node) {
1277 list.remove(item);
1278 list.selectedItem = bmm.listLookup[node.id];
1279 });
1280 } else {
1281 // Edit
1282 chrome.bookmarks.update(bookmarkNode.id, context);
1283 }
1284 });
1285
1286 list.addEventListener('canceledit', function(e) {
1287 var item = e.target;
1288 var bookmarkNode = item.bookmarkNode;
1289 if (bookmarkNode.id == 'new') {
1290 list.remove(item);
1291 list.selectionModel.leadItem = list.lastChild;
1292 list.selectionModel.anchorItem = list.lastChild;
1293 list.focus();
1294 }
1295 });
1296
1297 /**
1298 * Navigates to the folder that the selected item is in and selects it. This is
1299 * used for the show-in-folder command.
1300 */
1301 function showInFolder() {
1302 var bookmarkId = list.selectedItem.bookmarkNode.id;
1303 var parentId = list.selectedItem.bookmarkNode.parentId;
1304
1305 // After the list is loaded we should select the revealed item.
1306 var f = function(e) {
1307 var item = bmm.listLookup[bookmarkId];
1308 if (item) {
1309 list.selectionModel.leadItem = item;
1310 item.selected = true;
1311 }
1312 list.removeEventListener('load', f);
1313 }
1314 list.addEventListener('load', f);
1315 var treeItem = bmm.treeLookup[parentId];
1316 treeItem.reveal();
1317
1318 navigateTo(parentId);
1319 }
1320
1321 /**
1322 * Opens URLs in new tab, window or incognito mode.
1323 * @param {!Array.<string>} urls The URLs to open.
1324 * @param {string} kind The kind is either 'tab', 'window', or 'incognito'.
1325 */
1326 function openUrls(urls, kind) {
1327 if (urls.length < 1)
1328 return;
1329
1330 if (urls.length > 15) {
1331 if (!confirm(localStrings.getStringF('should_open_all', urls.length)))
1332 return;
1333 }
1334
1335 // Fix '#124' URLs since open those in a new window does not work. We prepend
1336 // the base URL when we encounter those.
1337 var base = window.location.href.split('#')[0];
1338 urls = urls.map(function(url) {
1339 return url[0] == '#' ? base + url : url;
1340 });
1341
1342 // Incognito mode is not yet supported by the extensions APIs.
1343 // http://code.google.com/p/chromium/issues/detail?id=12658
1344 if (kind == 'window') {
1345 chrome.windows.create({url: urls[0]}, function(window) {
1346 urls.forEach(function(url, i) {
1347 if (i > 0)
1348 chrome.tabs.create({url: url, windowId: window.id, selected: false});
1349 });
1350 });
1351 } else if (kind == 'tab') {
1352 urls.forEach(function(url, i) {
1353 chrome.tabs.create({url: url, selected: !i});
1354 });
1355 } else {
1356 window.location.href = urls[0];
1357 }
1358 }
1359
1360 /**
1361 * Returns the selected bookmark nodes of the active element. Only call this
1362 * if the list or the tree is focused.
1363 * @return {!Array} Array of bookmark nodes.
1364 */
1365 function getSelectedBookmarkNodes() {
1366 if (document.activeElement == list) {
1367 return list.selectedItems.map(function(item) {
1368 return item.bookmarkNode;
1369 });
1370 } else if (document.activeElement == tree) {
1371 return [tree.selectedItem.bookmarkNode];
1372 } else {
1373 throw Error('getSelectedBookmarkNodes called when wrong element focused.');
1374 }
1375 }
1376
1377 /**
1378 * @return {!Array.<string>} An array of the selected bookmark IDs.
1379 */
1380 function getSelectedBookmarkIds() {
1381 return getSelectedBookmarkNodes().map(function(node) {
1382 return node.id;
1383 });
1384 }
1385
1386 /**
1387 * Opens the selected bookmarks.
1388 */
1389 function openBookmarks(kind) {
1390 // If we have selected any folders we need to find all items recursively.
1391 // We can do several async calls to getChildren but instead we do a single
1392 // call to getTree and only add the subtrees of the selected items.
1393
1394 var urls = [];
1395 var idMap = {};
1396
1397 // Traverses the tree until it finds a node tree that should be added. Then
1398 // we switch over to use addNodes. We could merge these two functions into
1399 // one but that would make the code less readable.
1400 function traverseNodes(node) {
1401 // This is not using the iterator since it uses breadth first search.
1402 if (node.id in idMap) {
1403 addNodes(node);
1404 } else if (node.children) {
1405 for (var i = 0; i < node.children.length; i++) {
1406 traverseNodes(node.children[i]);
1407 }
1408 }
1409 }
1410
1411 // Adds the node and all the descendants
1412 function addNodes(node) {
1413 var it = new bmm.TreeIterator(node);
1414 while (it.moveNext()) {
1415 var n = it.current;
1416 if (!bmm.isFolder(n))
1417 urls.push(n.url);
1418 }
1419 }
1420
1421 var nodes = getSelectedBookmarkNodes();
1422
1423 // Create a map for simpler lookup later.
1424 nodes.forEach(function(node) {
1425 idMap[node.id] = true;
1426 });
1427 var p = bmm.loadTree();
1428 p.addListener(function(node) {
1429 traverseNodes(node);
1430 openUrls(urls, kind);
1431 });
1432 }
1433
1434 /**
1435 * Deletes the selected bookmarks.
1436 */
1437 function deleteBookmarks() {
1438 getSelectedBookmarkIds().forEach(function(id) {
1439 chrome.bookmarks.removeTree(id);
1440 });
1441 }
1442
1443 /**
1444 * Callback for the new folder command. This creates a new folder and starts
1445 * a rename of it.
1446 */
1447 function newFolder() {
1448 var parentId = list.parentId;
1449 var isTree = document.activeElement == tree;
1450 chrome.bookmarks.create({
1451 title: localStrings.getString('new_folder_name'),
1452 parentId: parentId
1453 }, function(newNode) {
1454 // We need to do this in a timeout to be able to focus the newly created
1455 // item.
1456 setTimeout(function() {
1457 var newItem = isTree ? bmm.treeLookup[newNode.id] :
1458 bmm.listLookup[newNode.id];
1459 document.activeElement.selectedItem = newItem;
1460 newItem.editing = true;
1461 });
1462 });
1463 }
1464
1465 /**
1466 * Adds a page to the current folder. This is called by the
1467 * add-new-bookmark-command handler.
1468 */
1469 function addPage() {
1470 var parentId = list.parentId;
1471 var fakeNode = {
1472 title: '',
1473 url: '',
1474 parentId: parentId,
1475 id: 'new'
1476 };
1477 var newListItem = bmm.createListItem(fakeNode, false);
1478 list.add(newListItem);
1479 list.selectedItem = newListItem;
1480 newListItem.editing = true;
1481 }
1482
1483 /**
1484 * Handler for the command event. This is used both for the tree and the list.
1485 * @param {!Event} e The event object.
1486 */
1487 function handleCommand(e) {
1488 var command = e.command;
1489 var commandId = command.id;
1490 switch (commandId) {
1491 case 'show-in-folder-command':
1492 showInFolder();
1493 break;
1494 case 'open-in-new-tab-command':
1495 openBookmarks('tab');
1496 break;
1497 case 'open-in-new-window-command':
1498 openBookmarks('window');
1499 break;
1500 case 'open-in-new-incognito-command':
1501 openBookmarks('incognito');
1502 break;
1503 case 'delete-command':
1504 deleteBookmarks();
1505 break;
1506 case 'copy-command':
1507 chrome.experimental.bookmarkManager.copy(getSelectedBookmarkIds());
1508 break;
1509 case 'cut-command':
1510 chrome.experimental.bookmarkManager.cut(getSelectedBookmarkIds());
1511 break;
1512 case 'paste-command':
1513 chrome.experimental.bookmarkManager.paste(list.parentId);
1514 break;
1515 case 'sort-command':
1516 chrome.experimental.bookmarkManager.sortChildren(list.parentId);
1517 break;
1518 case 'rename-folder-command':
1519 case 'edit-command':
1520 document.activeElement.selectedItem.editing = true;
1521 break;
1522 case 'new-folder-command':
1523 newFolder();
1524 break;
1525 case 'add-new-bookmark-command':
1526 addPage();
1527 break;
1528 }
1529 }
1530
1531 // TODO(arv): Move shortcut to HTML?
1532
1533 // Meta+Backspace on Mac, Del on other platforms.
1534 $('delete-command').shortcut = cr.isMac ? 'U+0008-meta' : 'U+007F';
1535
1536 list.addEventListener('command', handleCommand);
1537 tree.addEventListener('command', handleCommand);
1538
1539 // Execute the copy, cut and paste commands when those events are dispatched by
1540 // the browser. This allows us to rely on the browser to handle the keyboard
1541 // shortcuts for these commands.
1542 (function() {
1543 function handle(id) {
1544 return function(e) {
1545 var command = $(id);
1546 if (!command.disabled) {
1547 command.execute();
1548 e.preventDefault(); // Prevent the system beep
1549 }
1550 };
1551 }
1552
1553 // Listen to copy, cut and paste events and execute the associated commands.
1554 document.addEventListener('copy', handle('copy-command'));
1555 document.addEventListener('cut', handle('cut-command'));
1556
1557 var pasteHandler = handle('paste-command');
1558 document.addEventListener('paste', function(e) {
1559 // Paste is a bit special since we need to do an async call to see if we can
1560 // paste because the paste command might not be up to date.
1561 updatePasteCommand(pasteHandler);
1562 });
1563 })();
1564
1565 /**
1566 * The local strings object which is used to do the translation.
1567 * @type {!LocalStrings}
1568 */
1569 var localStrings = new LocalStrings;
1570
1571 // Get the localized strings from the backend.
1572 chrome.experimental.bookmarkManager.getStrings(function setTemplateData(data) {
1573 // The strings may contain & which we need to strip.
1574 for (var key in data) {
1575 data[key] = data[key].replace(/&/, '');
1576 }
1577
1578 localStrings.templateData = data;
1579 i18nTemplate.process(document, data);
1580 });
1581
1582 </script>
1583
1584 <div id="drop-overlay"></div>
1585
1586 </body>
1587 </html>
OLDNEW
« no previous file with comments | « resources/bookmark_manager/js/util.js ('k') | resources/bookmark_manager/manifest.json » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698