diff options
author | Jocelyn Turcotte <jocelyn.turcotte@digia.com> | 2014-08-08 14:30:41 +0200 |
---|---|---|
committer | Jocelyn Turcotte <jocelyn.turcotte@digia.com> | 2014-08-12 13:49:54 +0200 |
commit | ab0a50979b9eb4dfa3320eff7e187e41efedf7a9 (patch) | |
tree | 498dfb8a97ff3361a9f7486863a52bb4e26bb898 /chromium/chrome/browser/resources/enhanced_bookmark_manager/js | |
parent | 4ce69f7403811819800e7c5ae1318b2647e778d1 (diff) |
Update Chromium to beta version 37.0.2062.68
Change-Id: I188e3b5aff1bec75566014291b654eb19f5bc8ca
Reviewed-by: Andras Becsi <andras.becsi@digia.com>
Diffstat (limited to 'chromium/chrome/browser/resources/enhanced_bookmark_manager/js')
6 files changed, 0 insertions, 3048 deletions
diff --git a/chromium/chrome/browser/resources/enhanced_bookmark_manager/js/bmm.js b/chromium/chrome/browser/resources/enhanced_bookmark_manager/js/bmm.js deleted file mode 100644 index b238f4deabe..00000000000 --- a/chromium/chrome/browser/resources/enhanced_bookmark_manager/js/bmm.js +++ /dev/null @@ -1,248 +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. - -cr.define('bmm', function() { - var Promise = cr.Promise; - - /** - * Whether a node contains another node. - * TODO(yosin): Once JavaScript style guide is updated and linter follows - * that, we'll remove useless documentations for |parent| and |descendant|. - * TODO(yosin): bmm.contains() should be method of BookmarkTreeNode. - * @param {!BookmarkTreeNode} parent . - * @param {!BookmarkTreeNode} descendant . - * @return {boolean} Whether the parent contains the descendant. - */ - function contains(parent, descendant) { - if (descendant.parentId == parent.id) - return true; - // the bmm.treeLookup contains all folders - var parentTreeItem = bmm.treeLookup[descendant.parentId]; - if (!parentTreeItem || !parentTreeItem.bookmarkNode) - return false; - return this.contains(parent, parentTreeItem.bookmarkNode); - } - - /** - * @param {!BookmarkTreeNode} node The node to test. - * @return {boolean} Whether a bookmark node is a folder. - */ - function isFolder(node) { - return !('url' in node); - } - - var loadingPromises = {}; - - /** - * Loads a subtree of the bookmark tree and returns a {@code cr.Promise} that - * will be fulfilled when done. This reuses multiple loads so that we do not - * load the same subtree more than once at the same time. - * @return {!cr.Promise} The future promise for the load. - */ - function loadSubtree(id) { - var p = new Promise; - if (!(id in loadingPromises)) { - loadingPromises[id] = new Promise; - loadingPromises[id].addListener(function(n) { - p.value = n; - }); - chrome.bookmarkManagerPrivate.getSubtree(id, false, function(nodes) { - loadingPromises[id].value = nodes && nodes[0]; - delete loadingPromises[id]; - }); - } else { - loadingPromises[id].addListener(function(n) { - p.value = n; - }); - } - return p; - } - - /** - * Loads the entire bookmark tree and returns a {@code cr.Promise} that will - * be fulfilled when done. This reuses multiple loads so that we do not load - * the same tree more than once at the same time. - * @return {!cr.Promise} The future promise for the load. - */ - function loadTree() { - return loadSubtree(''); - } - - var bookmarkCache = { - /** - * Removes the cached item from both the list and tree lookups. - */ - remove: function(id) { - var treeItem = bmm.treeLookup[id]; - if (treeItem) { - var items = treeItem.items; // is an HTMLCollection - for (var i = 0; i < items.length; ++i) { - var item = items[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; - } - } - - chrome.bookmarks.getChildren(id, function(children) { - if (children) - children.forEach(updateItem); - - if (opt_f) - opt_f(children); - }); - } - }; - - /** - * Called when the title of a bookmark changes. - * @param {string} id The id of changed bookmark node. - * @param {!Object} changeInfo The information about how the node changed. - */ - function handleBookmarkChanged(id, changeInfo) { - if (bmm.tree) - bmm.tree.handleBookmarkChanged(id, changeInfo); - if (bmm.list) - bmm.list.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) { - if (bmm.tree) - bmm.tree.handleChildrenReordered(id, reorderInfo); - if (bmm.list) - bmm.list.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) { - if (bmm.list) - bmm.list.handleCreated(id, bookmarkNode); - if (bmm.tree) - bmm.tree.handleCreated(id, bookmarkNode); - bookmarkCache.updateChildren(bookmarkNode.parentId); - } - - /** - * Callback for when a bookmark node is moved. - * @param {string} id The id of the moved bookmark node. - * @param {!Object} moveInfo The information about move. - */ - function handleMoved(id, moveInfo) { - if (bmm.list) - bmm.list.handleMoved(id, moveInfo); - if (bmm.tree) - bmm.tree.handleMoved(id, moveInfo); - - bookmarkCache.updateChildren(moveInfo.parentId); - if (moveInfo.parentId != moveInfo.oldParentId) - bookmarkCache.updateChildren(moveInfo.oldParentId); - } - - /** - * Callback for when a bookmark node is removed. - * @param {string} id The id of the removed bookmark node. - * @param {!Object} bookmarkNode The information about removed. - */ - function handleRemoved(id, removeInfo) { - if (bmm.list) - bmm.list.handleRemoved(id, removeInfo); - if (bmm.tree) - bmm.tree.handleRemoved(id, removeInfo); - - bookmarkCache.updateChildren(removeInfo.parentId); - bookmarkCache.remove(id); - } - - /** - * Callback for when all bookmark nodes have been deleted. - */ - function handleRemoveAll() { - // Reload the list and the tree. - if (bmm.list) - bmm.list.reload(); - if (bmm.tree) - bmm.tree.reload(); - } - - /** - * Callback for when importing bookmark is started. - */ - function handleImportBegan() { - chrome.bookmarks.onCreated.removeListener(handleCreated); - chrome.bookmarks.onChanged.removeListener(handleBookmarkChanged); - } - - /** - * Callback for when importing bookmark node is finished. - */ - function handleImportEnded() { - // When importing is done we reload the tree and the list. - - function f() { - bmm.tree.removeEventListener('load', f); - - chrome.bookmarks.onCreated.addListener(handleCreated); - chrome.bookmarks.onChanged.addListener(handleBookmarkChanged); - - if (!bmm.list) - return; - - // TODO(estade): this should navigate to the newly imported folder, which - // may be the bookmark bar if there were no previous bookmarks. - bmm.list.reload(); - } - - if (bmm.tree) { - bmm.tree.addEventListener('load', f); - bmm.tree.reload(); - } - } - - /** - * 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.bookmarks.onImportBegan.addListener(handleImportBegan); - chrome.bookmarks.onImportEnded.addListener(handleImportEnded); - }; - - return { - contains: contains, - isFolder: isFolder, - loadSubtree: loadSubtree, - loadTree: loadTree, - addBookmarkModelListeners: addBookmarkModelListeners - }; -}); diff --git a/chromium/chrome/browser/resources/enhanced_bookmark_manager/js/bmm/bookmark_list.js b/chromium/chrome/browser/resources/enhanced_bookmark_manager/js/bmm/bookmark_list.js deleted file mode 100644 index 9d491c69ff5..00000000000 --- a/chromium/chrome/browser/resources/enhanced_bookmark_manager/js/bmm/bookmark_list.js +++ /dev/null @@ -1,566 +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. - -// TODO(arv): Now that this is driven by a data model, implement a data model -// that handles the loading and the events from the bookmark backend. - -cr.define('bmm', function() { - var List = cr.ui.List; - var ListItem = cr.ui.ListItem; - var ArrayDataModel = cr.ui.ArrayDataModel; - var ContextMenuButton = cr.ui.ContextMenuButton; - - var list; - - /** - * Basic array data model for use with bookmarks. - * @param {!Array.<!BookmarkTreeNode>} items The bookmark items. - * @constructor - * @extends {ArrayDataModel} - */ - function BookmarksArrayDataModel(items) { - ArrayDataModel.call(this, items); - } - - BookmarksArrayDataModel.prototype = { - __proto__: ArrayDataModel.prototype, - - /** - * Finds the index of the bookmark with the given ID. - * @param {string} id The ID of the bookmark node to find. - * @return {number} The index of the found node or -1 if not found. - */ - findIndexById: function(id) { - for (var i = 0; i < this.length; i++) { - if (this.item(i).id == id) - return i; - } - return -1; - } - }; - - /** - * Removes all children and appends a new child. - * @param {!Node} parent The node to remove all children from. - * @param {!Node} newChild The new child to append. - */ - function replaceAllChildren(parent, newChild) { - var n; - while ((n = parent.lastChild)) { - parent.removeChild(n); - } - parent.appendChild(newChild); - } - - /** - * Creates a new bookmark list. - * @param {Object=} opt_propertyBag Optional properties. - * @constructor - * @extends {HTMLButtonElement} - */ - var BookmarkList = cr.ui.define('list'); - - BookmarkList.prototype = { - __proto__: List.prototype, - - /** @override */ - decorate: function() { - List.prototype.decorate.call(this); - this.addEventListener('mousedown', this.handleMouseDown_); - - // HACK(arv): http://crbug.com/40902 - window.addEventListener('resize', this.redraw.bind(this)); - - // We could add the ContextMenuButton in the BookmarkListItem but it slows - // down redraws a lot so we do this on mouseovers instead. - this.addEventListener('mouseover', this.handleMouseOver_.bind(this)); - - bmm.list = this; - }, - - createItem: function(bookmarkNode) { - return new BookmarkListItem(bookmarkNode); - }, - - parentId_: '', - - /** - * Reloads the list from the bookmarks backend. - */ - reload: function() { - var parentId = this.parentId; - - var callback = this.handleBookmarkCallback_.bind(this); - this.loading_ = true; - - if (!parentId) - callback([]); - else if (/^q=/.test(parentId)) - chrome.bookmarks.search(parentId.slice(2), callback); - else - chrome.bookmarks.getChildren(parentId, callback); - }, - - /** - * Callback function for loading items. - * @param {Array.<!BookmarkTreeNode>} items The loaded items. - * @private - */ - handleBookmarkCallback_: function(items) { - if (!items) { - // Failed to load bookmarks. Most likely due to the bookmark being - // removed. - cr.dispatchSimpleEvent(this, 'invalidId'); - this.loading_ = false; - return; - } - - this.dataModel = new BookmarksArrayDataModel(items); - - this.loading_ = false; - this.fixWidth_(); - cr.dispatchSimpleEvent(this, 'load'); - }, - - /** - * The bookmark node that the list is currently displaying. If we are - * currently displaying search this returns null. - * @type {BookmarkTreeNode} - */ - get bookmarkNode() { - if (this.isSearch()) - return null; - var treeItem = bmm.treeLookup[this.parentId]; - return treeItem && treeItem.bookmarkNode; - }, - - /** - * @return {boolean} Whether we are currently showing search results. - */ - isSearch: function() { - return this.parentId_[0] == 'q'; - }, - - /** - * Handles mouseover on the list so that we can add the context menu button - * lazily. - * @private - * @param {!Event} e The mouseover event object. - */ - handleMouseOver_: function(e) { - var el = e.target; - while (el && el.parentNode != this) { - el = el.parentNode; - } - - if (el && el.parentNode == this && - !el.editing && - !(el.lastChild instanceof ContextMenuButton)) { - el.appendChild(new ContextMenuButton); - } - }, - - /** - * Dispatches an urlClicked event which is used to open URLs in new - * tabs etc. - * @private - * @param {string} url The URL that was clicked. - * @param {!Event} originalEvent The original click event object. - */ - dispatchUrlClickedEvent_: function(url, originalEvent) { - var event = new Event('urlClicked', {bubbles: true}); - event.url = url; - event.originalEvent = originalEvent; - this.dispatchEvent(event); - }, - - /** - * Handles mousedown events so that we can prevent the auto scroll as - * necessary. - * @private - * @param {!MouseEvent} e The mousedown event object. - */ - handleMouseDown_: function(e) { - if (e.button == 1) { - // WebKit no longer fires click events for middle clicks so we manually - // listen to mouse up to dispatch a click event. - this.addEventListener('mouseup', this.handleMiddleMouseUp_); - - // When the user does a middle click we need to prevent the auto scroll - // in case the user is trying to middle click to open a bookmark in a - // background tab. - // We do not do this in case the target is an input since middle click - // is also paste on Linux and we don't want to break that. - if (e.target.tagName != 'INPUT') - e.preventDefault(); - } - }, - - /** - * WebKit no longer dispatches click events for middle clicks so we need - * to emulate it. - * @private - * @param {!MouseEvent} e The mouse up event object. - */ - handleMiddleMouseUp_: function(e) { - this.removeEventListener('mouseup', this.handleMiddleMouseUp_); - if (e.button == 1) { - var el = e.target; - while (el.parentNode != this) { - el = el.parentNode; - } - var node = el.bookmarkNode; - if (node && !bmm.isFolder(node)) - this.dispatchUrlClickedEvent_(node.url, e); - } - }, - - // Bookmark model update callbacks - handleBookmarkChanged: function(id, changeInfo) { - var dataModel = this.dataModel; - var index = dataModel.findIndexById(id); - if (index != -1) { - var bookmarkNode = this.dataModel.item(index); - bookmarkNode.title = changeInfo.title; - if ('url' in changeInfo) - bookmarkNode.url = changeInfo['url']; - - dataModel.updateIndex(index); - } - }, - - handleChildrenReordered: function(id, reorderInfo) { - if (this.parentId == id) { - // We create a new data model with updated items in the right order. - var dataModel = this.dataModel; - var items = {}; - for (var i = this.dataModel.length - 1; i >= 0; i--) { - var bookmarkNode = dataModel.item(i); - items[bookmarkNode.id] = bookmarkNode; - } - var newArray = []; - for (var i = 0; i < reorderInfo.childIds.length; i++) { - newArray[i] = items[reorderInfo.childIds[i]]; - newArray[i].index = i; - } - - this.dataModel = new BookmarksArrayDataModel(newArray); - } - }, - - handleCreated: function(id, bookmarkNode) { - if (this.parentId == bookmarkNode.parentId) - this.dataModel.splice(bookmarkNode.index, 0, bookmarkNode); - }, - - handleMoved: function(id, moveInfo) { - if (moveInfo.parentId == this.parentId || - moveInfo.oldParentId == this.parentId) { - - var dataModel = this.dataModel; - - if (moveInfo.oldParentId == moveInfo.parentId) { - // Reorder within this folder - - this.startBatchUpdates(); - - var bookmarkNode = this.dataModel.item(moveInfo.oldIndex); - this.dataModel.splice(moveInfo.oldIndex, 1); - this.dataModel.splice(moveInfo.index, 0, bookmarkNode); - - this.endBatchUpdates(); - } else { - if (moveInfo.oldParentId == this.parentId) { - // Move out of this folder - - var index = dataModel.findIndexById(id); - if (index != -1) - dataModel.splice(index, 1); - } - - if (moveInfo.parentId == this.parentId) { - // Move to this folder - var self = this; - chrome.bookmarks.get(id, function(bookmarkNodes) { - var bookmarkNode = bookmarkNodes[0]; - dataModel.splice(bookmarkNode.index, 0, bookmarkNode); - }); - } - } - } - }, - - handleRemoved: function(id, removeInfo) { - var dataModel = this.dataModel; - var index = dataModel.findIndexById(id); - if (index != -1) - dataModel.splice(index, 1); - }, - - /** - * Workaround for http://crbug.com/40902 - * @private - */ - fixWidth_: function() { - var list = bmm.list; - if (this.loading_ || !list) - return; - - // The width of the list is wrong after its content has changed. - // Fortunately the reported offsetWidth is correct so we can detect the - //incorrect width. - if (list.offsetWidth != list.parentNode.clientWidth - list.offsetLeft) { - // Set the width to the correct size. This causes the relayout. - list.style.width = list.parentNode.clientWidth - list.offsetLeft + 'px'; - // Remove the temporary style.width in a timeout. Once the timer fires - // the size should not change since we already fixed the width. - window.setTimeout(function() { - list.style.width = ''; - }, 0); - } - } - }; - - /** - * The ID of the bookmark folder we are displaying. - * @type {string} - */ - cr.defineProperty(BookmarkList, 'parentId', cr.PropertyKind.JS, - function() { - this.reload(); - }); - - /** - * The contextMenu property. - * @type {cr.ui.Menu} - */ - cr.ui.contextMenuHandler.addContextMenuProperty(BookmarkList); - - /** - * Creates a new bookmark list item. - * @param {!BookmarkTreeNode} bookmarkNode The bookmark node this represents. - * @constructor - * @extends {cr.ui.ListItem} - */ - function BookmarkListItem(bookmarkNode) { - var el = cr.doc.createElement('div'); - el.bookmarkNode = bookmarkNode; - BookmarkListItem.decorate(el); - return el; - } - - /** - * Decorates an element as a bookmark list item. - * @param {!HTMLElement} el The element to decorate. - */ - BookmarkListItem.decorate = function(el) { - el.__proto__ = BookmarkListItem.prototype; - el.decorate(); - }; - - BookmarkListItem.prototype = { - __proto__: ListItem.prototype, - - /** @override */ - decorate: function() { - ListItem.prototype.decorate.call(this); - - var bookmarkNode = this.bookmarkNode; - - this.draggable = true; - - var labelEl = this.ownerDocument.createElement('div'); - labelEl.className = 'label'; - labelEl.textContent = bookmarkNode.title; - - var urlEl = this.ownerDocument.createElement('div'); - urlEl.className = 'url'; - - if (bmm.isFolder(bookmarkNode)) { - this.className = 'folder'; - } else { - labelEl.style.backgroundImage = getFaviconImageSet(bookmarkNode.url); - labelEl.style.backgroundSize = '16px'; - urlEl.textContent = bookmarkNode.url; - } - - this.appendChild(labelEl); - this.appendChild(urlEl); - - // Initially the ContextMenuButton was added here but it slowed down - // rendering a lot so it is now added using mouseover. - }, - - /** - * The ID of the bookmark folder we are currently showing or loading. - * @type {string} - */ - get bookmarkId() { - return this.bookmarkNode.id; - }, - - /** - * Whether the user is currently able to edit the list item. - * @type {boolean} - */ - get editing() { - return this.hasAttribute('editing'); - }, - set editing(editing) { - var oldEditing = this.editing; - if (oldEditing == editing) - return; - - var url = this.bookmarkNode.url; - var title = this.bookmarkNode.title; - var isFolder = bmm.isFolder(this.bookmarkNode); - var listItem = this; - var labelEl = this.firstChild; - var urlEl = labelEl.nextSibling; - var labelInput, urlInput; - - // Handles enter and escape which trigger reset and commit respectively. - function handleKeydown(e) { - // Make sure that the tree does not handle the key. - e.stopPropagation(); - - // Calling list.focus blurs the input which will stop editing the list - // item. - switch (e.keyIdentifier) { - case 'U+001B': // Esc - labelInput.value = title; - if (!isFolder) - urlInput.value = url; - // fall through - cr.dispatchSimpleEvent(listItem, 'canceledit', true); - case 'Enter': - if (listItem.parentNode) - listItem.parentNode.focus(); - } - } - - function handleBlur(e) { - // When the blur event happens we do not know who is getting focus so we - // delay this a bit since we want to know if the other input got focus - // before deciding if we should exit edit mode. - var doc = e.target.ownerDocument; - window.setTimeout(function() { - var activeElement = doc.hasFocus() && doc.activeElement; - if (activeElement != urlInput && activeElement != labelInput) { - listItem.editing = false; - } - }, 50); - } - - var doc = this.ownerDocument; - if (editing) { - this.setAttribute('editing', ''); - this.draggable = false; - - labelInput = doc.createElement('input'); - labelInput.placeholder = - loadTimeData.getString('name_input_placeholder'); - replaceAllChildren(labelEl, labelInput); - labelInput.value = title; - - if (!isFolder) { - urlInput = doc.createElement('input'); - urlInput.type = 'url'; - urlInput.required = true; - urlInput.placeholder = - loadTimeData.getString('url_input_placeholder'); - - // We also need a name for the input for the CSS to work. - urlInput.name = '-url-input-' + cr.createUid(); - replaceAllChildren(urlEl, urlInput); - urlInput.value = url; - } - - function stopPropagation(e) { - e.stopPropagation(); - } - - var eventsToStop = - ['mousedown', 'mouseup', 'contextmenu', 'dblclick', 'paste']; - eventsToStop.forEach(function(type) { - labelInput.addEventListener(type, stopPropagation); - }); - labelInput.addEventListener('keydown', handleKeydown); - labelInput.addEventListener('blur', handleBlur); - cr.ui.limitInputWidth(labelInput, this, 100, 0.5); - labelInput.focus(); - labelInput.select(); - - if (!isFolder) { - eventsToStop.forEach(function(type) { - urlInput.addEventListener(type, stopPropagation); - }); - urlInput.addEventListener('keydown', handleKeydown); - urlInput.addEventListener('blur', handleBlur); - cr.ui.limitInputWidth(urlInput, this, 200, 0.5); - } - - } else { - // Check that we have a valid URL and if not we do not change the - // editing mode. - if (!isFolder) { - var urlInput = this.querySelector('.url input'); - var newUrl = urlInput.value; - if (!newUrl) { - cr.dispatchSimpleEvent(this, 'canceledit', true); - return; - } - - if (!urlInput.validity.valid) { - // WebKit does not do URL fix up so we manually test if prepending - // 'http://' would make the URL valid. - // https://bugs.webkit.org/show_bug.cgi?id=29235 - urlInput.value = 'http://' + newUrl; - if (!urlInput.validity.valid) { - // still invalid - urlInput.value = newUrl; - - // In case the item was removed before getting here we should - // not alert. - if (listItem.parentNode) { - // Select the item again. - var dataModel = this.parentNode.dataModel; - var index = dataModel.indexOf(this.bookmarkNode); - var sm = this.parentNode.selectionModel; - sm.selectedIndex = sm.leadIndex = sm.anchorIndex = index; - - alert(loadTimeData.getString('invalid_url')); - } - urlInput.focus(); - urlInput.select(); - return; - } - newUrl = 'http://' + newUrl; - } - urlEl.textContent = this.bookmarkNode.url = newUrl; - } - - this.removeAttribute('editing'); - this.draggable = true; - - labelInput = this.querySelector('.label input'); - var newLabel = labelInput.value; - labelEl.textContent = this.bookmarkNode.title = newLabel; - - if (isFolder) { - if (newLabel != title) { - cr.dispatchSimpleEvent(this, 'rename', true); - } - } else if (newLabel != title || newUrl != url) { - cr.dispatchSimpleEvent(this, 'edit', true); - } - } - } - }; - - return { - BookmarkList: BookmarkList, - list: list - }; -}); diff --git a/chromium/chrome/browser/resources/enhanced_bookmark_manager/js/bmm/bookmark_tree.js b/chromium/chrome/browser/resources/enhanced_bookmark_manager/js/bmm/bookmark_tree.js deleted file mode 100644 index 8098b07e1f0..00000000000 --- a/chromium/chrome/browser/resources/enhanced_bookmark_manager/js/bmm/bookmark_tree.js +++ /dev/null @@ -1,307 +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. - - -cr.define('bmm', function() { - /** - * The id of the bookmark root. - * @type {string} - * @const - */ - var ROOT_ID = '0'; - - /** @const */ var Tree = cr.ui.Tree; - /** @const */ var TreeItem = cr.ui.TreeItem; - - var treeLookup = {}; - var tree; - - // Manager for persisting the expanded state. - var expandedManager = { - /** - * A map of the collapsed IDs. - * @type {Object} - */ - map: 'bookmarkTreeState' in localStorage ? - JSON.parse(localStorage['bookmarkTreeState']) : {}, - - /** - * Set the collapsed state for an ID. - * @param {string} The bookmark ID of the tree item that was expanded or - * collapsed. - * @param {boolean} expanded Whether the tree item was expanded. - */ - set: function(id, expanded) { - if (expanded) - delete this.map[id]; - else - this.map[id] = 1; - - this.save(); - }, - - /** - * @param {string} id The bookmark ID. - * @return {boolean} Whether the tree item should be expanded. - */ - get: function(id) { - return !(id in this.map); - }, - - /** - * Callback for the expand and collapse events from the tree. - * @param {!Event} e The collapse or expand event. - */ - handleEvent: function(e) { - this.set(e.target.bookmarkId, e.type == 'expand'); - }, - - /** - * Cleans up old bookmark IDs. - */ - cleanUp: function() { - for (var id in this.map) { - // If the id is no longer in the treeLookup the bookmark no longer - // exists. - if (!(id in treeLookup)) - delete this.map[id]; - } - this.save(); - }, - - timer: null, - - /** - * Saves the expanded state to the localStorage. - */ - save: function() { - clearTimeout(this.timer); - var map = this.map; - // Save in a timeout so that we can coalesce multiple changes. - this.timer = setTimeout(function() { - localStorage['bookmarkTreeState'] = JSON.stringify(map); - }, 100); - } - }; - - // Clean up once per session but wait until things settle down a bit. - setTimeout(expandedManager.cleanUp.bind(expandedManager), 1e4); - - /** - * Creates a new tree item for a bookmark node. - * @param {!Object} bookmarkNode The bookmark node. - * @constructor - * @extends {TreeItem} - */ - function BookmarkTreeItem(bookmarkNode) { - var ti = new TreeItem({ - label: bookmarkNode.title, - bookmarkNode: bookmarkNode, - // Bookmark toolbar and Other bookmarks are not draggable. - draggable: bookmarkNode.parentId != ROOT_ID - }); - ti.__proto__ = BookmarkTreeItem.prototype; - treeLookup[bookmarkNode.id] = ti; - return ti; - } - - BookmarkTreeItem.prototype = { - __proto__: TreeItem.prototype, - - /** - * The ID of the bookmark this tree item represents. - * @type {string} - */ - get bookmarkId() { - return this.bookmarkNode.id; - } - }; - - /** - * Asynchronousy adds a tree item at the correct index based on the bookmark - * backend. - * - * Since the bookmark tree only contains folders the index we get from certain - * callbacks is not very useful so we therefore have this async call which - * gets the children of the parent and adds the tree item at the desired - * index. - * - * This also exoands the parent so that newly added children are revealed. - * - * @param {!cr.ui.TreeItem} parent The parent tree item. - * @param {!cr.ui.TreeItem} treeItem The tree item to add. - * @param {Function=} f A function which gets called after the item has been - * added at the right index. - */ - function addTreeItem(parent, treeItem, opt_f) { - chrome.bookmarks.getChildren(parent.bookmarkNode.id, function(children) { - var index = children.filter(bmm.isFolder).map(function(item) { - return item.id; - }).indexOf(treeItem.bookmarkNode.id); - parent.addAt(treeItem, index); - parent.expanded = true; - if (opt_f) - opt_f(); - }); - } - - - /** - * Creates a new bookmark list. - * @param {Object=} opt_propertyBag Optional properties. - * @constructor - * @extends {HTMLButtonElement} - */ - var BookmarkTree = cr.ui.define('tree'); - - BookmarkTree.prototype = { - __proto__: Tree.prototype, - - decorate: function() { - Tree.prototype.decorate.call(this); - this.addEventListener('expand', expandedManager); - this.addEventListener('collapse', expandedManager); - - bmm.tree = this; - }, - - handleBookmarkChanged: function(id, changeInfo) { - var treeItem = treeLookup[id]; - if (treeItem) - treeItem.label = treeItem.bookmarkNode.title = changeInfo.title; - }, - - handleChildrenReordered: function(id, reorderInfo) { - var parentItem = treeLookup[id]; - // The tree only contains folders. - var dirIds = reorderInfo.childIds.filter(function(id) { - return id in treeLookup; - }).forEach(function(id, i) { - parentItem.addAt(treeLookup[id], i); - }); - }, - - handleCreated: function(id, bookmarkNode) { - if (bmm.isFolder(bookmarkNode)) { - var parentItem = treeLookup[bookmarkNode.parentId]; - var newItem = new BookmarkTreeItem(bookmarkNode); - addTreeItem(parentItem, newItem); - } - }, - - handleMoved: function(id, moveInfo) { - var treeItem = treeLookup[id]; - if (treeItem) { - var oldParentItem = treeLookup[moveInfo.oldParentId]; - oldParentItem.remove(treeItem); - var newParentItem = treeLookup[moveInfo.parentId]; - // The tree only shows folders so the index is not the index we want. We - // therefore get the children need to adjust the index. - addTreeItem(newParentItem, treeItem); - } - }, - - handleRemoved: function(id, removeInfo) { - var parentItem = treeLookup[removeInfo.parentId]; - var itemToRemove = treeLookup[id]; - if (parentItem && itemToRemove) - parentItem.remove(itemToRemove); - }, - - insertSubtree: function(folder) { - if (!bmm.isFolder(folder)) - return; - var children = folder.children; - this.handleCreated(folder.id, folder); - for (var i = 0; i < children.length; i++) { - var child = children[i]; - this.insertSubtree(child); - } - }, - - /** - * Returns the bookmark node with the given ID. The tree only maintains - * folder nodes. - * @param {string} id The ID of the node to find. - * @return {BookmarkTreeNode} The bookmark tree node or null if not found. - */ - getBookmarkNodeById: function(id) { - var treeItem = treeLookup[id]; - if (treeItem) - return treeItem.bookmarkNode; - return null; - }, - - /** - * Returns the selected bookmark folder node as an array. - * @type {!Array} Array of bookmark nodes. - */ - get selectedFolders() { - return this.selectedItem && this.selectedItem.bookmarkNode ? - [this.selectedItem.bookmarkNode] : []; - }, - - /** - * Fetches the bookmark items and builds the tree control. - */ - reload: function() { - /** - * Recursive helper function that adds all the directories to the - * parentTreeItem. - * @param {!cr.ui.Tree|!cr.ui.TreeItem} parentTreeItem The parent tree - * element to append to. - * @param {!Array.<BookmarkTreeNode>} bookmarkNodes A list of bookmark - * nodes to be added. - * @return {boolean} Whether any directories where added. - */ - function buildTreeItems(parentTreeItem, bookmarkNodes) { - var hasDirectories = false; - for (var i = 0, bookmarkNode; bookmarkNode = bookmarkNodes[i]; i++) { - if (bmm.isFolder(bookmarkNode)) { - hasDirectories = true; - var item = new BookmarkTreeItem(bookmarkNode); - parentTreeItem.add(item); - var anyChildren = buildTreeItems(item, bookmarkNode.children); - item.expanded = anyChildren && expandedManager.get(bookmarkNode.id); - } - } - return hasDirectories; - } - - var self = this; - chrome.bookmarkManagerPrivate.getSubtree('', true, function(root) { - self.clear(); - buildTreeItems(self, root[0].children); - cr.dispatchSimpleEvent(self, 'load'); - }); - }, - - /** - * Clears the tree. - */ - clear: function() { - // Remove all fields without recreating the object since other code - // references it. - for (var id in treeLookup) { - delete treeLookup[id]; - } - this.textContent = ''; - }, - - /** @override */ - remove: function(child) { - Tree.prototype.remove.call(this, child); - if (child.bookmarkNode) - delete treeLookup[child.bookmarkNode.id]; - } - }; - - return { - BookmarkTree: BookmarkTree, - BookmarkTreeItem: BookmarkTreeItem, - treeLookup: treeLookup, - tree: tree, - ROOT_ID: ROOT_ID - }; -}); diff --git a/chromium/chrome/browser/resources/enhanced_bookmark_manager/js/bmm_test.html b/chromium/chrome/browser/resources/enhanced_bookmark_manager/js/bmm_test.html deleted file mode 100644 index 88e8cbee91e..00000000000 --- a/chromium/chrome/browser/resources/enhanced_bookmark_manager/js/bmm_test.html +++ /dev/null @@ -1,117 +0,0 @@ -<!DOCTYPE html> -<html> -<head> -<!-- TODO(arv): Check in Closure unit tests and make this run as part of the - tests --> -<script src="http://closure-library.googlecode.com/svn/trunk/closure/goog/base.js"></script> -<script src="cr.js"></script> -<script src="cr/promise.js"></script> -<script src="bmm/treeiterator.js"></script> -<script src="bmm.js"></script> -<script> - -goog.require('goog.testing.jsunit'); - -</script> -</head> -<body> -<script> - -var tree = { - id: 0, - children: [ - { - id: 1, - children: [ - {id: 2}, - {id: 3, children: []} - ] - }, - {id: 4}, - {id: 5} - ] -}; - -// Mock chrome.bookmarkManagerPrivate.getSubtree -chrome = chrome || {}; -chrome.bookmarkManagerPrivate = chrome.bookmarkManagerPrivate || {}; - -var callbacks = {}; - -chrome.bookmarkManagerPrivate.getSubtree = function(id, foldersOnly, callback) { - callbacks[id] = callbacks[id] || []; - callbacks[id].push(callback); - callbacks[id].$calls = callbacks[id].$calls ? callbacks[id].$calls++ : 1; -}; - -chrome.bookmarkManagerPrivate.getSubtree.load = function(node) { - // getSubtree gets the root tree when id is ''. - var id = node.id; - if (id == tree.id) - id = ''; - for (var i = 0; i < callbacks[id].length; i++) { - callbacks[id][i].call(null, [node]); - } -}; - -function setUp() { - callbacks = {} -} - -function testLoadSingle() { - var calls = 0; - function f(node) { - calls++; - assertEquals(tree, node); - } - var p = bmm.loadTree(); - p.addListener(f); - - chrome.bookmarkManagerPrivate.getSubtree.load(tree); - - assertEquals(1, calls); - assertEquals(1, callbacks[''].$calls); -} - -function testLoadMultiple() { - var calls1 = 0; - var calls2 = 0; - function f1(node) { - calls1++; - assertEquals(tree, node); - } - function f2(node) { - calls2++; - assertEquals(tree, node); - } - - var p = bmm.loadTree(); - var p2 = bmm.loadTree(); - p.addListener(f1); - p2.addListener(f2); - - chrome.bookmarkManagerPrivate.getSubtree.load(tree); - - assertEquals(1, calls1); - assertEquals(1, calls2); - assertEquals(1, callbacks[''].$calls); -} - -function testLoadSubtree() { - var calls = 0; - function f(node) { - calls++; - assertEquals(tree.children[0], node); - } - var p = bmm.loadSubtree(1); - p.addListener(f); - - chrome.bookmarkManagerPrivate.getSubtree.load(tree.children[0]); - - assertEquals(1, calls); - assertEquals(1, callbacks[1].$calls); -} - -</script> -</body> -</html> diff --git a/chromium/chrome/browser/resources/enhanced_bookmark_manager/js/dnd.js b/chromium/chrome/browser/resources/enhanced_bookmark_manager/js/dnd.js deleted file mode 100644 index b4859a6310a..00000000000 --- a/chromium/chrome/browser/resources/enhanced_bookmark_manager/js/dnd.js +++ /dev/null @@ -1,502 +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. - -cr.define('dnd', function() { - 'use strict'; - - /** @const */ var BookmarkList = bmm.BookmarkList; - /** @const */ var ListItem = cr.ui.ListItem; - /** @const */ var TreeItem = cr.ui.TreeItem; - - /** - * Enumeration of valid drop locations relative to an element. These are - * bit masks to allow combining multiple locations in a single value. - * @enum {number} - * @const - */ - var DropPosition = { - NONE: 0, - ABOVE: 1, - ON: 2, - BELOW: 4 - }; - - /** - * @type {Object} Drop information calculated in |handleDragOver|. - */ - var dropDestination = null; - - /** - * @type {number} Timer id used to help minimize flicker. - */ - var removeDropIndicatorTimer; - - /** - * The element that had a style applied it to indicate the drop location. - * This is used to easily remove the style when necessary. - * @type {Element} - */ - var lastIndicatorElement; - - /** - * The style that was applied to indicate the drop location. - * @type {string} - */ - var lastIndicatorClassName; - - var dropIndicator = { - /** - * Applies the drop indicator style on the target element and stores that - * information to easily remove the style in the future. - */ - addDropIndicatorStyle: function(indicatorElement, position) { - var indicatorStyleName = position == DropPosition.ABOVE ? 'drag-above' : - position == DropPosition.BELOW ? 'drag-below' : - 'drag-on'; - - lastIndicatorElement = indicatorElement; - lastIndicatorClassName = indicatorStyleName; - - indicatorElement.classList.add(indicatorStyleName); - }, - - /** - * Clears the drop indicator style from the last element was the drop target - * so the drop indicator is no longer for that element. - */ - removeDropIndicatorStyle: function() { - if (!lastIndicatorElement || !lastIndicatorClassName) - return; - lastIndicatorElement.classList.remove(lastIndicatorClassName); - lastIndicatorElement = null; - lastIndicatorClassName = null; - }, - - /** - * Displays the drop indicator on the current drop target to give the - * user feedback on where the drop will occur. - */ - update: function(dropDest) { - window.clearTimeout(removeDropIndicatorTimer); - - var indicatorElement = dropDest.element; - var position = dropDest.position; - if (dropDest.element instanceof BookmarkList) { - // For an empty bookmark list use 'drop-above' style. - position = DropPosition.ABOVE; - } else if (dropDest.element instanceof TreeItem) { - indicatorElement = indicatorElement.querySelector('.tree-row'); - } - dropIndicator.removeDropIndicatorStyle(); - dropIndicator.addDropIndicatorStyle(indicatorElement, position); - }, - - /** - * Stop displaying the drop indicator. - */ - finish: function() { - // The use of a timeout is in order to reduce flickering as we move - // between valid drop targets. - window.clearTimeout(removeDropIndicatorTimer); - removeDropIndicatorTimer = window.setTimeout(function() { - dropIndicator.removeDropIndicatorStyle(); - }, 100); - } - }; - - /** - * Delay for expanding folder when pointer hovers on folder in tree view in - * milliseconds. - * @type {number} - * @const - */ - // TODO(yosin): EXPAND_FOLDER_DELAY should follow system settings. 400ms is - // taken from Windows default settings. - var EXPAND_FOLDER_DELAY = 400; - - /** - * The timestamp when the mouse was over a folder during a drag operation. - * Used to open the hovered folder after a certain time. - * @type {number} - */ - var lastHoverOnFolderTimeStamp = 0; - - /** - * Expand a folder if the user has hovered for longer than the specified - * time during a drag action. - */ - function updateAutoExpander(eventTimeStamp, overElement) { - // Expands a folder in tree view when pointer hovers on it longer than - // EXPAND_FOLDER_DELAY. - var hoverOnFolderTimeStamp = lastHoverOnFolderTimeStamp; - lastHoverOnFolderTimeStamp = 0; - if (hoverOnFolderTimeStamp) { - if (eventTimeStamp - hoverOnFolderTimeStamp >= EXPAND_FOLDER_DELAY) - overElement.expanded = true; - else - lastHoverOnFolderTimeStamp = hoverOnFolderTimeStamp; - } else if (overElement instanceof TreeItem && - bmm.isFolder(overElement.bookmarkNode) && - overElement.hasChildren && - !overElement.expanded) { - lastHoverOnFolderTimeStamp = eventTimeStamp; - } - } - - /** - * Stores the information abou the bookmark and folders being dragged. - * @type {Object} - */ - var dragData = null; - var dragInfo = { - handleChromeDragEnter: function(newDragData) { - dragData = newDragData; - }, - clearDragData: function() { - dragData = null; - }, - isDragValid: function() { - return !!dragData; - }, - isSameProfile: function() { - return dragData && dragData.sameProfile; - }, - isDraggingFolders: function() { - return dragData && dragData.elements.some(function(node) { - return !node.url; - }); - }, - isDraggingBookmark: function(bookmarkId) { - return dragData && dragData.elements.some(function(node) { - return node.id == bookmarkId; - }); - }, - isDraggingChildBookmark: function(folderId) { - return dragData && dragData.elements.some(function(node) { - return node.parentId == folderId; - }); - }, - isDraggingFolderToDescendant: function(bookmarkNode) { - return dragData && dragData.elements.some(function(node) { - var dragFolder = bmm.treeLookup[node.id]; - var dragFolderNode = dragFolder && dragFolder.bookmarkNode; - return dragFolderNode && bmm.contains(dragFolderNode, bookmarkNode); - }); - } - }; - - /** - * External function to select folders or bookmarks after a drop action. - * @type {function} - */ - var selectItemsAfterUserAction = null; - - function getBookmarkElement(el) { - while (el && !el.bookmarkNode) { - el = el.parentNode; - } - return el; - } - - // If we are over the list and the list is showing search result, we cannot - // drop. - function isOverSearch(overElement) { - return list.isSearch() && list.contains(overElement); - } - - /** - * Determines the valid drop positions for the given target element. - * @param {!HTMLElement} overElement The element that we are currently - * dragging over. - * @return {DropPosition} An bit field enumeration of valid drop locations. - */ - function calculateValidDropTargets(overElement) { - if (!dragInfo.isDragValid() || isOverSearch(overElement)) - return DropPosition.NONE; - - if (dragInfo.isSameProfile() && - (dragInfo.isDraggingBookmark(overElement.bookmarkNode.id) || - dragInfo.isDraggingFolderToDescendant(overElement.bookmarkNode))) { - return DropPosition.NONE; - } - - var canDropInfo = calculateDropAboveBelow(overElement); - if (canDropOn(overElement)) - canDropInfo |= DropPosition.ON; - - return canDropInfo; - } - - function calculateDropAboveBelow(overElement) { - if (overElement instanceof BookmarkList) - return DropPosition.NONE; - - // We cannot drop between Bookmarks bar and Other bookmarks. - if (overElement.bookmarkNode.parentId == bmm.ROOT_ID) - return DropPosition.NONE; - - var isOverTreeItem = overElement instanceof TreeItem; - var isOverExpandedTree = isOverTreeItem && overElement.expanded; - var isDraggingFolders = dragInfo.isDraggingFolders(); - - // We can only drop between items in the tree if we have any folders. - if (isOverTreeItem && !isDraggingFolders) - return DropPosition.NONE; - - // When dragging from a different profile we do not need to consider - // conflicts between the dragged items and the drop target. - if (!dragInfo.isSameProfile()) { - // Don't allow dropping below an expanded tree item since it is confusing - // to the user anyway. - return isOverExpandedTree ? DropPosition.ABOVE : - (DropPosition.ABOVE | DropPosition.BELOW); - } - - var resultPositions = DropPosition.NONE; - - // Cannot drop above if the item above is already in the drag source. - var previousElem = overElement.previousElementSibling; - if (!previousElem || !dragInfo.isDraggingBookmark(previousElem.bookmarkId)) - resultPositions |= DropPosition.ABOVE; - - // Don't allow dropping below an expanded tree item since it is confusing - // to the user anyway. - if (isOverExpandedTree) - return resultPositions; - - // Cannot drop below if the item below is already in the drag source. - var nextElement = overElement.nextElementSibling; - if (!nextElement || !dragInfo.isDraggingBookmark(nextElement.bookmarkId)) - resultPositions |= DropPosition.BELOW; - - return resultPositions; - } - - /** - * Determine whether we can drop the dragged items on the drop target. - * @param {!HTMLElement} overElement The element that we are currently - * dragging over. - * @return {boolean} Whether we can drop the dragged items on the drop - * target. - */ - function canDropOn(overElement) { - // We can only drop on a folder. - if (!bmm.isFolder(overElement.bookmarkNode)) - return false; - - if (!dragInfo.isSameProfile()) - return true; - - if (overElement instanceof BookmarkList) { - // We are trying to drop an item past the last item. This is - // only allowed if dragged item is different from the last item - // in the list. - var listItems = list.items; - var len = listItems.length; - if (!len || !dragInfo.isDraggingBookmark(listItems[len - 1].bookmarkId)) - return true; - } - - return !dragInfo.isDraggingChildBookmark(overElement.bookmarkNode.id); - } - - /** - * Callback for the dragstart event. - * @param {Event} e The dragstart event. - */ - function handleDragStart(e) { - // Determine the selected bookmarks. - var target = e.target; - var draggedNodes = []; - if (target instanceof ListItem) { - // Use selected items. - draggedNodes = target.parentNode.selectedItems; - } else if (target instanceof TreeItem) { - draggedNodes.push(target.bookmarkNode); - } - - // We manage starting the drag by using the extension API. - e.preventDefault(); - - if (draggedNodes.length) { - // If we are dragging a single link, we can do the *Link* effect. - // Otherwise, we only allow copy and move. - e.dataTransfer.effectAllowed = draggedNodes.length == 1 && - !bmm.isFolder(draggedNodes[0]) ? 'copyMoveLink' : 'copyMove'; - - chrome.bookmarkManagerPrivate.startDrag(draggedNodes.map(function(node) { - return node.id; - })); - } - } - - function handleDragEnter(e) { - e.preventDefault(); - } - - /** - * Calback for the dragover event. - * @param {Event} e The dragover event. - */ - function handleDragOver(e) { - // Allow DND on text inputs. - if (e.target.tagName != 'INPUT') { - // The default operation is to allow dropping links etc to do navigation. - // We never want to do that for the bookmark manager. - e.preventDefault(); - - // Set to none. This will get set to something if we can do the drop. - e.dataTransfer.dropEffect = 'none'; - } - - if (!dragInfo.isDragValid()) - return; - - var overElement = getBookmarkElement(e.target) || - (e.target == list ? list : null); - if (!overElement) - return; - - updateAutoExpander(e.timeStamp, overElement); - - var canDropInfo = calculateValidDropTargets(overElement); - if (canDropInfo == DropPosition.NONE) - return; - - // Now we know that we can drop. Determine if we will drop above, on or - // below based on mouse position etc. - - dropDestination = calcDropPosition(e.clientY, overElement, canDropInfo); - if (!dropDestination) { - e.dataTransfer.dropEffect = 'none'; - return; - } - - e.dataTransfer.dropEffect = dragInfo.isSameProfile() ? 'move' : 'copy'; - dropIndicator.update(dropDestination); - } - - /** - * This function determines where the drop will occur relative to the element. - * @return {?Object} If no valid drop position is found, null, otherwise - * an object containing the following parameters: - * element - The target element that will receive the drop. - * position - A |DropPosition| relative to the |element|. - */ - function calcDropPosition(elementClientY, overElement, canDropInfo) { - if (overElement instanceof BookmarkList) { - // Dropping on the BookmarkList either means dropping below the last - // bookmark element or on the list itself if it is empty. - var length = overElement.items.length; - if (length) - return { - element: overElement.getListItemByIndex(length - 1), - position: DropPosition.BELOW - }; - return {element: overElement, position: DropPosition.ON}; - } - - var above = canDropInfo & DropPosition.ABOVE; - var below = canDropInfo & DropPosition.BELOW; - var on = canDropInfo & DropPosition.ON; - var rect = overElement.getBoundingClientRect(); - var yRatio = (elementClientY - rect.top) / rect.height; - - if (above && (yRatio <= .25 || yRatio <= .5 && (!below || !on))) - return {element: overElement, position: DropPosition.ABOVE}; - if (below && (yRatio > .75 || yRatio > .5 && (!above || !on))) - return {element: overElement, position: DropPosition.BELOW}; - if (on) - return {element: overElement, position: DropPosition.ON}; - return null; - } - - function calculateDropInfo(eventTarget, dropDestination) { - if (!dropDestination || !dragInfo.isDragValid()) - return null; - - var dropPos = dropDestination.position; - var relatedNode = dropDestination.element.bookmarkNode; - var dropInfoResult = { - selectTarget: null, - selectedTreeId: -1, - parentId: dropPos == DropPosition.ON ? relatedNode.id : - relatedNode.parentId, - index: -1, - relatedIndex: -1 - }; - - // Try to find the index in the dataModel so we don't have to always keep - // the index for the list items up to date. - var overElement = getBookmarkElement(eventTarget); - if (overElement instanceof ListItem) { - dropInfoResult.relatedIndex = - overElement.parentNode.dataModel.indexOf(relatedNode); - dropInfoResult.selectTarget = list; - } else if (overElement instanceof BookmarkList) { - dropInfoResult.relatedIndex = overElement.dataModel.length - 1; - dropInfoResult.selectTarget = list; - } else { - // Tree - dropInfoResult.relatedIndex = relatedNode.index; - dropInfoResult.selectTarget = tree; - dropInfoResult.selectedTreeId = - tree.selectedItem ? tree.selectedItem.bookmarkId : null; - } - - if (dropPos == DropPosition.ABOVE) - dropInfoResult.index = dropInfoResult.relatedIndex; - else if (dropPos == DropPosition.BELOW) - dropInfoResult.index = dropInfoResult.relatedIndex + 1; - - return dropInfoResult; - } - - function handleDragLeave(e) { - dropIndicator.finish(); - } - - function handleDrop(e) { - var dropInfo = calculateDropInfo(e.target, dropDestination); - if (dropInfo) { - selectItemsAfterUserAction(dropInfo.selectTarget, - dropInfo.selectedTreeId); - if (dropInfo.index != -1) - chrome.bookmarkManagerPrivate.drop(dropInfo.parentId, dropInfo.index); - else - chrome.bookmarkManagerPrivate.drop(dropInfo.parentId); - - e.preventDefault(); - } - dropDestination = null; - dropIndicator.finish(); - } - - function clearDragData() { - dragInfo.clearDragData(); - dropDestination = null; - } - - function init(selectItemsAfterUserActionFunction) { - function deferredClearData() { - setTimeout(clearDragData); - } - - selectItemsAfterUserAction = selectItemsAfterUserActionFunction; - - document.addEventListener('dragstart', handleDragStart); - document.addEventListener('dragenter', handleDragEnter); - document.addEventListener('dragover', handleDragOver); - document.addEventListener('dragleave', handleDragLeave); - document.addEventListener('drop', handleDrop); - document.addEventListener('dragend', deferredClearData); - document.addEventListener('mouseup', deferredClearData); - - chrome.bookmarkManagerPrivate.onDragEnter.addListener( - dragInfo.handleChromeDragEnter); - chrome.bookmarkManagerPrivate.onDragLeave.addListener(deferredClearData); - chrome.bookmarkManagerPrivate.onDrop.addListener(deferredClearData); - } - return {init: init}; -}); 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(); -})(); |