diff options
Diffstat (limited to 'chromium/chrome/browser/resources/enhanced_bookmark_manager/js/main.js')
-rw-r--r-- | chromium/chrome/browser/resources/enhanced_bookmark_manager/js/main.js | 1308 |
1 files changed, 0 insertions, 1308 deletions
diff --git a/chromium/chrome/browser/resources/enhanced_bookmark_manager/js/main.js b/chromium/chrome/browser/resources/enhanced_bookmark_manager/js/main.js deleted file mode 100644 index 5c23f40337e..00000000000 --- a/chromium/chrome/browser/resources/enhanced_bookmark_manager/js/main.js +++ /dev/null @@ -1,1308 +0,0 @@ -// Copyright 2013 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. - -(function() { -/** @const */ var BookmarkList = bmm.BookmarkList; -/** @const */ var BookmarkTree = bmm.BookmarkTree; -/** @const */ var Command = cr.ui.Command; -/** @const */ var CommandBinding = cr.ui.CommandBinding; -/** @const */ var LinkKind = cr.LinkKind; -/** @const */ var ListItem = cr.ui.ListItem; -/** @const */ var Menu = cr.ui.Menu; -/** @const */ var MenuButton = cr.ui.MenuButton; -/** @const */ var Promise = cr.Promise; -/** @const */ var Splitter = cr.ui.Splitter; -/** @const */ var TreeItem = cr.ui.TreeItem; - -/** - * An array containing the BookmarkTreeNodes that were deleted in the last - * deletion action. This is used for implementing undo. - * @type {Array.<BookmarkTreeNode>} - */ -var lastDeletedNodes; - -/** - * - * Holds the last DOMTimeStamp when mouse pointer hovers on folder in tree - * view. Zero means pointer doesn't hover on folder. - * @type {number} - */ -var lastHoverOnFolderTimeStamp = 0; - -/** - * Holds a function that will undo that last action, if global undo is enabled. - * @type {Function} - */ -var performGlobalUndo; - -/** - * Holds a link controller singleton. Use getLinkController() rarther than - * accessing this variabie. - * @type {LinkController} - */ -var linkController; - -/** - * New Windows are not allowed in Windows 8 metro mode. - */ -var canOpenNewWindows = true; - -/** - * Incognito mode availability can take the following values: , - * - 'enabled' for when both normal and incognito modes are available; - * - 'disabled' for when incognito mode is disabled; - * - 'forced' for when incognito mode is forced (normal mode is unavailable). - */ -var incognitoModeAvailability = 'enabled'; - -/** - * Whether bookmarks can be modified. - * @type {boolean} - */ -var canEdit = true; - -/** - * @type {TreeItem} - * @const - */ -var searchTreeItem = new TreeItem({ - bookmarkId: 'q=' -}); - -/** - * Command shortcut mapping. - * @const - */ -var commandShortcutMap = cr.isMac ? { - 'edit': 'Enter', - // On Mac we also allow Meta+Backspace. - 'delete': 'U+007F U+0008 Meta-U+0008', - 'open-in-background-tab': 'Meta-Enter', - 'open-in-new-tab': 'Shift-Meta-Enter', - 'open-in-same-window': 'Meta-Down', - 'open-in-new-window': 'Shift-Enter', - 'rename-folder': 'Enter', - // Global undo is Command-Z. It is not in any menu. - 'undo': 'Meta-U+005A', -} : { - 'edit': 'F2', - 'delete': 'U+007F', - 'open-in-background-tab': 'Ctrl-Enter', - 'open-in-new-tab': 'Shift-Ctrl-Enter', - 'open-in-same-window': 'Enter', - 'open-in-new-window': 'Shift-Enter', - 'rename-folder': 'F2', - // Global undo is Ctrl-Z. It is not in any menu. - 'undo': 'Ctrl-U+005A', -}; - -/** - * Mapping for folder id to suffix of UMA. These names will be appeared - * after "BookmarkManager_NavigateTo_" in UMA dashboard. - * @const - */ -var folderMetricsNameMap = { - '1': 'BookmarkBar', - '2': 'Other', - '3': 'Mobile', - 'q=': 'Search', - 'subfolder': 'SubFolder', -}; - -/** - * Adds an event listener to a node that will remove itself after firing once. - * @param {!Element} node The DOM node to add the listener to. - * @param {string} name The name of the event listener to add to. - * @param {function(Event)} handler Function called when the event fires. - */ -function addOneShotEventListener(node, name, handler) { - var f = function(e) { - handler(e); - node.removeEventListener(name, f); - }; - node.addEventListener(name, f); -} - -// Get the localized strings from the backend via bookmakrManagerPrivate API. -function loadLocalizedStrings(data) { - // The strings may contain & which we need to strip. - for (var key in data) { - data[key] = data[key].replace(/&/, ''); - } - - loadTimeData.data = data; - i18nTemplate.process(document, loadTimeData); - - searchTreeItem.label = loadTimeData.getString('search'); - searchTreeItem.icon = isRTL() ? 'images/bookmark_manager_search_rtl.png' : - 'images/bookmark_manager_search.png'; -} - -/** - * Updates the location hash to reflect the current state of the application. - */ -function updateHash() { - window.location.hash = tree.selectedItem.bookmarkId; -} - -/** - * Navigates to a bookmark ID. - * @param {string} id The ID to navigate to. - * @param {function()} callback Function called when list view loaded or - * displayed specified folder. - */ -function navigateTo(id, callback) { - if (list.parentId == id) { - callback(); - return; - } - - var metricsId = folderMetricsNameMap[id.replace(/^q=.*/, 'q=')] || - folderMetricsNameMap['subfolder']; - chrome.metricsPrivate.recordUserAction( - 'BookmarkManager_NavigateTo_' + metricsId); - - addOneShotEventListener(list, 'load', callback); - updateParentId(id); -} - -/** - * Updates the parent ID of the bookmark list and selects the correct tree item. - * @param {string} id The id. - */ -function updateParentId(id) { - // Setting list.parentId fires 'load' event. - list.parentId = id; - - // When tree.selectedItem changed, tree view calls navigatTo() then it - // calls updateHash() when list view displayed specified folder. - tree.selectedItem = bmm.treeLookup[id] || tree.selectedItem; -} - -// Process the location hash. This is called by onhashchange and when the page -// is first loaded. -function processHash() { - var id = window.location.hash.slice(1); - if (!id) { - // If we do not have a hash, select first item in the tree. - id = tree.items[0].bookmarkId; - } - - var valid = false; - if (/^e=/.test(id)) { - id = id.slice(2); - - // If hash contains e=, edit the item specified. - chrome.bookmarks.get(id, function(bookmarkNodes) { - // Verify the node to edit is a valid node. - if (!bookmarkNodes || bookmarkNodes.length != 1) - return; - var bookmarkNode = bookmarkNodes[0]; - - // After the list reloads, edit the desired bookmark. - var editBookmark = function(e) { - var index = list.dataModel.findIndexById(bookmarkNode.id); - if (index != -1) { - var sm = list.selectionModel; - sm.anchorIndex = sm.leadIndex = sm.selectedIndex = index; - scrollIntoViewAndMakeEditable(index); - } - }; - - navigateTo(bookmarkNode.parentId, editBookmark); - }); - - // We handle the two cases of navigating to the bookmark to be edited - // above. Don't run the standard navigation code below. - return; - } else if (/^q=/.test(id)) { - // In case we got a search hash, update the text input and the - // bmm.treeLookup to use the new id. - setSearch(id.slice(2)); - valid = true; - } - - // Navigate to bookmark 'id' (which may be a query of the form q=query). - 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); - }); - } -} - -// Activate is handled by the open-in-same-window-command. -function handleDoubleClickForList(e) { - if (e.button == 0) - $('open-in-same-window-command').execute(); -} - -// The list dispatches an event when the user clicks on the URL or the Show in -// folder part. -function handleUrlClickedForList(e) { - getLinkController().openUrlFromEvent(e.url, e.originalEvent); - chrome.bookmarkManagerPrivate.recordLaunch(); -} - -function handleSearch(e) { - setSearch(this.value); -} - -/** - * Navigates to the search results for the search text. - * @param {string} searchText The text to search for. - */ -function setSearch(searchText) { - if (searchText) { - // Only update search item if we have a search term. We never want the - // search item to be for an empty search. - delete bmm.treeLookup[searchTreeItem.bookmarkId]; - var id = searchTreeItem.bookmarkId = 'q=' + searchText; - bmm.treeLookup[searchTreeItem.bookmarkId] = searchTreeItem; - } - - var input = $('term'); - // Do not update the input if the user is actively using the text input. - if (document.activeElement != input) - input.value = searchText; - - if (searchText) { - tree.add(searchTreeItem); - tree.selectedItem = searchTreeItem; - } else { - // Go "home". - tree.selectedItem = tree.items[0]; - id = tree.selectedItem.bookmarkId; - } - - // Navigate now and update hash immediately. - navigateTo(id, updateHash); -} - -// Handle the logo button UI. -// When the user clicks the button we should navigate "home" and focus the list. -function handleClickOnLogoButton(e) { - setSearch(''); - $('list').focus(); -} - -/** - * 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 != bmm.ROOT_ID) { - return getFolder(parentNode.parentId) + '/' + s; - } - return s; - } -} - -function handleLoadForTree(e) { - processHash(); -} - -function getAllUrls(nodes) { - var urls = []; - - // Adds the node and all its direct children. - function addNodes(node) { - if (node.id == 'new') - return; - - if (node.children) { - node.children.forEach(function(child) { - if (!bmm.isFolder(child)) - urls.push(child.url); - }); - } else { - urls.push(node.url); - } - } - - // Get a future promise for the nodes. - var promises = nodes.map(function(node) { - if (bmm.isFolder(node)) - return bmm.loadSubtree(node.id); - // Not a folder so we already have all the data we need. - return new Promise(node); - }); - - var urlsPromise = new Promise(); - - var p = Promise.all.apply(null, promises); - p.addListener(function(nodes) { - nodes.forEach(function(node) { - addNodes(node); - }); - urlsPromise.value = urls; - }); - - return urlsPromise; -} - -/** - * Returns the nodes (non recursive) to use for the open commands. - * @param {HTMLElement} target . - * @return {Array.<BookmarkTreeNode>} . - */ -function getNodesForOpen(target) { - if (target == tree) { - var folderItem = tree.selectedItem; - return folderItem == searchTreeItem ? - list.dataModel.slice() : tree.selectedFolders; - } - var items = list.selectedItems; - return items.length ? items : list.dataModel.slice(); -} - -/** - * Returns a promise that will contain all URLs of all the selected bookmarks - * and the nested bookmarks for use with the open commands. - * @param {HTMLElement} target The target list or tree. - * @return {Promise} . - */ -function getUrlsForOpenCommands(target) { - return getAllUrls(getNodesForOpen(target)); -} - -function notNewNode(node) { - return node.id != 'new'; -} - -/** - * 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 processing. - * @param {string} singularId The string id of singular form of the menu label. - * @param {string} pluralId The string id of menu label if the singular form is - not used. - * @param {boolean} commandDisabled Whether the menu item should be disabled - no matter what bookmarks are selected. - */ -function updateOpenCommand(e, command, singularId, pluralId, commandDisabled) { - if (singularId) { - // The command label reflects the selection which might not reflect - // how many bookmarks will be opened. For example if you right click an - // empty area in a folder with 1 bookmark the text should still say "all". - var selectedNodes = getSelectedBookmarkNodes(e.target).filter(notNewNode); - var singular = selectedNodes.length == 1 && !bmm.isFolder(selectedNodes[0]); - command.label = loadTimeData.getString(singular ? singularId : pluralId); - } - - if (commandDisabled) { - command.disabled = true; - e.canExecute = false; - return; - } - - getUrlsForOpenCommands(e.target).addListener(function(urls) { - var disabled = !urls.length; - command.disabled = disabled; - e.canExecute = !disabled; - }); -} - -/** - * 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 organizeMenuCommand = $('paste-from-organize-menu-command'); - var contextMenuCommand = $('paste-from-context-menu-command'); - organizeMenuCommand.disabled = !canPaste; - contextMenuCommand.disabled = !canPaste; - if (opt_f) - opt_f(); - } - // We cannot paste into search view. - if (list.isSearch()) - update(false); - else - chrome.bookmarkManagerPrivate.canPaste(list.parentId, update); -} - -function handleCanExecuteForDocument(e) { - var command = e.command; - switch (command.id) { - case 'import-menu-command': - e.canExecute = canEdit; - break; - case 'export-menu-command': - // We can always execute the export-menu command. - e.canExecute = true; - break; - case 'sort-command': - e.canExecute = !list.isSearch() && list.dataModel.length > 1; - break; - case 'undo-command': - // The global undo command has no visible UI, so always enable it, and - // just make it a no-op if undo is not possible. - e.canExecute = true; - break; - default: - canExecuteForList(e); - break; - } -} - -/** - * Helper function for handling canExecute for the list and the tree. - * @param {!Event} e Can execute event object. - * @param {boolean} isSearch Whether the user is trying to do a command on - * search. - */ -function canExecuteShared(e, isSearch) { - var command = e.command; - var commandId = command.id; - switch (commandId) { - case 'paste-from-organize-menu-command': - case 'paste-from-context-menu-command': - updatePasteCommand(); - break; - - case 'add-new-bookmark-command': - case 'new-folder-command': - e.canExecute = !isSearch && canEdit; - break; - - case 'open-in-new-tab-command': - updateOpenCommand(e, command, 'open_in_new_tab', 'open_all', false); - break; - case 'open-in-background-tab-command': - updateOpenCommand(e, command, '', '', false); - break; - case 'open-in-new-window-command': - updateOpenCommand(e, command, - 'open_in_new_window', 'open_all_new_window', - // Disabled when incognito is forced. - incognitoModeAvailability == 'forced' || !canOpenNewWindows); - break; - case 'open-incognito-window-command': - updateOpenCommand(e, command, - 'open_incognito', 'open_all_incognito', - // Not available when incognito is disabled. - incognitoModeAvailability == 'disabled'); - break; - - case 'undo-delete-command': - e.canExecute = !!lastDeletedNodes; - break; - } -} - -/** - * Helper function for handling canExecute for the list and document. - * @param {!Event} e Can execute event object. - */ -function canExecuteForList(e) { - var command = e.command; - var commandId = command.id; - - function hasSelected() { - return !!list.selectedItem; - } - - function hasSingleSelected() { - return list.selectedItems.length == 1; - } - - function canCopyItem(item) { - return item.id != 'new'; - } - - function canCopyItems() { - var selectedItems = list.selectedItems; - return selectedItems && selectedItems.some(canCopyItem); - } - - function isSearch() { - return list.isSearch(); - } - - switch (commandId) { - case 'rename-folder-command': - // Show rename if a single folder is selected. - var items = list.selectedItems; - if (items.length != 1) { - e.canExecute = false; - command.hidden = true; - } else { - var isFolder = bmm.isFolder(items[0]); - e.canExecute = isFolder && canEdit; - command.hidden = !isFolder; - } - break; - - case 'edit-command': - // Show the edit command if not a folder. - var items = list.selectedItems; - if (items.length != 1) { - e.canExecute = false; - command.hidden = false; - } else { - var isFolder = bmm.isFolder(items[0]); - e.canExecute = !isFolder && canEdit; - command.hidden = isFolder; - } - break; - - case 'show-in-folder-command': - e.canExecute = isSearch() && hasSingleSelected(); - break; - - case 'delete-command': - case 'cut-command': - e.canExecute = canCopyItems() && canEdit; - break; - - case 'copy-command': - e.canExecute = canCopyItems(); - break; - - case 'open-in-same-window-command': - e.canExecute = hasSelected(); - break; - - default: - canExecuteShared(e, isSearch()); - } -} - -// Update canExecute for the commands when the list is the active element. -function handleCanExecuteForList(e) { - if (e.target != list) return; - canExecuteForList(e); -} - -// Update canExecute for the commands when the tree is the active element. -function handleCanExecuteForTree(e) { - if (e.target != tree) return; - - var command = e.command; - var commandId = command.id; - - function hasSelected() { - return !!e.target.selectedItem; - } - - function isSearch() { - var item = e.target.selectedItem; - return item == searchTreeItem; - } - - function isTopLevelItem() { - return e.target.selectedItem.parentNode == tree; - } - - switch (commandId) { - case 'rename-folder-command': - command.hidden = false; - e.canExecute = hasSelected() && !isTopLevelItem() && canEdit; - break; - - case 'edit-command': - command.hidden = true; - e.canExecute = false; - break; - - case 'delete-command': - case 'cut-command': - e.canExecute = hasSelected() && !isTopLevelItem() && canEdit; - break; - - case 'copy-command': - e.canExecute = hasSelected() && !isTopLevelItem(); - break; - - default: - canExecuteShared(e, isSearch()); - } -} - -/** - * 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 be 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-background-tab', 'open-in-new-window', 'open-incognito-window', - 'open-in-same-window', 'show-in-folder']; - - if (e.target == tree) { - commandNames.push('paste-from-context-menu', 'paste-from-organize-menu', - 'sort'); - } - - commandNames.forEach(function(baseId) { - $(baseId + '-command').canExecuteChange(); - }); - } -} - -function updateEditingCommands() { - var editingCommands = ['cut', 'delete', 'rename-folder', 'edit', - 'add-new-bookmark', 'new-folder', 'sort', - 'paste-from-context-menu', 'paste-from-organize-menu']; - - chrome.bookmarkManagerPrivate.canEdit(function(result) { - if (result != canEdit) { - canEdit = result; - editingCommands.forEach(function(baseId) { - $(baseId + '-command').canExecuteChange(); - }); - } - }); -} - -function handleChangeForTree(e) { - updateCommandsBasedOnSelection(e); - navigateTo(tree.selectedItem.bookmarkId, updateHash); -} - -function handleOrganizeButtonClick(e) { - updateEditingCommands(); - $('add-new-bookmark-command').canExecuteChange(); - $('new-folder-command').canExecuteChange(); - $('sort-command').canExecuteChange(); -} - -function handleRename(e) { - var item = e.target; - var bookmarkNode = item.bookmarkNode; - chrome.bookmarks.update(bookmarkNode.id, {title: item.label}); - performGlobalUndo = null; // This can't be undone, so disable global undo. -} - -function handleEdit(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') { - selectItemsAfterUserAction(list); - - // New page - context.parentId = bookmarkNode.parentId; - chrome.bookmarks.create(context, function(node) { - // A new node was created and will get added to the list due to the - // handler. - var dataModel = list.dataModel; - var index = dataModel.indexOf(bookmarkNode); - dataModel.splice(index, 1); - - // Select new item. - var newIndex = dataModel.findIndexById(node.id); - if (newIndex != -1) { - var sm = list.selectionModel; - list.scrollIndexIntoView(newIndex); - sm.leadIndex = sm.anchorIndex = sm.selectedIndex = newIndex; - } - }); - } else { - // Edit - chrome.bookmarks.update(bookmarkNode.id, context); - } - performGlobalUndo = null; // This can't be undone, so disable global undo. -} - -function handleCancelEdit(e) { - var item = e.target; - var bookmarkNode = item.bookmarkNode; - if (bookmarkNode.id == 'new') { - var dataModel = list.dataModel; - var index = dataModel.findIndexById('new'); - dataModel.splice(index, 1); - } -} - -/** - * 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 bookmarkNode = list.selectedItem; - if (!bookmarkNode) - return; - var parentId = bookmarkNode.parentId; - - // After the list is loaded we should select the revealed item. - function selectItem() { - var index = list.dataModel.findIndexById(bookmarkNode.id); - if (index == -1) - return; - var sm = list.selectionModel; - sm.anchorIndex = sm.leadIndex = sm.selectedIndex = index; - list.scrollIndexIntoView(index); - } - - var treeItem = bmm.treeLookup[parentId]; - treeItem.reveal(); - - navigateTo(parentId, selectItem); -} - -/** - * @return {!cr.LinkController} The link controller used to open links based on - * user clicks and keyboard actions. - */ -function getLinkController() { - return linkController || - (linkController = new cr.LinkController(loadTimeData)); -} - -/** - * Returns the selected bookmark nodes of the provided tree or list. - * If |opt_target| is not provided or null the active element is used. - * Only call this if the list or the tree is focused. - * @param {BookmarkList|BookmarkTree} opt_target The target list or tree. - * @return {!Array} Array of bookmark nodes. - */ -function getSelectedBookmarkNodes(opt_target) { - return (opt_target || document.activeElement) == tree ? - tree.selectedFolders : list.selectedItems; -} - -/** - * @return {!Array.<string>} An array of the selected bookmark IDs. - */ -function getSelectedBookmarkIds() { - var selectedNodes = getSelectedBookmarkNodes(); - selectedNodes.sort(function(a, b) { return a.index - b.index }); - return selectedNodes.map(function(node) { - return node.id; - }); -} - -/** - * Opens the selected bookmarks. - * @param {LinkKind} kind The kind of link we want to open. - * @param {HTMLElement} opt_eventTarget The target of the user initiated event. - */ -function openBookmarks(kind, opt_eventTarget) { - // If we have selected any folders, we need to find all the bookmarks one - // level down. We use multiple async calls to getSubtree instead of getting - // the whole tree since we would like to minimize the amount of data sent. - - var urlsP = getUrlsForOpenCommands(opt_eventTarget); - urlsP.addListener(function(urls) { - getLinkController().openUrls(urls, kind); - chrome.bookmarkManagerPrivate.recordLaunch(); - }); -} - -/** - * Opens an item in the list. - */ -function openItem() { - 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, updateHash); - } else { - openBookmarks(LinkKind.FOREGROUND_TAB); - } -} - -/** - * Deletes the selected bookmarks. The bookmarks are saved in memory in case - * the user needs to undo the deletion. - */ -function deleteBookmarks() { - var selectedIds = getSelectedBookmarkIds(); - lastDeletedNodes = []; - - function performDelete() { - chrome.bookmarkManagerPrivate.removeTrees(selectedIds); - $('undo-delete-command').canExecuteChange(); - performGlobalUndo = undoDelete; - } - - // First, store information about the bookmarks being deleted. - selectedIds.forEach(function(id) { - chrome.bookmarks.getSubTree(id, function(results) { - lastDeletedNodes.push(results); - - // When all nodes have been saved, perform the deletion. - if (lastDeletedNodes.length === selectedIds.length) - performDelete(); - }); - }); -} - -/** - * Restores a tree of bookmarks under a specified folder. - * @param {BookmarkTreeNode} node The node to restore. - * @param {=string} parentId The ID of the folder to restore under. If not - * specified, the original parentId of the node will be used. - */ -function restoreTree(node, parentId) { - var bookmarkInfo = { - parentId: parentId || node.parentId, - title: node.title, - index: node.index, - url: node.url - }; - - chrome.bookmarks.create(bookmarkInfo, function(result) { - if (!result) { - console.error('Failed to restore bookmark.'); - return; - } - - if (node.children) { - // Restore the children using the new ID for this node. - node.children.forEach(function(child) { - restoreTree(child, result.id); - }); - } - }); -} - -/** - * Restores the last set of bookmarks that was deleted. - */ -function undoDelete() { - lastDeletedNodes.forEach(function(arr) { - arr.forEach(restoreTree); - }); - lastDeletedNodes = null; - $('undo-delete-command').canExecuteChange(); - - // Only a single level of undo is supported, so disable global undo now. - performGlobalUndo = null; -} - -/** - * Computes folder for "Add Page" and "Add Folder". - * @return {string} The id of folder node where we'll create new page/folder. - */ -function computeParentFolderForNewItem() { - if (document.activeElement == tree) - return list.parentId; - var selectedItem = list.selectedItem; - return selectedItem && bmm.isFolder(selectedItem) ? - selectedItem.id : list.parentId; -} - -/** - * Callback for rename folder and edit command. This starts editing for - * selected item. - */ -function editSelectedItem() { - if (document.activeElement == tree) { - tree.selectedItem.editing = true; - } else { - var li = list.getListItem(list.selectedItem); - if (li) - li.editing = true; - } -} - -/** - * Callback for the new folder command. This creates a new folder and starts - * a rename of it. - */ -function newFolder() { - performGlobalUndo = null; // This can't be undone, so disable global undo. - - var parentId = computeParentFolderForNewItem(); - - // Callback is called after tree and list data model updated. - function createFolder(callback) { - chrome.bookmarks.create({ - title: loadTimeData.getString('new_folder_name'), - parentId: parentId - }, callback); - } - - if (document.activeElement == tree) { - createFolder(function(newNode) { - navigateTo(newNode.id, function() { - bmm.treeLookup[newNode.id].editing = true; - }); - }); - return; - } - - function editNewFolderInList() { - createFolder(function() { - var index = list.dataModel.length - 1; - var sm = list.selectionModel; - sm.anchorIndex = sm.leadIndex = sm.selectedIndex = index; - scrollIntoViewAndMakeEditable(index); - }); - } - - navigateTo(parentId, editNewFolderInList); -} - -/** - * Scrolls the list item into view and makes it editable. - * @param {number} index The index of the item to make editable. - */ -function scrollIntoViewAndMakeEditable(index) { - list.scrollIndexIntoView(index); - // onscroll is now dispatched asynchronously so we have to postpone - // the rest. - setTimeout(function() { - var item = list.getListItemByIndex(index); - if (item) - item.editing = true; - }); -} - -/** - * Adds a page to the current folder. This is called by the - * add-new-bookmark-command handler. - */ -function addPage() { - var parentId = computeParentFolderForNewItem(); - - function editNewBookmark() { - var fakeNode = { - title: '', - url: '', - parentId: parentId, - id: 'new' - }; - var dataModel = list.dataModel; - var length = dataModel.length; - dataModel.splice(length, 0, fakeNode); - var sm = list.selectionModel; - sm.anchorIndex = sm.leadIndex = sm.selectedIndex = length; - scrollIntoViewAndMakeEditable(length); - }; - - navigateTo(parentId, editNewBookmark); -} - -/** - * This function is used to select items after a user action such as paste, drop - * add page etc. - * @param {BookmarkList|BookmarkTree} target The target of the user action. - * @param {=string} opt_selectedTreeId If provided, then select that tree id. - */ -function selectItemsAfterUserAction(target, opt_selectedTreeId) { - // We get one onCreated event per item so we delay the handling until we get - // no more events coming. - - var ids = []; - var timer; - - function handle(id, bookmarkNode) { - clearTimeout(timer); - if (opt_selectedTreeId || list.parentId == bookmarkNode.parentId) - ids.push(id); - timer = setTimeout(handleTimeout, 50); - } - - function handleTimeout() { - chrome.bookmarks.onCreated.removeListener(handle); - chrome.bookmarks.onMoved.removeListener(handle); - - if (opt_selectedTreeId && ids.indexOf(opt_selectedTreeId) != -1) { - var index = ids.indexOf(opt_selectedTreeId); - if (index != -1 && opt_selectedTreeId in bmm.treeLookup) { - tree.selectedItem = bmm.treeLookup[opt_selectedTreeId]; - } - } else if (target == list) { - var dataModel = list.dataModel; - var firstIndex = dataModel.findIndexById(ids[0]); - var lastIndex = dataModel.findIndexById(ids[ids.length - 1]); - if (firstIndex != -1 && lastIndex != -1) { - var selectionModel = list.selectionModel; - selectionModel.selectedIndex = -1; - selectionModel.selectRange(firstIndex, lastIndex); - selectionModel.anchorIndex = selectionModel.leadIndex = lastIndex; - list.focus(); - } - } - - list.endBatchUpdates(); - } - - list.startBatchUpdates(); - - chrome.bookmarks.onCreated.addListener(handle); - chrome.bookmarks.onMoved.addListener(handle); - timer = setTimeout(handleTimeout, 300); -} - -/** - * Record user action. - * @param {string} name An user action name. - */ -function recordUserAction(name) { - chrome.metricsPrivate.recordUserAction('BookmarkManager_Command_' + name); -} - -/** - * The currently selected bookmark, based on where the user is clicking. - * @return {string} The ID of the currently selected bookmark (could be from - * tree view or list view). - */ -function getSelectedId() { - if (document.activeElement == tree) - return tree.selectedItem.bookmarkId; - var selectedItem = list.selectedItem; - return selectedItem && bmm.isFolder(selectedItem) ? - selectedItem.id : tree.selectedItem.bookmarkId; -} - -/** - * Pastes the copied/cutted bookmark into the right location depending whether - * if it was called from Organize Menu or from Context Menu. - * @param {string} id The id of the element being pasted from. - */ -function pasteBookmark(id) { - recordUserAction('Paste'); - selectItemsAfterUserAction(list); - chrome.bookmarkManagerPrivate.paste(id, getSelectedBookmarkIds()); -} - -/** - * Handler for the command event. This is used for context menu of list/tree - * and organized menu. - * @param {!Event} e The event object. - */ -function handleCommand(e) { - var command = e.command; - var commandId = command.id; - switch (commandId) { - case 'import-menu-command': - recordUserAction('Import'); - chrome.bookmarks.import(); - break; - case 'export-menu-command': - recordUserAction('Export'); - chrome.bookmarks.export(); - break; - case 'undo-command': - if (performGlobalUndo) { - recordUserAction('UndoGlobal'); - performGlobalUndo(); - } else { - recordUserAction('UndoNone'); - } - break; - case 'show-in-folder-command': - recordUserAction('ShowInFolder'); - showInFolder(); - break; - case 'open-in-new-tab-command': - case 'open-in-background-tab-command': - recordUserAction('OpenInNewTab'); - openBookmarks(LinkKind.BACKGROUND_TAB, e.target); - break; - case 'open-in-new-window-command': - recordUserAction('OpenInNewWindow'); - openBookmarks(LinkKind.WINDOW, e.target); - break; - case 'open-incognito-window-command': - recordUserAction('OpenIncognito'); - openBookmarks(LinkKind.INCOGNITO, e.target); - break; - case 'delete-command': - recordUserAction('Delete'); - deleteBookmarks(); - break; - case 'copy-command': - recordUserAction('Copy'); - chrome.bookmarkManagerPrivate.copy(getSelectedBookmarkIds(), - updatePasteCommand); - break; - case 'cut-command': - recordUserAction('Cut'); - chrome.bookmarkManagerPrivate.cut(getSelectedBookmarkIds(), - updatePasteCommand); - break; - case 'paste-from-organize-menu-command': - pasteBookmark(list.parentId); - break; - case 'paste-from-context-menu-command': - pasteBookmark(getSelectedId()); - break; - case 'sort-command': - recordUserAction('Sort'); - chrome.bookmarkManagerPrivate.sortChildren(list.parentId); - break; - case 'rename-folder-command': - editSelectedItem(); - break; - case 'edit-command': - recordUserAction('Edit'); - editSelectedItem(); - break; - case 'new-folder-command': - recordUserAction('NewFolder'); - newFolder(); - break; - case 'add-new-bookmark-command': - recordUserAction('AddPage'); - addPage(); - break; - case 'open-in-same-window-command': - recordUserAction('OpenInSame'); - openItem(); - break; - case 'undo-delete-command': - recordUserAction('UndoDelete'); - undoDelete(); - break; - } -} - -// 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 installEventHandlerForCommand(eventName, commandId) { - function handle(e) { - if (document.activeElement != list && document.activeElement != tree) - return; - var command = $(commandId); - if (!command.disabled) { - command.execute(); - if (e) - e.preventDefault(); // Prevent the system beep. - } - } - if (eventName == 'paste') { - // 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. - document.addEventListener(eventName, function(e) { - updatePasteCommand(handle); - }); - } else { - document.addEventListener(eventName, handle); - } -} - -function initializeSplitter() { - var splitter = document.querySelector('.main > .splitter'); - Splitter.decorate(splitter); - - // The splitter persists the size of the left component in the local store. - if ('treeWidth' in localStorage) - splitter.previousElementSibling.style.width = localStorage['treeWidth']; - - splitter.addEventListener('resize', function(e) { - localStorage['treeWidth'] = splitter.previousElementSibling.style.width; - }); -} - -function initializeBookmarkManager() { - // Sometimes the extension API is not initialized. - if (!chrome.bookmarks) - console.error('Bookmarks extension API is not available'); - - chrome.bookmarkManagerPrivate.getStrings(loadLocalizedStrings); - - bmm.treeLookup[searchTreeItem.bookmarkId] = searchTreeItem; - - cr.ui.decorate('menu', Menu); - cr.ui.decorate('button[menu]', MenuButton); - cr.ui.decorate('command', Command); - BookmarkList.decorate(list); - BookmarkTree.decorate(tree); - - list.addEventListener('canceledit', handleCancelEdit); - list.addEventListener('canExecute', handleCanExecuteForList); - list.addEventListener('change', updateCommandsBasedOnSelection); - list.addEventListener('contextmenu', updateEditingCommands); - list.addEventListener('dblclick', handleDoubleClickForList); - list.addEventListener('edit', handleEdit); - list.addEventListener('rename', handleRename); - list.addEventListener('urlClicked', handleUrlClickedForList); - - tree.addEventListener('canExecute', handleCanExecuteForTree); - tree.addEventListener('change', handleChangeForTree); - tree.addEventListener('contextmenu', updateEditingCommands); - tree.addEventListener('rename', handleRename); - tree.addEventListener('load', handleLoadForTree); - - cr.ui.contextMenuHandler.addContextMenuProperty(tree); - list.contextMenu = $('context-menu'); - tree.contextMenu = $('context-menu'); - - // We listen to hashchange so that we can update the currently shown folder - // when // the user goes back and forward in the history. - window.addEventListener('hashchange', processHash); - - document.querySelector('.header form').onsubmit = function(e) { - setSearch($('term').value); - e.preventDefault(); - }; - - $('term').addEventListener('search', handleSearch); - - document.querySelector('.summary > button').addEventListener( - 'click', handleOrganizeButtonClick); - - document.querySelector('button.logo').addEventListener( - 'click', handleClickOnLogoButton); - - document.addEventListener('canExecute', handleCanExecuteForDocument); - document.addEventListener('command', handleCommand); - - // Listen to copy, cut and paste events and execute the associated commands. - installEventHandlerForCommand('copy', 'copy-command'); - installEventHandlerForCommand('cut', 'cut-command'); - installEventHandlerForCommand('paste', 'paste-from-organize-menu-command'); - - // Install shortcuts - for (var name in commandShortcutMap) { - $(name + '-command').shortcut = commandShortcutMap[name]; - } - - // Disable almost all commands at startup. - var commands = document.querySelectorAll('command'); - for (var i = 0, command; command = commands[i]; ++i) { - if (command.id != 'import-menu-command' && - command.id != 'export-menu-command') { - command.disabled = true; - } - } - - chrome.bookmarkManagerPrivate.canEdit(function(result) { - canEdit = result; - }); - - chrome.systemPrivate.getIncognitoModeAvailability(function(result) { - // TODO(rustema): propagate policy value to the bookmark manager when it - // changes. - incognitoModeAvailability = result; - }); - - chrome.bookmarkManagerPrivate.canOpenNewWindows(function(result) { - canOpenNewWindows = result; - }); - - cr.ui.FocusOutlineManager.forDocument(document); - initializeSplitter(); - bmm.addBookmarkModelListeners(); - dnd.init(selectItemsAfterUserAction); - tree.reload(); -} - -initializeBookmarkManager(); -})(); |