| Index: chrome/browser/resources/enhanced_bookmark_manager/js/main.js
|
| diff --git a/chrome/browser/resources/enhanced_bookmark_manager/js/main.js b/chrome/browser/resources/enhanced_bookmark_manager/js/main.js
|
| deleted file mode 100644
|
| index 5c23f40337e5d5fcc364cf62a81922ada7980325..0000000000000000000000000000000000000000
|
| --- a/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();
|
| -})();
|
|
|