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

Unified 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 side-by-side diff with in-line comments
Download patch
« no previous file with comments | « resources/bookmark_manager/js/util.js ('k') | resources/bookmark_manager/manifest.json » ('j') | no next file with comments »
Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
Index: resources/bookmark_manager/main.html
===================================================================
--- resources/bookmark_manager/main.html (revision 0)
+++ resources/bookmark_manager/main.html (revision 0)
@@ -0,0 +1,1587 @@
+<!DOCTYPE html>
+<html i18n-values="dir:textdirection">
+<!--
+
+Copyright (c) 2010 The Chromium Authors. All rights reserved.
+Use of this source code is governed by a BSD-style license that can be
+found in the LICENSE file.
+
+
+This is work in progress:
+
+Favicons: chrome-extension: is not allowed to access chrome://favicon. We need
+to whitelist it or expose a way to get the data URI for the favicon (slow and
+sucky).
+
+Favicon of bmm does not work. No icon is showed.
+
+-->
+<head>
+<title i18n-content="title"></title>
+
+<link rel="stylesheet" href="css/list.css">
+<link rel="stylesheet" href="css/tree.css">
+<link rel="stylesheet" href="css/menu.css">
+<link rel="stylesheet" href="css/bmm.css">
+
+<script src="css/tree.css.js"></script>
+<script src="css/bmm.css.js"></script>
+
+<script src="js/cr.js"></script>
+<script src="js/cr/event.js"></script>
+<script src="js/cr/eventtarget.js"></script>
+<script src="js/cr/promise.js"></script>
+<script src="js/cr/ui.js"></script>
+<script src="js/cr/ui/listselectionmodel.js"></script>
+<script src="js/cr/ui/listitem.js"></script>
+<script src="js/cr/ui/list.js"></script>
+<script src="js/cr/ui/tree.js"></script>
+<script src="js/cr/ui/command.js"></script>
+<script src="js/cr/ui/menuitem.js"></script>
+<script src="js/cr/ui/menu.js"></script>
+<script src="js/cr/ui/menubutton.js"></script>
+<script src="js/cr/ui/contextmenuhandler.js"></script>
+
+<script src="js/util.js"></script>
+<script src="js/localstrings.js"></script>
+<script src="js/i18ntemplate.js"></script>
+
+<script src="js/bmm/treeiterator.js"></script>
+<script src="js/bmm.js"></script>
+<script src="js/bmm/bookmarklist.js"></script>
+<script src="js/bmm/bookmarktree.js"></script>
+
+<script>
+
+// Sometimes the extension API is not initialized.
+if (!chrome.bookmarks)
+ window.location.reload();
+
+// Allow platform specific CSS rules.
+if (cr.isMac)
+ document.documentElement.setAttribute('os', 'mac');
+
+</script>
+</head>
+<body i18n-values=".style.fontFamily:fontfamily;.style.fontSize:fontsize">
+
+<div class="header">
+ <button onclick="resetSearch()" class="logo" tabindex=3></button>
+ <div>
+ <form onsubmit="setSearch(this.term.value); return false;"
+ class="form">
+ <input type="text" id="term" tabindex=1 autofocus>
+ <input type="submit" i18n-values=".value:search_button" tabindex=1>
+ </form>
+ <div class=toolbar>
+ <button menu="#organize-menu" tabindex="-1" i18n-content="organize_menu"></button>
+ <button menu="#tools-menu" tabindex="-1" i18n-content="tools_menu"></button>
+ </div>
+ </div>
+</div>
+
+<div class=main>
+ <div id=tree-container>
+ <tree id=tree tabindex=2></tree>
+ </div>
+ <list id=list tabindex=2></list>
+</div>
+
+
+<script>
+
+const BookmarkList = bmm.BookmarkList;
+const BookmarkTree = bmm.BookmarkTree;
+const ListItem = cr.ui.ListItem;
+const TreeItem = cr.ui.TreeItem;
+
+/**
+ * The id of the bookmark root.
+ * @type {number}
+ */
+const ROOT_ID = '0';
+
+var bookmarkCache = {
+ /**
+ * This returns a reference to the bookmark node that is cached by the tree
+ * or list. Use this funciton when we need to update the local cachea after
+ * changes. It only returns bookmarks that are used by the tree and/or the
+ * list.
+ * @param {string} The ID of the bookmark that we want to get.
+ * @return {BookmarkTreeNode}
+ */
+ getById: function(id) {
+ var el = bmm.treeLookup[id] || bmm.listLookup[id];
+ return el && el.bookmarkNode;
+ },
+
+ /**
+ * Removes the cached item from both the list and tree lookups.
+ */
+ remove: function(id) {
+ delete bmm.listLookup[id];
+
+ var treeItem = bmm.treeLookup[id];
+ if (treeItem) {
+ var items = treeItem.items; // is an HTMLCollection
+ for (var i = 0, item; item = items[i]; i++) {
+ var bookmarkNode = item.bookmarkNode;
+ delete bmm.treeLookup[bookmarkNode.id];
+ }
+ delete bmm.treeLookup[id];
+ }
+ },
+
+ /**
+ * Updates the underlying bookmark node for the tree items and list items by
+ * querying the bookmark backend.
+ * @param {string} id The id of the node to update the children for.
+ * @param {Function=} opt_f A funciton to call when done.
+ */
+ updateChildren: function(id, opt_f) {
+ function updateItem(bookmarkNode) {
+ var treeItem = bmm.treeLookup[bookmarkNode.id];
+ if (treeItem) {
+ treeItem.bookmarkNode = bookmarkNode;
+ }
+ var listItem = bmm.listLookup[bookmarkNode.id];
+ if (listItem) {
+ listItem.bookmarkNode = bookmarkNode;
+ }
+ }
+
+ chrome.bookmarks.getChildren(id, function(children) {
+ children.forEach(updateItem);
+
+ if (opt_f)
+ opt_f(children);
+ });
+ }
+};
+
+</script>
+<script>
+
+BookmarkList.decorate(list);
+
+var searchTreeItem = new TreeItem({
+ label: 'Search',
+ icon: 'images/bookmark_manager_search.png',
+ bookmarkId: 'q='
+});
+bmm.treeLookup[searchTreeItem.bookmarkId] = searchTreeItem;
+
+var recentTreeItem = new TreeItem({
+ label: 'Recent',
+ icon: 'images/bookmark_manager_recent.png',
+ bookmarkId: 'recent'
+});
+bmm.treeLookup[recentTreeItem.bookmarkId] = recentTreeItem;
+
+BookmarkTree.decorate(tree);
+
+tree.addEventListener('change', function() {
+ navigateTo(tree.selectedItem.bookmarkId);
+});
+
+/**
+ * Navigates to a bookmark ID.
+ * @param {string} id The ID to navigate to.
+ */
+function navigateTo(id) {
+ console.info('navigateTo', window.location.hash, id);
+ // Update the location hash using a timer to prevent reentrancy. This is how
+ // often we add history entries and the time here is a bit arbitrary but was
+ // picked as the smallest time a human perceives as instant.
+ clearTimeout(navigateTo.timer_);
+ navigateTo.timer_ = setTimeout(function() {
+ window.location.hash = tree.selectedItem.bookmarkId;
+ }, 300);
+ updateParentId(id);
+}
+
+/**
+ * Updates the parent ID of the bookmark list and selects the correct tree item.
+ * @param {string} id The id.
+ */
+function updateParentId(id) {
+ list.parentId = id;
+ if (id in bmm.treeLookup)
+ tree.selectedItem = bmm.treeLookup[id];
+}
+
+// We listen to hashchange so that we can update the currently shown folder when
+// the user goes back and forward in the history.
+window.onhashchange = function(e) {
+ var id = window.location.hash.slice(1);
+
+ var valid = false;
+
+ // In case we got a search hash update the text input and the bmm.treeLookup
+ // to use the new id.
+ if (/^q=/.test(id)) {
+ delete bmm.treeLookup[searchTreeItem.bookmarkId];
+ $('term').value = id.slice(2);
+ searchTreeItem.bookmarkId = id;
+ bmm.treeLookup[id] = searchTreeItem;
+ valid = true;
+ } else if (id == 'recent') {
+ valid = true;
+ }
+
+ if (valid) {
+ updateParentId(id);
+ } else {
+ // We need to verify that this is a correct ID.
+ chrome.bookmarks.get(id, function(items) {
+ if (items && items.length == 1)
+ updateParentId(id);
+ });
+ }
+};
+
+list.addEventListener('activate', function(e) {
+ var bookmarkNodes = getSelectedBookmarkNodes();
+
+ // If we double clicked or pressed enter on a single folder navigate to it.
+ if (bookmarkNodes.length == 1 && bmm.isFolder(bookmarkNodes[0])) {
+ navigateTo(bookmarkNodes[0].id);
+ } else {
+ var command = $('open-in-new-tab-command');
+ command.execute();
+ }
+});
+
+// The list dispatches an event when the user clicks on the URL or the Show in
+// folder part.
+list.addEventListener('urlClicked', function(e) {
+ openUrls([e.url], e.kind);
+});
+
+/**
+ * Timer id used for delaying find-as-you-type
+ */
+var inputDelayTimer;
+
+// Capture input changes to the search term input element and delay searching
+// for 250ms to reduce flicker.
+$('term').oninput = function(e) {
+ clearTimeout(inputDelayTimer);
+ inputDelayTimer = setTimeout(function() {
+ setSearch($('term').value);
+ }, 250);
+};
+
+/**
+ * Navigates to the search results for the search text.
+ * @para {string} searchText The text to search for.
+ */
+function setSearch(searchText) {
+ navigateTo('q=' + searchText);
+}
+
+/**
+ * Clears the search.
+ */
+function resetSearch() {
+ $('term').value = '';
+ setSearch('');
+ $('term').focus();
+}
+
+/**
+ * Called when the title of a bookmark changes.
+ * @param {string} id
+ * @param {!Object} changeInfo
+ */
+function handleBookmarkChanged(id, changeInfo) {
+ // console.log('handleBookmarkChanged', id, changeInfo);
+ list.handleBookmarkChanged(id, changeInfo);
+ tree.handleBookmarkChanged(id, changeInfo);
+}
+
+/**
+ * Callback for when the user reorders by title.
+ * @param {string} id The id of the bookmark folder that was reordered.
+ * @param {!Object} reorderInfo The information about how the items where
+ * reordered.
+ */
+function handleChildrenReordered(id, reorderInfo) {
+ // console.info('handleChildrenReordered', id, reorderInfo);
+ list.handleChildrenReordered(id, reorderInfo);
+ tree.handleChildrenReordered(id, reorderInfo);
+ bookmarkCache.updateChildren(id);
+}
+
+/**
+ * Callback for when a bookmark node is created.
+ * @param {string} id The id of the newly created bookmark node.
+ * @param {!Object} bookmarkNode The new bookmark node.
+ */
+function handleCreated(id, bookmarkNode) {
+ // console.info('handleCreated', id, bookmarkNode);
+ list.handleCreated(id, bookmarkNode);
+ tree.handleCreated(id, bookmarkNode);
+ bookmarkCache.updateChildren(bookmarkNode.parentId);
+}
+
+function handleMoved(id, moveInfo) {
+ // console.info('handleMoved', id, moveInfo);
+ list.handleMoved(id, moveInfo);
+ tree.handleMoved(id, moveInfo);
+
+ bookmarkCache.updateChildren(moveInfo.parentId);
+ if (moveInfo.parentId != moveInfo.oldParentId)
+ bookmarkCache.updateChildren(moveInfo.oldParentId);
+}
+
+function handleRemoved(id, removeInfo) {
+ // console.info('handleRemoved', id, removeInfo);
+ list.handleRemoved(id, removeInfo);
+ tree.handleRemoved(id, removeInfo);
+
+ bookmarkCache.updateChildren(removeInfo.parentId);
+ bookmarkCache.remove(id);
+}
+
+function handleImportBegan() {
+ chrome.bookmarks.onCreated.removeListener(handleCreated);
+}
+
+function handleImportEnded() {
+ chrome.bookmarks.onCreated.addListener(handleCreated);
+ var p = bmm.loadTree();
+ p.addListener(function(node) {
+ var otherBookmarks = node.children[1].children;
+ var importedFolder = otherBookmarks[otherBookmarks.length - 1];
+ var importId = importedFolder.id;
+ tree.insertSubtree(importedFolder);
+ navigateTo(importId)
+ });
+}
+
+/**
+ * Adds the listeners for the bookmark model change events.
+ */
+function addBookmarkModelListeners() {
+ chrome.bookmarks.onChanged.addListener(handleBookmarkChanged);
+ chrome.bookmarks.onChildrenReordered.addListener(handleChildrenReordered);
+ chrome.bookmarks.onCreated.addListener(handleCreated);
+ chrome.bookmarks.onMoved.addListener(handleMoved);
+ chrome.bookmarks.onRemoved.addListener(handleRemoved);
+ chrome.experimental.bookmarkManager.onImportBegan.addListener(
+ handleImportBegan);
+ chrome.experimental.bookmarkManager.onImportEnded.addListener(
+ handleImportEnded);
+}
+
+/**
+ * This returns the user visible path to the folder where the bookmark is
+ * located.
+ * @param {number} parentId The ID of the parent folder.
+ * @return {string} The path to the the bookmark,
+ */
+function getFolder(parentId) {
+ var parentNode = tree.getBookmarkNodeById(parentId);
+ if (parentNode) {
+ var s = parentNode.title;
+ if (parentNode.parentId != ROOT_ID) {
+ return getFolder(parentNode.parentId) + '/' + s;
+ }
+ return s;
+ }
+}
+
+tree.addEventListener('load', function(e) {
+ // Add hard coded tree items
+ tree.add(recentTreeItem);
+ tree.add(searchTreeItem);
+
+ // Now we can select a tree item.
+ var hash = window.location.hash.slice(1);
+ if (!hash) {
+ // If we do not have a hash select first item in the tree.
+ hash = tree.items[0].bookmarkId;
+ }
+
+ if (/^q=/.test(hash))
+ $('term').value = hash.slice(2);
+ navigateTo(hash);
+});
+
+tree.buildTree();
+addBookmarkModelListeners();
+
+var dnd = {
+ DND_EFFECT_COPY: 'copy',
+ DND_EFFECT_MOVE: cr.isMac ? 'move' : 'copy', // http://crbug.com/14654
+
+ dragData: null,
+
+ getBookmarkElement: function(el) {
+ while (el && !el.bookmarkNode) {
+ el = el.parentNode;
+ }
+ return el;
+ },
+
+ // If we are over the list and the list is showing recent or search result
+ // we cannot drop.
+ isOverRecentOrSearch: function(overElement) {
+ return (list.isRecent() || list.isSearch()) && list.contains(overElement);
+ },
+
+ checkEvery_: function(f, overBookmarkNode, overElement) {
+ return this.dragData.elements.every(function(element) {
+ return f.call(this, element, overBookmarkNode, overElement);
+ }, this);
+ },
+
+ /**
+ * @return {boolean} Whether we are currently dragging any folders.
+ */
+ isDraggingFolders: function() {
+ return !!this.dragData && this.dragData.elements.some(function(node) {
+ return !node.url;
+ });
+ },
+
+ /**
+ * This is a first pass wether we can drop the dragged items.
+ *
+ * @param {!BookmarkTreeNode} overBookmarkNode The bookmark that we are
+ * currently dragging over.
+ * @param {!HTMLElement} overElement The element that we are currently
+ * dragging over.
+ * @return {boolean} If this returns false then we know we should not drop
+ * the items. If it returns true we still have to call canDropOn,
+ * canDropAbove and canDropBelow.
+ */
+ canDrop: function(overBookmarkNode, overElement) {
+ var dragData = this.dragData;
+ if (!dragData)
+ return false;
+
+ if (this.isOverRecentOrSearch(overElement))
+ return false;
+
+ if (!dragData.sameProfile)
+ return true;
+
+ return this.checkEvery_(this.canDrop_, overBookmarkNode, overElement);
+ },
+
+ /**
+ * Helper for canDrop that only checks one bookmark node.
+ * @private
+ */
+ canDrop_: function(dragNode, overBookmarkNode, overElement) {
+ var dragId = dragNode.id;
+
+ if (overBookmarkNode.id == dragId)
+ return false;
+
+ // If we are dragging a folder we cannot drop it on any of its descendants
+ var dragBookmarkItem = bmm.treeLookup[dragId];
+ var dragBookmarkNode = dragBookmarkItem && dragBookmarkItem.bookmarkNode;
+ if (dragBookmarkNode && bmm.contains(dragBookmarkNode, overBookmarkNode)) {
+ return false;
+ }
+
+ return true;
+ },
+
+ /**
+ * Whether we can drop the dragged items above the drop target.
+ *
+ * @param {!BookmarkTreeNode} overBookmarkNode The bookmark that we are
+ * currently dragging over.
+ * @param {!HTMLElement} overElement The element that we are currently
+ * dragging over.
+ * @return {boolean} Whether we can drop the dragged items above the drop
+ * target.
+ */
+ canDropAbove: function(overBookmarkNode, overElement) {
+ if (overElement instanceof BookmarkList)
+ return false;
+
+ // We cannot drop between Bookmarks bar and Other bookmarks
+ if (overBookmarkNode.parentId == ROOT_ID)
+ return false;
+
+ var isOverTreeItem = overElement instanceof TreeItem;
+
+ // We can only drop between items in the tree if we have any folders.
+ if (isOverTreeItem && !this.isDraggingFolders())
+ return false;
+
+ if (!this.dragData.sameProfile)
+ return this.isDraggingFolders() || !isOverTreeItem;
+
+ return this.checkEvery_(this.canDropAbove_, overBookmarkNode, overElement);
+ },
+
+ /**
+ * Helper for canDropAbove that only checks one bookmark node.
+ * @private
+ */
+ canDropAbove_: function(dragNode, overBookmarkNode, overElement) {
+ var dragId = dragNode.id;
+
+ // We cannot drop above if the item below is already in the drag source
+ var previousElement = overElement.previousElementSibling;
+ if (previousElement &&
+ previousElement.bookmarkId == dragId)
+ return false;
+
+ return true;
+ },
+
+ /**
+ * Whether we can drop the dragged items below the drop target.
+ *
+ * @param {!BookmarkTreeNode} overBookmarkNode The bookmark that we are
+ * currently dragging over.
+ * @param {!HTMLElement} overElement The element that we are currently
+ * dragging over.
+ * @return {boolean} Whether we can drop the dragged items below the drop
+ * target.
+ */
+ canDropBelow: function(overBookmarkNode, overElement) {
+ if (overElement instanceof BookmarkList)
+ return false;
+
+ // We cannot drop between Bookmarks bar and Other bookmarks
+ if (overBookmarkNode.parentId == ROOT_ID)
+ return false;
+
+ // We can only drop between items in the tree if we have any folders.
+ if (!this.isDraggingFolders() && overElement instanceof TreeItem)
+ return false;
+
+ var isOverTreeItem = overElement instanceof TreeItem;
+
+ // Don't allow dropping below an expanded tree item since it is confusing
+ // to the user anyway.
+ if (isOverTreeItem && overElement.expanded)
+ return false;
+
+ if (!this.dragData.sameProfile)
+ return this.isDraggingFolders() || !isOverTreeItem;
+
+ return this.checkEvery_(this.canDropBelow_, overBookmarkNode, overElement);
+ },
+
+ /**
+ * Helper for canDropBelow that only checks one bookmark node.
+ * @private
+ */
+ canDropBelow_: function(dragNode, overBookmarkNode, overElement) {
+ var dragId = dragNode.id;
+
+ // We cannot drop below if the item below is already in the drag source
+ var nextElement = overElement.nextElementSibling;
+ if (nextElement &&
+ nextElement.bookmarkId == dragId)
+ return false;
+
+ return true;
+ },
+
+ /**
+ * Whether we can drop the dragged items on the drop target.
+ *
+ * @param {!BookmarkTreeNode} overBookmarkNode The bookmark that we are
+ * currently dragging over.
+ * @param {!HTMLElement} overElement The element that we are currently
+ * dragging over.
+ * @return {boolean} Whether we can drop the dragged items on the drop
+ * target.
+ */
+ canDropOn: function(overBookmarkNode, overElement) {
+ // We can only drop on a folder.
+ if (!bmm.isFolder(overBookmarkNode))
+ return false;
+
+ if (!this.dragData.sameProfile)
+ return true;
+
+ return this.checkEvery_(this.canDropOn_, overBookmarkNode, overElement);
+ },
+
+ /**
+ * Helper for canDropOn that only checks one bookmark node.
+ * @private
+ */
+ canDropOn_: function(dragNode, overBookmarkNode, overElement) {
+ var dragId = dragNode.id;
+
+ if (overElement instanceof BookmarkList) {
+ // We are trying to drop an item after the last item in the list. This
+ // is allowed if the item is different from the last item in the list
+ var listItems = list.items;
+ var len = listItems.length;
+ if (len == 0 ||
+ listItems[len - 1].bookmarkId != dragId) {
+ return true;
+ }
+ }
+
+ // Cannot drop on current parent.
+ if (overBookmarkNode.id == dragNode.parentId)
+ return false;
+
+ return true;
+ },
+
+ /**
+ * Callback for the dragstart event.
+ * @param {Event} e The dragstart event.
+ */
+ handleDragStart: function(e) {
+ // console.log(e.type);
+
+ // Determine the selected bookmarks.
+ var target = e.target;
+ var draggedItems = [];
+ if (target instanceof ListItem) {
+ // Use selected items.
+ draggedItems = target.parentNode.selectedItems;
+ } else if (target instanceof TreeItem) {
+ draggedItems.push(target);
+ }
+
+ // We manage starting the drag by using the extension API.
+ e.preventDefault();
+
+ if (draggedItems.length) {
+ // If we are dragging a single link we can do the *Link* effect, otherwise
+ // we only allow copy and move.
+ var effectAllowed;
+ if (draggedItems.length == 1 &&
+ !bmm.isFolder(draggedItems[0].bookmarkNode)) {
+ effectAllowed = 'copyMoveLink';
+ } else {
+ effectAllowed = 'copyMove';
+ }
+ e.dataTransfer.effectAllowed = effectAllowed;
+
+ var ids = draggedItems.map(function(el) {
+ return el.bookmarkId;
+ });
+
+ chrome.experimental.bookmarkManager.startDrag(ids);
+ }
+ },
+
+ handleDragEnter: function(e) {
+ // console.log(e.type);
+
+ e.preventDefault();
+ },
+
+ /**
+ * Calback for the dragover event.
+ * @param {Event} e The dragover event.
+ */
+ handleDragOver: function(e) {
+ // console.log(e.type);
+
+ // The default operation is to allow dropping links etc to do navigation.
+ // We never want to do that for the bookmark manager.
+ e.preventDefault();
+
+ if (!this.dragData)
+ return;
+
+ var overElement = this.getBookmarkElement(e.target);
+ if (!overElement && e.target == list)
+ overElement = list;
+
+ if (!overElement)
+ return;
+
+ var overBookmarkNode = overElement.bookmarkNode;
+
+ if (!this.canDrop(overBookmarkNode, overElement))
+ return;
+
+ var bookmarkNode = overElement.bookmarkNode;
+
+ var canDropAbove = this.canDropAbove(overBookmarkNode, overElement);
+ var canDropOn = this.canDropOn(overBookmarkNode, overElement);
+ var canDropBelow = this.canDropBelow(overBookmarkNode, overElement);
+
+ if (!canDropAbove && !canDropOn && !canDropBelow)
+ return;
+
+ // Now we know that we can drop. Determine if we will drop above, on or
+ // below based on mouse position etc.
+
+ var dropPos;
+
+ e.dataTransfer.dropEffect = this.dragData.sameProfile ?
+ this.DND_EFFECT_MOVE : this.DND_EFFECT_COPY;
+
+ var rect;
+ if (overElement instanceof TreeItem) {
+ // We only want the rect of the row representing the item and not
+ // its children
+ rect = overElement.rowElement.getBoundingClientRect();
+ } else {
+ rect = overElement.getBoundingClientRect();
+ }
+
+ var dy = e.clientY - rect.top;
+ var yRatio = dy / rect.height;
+
+ // above
+ if (canDropAbove &&
+ (yRatio <= .25 || yRatio <= .5 && !(canDropBelow && canDropOn))) {
+ dropPos = 'above';
+
+ // below
+ } else if (canDropBelow &&
+ (yRatio > .75 || yRatio > .5 && !(canDropAbove && canDropOn))) {
+ dropPos = 'below';
+
+ // on
+ } else if (canDropOn) {
+ dropPos = 'on';
+
+ // none
+ } else {
+ // No drop can happen. Exit now.
+ e.dataTransfer.dropEffect = 'none';
+ return;
+ }
+
+ function cloneClientRect(rect) {
+ var newRect = {};
+ for (var key in rect) {
+ newRect[key] = rect[key];
+ }
+ return newRect;
+ }
+
+ // If we are dropping above or below a tree item adjust the width so
+ // that it is clearer where the item will be dropped.
+ if ((dropPos == 'above' || dropPos == 'below') &&
+ overElement instanceof TreeItem) {
+ // ClientRect is read only so clone in into a read-write object.
+ rect = cloneClientRect(rect);
+ var rtl = getComputedStyle(overElement).direction == 'rtl';
+ var labelElement = overElement.labelElement;
+ var labelRect = labelElement.getBoundingClientRect();
+ if (rtl) {
+ rect.width = labelRect.left + labelRect.width - rect.left;
+ } else {
+ rect.left = labelRect.left;
+ rect.width -= rect.left
+ }
+ }
+
+ var overlayType = dropPos;
+
+ // If we are dropping on a list we want to show a overlay drop line after
+ // the last element
+ if (overElement instanceof BookmarkList) {
+ overlayType = 'below';
+
+ // Get the rect of the last list item.
+ var items = overElement.items;
+ var length = items.length;
+ if (length) {
+ dropPos = 'below';
+ overElement = items[length - 1];
+ rect = overElement.getBoundingClientRect();
+ } else {
+ // If there are no items, collapse the height of the rect
+ rect = cloneClientRect(rect);
+ rect.height = 0;
+ // We do not use bottom so we don't care to adjust it.
+ }
+ }
+
+ this.showDropOverlay_(rect, overlayType);
+
+ this.dropDestination = {
+ dropPos: dropPos,
+ relatedNode: overElement.bookmarkNode
+ };
+ },
+
+ /**
+ * Shows and positions the drop marker overlay.
+ * @param {ClientRect} targetRect The drop target rect
+ * @param {string} overlayType The position relative to the target rect.
+ * @private
+ */
+ showDropOverlay_: function(targetRect, overlayType) {
+ window.clearTimeout(this.hideDropOverlayTimer_);
+ var overlay = $('drop-overlay');
+ if (overlayType == 'on') {
+ overlay.className = '';
+ overlay.style.top = targetRect.top + 'px';
+ overlay.style.height = targetRect.height + 'px';
+ } else {
+ overlay.className = 'line';
+ overlay.style.height = '';
+ }
+ overlay.style.width = targetRect.width + 'px';
+ overlay.style.left = targetRect.left + 'px';
+ overlay.style.display = 'block';
+
+ if (overlayType != 'on') {
+ var overlayRect = overlay.getBoundingClientRect();
+ if (overlayType == 'above') {
+ overlay.style.top = targetRect.top - overlayRect.height / 2 + 'px';
+ } else {
+ overlay.style.top = targetRect.top + targetRect.height -
+ overlayRect.height / 2 + 'px';
+ }
+ }
+ },
+
+ /**
+ * Hides the drop overlay element.
+ * @private
+ */
+ hideDropOverlay_: function() {
+ // Hide the overlay in a timeout to reduce flickering as we move between
+ // valid drop targets.
+ window.clearTimeout(this.hideDropOverlayTimer_);
+ this.hideDropOverlayTimer_ = window.setTimeout(function() {
+ $('drop-overlay').style.display = '';
+ }, 100);
+ },
+
+ handleDragLeave: function(e) {
+ // console.log(e.type);
+
+ this.hideDropOverlay_();
+ },
+
+ handleDrop: function(e) {
+ // console.log(e.type);
+
+ if (this.dropDestination && this.dragData) {
+ var dropPos = this.dropDestination.dropPos;
+ var relatedNode = this.dropDestination.relatedNode;
+ var parentId = dropPos == 'on' ? relatedNode.id : relatedNode.parentId;
+
+ var index;
+ if (dropPos == 'above')
+ index = relatedNode.index;
+ else if (dropPos == 'below')
+ index = relatedNode.index + 1;
+
+ if (index != undefined)
+ chrome.experimental.bookmarkManager.drop(parentId, index);
+ else
+ chrome.experimental.bookmarkManager.drop(parentId);
+
+ // TODO(arv): Select the newly dropped items.
+ }
+ this.dropDestination = null;
+ this.hideDropOverlay_();
+ },
+
+ handleDrag: function(e) {
+ // console.log(e.type);
+ },
+
+ handleDragEnd: function(e) {
+ // console.log(e.type);
+
+ var self = this;
+ // Chromium Win incorrectly fires the dragend event before the drop event.
+ // http://code.google.com/p/chromium/issues/detail?id=31292
+ window.setTimeout(function() {
+ self.dragData = null;
+ }, 1)
+ },
+
+ handleChromeDragEnter: function(dragData) {
+ this.dragData = dragData;
+ },
+
+ init: function() {
+ document.addEventListener('dragstart', cr.bind(this.handleDragStart, this));
+ document.addEventListener('dragenter', cr.bind(this.handleDragEnter, this));
+ document.addEventListener('dragover', cr.bind(this.handleDragOver, this));
+ document.addEventListener('dragleave', cr.bind(this.handleDragLeave, this));
+ document.addEventListener('drop', cr.bind(this.handleDrop, this));
+ document.addEventListener('dragend', cr.bind(this.handleDragEnd, this));
+ document.addEventListener('drag', cr.bind(this.handleDrag, this));
+
+ chrome.experimental.bookmarkManager.onDragEnter.addListener(cr.bind(
+ this.handleChromeDragEnter, this));
+ }
+
+};
+
+dnd.init();
+
+</script>
+
+<!-- Organize menu -->
+<command i18n-values=".label:rename_folder" id="rename-folder-command"></command>
+<command i18n-values=".label:edit" id="edit-command"></command>
+<command i18n-values=".label:delete" id="delete-command"></command>
+<command i18n-values=".label:show_in_folder" id="show-in-folder-command"></command>
+<command i18n-values=".label:cut" id="cut-command"></command>
+<command i18n-values=".label:copy" id="copy-command"></command>
+<command i18n-values=".label:paste" id="paste-command"></command>
+<command i18n-values=".label:sort" id="sort-command"></command>
+<command i18n-values=".label:add_new_bookmark" id="add-new-bookmark-command"></command>
+<command i18n-values=".label:new_folder" id="new-folder-command"></command>
+
+<!-- Tools menu -->
+<command i18n-values=".label:import_menu" id="import-menu-command"></command>
+<command i18n-values=".label:export_menu" id="export-menu-command"></command>
+
+<!-- open * are handled in canExecute handler -->
+<command id="open-in-new-tab-command"></command>
+<command id="open-in-new-window-command"></command>
+<command id="open-incognito-window-command"></command>
+
+<!-- TODO(arv): I think the commands might be better created in code? -->
+
+<menu id="organize-menu">
+ <button command="#rename-folder-command"></button>
+ <button command="#edit-command"></button>
+ <button command="#delete-command"></button>
+ <button command="#show-in-folder-command"></button>
+ <hr>
+ <button command="#cut-command"></button>
+ <button command="#copy-command"></button>
+ <button command="#paste-command"></button>
+ <hr>
+ <button command="#sort-command"></button>
+ <hr>
+ <button command="#add-new-bookmark-command"></button>
+ <button command="#new-folder-command"></button>
+</menu>
+
+<menu id="tools-menu">
+ <button command="#import-menu-command"></button>
+ <button command="#export-menu-command"></button>
+</menu>
+
+<menu id="context-menu">
+ <button command="#open-in-new-tab-command"></button>
+ <button command="#open-in-new-window-command"></button>
+ <button command="#open-incognito-window-command"></button>
+ <hr>
+ <button command="#rename-folder-command"></button>
+ <button command="#edit-command"></button>
+ <button command="#delete-command"></button>
+ <button command="#show-in-folder-command"></button>
+ <hr>
+ <button command="#cut-command"></button>
+ <button command="#copy-command"></button>
+ <button command="#paste-command"></button>
+ <hr>
+ <button command="#add-new-bookmark-command"></button>
+ <button command="#new-folder-command"></button>
+</menu>
+
+<script>
+
+// Commands
+
+const Command = cr.ui.Command;
+const CommandBinding = cr.ui.CommandBinding;
+const Menu = cr.ui.Menu;
+const MenuButton = cr.ui.MenuButton;
+
+cr.ui.decorate('menu', Menu);
+cr.ui.decorate('button[menu]', MenuButton);
+cr.ui.decorate('command', Command);
+
+cr.ui.contextMenuHandler.addContextMenuProperty(tree);
+list.contextMenu = $('context-menu');
+tree.contextMenu = $('context-menu');
+
+/**
+ * Helper function that updates the canExecute and labels for the open like
+ * commands.
+ * @param {!cr.ui.CanExecuteEvent} e The event fired by the command system.
+ * @param {!cr.ui.Command} command The command we are currently precessing.
+ */
+function updateOpenCommands(e, command) {
+ var selectedItem = e.target.selectedItem;
+ var selectionCount;
+ if (e.target == tree)
+ selectionCount = selectedItem ? 1 : 0;
+ else
+ selectionCount = e.target.selectedItems.length;
+
+ var isFolder = selectionCount == 1 &&
+ selectedItem.bookmarkNode &&
+ bmm.isFolder(selectedItem.bookmarkNode);
+ var multiple = selectionCount != 1 || isFolder;
+
+ function hasBookmarks(node) {
+ var it = new bmm.TreeIterator(node);
+ while (it.moveNext()) {
+ if (!bmm.isFolder(it.current))
+ return true;
+ }
+ return false;
+ }
+
+ switch (command.id) {
+ case 'open-in-new-tab-command':
+ command.label = localStrings.getString(multiple ?
+ 'open_all' : 'open_in_new_tab');
+ break;
+
+ case 'open-in-new-window-command':
+ command.label = localStrings.getString(multiple ?
+ 'open_all_new_window' : 'open_in_new_window');
+ break;
+ case 'open-incognito-window-command':
+ command.label = localStrings.getString(multiple ?
+ 'open_all_incognito' : 'open_incognito');
+ break;
+ }
+ e.canExecute = selectionCount > 0 && !!selectedItem.bookmarkNode;
+ if (isFolder && e.canExecute) {
+ // We need to get all the bookmark items in this tree. If the tree does not
+ // contain any non-folders we need to disable the command.
+ var p = bmm.loadSubtree(selectedItem.bookmarkId);
+ p.addListener(function(node) {
+ command.disabled = !node || !hasBookmarks(node);
+ });
+ }
+}
+
+/**
+ * Calls the backend to figure out if we can paste the clipboard into the active
+ * folder.
+ * @param {Function=} opt_f Function to call after the state has been
+ * updated.
+ */
+function updatePasteCommand(opt_f) {
+ function update(canPaste) {
+ var command = $('paste-command');
+ command.disabled = !canPaste;
+ if (opt_f)
+ opt_f();
+ }
+ // We cannot paste into search and recent view.
+ if (list.isSearch() || list.isRecent()) {
+ update(false);
+ } else {
+ chrome.experimental.bookmarkManager.canPaste(list.parentId, update);
+ }
+}
+
+// We can always execute the import-menu and export-menu commands.
+document.addEventListener('canExecute', function(e) {
+ var command = e.command;
+ var commandId = command.id;
+ if (commandId == 'import-menu-command' || commandId == 'export-menu-command') {
+ e.canExecute = true;
+ }
+});
+
+// Update canExecute for the commands when the list is the active element.
+list.addEventListener('canExecute', function(e) {
+ if (e.target != list) return;
+
+ var command = e.command;
+ var commandId = command.id;
+
+ function hasSelected() {
+ return !!e.target.selectedItem;
+ }
+
+ function hasSingleSelected() {
+ return e.target.selectedItems.length == 1;
+ }
+
+ function isRecentOrSearch() {
+ return list.isRecent() || list.isSearch();
+ }
+
+ switch (commandId) {
+ case 'rename-folder-command':
+ // Show rename if a single folder is selected
+ var items = e.target.selectedItems;
+ if (items.length != 1) {
+ e.canExecute = false;
+ command.hidden = true;
+ } else {
+ var isFolder = bmm.isFolder(items[0].bookmarkNode);
+ e.canExecute = isFolder;
+ command.hidden = !isFolder;
+ }
+ break;
+
+ case 'edit-command':
+ // Show the edit command if not a folder
+ var items = e.target.selectedItems;
+ if (items.length != 1) {
+ e.canExecute = false;
+ command.hidden = false;
+ } else {
+ var isFolder = bmm.isFolder(items[0].bookmarkNode);
+ e.canExecute = !isFolder;
+ command.hidden = isFolder;
+ }
+ break;
+
+ case 'show-in-folder-command':
+ e.canExecute = isRecentOrSearch() && hasSingleSelected();
+ break;
+
+ case 'delete-command':
+ case 'cut-command':
+ case 'copy-command':
+ e.canExecute = hasSelected();
+ break;
+
+ case 'paste-command':
+ updatePasteCommand();
+ break;
+
+ case 'sort-command':
+ case 'add-new-bookmark-command':
+ case 'new-folder-command':
+ e.canExecute = !isRecentOrSearch();
+ break;
+
+ case 'open-in-new-tab-command':
+ case 'open-in-new-window-command':
+ case 'open-incognito-window-command':
+ updateOpenCommands(e, command);
+ break;
+ }
+});
+
+// Update canExecute for the commands when the tree is the active element.
+tree.addEventListener('canExecute', function(e) {
+ if (e.target != tree) return;
+
+ var command = e.command;
+ var commandId = command.id;
+
+ function hasSelected() {
+ return !!e.target.selectedItem;
+ }
+
+ function isRecentOrSearch() {
+ var item = e.target.selectedItem;
+ return item == recentTreeItem || item == searchTreeItem;
+ }
+
+ function isTopLevelItem() {
+ return e.target.selectedItem.parentNode == tree;
+ }
+
+ switch (commandId) {
+ case 'rename-folder-command':
+ command.hidden = false;
+ e.canExecute = hasSelected() && !isTopLevelItem();
+ break;
+
+ case 'edit-command':
+ command.hidden = true;
+ e.canExecute = false;
+ break;
+
+ case 'delete-command':
+ case 'cut-command':
+ case 'copy-command':
+ e.canExecute = hasSelected() && !isTopLevelItem();
+ break;
+
+ case 'paste-command':
+ updatePasteCommand();
+ break;
+
+ case 'sort-command':
+ case 'add-new-bookmark-command':
+ case 'new-folder-command':
+ e.canExecute = !isRecentOrSearch();
+ break;
+
+ case 'open-in-new-tab-command':
+ case 'open-in-new-window-command':
+ case 'open-incognito-window-command':
+ updateOpenCommands(e, command);
+ break;
+ }
+});
+
+/**
+ * Update the canExecute state of the commands when the selection changes.
+ * @param {Event} e The change event object.
+ */
+function updateCommandsBasedOnSelection(e) {
+ if (e.target == document.activeElement) {
+ // Paste only needs to updated when the tree selection changes.
+ var commandNames = ['copy', 'cut', 'delete', 'rename-folder', 'edit',
+ 'add-new-bookmark', 'new-folder', 'open-in-new-tab',
+ 'open-in-new-window', 'open-incognito-window'];
+
+ if (e.target == tree) {
+ commandNames.push('paste', 'show-in-folder', 'sort');
+ }
+
+ commandNames.forEach(function(baseId) {
+ $(baseId + '-command').canExecuteChange();
+ });
+ }
+}
+
+list.addEventListener('change', updateCommandsBasedOnSelection);
+tree.addEventListener('change', updateCommandsBasedOnSelection);
+
+document.addEventListener('command', function(e) {
+ var command = e.command;
+ var commandId = command.id;
+ console.log(command.id, 'executed', 'on', e.target);
+ if (commandId == 'import-menu-command') {
+ chrome.experimental.bookmarkManager.import();
+ } else if (command.id == 'export-menu-command') {
+ chrome.experimental.bookmarkManager.export();
+ }
+});
+
+function handleRename(e) {
+ var item = e.target;
+ var bookmarkNode = item.bookmarkNode;
+ chrome.bookmarks.update(bookmarkNode.id, {title: item.label});
+}
+
+tree.addEventListener('rename', handleRename);
+list.addEventListener('rename', handleRename);
+
+list.addEventListener('edit', function(e) {
+ var item = e.target;
+ var bookmarkNode = item.bookmarkNode;
+ var context = {
+ title: bookmarkNode.title
+ };
+ if (!bmm.isFolder(bookmarkNode))
+ context.url = bookmarkNode.url;
+
+ if (bookmarkNode.id == 'new') {
+ // New page
+ context.parentId = bookmarkNode.parentId;
+ chrome.bookmarks.create(context, function(node) {
+ list.remove(item);
+ list.selectedItem = bmm.listLookup[node.id];
+ });
+ } else {
+ // Edit
+ chrome.bookmarks.update(bookmarkNode.id, context);
+ }
+});
+
+list.addEventListener('canceledit', function(e) {
+ var item = e.target;
+ var bookmarkNode = item.bookmarkNode;
+ if (bookmarkNode.id == 'new') {
+ list.remove(item);
+ list.selectionModel.leadItem = list.lastChild;
+ list.selectionModel.anchorItem = list.lastChild;
+ list.focus();
+ }
+});
+
+/**
+ * Navigates to the folder that the selected item is in and selects it. This is
+ * used for the show-in-folder command.
+ */
+function showInFolder() {
+ var bookmarkId = list.selectedItem.bookmarkNode.id;
+ var parentId = list.selectedItem.bookmarkNode.parentId;
+
+ // After the list is loaded we should select the revealed item.
+ var f = function(e) {
+ var item = bmm.listLookup[bookmarkId];
+ if (item) {
+ list.selectionModel.leadItem = item;
+ item.selected = true;
+ }
+ list.removeEventListener('load', f);
+ }
+ list.addEventListener('load', f);
+ var treeItem = bmm.treeLookup[parentId];
+ treeItem.reveal();
+
+ navigateTo(parentId);
+}
+
+/**
+ * Opens URLs in new tab, window or incognito mode.
+ * @param {!Array.<string>} urls The URLs to open.
+ * @param {string} kind The kind is either 'tab', 'window', or 'incognito'.
+ */
+function openUrls(urls, kind) {
+ if (urls.length < 1)
+ return;
+
+ if (urls.length > 15) {
+ if (!confirm(localStrings.getStringF('should_open_all', urls.length)))
+ return;
+ }
+
+ // Fix '#124' URLs since open those in a new window does not work. We prepend
+ // the base URL when we encounter those.
+ var base = window.location.href.split('#')[0];
+ urls = urls.map(function(url) {
+ return url[0] == '#' ? base + url : url;
+ });
+
+ // Incognito mode is not yet supported by the extensions APIs.
+ // http://code.google.com/p/chromium/issues/detail?id=12658
+ if (kind == 'window') {
+ chrome.windows.create({url: urls[0]}, function(window) {
+ urls.forEach(function(url, i) {
+ if (i > 0)
+ chrome.tabs.create({url: url, windowId: window.id, selected: false});
+ });
+ });
+ } else if (kind == 'tab') {
+ urls.forEach(function(url, i) {
+ chrome.tabs.create({url: url, selected: !i});
+ });
+ } else {
+ window.location.href = urls[0];
+ }
+}
+
+/**
+ * Returns the selected bookmark nodes of the active element. Only call this
+ * if the list or the tree is focused.
+ * @return {!Array} Array of bookmark nodes.
+ */
+function getSelectedBookmarkNodes() {
+ if (document.activeElement == list) {
+ return list.selectedItems.map(function(item) {
+ return item.bookmarkNode;
+ });
+ } else if (document.activeElement == tree) {
+ return [tree.selectedItem.bookmarkNode];
+ } else {
+ throw Error('getSelectedBookmarkNodes called when wrong element focused.');
+ }
+}
+
+/**
+ * @return {!Array.<string>} An array of the selected bookmark IDs.
+ */
+function getSelectedBookmarkIds() {
+ return getSelectedBookmarkNodes().map(function(node) {
+ return node.id;
+ });
+}
+
+/**
+ * Opens the selected bookmarks.
+ */
+function openBookmarks(kind) {
+ // If we have selected any folders we need to find all items recursively.
+ // We can do several async calls to getChildren but instead we do a single
+ // call to getTree and only add the subtrees of the selected items.
+
+ var urls = [];
+ var idMap = {};
+
+ // Traverses the tree until it finds a node tree that should be added. Then
+ // we switch over to use addNodes. We could merge these two functions into
+ // one but that would make the code less readable.
+ function traverseNodes(node) {
+ // This is not using the iterator since it uses breadth first search.
+ if (node.id in idMap) {
+ addNodes(node);
+ } else if (node.children) {
+ for (var i = 0; i < node.children.length; i++) {
+ traverseNodes(node.children[i]);
+ }
+ }
+ }
+
+ // Adds the node and all the descendants
+ function addNodes(node) {
+ var it = new bmm.TreeIterator(node);
+ while (it.moveNext()) {
+ var n = it.current;
+ if (!bmm.isFolder(n))
+ urls.push(n.url);
+ }
+ }
+
+ var nodes = getSelectedBookmarkNodes();
+
+ // Create a map for simpler lookup later.
+ nodes.forEach(function(node) {
+ idMap[node.id] = true;
+ });
+ var p = bmm.loadTree();
+ p.addListener(function(node) {
+ traverseNodes(node);
+ openUrls(urls, kind);
+ });
+}
+
+/**
+ * Deletes the selected bookmarks.
+ */
+function deleteBookmarks() {
+ getSelectedBookmarkIds().forEach(function(id) {
+ chrome.bookmarks.removeTree(id);
+ });
+}
+
+/**
+ * Callback for the new folder command. This creates a new folder and starts
+ * a rename of it.
+ */
+function newFolder() {
+ var parentId = list.parentId;
+ var isTree = document.activeElement == tree;
+ chrome.bookmarks.create({
+ title: localStrings.getString('new_folder_name'),
+ parentId: parentId
+ }, function(newNode) {
+ // We need to do this in a timeout to be able to focus the newly created
+ // item.
+ setTimeout(function() {
+ var newItem = isTree ? bmm.treeLookup[newNode.id] :
+ bmm.listLookup[newNode.id];
+ document.activeElement.selectedItem = newItem;
+ newItem.editing = true;
+ });
+ });
+}
+
+/**
+ * Adds a page to the current folder. This is called by the
+ * add-new-bookmark-command handler.
+ */
+function addPage() {
+ var parentId = list.parentId;
+ var fakeNode = {
+ title: '',
+ url: '',
+ parentId: parentId,
+ id: 'new'
+ };
+ var newListItem = bmm.createListItem(fakeNode, false);
+ list.add(newListItem);
+ list.selectedItem = newListItem;
+ newListItem.editing = true;
+}
+
+/**
+ * Handler for the command event. This is used both for the tree and the list.
+ * @param {!Event} e The event object.
+ */
+function handleCommand(e) {
+ var command = e.command;
+ var commandId = command.id;
+ switch (commandId) {
+ case 'show-in-folder-command':
+ showInFolder();
+ break;
+ case 'open-in-new-tab-command':
+ openBookmarks('tab');
+ break;
+ case 'open-in-new-window-command':
+ openBookmarks('window');
+ break;
+ case 'open-in-new-incognito-command':
+ openBookmarks('incognito');
+ break;
+ case 'delete-command':
+ deleteBookmarks();
+ break;
+ case 'copy-command':
+ chrome.experimental.bookmarkManager.copy(getSelectedBookmarkIds());
+ break;
+ case 'cut-command':
+ chrome.experimental.bookmarkManager.cut(getSelectedBookmarkIds());
+ break;
+ case 'paste-command':
+ chrome.experimental.bookmarkManager.paste(list.parentId);
+ break;
+ case 'sort-command':
+ chrome.experimental.bookmarkManager.sortChildren(list.parentId);
+ break;
+ case 'rename-folder-command':
+ case 'edit-command':
+ document.activeElement.selectedItem.editing = true;
+ break;
+ case 'new-folder-command':
+ newFolder();
+ break;
+ case 'add-new-bookmark-command':
+ addPage();
+ break;
+ }
+}
+
+// TODO(arv): Move shortcut to HTML?
+
+// Meta+Backspace on Mac, Del on other platforms.
+$('delete-command').shortcut = cr.isMac ? 'U+0008-meta' : 'U+007F';
+
+list.addEventListener('command', handleCommand);
+tree.addEventListener('command', handleCommand);
+
+// Execute the copy, cut and paste commands when those events are dispatched by
+// the browser. This allows us to rely on the browser to handle the keyboard
+// shortcuts for these commands.
+(function() {
+ function handle(id) {
+ return function(e) {
+ var command = $(id);
+ if (!command.disabled) {
+ command.execute();
+ e.preventDefault(); // Prevent the system beep
+ }
+ };
+ }
+
+ // Listen to copy, cut and paste events and execute the associated commands.
+ document.addEventListener('copy', handle('copy-command'));
+ document.addEventListener('cut', handle('cut-command'));
+
+ var pasteHandler = handle('paste-command');
+ document.addEventListener('paste', function(e) {
+ // Paste is a bit special since we need to do an async call to see if we can
+ // paste because the paste command might not be up to date.
+ updatePasteCommand(pasteHandler);
+ });
+})();
+
+/**
+ * The local strings object which is used to do the translation.
+ * @type {!LocalStrings}
+ */
+var localStrings = new LocalStrings;
+
+// Get the localized strings from the backend.
+chrome.experimental.bookmarkManager.getStrings(function setTemplateData(data) {
+ // The strings may contain & which we need to strip.
+ for (var key in data) {
+ data[key] = data[key].replace(/&/, '');
+ }
+
+ localStrings.templateData = data;
+ i18nTemplate.process(document, data);
+});
+
+</script>
+
+<div id="drop-overlay"></div>
+
+</body>
+</html>
« 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