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

Side by Side Diff: chrome/browser/resources/bookmark_manager/js/main.js

Issue 2617663002: WIP: run clang-format-js on lots of things (Closed)
Patch Set: merge Created 3 years, 11 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch
OLDNEW
1 // Copyright (c) 2012 The Chromium Authors. All rights reserved. 1 // Copyright (c) 2012 The Chromium Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be 2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file. 3 // found in the LICENSE file.
4 4
5 (function() { 5 (function() {
6 'use strict'; 6 'use strict';
7 7
8 /** @const */ var BookmarkList = bmm.BookmarkList; 8 /** @const */ var BookmarkList = bmm.BookmarkList;
9 /** @const */ var BookmarkTree = bmm.BookmarkTree; 9 /** @const */ var BookmarkTree = bmm.BookmarkTree;
10 /** @const */ var Command = cr.ui.Command; 10 /** @const */ var Command = cr.ui.Command;
11 /** @const */ var LinkKind = cr.LinkKind; 11 /** @const */ var LinkKind = cr.LinkKind;
12 /** @const */ var ListItem = cr.ui.ListItem; 12 /** @const */ var ListItem = cr.ui.ListItem;
13 /** @const */ var Menu = cr.ui.Menu; 13 /** @const */ var Menu = cr.ui.Menu;
14 /** @const */ var MenuButton = cr.ui.MenuButton; 14 /** @const */ var MenuButton = cr.ui.MenuButton;
15 /** @const */ var Splitter = cr.ui.Splitter; 15 /** @const */ var Splitter = cr.ui.Splitter;
16 /** @const */ var TreeItem = cr.ui.TreeItem; 16 /** @const */ var TreeItem = cr.ui.TreeItem;
17 17
18 /** 18 /**
19 * An array containing the BookmarkTreeNodes that were deleted in the last 19 * An array containing the BookmarkTreeNodes that were deleted in the last
20 * deletion action. This is used for implementing undo. 20 * deletion action. This is used for implementing undo.
21 * @type {?{nodes: Array<Array<BookmarkTreeNode>>, target: EventTarget}} 21 * @type {?{nodes: Array<Array<BookmarkTreeNode>>, target: EventTarget}}
22 */ 22 */
23 var lastDeleted; 23 var lastDeleted;
24 24
25 /** 25 /**
26 * 26 *
27 * Holds the last DOMTimeStamp when mouse pointer hovers on folder in tree 27 * Holds the last DOMTimeStamp when mouse pointer hovers on folder in tree
28 * view. Zero means pointer doesn't hover on folder. 28 * view. Zero means pointer doesn't hover on folder.
29 * @type {number} 29 * @type {number}
30 */ 30 */
31 var lastHoverOnFolderTimeStamp = 0; 31 var lastHoverOnFolderTimeStamp = 0;
32 32
33 /** 33 /**
34 * Holds a function that will undo that last action, if global undo is enabled. 34 * Holds a function that will undo that last action, if global undo is
35 * @type {Function} 35 * enabled.
36 */ 36 * @type {Function}
37 var performGlobalUndo; 37 */
38 38 var performGlobalUndo;
39 /** 39
40 * Holds a link controller singleton. Use getLinkController() rarther than 40 /**
41 * accessing this variabie. 41 * Holds a link controller singleton. Use getLinkController() rarther than
42 * @type {cr.LinkController} 42 * accessing this variabie.
43 */ 43 * @type {cr.LinkController}
44 var linkController; 44 */
45 45 var linkController;
46 /** 46
47 * New Windows are not allowed in Windows 8 metro mode. 47 /**
48 */ 48 * New Windows are not allowed in Windows 8 metro mode.
49 var canOpenNewWindows = true; 49 */
50 50 var canOpenNewWindows = true;
51 /** 51
52 * Incognito mode availability can take the following values: , 52 /**
53 * - 'enabled' for when both normal and incognito modes are available; 53 * Incognito mode availability can take the following values: ,
54 * - 'disabled' for when incognito mode is disabled; 54 * - 'enabled' for when both normal and incognito modes are available;
55 * - 'forced' for when incognito mode is forced (normal mode is unavailable). 55 * - 'disabled' for when incognito mode is disabled;
56 */ 56 * - 'forced' for when incognito mode is forced (normal mode is
57 var incognitoModeAvailability = 'enabled'; 57 * unavailable).
58 58 */
59 /** 59 var incognitoModeAvailability = 'enabled';
60 * Whether bookmarks can be modified. 60
61 * @type {boolean} 61 /**
62 */ 62 * Whether bookmarks can be modified.
63 var canEdit = true; 63 * @type {boolean}
64 64 */
65 /** 65 var canEdit = true;
66 * @type {TreeItem} 66
67 * @const 67 /**
68 */ 68 * @type {TreeItem}
69 var searchTreeItem = new TreeItem({ 69 * @const
70 bookmarkId: 'q=' 70 */
71 }); 71 var searchTreeItem = new TreeItem({bookmarkId: 'q='});
72 72
73 /** 73 /**
74 * Command shortcut mapping. 74 * Command shortcut mapping.
75 * @const 75 * @const
76 */ 76 */
77 var commandShortcutMap = cr.isMac ? { 77 var commandShortcutMap = cr.isMac ?
78 'edit': 'Enter', 78 {
79 // On Mac we also allow Meta+Backspace. 79 'edit': 'Enter',
80 'delete': 'Delete Backspace Meta|Backspace', 80 // On Mac we also allow Meta+Backspace.
81 'open-in-background-tab': 'Meta|Enter', 81 'delete': 'Delete Backspace Meta|Backspace',
82 'open-in-new-tab': 'Shift|Meta|Enter', 82 'open-in-background-tab': 'Meta|Enter',
83 'open-in-same-window': 'Meta|Down', 83 'open-in-new-tab': 'Shift|Meta|Enter',
84 'open-in-new-window': 'Shift|Enter', 84 'open-in-same-window': 'Meta|Down',
85 'rename-folder': 'Enter', 85 'open-in-new-window': 'Shift|Enter',
86 // Global undo is Command-Z. It is not in any menu. 86 'rename-folder': 'Enter',
87 'undo': 'Meta|z', 87 // Global undo is Command-Z. It is not in any menu.
88 } : { 88 'undo': 'Meta|z',
89 'edit': 'F2', 89 } :
90 'delete': 'Delete', 90 {
91 'open-in-background-tab': 'Ctrl|Enter', 91 'edit': 'F2',
92 'open-in-new-tab': 'Shift|Ctrl|Enter', 92 'delete': 'Delete',
93 'open-in-same-window': 'Enter', 93 'open-in-background-tab': 'Ctrl|Enter',
94 'open-in-new-window': 'Shift|Enter', 94 'open-in-new-tab': 'Shift|Ctrl|Enter',
95 'rename-folder': 'F2', 95 'open-in-same-window': 'Enter',
96 // Global undo is Ctrl-Z. It is not in any menu. 96 'open-in-new-window': 'Shift|Enter',
97 'undo': 'Ctrl|z', 97 'rename-folder': 'F2',
98 }; 98 // Global undo is Ctrl-Z. It is not in any menu.
99 99 'undo': 'Ctrl|z',
100 /** 100 };
101 * Mapping for folder id to suffix of UMA. These names will be appeared 101
102 * after "BookmarkManager_NavigateTo_" in UMA dashboard. 102 /**
103 * @const 103 * Mapping for folder id to suffix of UMA. These names will be appeared
104 */ 104 * after "BookmarkManager_NavigateTo_" in UMA dashboard.
105 var folderMetricsNameMap = { 105 * @const
106 '1': 'BookmarkBar', 106 */
107 '2': 'Other', 107 var folderMetricsNameMap = {
108 '3': 'Mobile', 108 '1': 'BookmarkBar',
109 'q=': 'Search', 109 '2': 'Other',
110 'subfolder': 'SubFolder', 110 '3': 'Mobile',
111 }; 111 'q=': 'Search',
112 112 'subfolder': 'SubFolder',
113 /**
114 * Adds an event listener to a node that will remove itself after firing once.
115 * @param {!Element} node The DOM node to add the listener to.
116 * @param {string} name The name of the event listener to add to.
117 * @param {function(Event)} handler Function called when the event fires.
118 */
119 function addOneShotEventListener(node, name, handler) {
120 var f = function(e) {
121 handler(e);
122 node.removeEventListener(name, f);
123 }; 113 };
124 node.addEventListener(name, f); 114
125 } 115 /**
126 116 * Adds an event listener to a node that will remove itself after firing once.
127 // Get the localized strings from the backend via bookmakrManagerPrivate API. 117 * @param {!Element} node The DOM node to add the listener to.
128 function loadLocalizedStrings(data) { 118 * @param {string} name The name of the event listener to add to.
129 // The strings may contain & which we need to strip. 119 * @param {function(Event)} handler Function called when the event fires.
130 for (var key in data) { 120 */
131 data[key] = data[key].replace(/&/, ''); 121 function addOneShotEventListener(node, name, handler) {
132 } 122 var f = function(e) {
133 123 handler(e);
134 loadTimeData.data = data; 124 node.removeEventListener(name, f);
135 i18nTemplate.process(document, loadTimeData); 125 };
136 126 node.addEventListener(name, f);
137 searchTreeItem.label = loadTimeData.getString('search'); 127 }
138 searchTreeItem.icon = isRTL() ? 'images/bookmark_manager_search_rtl.png' : 128
139 'images/bookmark_manager_search.png'; 129 // Get the localized strings from the backend via bookmakrManagerPrivate API.
140 } 130 function loadLocalizedStrings(data) {
141 131 // The strings may contain & which we need to strip.
142 /** 132 for (var key in data) {
143 * Updates the location hash to reflect the current state of the application. 133 data[key] = data[key].replace(/&/, '');
144 */ 134 }
145 function updateHash() { 135
146 window.location.hash = bmm.tree.selectedItem.bookmarkId; 136 loadTimeData.data = data;
147 updateAllCommands(); 137 i18nTemplate.process(document, loadTimeData);
148 } 138
149 139 searchTreeItem.label = loadTimeData.getString('search');
150 /** 140 searchTreeItem.icon = isRTL() ? 'images/bookmark_manager_search_rtl.png' :
151 * Navigates to a bookmark ID. 141 'images/bookmark_manager_search.png';
152 * @param {string} id The ID to navigate to. 142 }
153 * @param {function()=} opt_callback Function called when list view loaded or 143
154 * displayed specified folder. 144 /**
155 */ 145 * Updates the location hash to reflect the current state of the application.
156 function navigateTo(id, opt_callback) { 146 */
157 window.location.hash = id; 147 function updateHash() {
158 148 window.location.hash = bmm.tree.selectedItem.bookmarkId;
159 var sameParent = bmm.list.parentId == id; 149 updateAllCommands();
160 if (!sameParent) 150 }
161 updateParentId(id); 151
162 152 /**
163 updateAllCommands(); 153 * Navigates to a bookmark ID.
164 154 * @param {string} id The ID to navigate to.
165 var metricsId = folderMetricsNameMap[id.replace(/^q=.*/, 'q=')] || 155 * @param {function()=} opt_callback Function called when list view loaded or
166 folderMetricsNameMap['subfolder']; 156 * displayed specified folder.
167 chrome.metricsPrivate.recordUserAction( 157 */
168 'BookmarkManager_NavigateTo_' + metricsId); 158 function navigateTo(id, opt_callback) {
169 159 window.location.hash = id;
170 if (opt_callback) { 160
171 if (sameParent) 161 var sameParent = bmm.list.parentId == id;
172 opt_callback(); 162 if (!sameParent)
173 else 163 updateParentId(id);
174 addOneShotEventListener(bmm.list, 'load', opt_callback); 164
175 } 165 updateAllCommands();
176 } 166
177 167 var metricsId = folderMetricsNameMap[id.replace(/^q=.*/, 'q=')] ||
178 /** 168 folderMetricsNameMap['subfolder'];
179 * Updates the parent ID of the bookmark list and selects the correct tree item. 169 chrome.metricsPrivate.recordUserAction(
180 * @param {string} id The id. 170 'BookmarkManager_NavigateTo_' + metricsId);
181 */ 171
182 function updateParentId(id) { 172 if (opt_callback) {
183 // Setting list.parentId fires 'load' event. 173 if (sameParent)
184 bmm.list.parentId = id; 174 opt_callback();
185 175 else
186 // When tree.selectedItem changed, tree view calls navigatTo() then it 176 addOneShotEventListener(bmm.list, 'load', opt_callback);
187 // calls updateHash() when list view displayed specified folder. 177 }
188 bmm.tree.selectedItem = bmm.treeLookup[id] || bmm.tree.selectedItem; 178 }
189 } 179
190 180 /**
191 // Process the location hash. This is called by onhashchange and when the page 181 * Updates the parent ID of the bookmark list and selects the correct tree
192 // is first loaded. 182 * item.
193 function processHash() { 183 * @param {string} id The id.
194 var id = window.location.hash.slice(1); 184 */
195 if (!id) { 185 function updateParentId(id) {
196 // If we do not have a hash, select first item in the tree. 186 // Setting list.parentId fires 'load' event.
197 id = bmm.tree.items[0].bookmarkId; 187 bmm.list.parentId = id;
198 } 188
199 189 // When tree.selectedItem changed, tree view calls navigatTo() then it
200 var valid = false; 190 // calls updateHash() when list view displayed specified folder.
201 if (/^e=/.test(id)) { 191 bmm.tree.selectedItem = bmm.treeLookup[id] || bmm.tree.selectedItem;
202 id = id.slice(2); 192 }
203 193
204 // If hash contains e=, edit the item specified. 194 // Process the location hash. This is called by onhashchange and when the page
205 chrome.bookmarks.get(id, function(bookmarkNodes) { 195 // is first loaded.
206 // Verify the node to edit is a valid node. 196 function processHash() {
207 if (!bookmarkNodes || bookmarkNodes.length != 1) 197 var id = window.location.hash.slice(1);
208 return; 198 if (!id) {
209 var bookmarkNode = bookmarkNodes[0]; 199 // If we do not have a hash, select first item in the tree.
210 200 id = bmm.tree.items[0].bookmarkId;
211 // After the list reloads, edit the desired bookmark. 201 }
212 var editBookmark = function() { 202
213 var index = bmm.list.dataModel.findIndexById(bookmarkNode.id); 203 var valid = false;
214 if (index != -1) { 204 if (/^e=/.test(id)) {
215 var sm = bmm.list.selectionModel; 205 id = id.slice(2);
216 sm.anchorIndex = sm.leadIndex = sm.selectedIndex = index; 206
217 scrollIntoViewAndMakeEditable(index); 207 // If hash contains e=, edit the item specified.
218 } 208 chrome.bookmarks.get(id, function(bookmarkNodes) {
219 }; 209 // Verify the node to edit is a valid node.
220 210 if (!bookmarkNodes || bookmarkNodes.length != 1)
221 var parentId = assert(bookmarkNode.parentId); 211 return;
222 navigateTo(parentId, editBookmark); 212 var bookmarkNode = bookmarkNodes[0];
223 }); 213
224 214 // After the list reloads, edit the desired bookmark.
225 // We handle the two cases of navigating to the bookmark to be edited 215 var editBookmark = function() {
226 // above. Don't run the standard navigation code below. 216 var index = bmm.list.dataModel.findIndexById(bookmarkNode.id);
227 return; 217 if (index != -1) {
228 } else if (/^q=/.test(id)) { 218 var sm = bmm.list.selectionModel;
229 // In case we got a search hash, update the text input and the 219 sm.anchorIndex = sm.leadIndex = sm.selectedIndex = index;
230 // bmm.treeLookup to use the new id. 220 scrollIntoViewAndMakeEditable(index);
231 setSearch(id.slice(2)); 221 }
232 valid = true; 222 };
233 } 223
234 224 var parentId = assert(bookmarkNode.parentId);
235 // Navigate to bookmark 'id' (which may be a query of the form q=query). 225 navigateTo(parentId, editBookmark);
236 if (valid) { 226 });
237 updateParentId(id); 227
238 } else { 228 // We handle the two cases of navigating to the bookmark to be edited
239 // We need to verify that this is a correct ID. 229 // above. Don't run the standard navigation code below.
240 chrome.bookmarks.get(id, function(items) { 230 return;
241 if (items && items.length == 1) 231 } else if (/^q=/.test(id)) {
242 updateParentId(id); 232 // In case we got a search hash, update the text input and the
243 }); 233 // bmm.treeLookup to use the new id.
244 } 234 setSearch(id.slice(2));
245 } 235 valid = true;
246 236 }
247 // Activate is handled by the open-in-same-window-command. 237
248 function handleDoubleClickForList(e) { 238 // Navigate to bookmark 'id' (which may be a query of the form q=query).
249 if (e.button == 0) 239 if (valid) {
250 $('open-in-same-window-command').execute(); 240 updateParentId(id);
251 } 241 } else {
252 242 // We need to verify that this is a correct ID.
253 // The list dispatches an event when the user clicks on the URL or the Show in 243 chrome.bookmarks.get(id, function(items) {
254 // folder part. 244 if (items && items.length == 1)
255 function handleUrlClickedForList(e) { 245 updateParentId(id);
256 getLinkController().openUrlFromEvent(e.url, e.originalEvent); 246 });
257 chrome.bookmarkManagerPrivate.recordLaunch(); 247 }
258 } 248 }
259 249
260 function handleSearch(e) { 250 // Activate is handled by the open-in-same-window-command.
261 setSearch(this.value); 251 function handleDoubleClickForList(e) {
262 } 252 if (e.button == 0)
263 253 $('open-in-same-window-command').execute();
264 /** 254 }
265 * Navigates to the search results for the search text. 255
266 * @param {string} searchText The text to search for. 256 // The list dispatches an event when the user clicks on the URL or the Show in
267 */ 257 // folder part.
268 function setSearch(searchText) { 258 function handleUrlClickedForList(e) {
269 if (searchText) { 259 getLinkController().openUrlFromEvent(e.url, e.originalEvent);
270 // Only update search item if we have a search term. We never want the 260 chrome.bookmarkManagerPrivate.recordLaunch();
271 // search item to be for an empty search. 261 }
272 delete bmm.treeLookup[searchTreeItem.bookmarkId]; 262
273 var id = searchTreeItem.bookmarkId = 'q=' + searchText; 263 function handleSearch(e) {
274 bmm.treeLookup[searchTreeItem.bookmarkId] = searchTreeItem; 264 setSearch(this.value);
275 } 265 }
276 266
277 var input = $('term'); 267 /**
278 // Do not update the input if the user is actively using the text input. 268 * Navigates to the search results for the search text.
279 if (document.activeElement != input) 269 * @param {string} searchText The text to search for.
280 input.value = searchText; 270 */
281 271 function setSearch(searchText) {
282 if (searchText) { 272 if (searchText) {
283 bmm.tree.add(searchTreeItem); 273 // Only update search item if we have a search term. We never want the
284 bmm.tree.selectedItem = searchTreeItem; 274 // search item to be for an empty search.
285 } else { 275 delete bmm.treeLookup[searchTreeItem.bookmarkId];
286 // Go "home". 276 var id = searchTreeItem.bookmarkId = 'q=' + searchText;
287 bmm.tree.selectedItem = bmm.tree.items[0]; 277 bmm.treeLookup[searchTreeItem.bookmarkId] = searchTreeItem;
288 id = bmm.tree.selectedItem.bookmarkId; 278 }
289 } 279
290 280 var input = $('term');
291 navigateTo(id); 281 // Do not update the input if the user is actively using the text input.
292 } 282 if (document.activeElement != input)
293 283 input.value = searchText;
294 /** 284
285 if (searchText) {
286 bmm.tree.add(searchTreeItem);
287 bmm.tree.selectedItem = searchTreeItem;
288 } else {
289 // Go "home".
290 bmm.tree.selectedItem = bmm.tree.items[0];
291 id = bmm.tree.selectedItem.bookmarkId;
292 }
293
294 navigateTo(id);
295 }
296
297 /**
295 * This returns the user visible path to the folder where the bookmark is 298 * This returns the user visible path to the folder where the bookmark is
296 * located. 299 * located.
297 * @param {number} parentId The ID of the parent folder. 300 * @param {number} parentId The ID of the parent folder.
298 * @return {string|undefined} The path to the the bookmark, 301 * @return {string|undefined} The path to the the bookmark,
299 */ 302 */
300 function getFolder(parentId) { 303 function getFolder(parentId) {
301 var parentNode = bmm.tree.getBookmarkNodeById(parentId); 304 var parentNode = bmm.tree.getBookmarkNodeById(parentId);
302 if (parentNode) { 305 if (parentNode) {
303 var s = parentNode.title; 306 var s = parentNode.title;
304 if (parentNode.parentId != bmm.ROOT_ID) { 307 if (parentNode.parentId != bmm.ROOT_ID) {
305 return getFolder(parentNode.parentId) + '/' + s; 308 return getFolder(parentNode.parentId) + '/' + s;
306 } 309 }
307 return s; 310 return s;
308 } 311 }
309 } 312 }
310 313
311 function handleLoadForTree(e) { 314 function handleLoadForTree(e) {
312 processHash(); 315 processHash();
313 } 316 }
314 317
315 /** 318 /**
316 * Returns a promise for all the URLs in the {@code nodes} and the direct 319 * Returns a promise for all the URLs in the {@code nodes} and the direct
317 * children of {@code nodes}. 320 * children of {@code nodes}.
318 * @param {!Array<BookmarkTreeNode>} nodes . 321 * @param {!Array<BookmarkTreeNode>} nodes .
319 * @return {!Promise<Array<string>>} . 322 * @return {!Promise<Array<string>>} .
320 */ 323 */
321 function getAllUrls(nodes) { 324 function getAllUrls(nodes) {
322 var urls = []; 325 var urls = [];
323 326
324 // Adds the node and all its direct children. 327 // Adds the node and all its direct children.
325 // TODO(deepak.m1): Here node should exist. When we delete the nodes then 328 // TODO(deepak.m1): Here node should exist. When we delete the nodes then
326 // datamodel gets updated but still it shows deleted items as selected items 329 // datamodel gets updated but still it shows deleted items as selected items
327 // and accessing those nodes throws chrome.runtime.lastError. This cause 330 // and accessing those nodes throws chrome.runtime.lastError. This cause
328 // undefined value for node. Please refer https://crbug.com/480935. 331 // undefined value for node. Please refer https://crbug.com/480935.
329 function addNodes(node) { 332 function addNodes(node) {
330 if (!node || node.id == 'new') 333 if (!node || node.id == 'new')
331 return; 334 return;
332 335
333 if (node.children) { 336 if (node.children) {
334 node.children.forEach(function(child) { 337 node.children.forEach(function(child) {
335 if (!bmm.isFolder(child)) 338 if (!bmm.isFolder(child))
336 urls.push(child.url); 339 urls.push(child.url);
337 }); 340 });
338 } else { 341 } else {
339 urls.push(node.url); 342 urls.push(node.url);
340 } 343 }
341 } 344 }
342 345
343 // Get a future promise for the nodes. 346 // Get a future promise for the nodes.
344 var promises = nodes.map(function(node) { 347 var promises = nodes.map(function(node) {
345 if (bmm.isFolder(assert(node))) 348 if (bmm.isFolder(assert(node)))
346 return bmm.loadSubtree(node.id); 349 return bmm.loadSubtree(node.id);
347 // Not a folder so we already have all the data we need. 350 // Not a folder so we already have all the data we need.
348 return Promise.resolve(node); 351 return Promise.resolve(node);
349 }); 352 });
350 353
351 return Promise.all(promises).then(function(nodes) { 354 return Promise.all(promises).then(function(nodes) {
352 nodes.forEach(addNodes); 355 nodes.forEach(addNodes);
353 return urls; 356 return urls;
354 }); 357 });
355 } 358 }
356 359
357 /** 360 /**
358 * Returns the nodes (non recursive) to use for the open commands. 361 * Returns the nodes (non recursive) to use for the open commands.
359 * @param {HTMLElement} target 362 * @param {HTMLElement} target
360 * @return {!Array<BookmarkTreeNode>} 363 * @return {!Array<BookmarkTreeNode>}
361 */ 364 */
362 function getNodesForOpen(target) { 365 function getNodesForOpen(target) {
363 if (target == bmm.tree) { 366 if (target == bmm.tree) {
364 if (bmm.tree.selectedItem != searchTreeItem) 367 if (bmm.tree.selectedItem != searchTreeItem)
365 return bmm.tree.selectedFolders; 368 return bmm.tree.selectedFolders;
366 // Fall through to use all nodes in the list. 369 // Fall through to use all nodes in the list.
367 } else { 370 } else {
368 var items = bmm.list.selectedItems; 371 var items = bmm.list.selectedItems;
369 if (items.length) 372 if (items.length)
370 return items; 373 return items;
371 } 374 }
372 375
373 // The list starts off with a null dataModel. We can get here during startup. 376 // The list starts off with a null dataModel. We can get here during
374 if (!bmm.list.dataModel) 377 // startup.
375 return []; 378 if (!bmm.list.dataModel)
376 379 return [];
377 // Return an array based on the dataModel. 380
378 return bmm.list.dataModel.slice(); 381 // Return an array based on the dataModel.
379 } 382 return bmm.list.dataModel.slice();
380 383 }
381 /** 384
385 /**
382 * Returns a promise that will contain all URLs of all the selected bookmarks 386 * Returns a promise that will contain all URLs of all the selected bookmarks
383 * and the nested bookmarks for use with the open commands. 387 * and the nested bookmarks for use with the open commands.
384 * @param {HTMLElement} target The target list or tree. 388 * @param {HTMLElement} target The target list or tree.
385 * @return {Promise<Array<string>>} . 389 * @return {Promise<Array<string>>} .
386 */ 390 */
387 function getUrlsForOpenCommands(target) { 391 function getUrlsForOpenCommands(target) {
388 return getAllUrls(getNodesForOpen(target)); 392 return getAllUrls(getNodesForOpen(target));
389 } 393 }
390 394
391 function notNewNode(node) { 395 function notNewNode(node) {
392 return node.id != 'new'; 396 return node.id != 'new';
393 } 397 }
394 398
395 /** 399 /**
396 * Helper function that updates the canExecute and labels for the open-like 400 * Helper function that updates the canExecute and labels for the open-like
397 * commands. 401 * commands.
398 * @param {!cr.ui.CanExecuteEvent} e The event fired by the command system. 402 * @param {!cr.ui.CanExecuteEvent} e The event fired by the command system.
399 * @param {!cr.ui.Command} command The command we are currently processing. 403 * @param {!cr.ui.Command} command The command we are currently processing.
400 * @param {string} singularId The string id of singular form of the menu label. 404 * @param {string} singularId The string id of singular form of the menu
401 * @param {string} pluralId The string id of menu label if the singular form is 405 label.
402 not used. 406 * @param {string} pluralId The string id of menu label if the singular form
403 * @param {boolean} commandDisabled Whether the menu item should be disabled 407 is
404 no matter what bookmarks are selected. 408 not used.
405 */ 409 * @param {boolean} commandDisabled Whether the menu item should be disabled
406 function updateOpenCommand(e, command, singularId, pluralId, commandDisabled) { 410 no matter what bookmarks are selected.
407 if (singularId) { 411 */
408 // The command label reflects the selection which might not reflect 412 function updateOpenCommand(
409 // how many bookmarks will be opened. For example if you right click an 413 e, command, singularId, pluralId, commandDisabled) {
410 // empty area in a folder with 1 bookmark the text should still say "all". 414 if (singularId) {
411 var selectedNodes = getSelectedBookmarkNodes(e.target).filter(notNewNode); 415 // The command label reflects the selection which might not reflect
412 var singular = selectedNodes.length == 1 && !bmm.isFolder(selectedNodes[0]); 416 // how many bookmarks will be opened. For example if you right click an
413 command.label = loadTimeData.getString(singular ? singularId : pluralId); 417 // empty area in a folder with 1 bookmark the text should still say "all".
414 } 418 var selectedNodes = getSelectedBookmarkNodes(e.target).filter(notNewNode);
415 419 var singular =
416 if (commandDisabled) { 420 selectedNodes.length == 1 && !bmm.isFolder(selectedNodes[0]);
417 command.disabled = true; 421 command.label = loadTimeData.getString(singular ? singularId : pluralId);
418 e.canExecute = false; 422 }
419 return; 423
420 } 424 if (commandDisabled) {
421 425 command.disabled = true;
422 getUrlsForOpenCommands(assertInstanceof(e.target, HTMLElement)).then(
423 function(urls) {
424 var disabled = !urls.length;
425 command.disabled = disabled;
426 e.canExecute = !disabled;
427 });
428 }
429
430 /**
431 * Calls the backend to figure out if we can paste the clipboard into the active
432 * folder.
433 * @param {Function=} opt_f Function to call after the state has been updated.
434 */
435 function updatePasteCommand(opt_f) {
436 function update(commandId, canPaste) {
437 $(commandId).disabled = !canPaste;
438 }
439
440 var promises = [];
441
442 // The folders menu.
443 // We can not paste into search item in tree.
444 if (bmm.tree.selectedItem && bmm.tree.selectedItem != searchTreeItem) {
445 promises.push(new Promise(function(resolve) {
446 var id = bmm.tree.selectedItem.bookmarkId;
447 chrome.bookmarkManagerPrivate.canPaste(id, function(canPaste) {
448 update('paste-from-folders-menu-command', canPaste);
449 resolve(canPaste);
450 });
451 }));
452 } else {
453 // Tree's not loaded yet.
454 update('paste-from-folders-menu-command', false);
455 }
456
457 // The organize menu.
458 var listId = bmm.list.parentId;
459 if (bmm.list.isSearch() || !listId) {
460 // We cannot paste into search view or the list isn't ready.
461 update('paste-from-organize-menu-command', false);
462 } else {
463 promises.push(new Promise(function(resolve) {
464 chrome.bookmarkManagerPrivate.canPaste(listId, function(canPaste) {
465 update('paste-from-organize-menu-command', canPaste);
466 resolve(canPaste);
467 });
468 }));
469 }
470
471 Promise.all(promises).then(function() {
472 var cmd;
473 if (document.activeElement == bmm.list)
474 cmd = 'paste-from-organize-menu-command';
475 else if (document.activeElement == bmm.tree)
476 cmd = 'paste-from-folders-menu-command';
477
478 if (cmd)
479 update('paste-from-context-menu-command', !$(cmd).disabled);
480
481 if (opt_f) opt_f();
482 });
483 }
484
485 function handleCanExecuteForSearchBox(e) {
486 var command = e.command;
487 switch (command.id) {
488 case 'delete-command':
489 case 'undo-command':
490 // Pass the delete and undo commands through
491 // (fixes http://crbug.com/278112).
492 e.canExecute = false; 426 e.canExecute = false;
493 break; 427 return;
494 } 428 }
495 } 429
496 430 getUrlsForOpenCommands(assertInstanceof(e.target, HTMLElement))
497 function handleCanExecuteForDocument(e) { 431 .then(function(urls) {
498 var command = e.command; 432 var disabled = !urls.length;
499 switch (command.id) { 433 command.disabled = disabled;
500 case 'import-menu-command': 434 e.canExecute = !disabled;
501 e.canExecute = canEdit; 435 });
502 break; 436 }
503 437
504 case 'export-menu-command': 438 /**
505 // We can always execute the export-menu command. 439 * Calls the backend to figure out if we can paste the clipboard into the
506 e.canExecute = true; 440 * active
507 break; 441 * folder.
508 442 * @param {Function=} opt_f Function to call after the state has been updated.
509 case 'sort-command': 443 */
510 e.canExecute = !bmm.list.isSearch() && 444 function updatePasteCommand(opt_f) {
511 bmm.list.dataModel && bmm.list.dataModel.length > 1 && 445 function update(commandId, canPaste) {
512 !isUnmodifiable(bmm.tree.getBookmarkNodeById(bmm.list.parentId)); 446 $(commandId).disabled = !canPaste;
513 break; 447 }
514 448
515 case 'undo-command': 449 var promises = [];
516 // Because the global undo command has no visible UI, always enable it, 450
517 // and just make it a no-op if undo is not possible. 451 // The folders menu.
518 e.canExecute = true; 452 // We can not paste into search item in tree.
519 break; 453 if (bmm.tree.selectedItem && bmm.tree.selectedItem != searchTreeItem) {
520 454 promises.push(new Promise(function(resolve) {
521 default: 455 var id = bmm.tree.selectedItem.bookmarkId;
522 canExecuteForList(e); 456 chrome.bookmarkManagerPrivate.canPaste(id, function(canPaste) {
523 if (!e.defaultPrevented) 457 update('paste-from-folders-menu-command', canPaste);
524 canExecuteForTree(e); 458 resolve(canPaste);
525 break; 459 });
526 } 460 }));
527 } 461 } else {
528 462 // Tree's not loaded yet.
529 /** 463 update('paste-from-folders-menu-command', false);
530 * Helper function for handling canExecute for the list and the tree. 464 }
531 * @param {!cr.ui.CanExecuteEvent} e Can execute event object. 465
532 * @param {boolean} isSearch Whether the user is trying to do a command on 466 // The organize menu.
533 * search. 467 var listId = bmm.list.parentId;
534 */ 468 if (bmm.list.isSearch() || !listId) {
535 function canExecuteShared(e, isSearch) { 469 // We cannot paste into search view or the list isn't ready.
536 var command = e.command; 470 update('paste-from-organize-menu-command', false);
537 switch (command.id) { 471 } else {
538 case 'paste-from-folders-menu-command': 472 promises.push(new Promise(function(resolve) {
539 case 'paste-from-organize-menu-command': 473 chrome.bookmarkManagerPrivate.canPaste(listId, function(canPaste) {
540 case 'paste-from-context-menu-command': 474 update('paste-from-organize-menu-command', canPaste);
541 updatePasteCommand(); 475 resolve(canPaste);
542 break; 476 });
543 477 }));
544 case 'add-new-bookmark-command': 478 }
545 case 'new-folder-command': 479
546 case 'new-folder-from-folders-menu-command': 480 Promise.all(promises).then(function() {
547 var parentId = computeParentFolderForNewItem(); 481 var cmd;
548 var unmodifiable = isUnmodifiable( 482 if (document.activeElement == bmm.list)
549 bmm.tree.getBookmarkNodeById(parentId)); 483 cmd = 'paste-from-organize-menu-command';
550 e.canExecute = !isSearch && canEdit && !unmodifiable; 484 else if (document.activeElement == bmm.tree)
551 break; 485 cmd = 'paste-from-folders-menu-command';
552 486
553 case 'open-in-new-tab-command': 487 if (cmd)
554 updateOpenCommand(e, command, 'open_in_new_tab', 'open_all', false); 488 update('paste-from-context-menu-command', !$(cmd).disabled);
555 break; 489
556 490 if (opt_f)
557 case 'open-in-background-tab-command': 491 opt_f();
558 updateOpenCommand(e, command, '', '', false); 492 });
559 break; 493 }
560 494
561 case 'open-in-new-window-command': 495 function handleCanExecuteForSearchBox(e) {
562 updateOpenCommand(e, command, 496 var command = e.command;
563 'open_in_new_window', 'open_all_new_window', 497 switch (command.id) {
564 // Disabled when incognito is forced. 498 case 'delete-command':
565 incognitoModeAvailability == 'forced' || !canOpenNewWindows); 499 case 'undo-command':
566 break; 500 // Pass the delete and undo commands through
567 501 // (fixes http://crbug.com/278112).
568 case 'open-incognito-window-command':
569 updateOpenCommand(e, command,
570 'open_incognito', 'open_all_incognito',
571 // Not available when incognito is disabled.
572 incognitoModeAvailability == 'disabled');
573 break;
574
575 case 'undo-delete-command':
576 e.canExecute = !!lastDeleted;
577 break;
578 }
579 }
580
581 /**
582 * Helper function for handling canExecute for the list and document.
583 * @param {!cr.ui.CanExecuteEvent} e Can execute event object.
584 */
585 function canExecuteForList(e) {
586 function hasSelected() {
587 return !!bmm.list.selectedItem;
588 }
589
590 function hasSingleSelected() {
591 return bmm.list.selectedItems.length == 1;
592 }
593
594 function canCopyItem(item) {
595 return item.id != 'new';
596 }
597
598 function canCopyItems() {
599 var selectedItems = bmm.list.selectedItems;
600 return selectedItems && selectedItems.some(canCopyItem);
601 }
602
603 function isSearch() {
604 return bmm.list.isSearch();
605 }
606
607 var command = e.command;
608 switch (command.id) {
609 case 'rename-folder-command':
610 // Show rename if a single folder is selected.
611 var items = bmm.list.selectedItems;
612 if (items.length != 1) {
613 e.canExecute = false; 502 e.canExecute = false;
503 break;
504 }
505 }
506
507 function handleCanExecuteForDocument(e) {
508 var command = e.command;
509 switch (command.id) {
510 case 'import-menu-command':
511 e.canExecute = canEdit;
512 break;
513
514 case 'export-menu-command':
515 // We can always execute the export-menu command.
516 e.canExecute = true;
517 break;
518
519 case 'sort-command':
520 e.canExecute = !bmm.list.isSearch() && bmm.list.dataModel &&
521 bmm.list.dataModel.length > 1 &&
522 !isUnmodifiable(bmm.tree.getBookmarkNodeById(bmm.list.parentId));
523 break;
524
525 case 'undo-command':
526 // Because the global undo command has no visible UI, always enable it,
527 // and just make it a no-op if undo is not possible.
528 e.canExecute = true;
529 break;
530
531 default:
532 canExecuteForList(e);
533 if (!e.defaultPrevented)
534 canExecuteForTree(e);
535 break;
536 }
537 }
538
539 /**
540 * Helper function for handling canExecute for the list and the tree.
541 * @param {!cr.ui.CanExecuteEvent} e Can execute event object.
542 * @param {boolean} isSearch Whether the user is trying to do a command on
543 * search.
544 */
545 function canExecuteShared(e, isSearch) {
546 var command = e.command;
547 switch (command.id) {
548 case 'paste-from-folders-menu-command':
549 case 'paste-from-organize-menu-command':
550 case 'paste-from-context-menu-command':
551 updatePasteCommand();
552 break;
553
554 case 'add-new-bookmark-command':
555 case 'new-folder-command':
556 case 'new-folder-from-folders-menu-command':
557 var parentId = computeParentFolderForNewItem();
558 var unmodifiable =
559 isUnmodifiable(bmm.tree.getBookmarkNodeById(parentId));
560 e.canExecute = !isSearch && canEdit && !unmodifiable;
561 break;
562
563 case 'open-in-new-tab-command':
564 updateOpenCommand(e, command, 'open_in_new_tab', 'open_all', false);
565 break;
566
567 case 'open-in-background-tab-command':
568 updateOpenCommand(e, command, '', '', false);
569 break;
570
571 case 'open-in-new-window-command':
572 updateOpenCommand(
573 e, command, 'open_in_new_window', 'open_all_new_window',
574 // Disabled when incognito is forced.
575 incognitoModeAvailability == 'forced' || !canOpenNewWindows);
576 break;
577
578 case 'open-incognito-window-command':
579 updateOpenCommand(
580 e, command, 'open_incognito', 'open_all_incognito',
581 // Not available when incognito is disabled.
582 incognitoModeAvailability == 'disabled');
583 break;
584
585 case 'undo-delete-command':
586 e.canExecute = !!lastDeleted;
587 break;
588 }
589 }
590
591 /**
592 * Helper function for handling canExecute for the list and document.
593 * @param {!cr.ui.CanExecuteEvent} e Can execute event object.
594 */
595 function canExecuteForList(e) {
596 function hasSelected() {
597 return !!bmm.list.selectedItem;
598 }
599
600 function hasSingleSelected() {
601 return bmm.list.selectedItems.length == 1;
602 }
603
604 function canCopyItem(item) {
605 return item.id != 'new';
606 }
607
608 function canCopyItems() {
609 var selectedItems = bmm.list.selectedItems;
610 return selectedItems && selectedItems.some(canCopyItem);
611 }
612
613 function isSearch() {
614 return bmm.list.isSearch();
615 }
616
617 var command = e.command;
618 switch (command.id) {
619 case 'rename-folder-command':
620 // Show rename if a single folder is selected.
621 var items = bmm.list.selectedItems;
622 if (items.length != 1) {
623 e.canExecute = false;
624 command.hidden = true;
625 } else {
626 var isFolder = bmm.isFolder(items[0]);
627 e.canExecute = isFolder && canEdit && !hasUnmodifiable(items);
628 command.hidden = !isFolder;
629 }
630 break;
631
632 case 'edit-command':
633 // Show the edit command if not a folder.
634 var items = bmm.list.selectedItems;
635 if (items.length != 1) {
636 e.canExecute = false;
637 command.hidden = false;
638 } else {
639 var isFolder = bmm.isFolder(items[0]);
640 e.canExecute = !isFolder && canEdit && !hasUnmodifiable(items);
641 command.hidden = isFolder;
642 }
643 break;
644
645 case 'show-in-folder-command':
646 e.canExecute = isSearch() && hasSingleSelected();
647 break;
648
649 case 'delete-command':
650 case 'cut-command':
651 e.canExecute = canCopyItems() && canEdit &&
652 !hasUnmodifiable(bmm.list.selectedItems);
653 break;
654
655 case 'copy-command':
656 e.canExecute = canCopyItems();
657 break;
658
659 case 'open-in-same-window-command':
660 e.canExecute = (e.target == bmm.list) && hasSelected();
661 break;
662
663 default:
664 canExecuteShared(e, isSearch());
665 }
666 }
667
668 // Update canExecute for the commands when the list is the active element.
669 function handleCanExecuteForList(e) {
670 if (e.target != bmm.list)
671 return;
672 canExecuteForList(e);
673 }
674
675 // Update canExecute for the commands when the tree is the active element.
676 function handleCanExecuteForTree(e) {
677 if (e.target != bmm.tree)
678 return;
679 canExecuteForTree(e);
680 }
681
682 function canExecuteForTree(e) {
683 function hasSelected() {
684 return !!bmm.tree.selectedItem;
685 }
686
687 function isSearch() {
688 return bmm.tree.selectedItem == searchTreeItem;
689 }
690
691 function isTopLevelItem() {
692 return bmm.tree.selectedItem &&
693 bmm.tree.selectedItem.parentNode == bmm.tree;
694 }
695
696 var command = e.command;
697 switch (command.id) {
698 case 'rename-folder-command':
699 case 'rename-folder-from-folders-menu-command':
700 command.hidden = false;
701 e.canExecute = hasSelected() && !isTopLevelItem() && canEdit &&
702 !hasUnmodifiable(bmm.tree.selectedFolders);
703 break;
704
705 case 'edit-command':
614 command.hidden = true; 706 command.hidden = true;
615 } else {
616 var isFolder = bmm.isFolder(items[0]);
617 e.canExecute = isFolder && canEdit && !hasUnmodifiable(items);
618 command.hidden = !isFolder;
619 }
620 break;
621
622 case 'edit-command':
623 // Show the edit command if not a folder.
624 var items = bmm.list.selectedItems;
625 if (items.length != 1) {
626 e.canExecute = false; 707 e.canExecute = false;
627 command.hidden = false; 708 break;
628 } else { 709
629 var isFolder = bmm.isFolder(items[0]); 710 case 'delete-command':
630 e.canExecute = !isFolder && canEdit && !hasUnmodifiable(items); 711 case 'delete-from-folders-menu-command':
631 command.hidden = isFolder; 712 case 'cut-command':
632 } 713 case 'cut-from-folders-menu-command':
633 break; 714 e.canExecute = hasSelected() && !isTopLevelItem() && canEdit &&
634 715 !hasUnmodifiable(bmm.tree.selectedFolders);
635 case 'show-in-folder-command': 716 break;
636 e.canExecute = isSearch() && hasSingleSelected(); 717
637 break; 718 case 'copy-command':
638 719 case 'copy-from-folders-menu-command':
639 case 'delete-command': 720 e.canExecute = hasSelected() && !isTopLevelItem();
640 case 'cut-command': 721 break;
641 e.canExecute = canCopyItems() && canEdit && 722
642 !hasUnmodifiable(bmm.list.selectedItems); 723 case 'undo-delete-from-folders-menu-command':
643 break; 724 e.canExecute = lastDeleted && lastDeleted.target == bmm.tree;
644 725 break;
645 case 'copy-command': 726
646 e.canExecute = canCopyItems(); 727 default:
647 break; 728 canExecuteShared(e, isSearch());
648 729 }
649 case 'open-in-same-window-command': 730 }
650 e.canExecute = (e.target == bmm.list) && hasSelected(); 731
651 break; 732 /**
652 733 * Update the canExecute state of all the commands.
653 default: 734 */
654 canExecuteShared(e, isSearch()); 735 function updateAllCommands() {
655 } 736 var commands = document.querySelectorAll('command');
656 } 737 for (var i = 0; i < commands.length; i++) {
657 738 commands[i].canExecuteChange();
658 // Update canExecute for the commands when the list is the active element. 739 }
659 function handleCanExecuteForList(e) { 740 }
660 if (e.target != bmm.list) return; 741
661 canExecuteForList(e); 742 function updateEditingCommands() {
662 } 743 var editingCommands = [
663 744 'add-new-bookmark',
664 // Update canExecute for the commands when the tree is the active element. 745 'cut',
665 function handleCanExecuteForTree(e) { 746 'cut-from-folders-menu',
666 if (e.target != bmm.tree) return; 747 'delete',
667 canExecuteForTree(e); 748 'edit',
668 } 749 'new-folder',
669 750 'paste-from-context-menu',
670 function canExecuteForTree(e) { 751 'paste-from-folders-menu',
671 function hasSelected() { 752 'paste-from-organize-menu',
672 return !!bmm.tree.selectedItem; 753 'rename-folder',
673 } 754 'sort',
674 755 ];
675 function isSearch() { 756
676 return bmm.tree.selectedItem == searchTreeItem; 757 chrome.bookmarkManagerPrivate.canEdit(function(result) {
677 } 758 if (result != canEdit) {
678 759 canEdit = result;
679 function isTopLevelItem() { 760 editingCommands.forEach(function(baseId) {
680 return bmm.tree.selectedItem && 761 $(baseId + '-command').canExecuteChange();
681 bmm.tree.selectedItem.parentNode == bmm.tree; 762 });
682 }
683
684 var command = e.command;
685 switch (command.id) {
686 case 'rename-folder-command':
687 case 'rename-folder-from-folders-menu-command':
688 command.hidden = false;
689 e.canExecute = hasSelected() && !isTopLevelItem() && canEdit &&
690 !hasUnmodifiable(bmm.tree.selectedFolders);
691 break;
692
693 case 'edit-command':
694 command.hidden = true;
695 e.canExecute = false;
696 break;
697
698 case 'delete-command':
699 case 'delete-from-folders-menu-command':
700 case 'cut-command':
701 case 'cut-from-folders-menu-command':
702 e.canExecute = hasSelected() && !isTopLevelItem() && canEdit &&
703 !hasUnmodifiable(bmm.tree.selectedFolders);
704 break;
705
706 case 'copy-command':
707 case 'copy-from-folders-menu-command':
708 e.canExecute = hasSelected() && !isTopLevelItem();
709 break;
710
711 case 'undo-delete-from-folders-menu-command':
712 e.canExecute = lastDeleted && lastDeleted.target == bmm.tree;
713 break;
714
715 default:
716 canExecuteShared(e, isSearch());
717 }
718 }
719
720 /**
721 * Update the canExecute state of all the commands.
722 */
723 function updateAllCommands() {
724 var commands = document.querySelectorAll('command');
725 for (var i = 0; i < commands.length; i++) {
726 commands[i].canExecuteChange();
727 }
728 }
729
730 function updateEditingCommands() {
731 var editingCommands = [
732 'add-new-bookmark',
733 'cut',
734 'cut-from-folders-menu',
735 'delete',
736 'edit',
737 'new-folder',
738 'paste-from-context-menu',
739 'paste-from-folders-menu',
740 'paste-from-organize-menu',
741 'rename-folder',
742 'sort',
743 ];
744
745 chrome.bookmarkManagerPrivate.canEdit(function(result) {
746 if (result != canEdit) {
747 canEdit = result;
748 editingCommands.forEach(function(baseId) {
749 $(baseId + '-command').canExecuteChange();
750 });
751 }
752 });
753 }
754
755 function handleChangeForTree(e) {
756 navigateTo(bmm.tree.selectedItem.bookmarkId);
757 }
758
759 function handleMenuButtonClicked(e) {
760 updateEditingCommands();
761
762 if (e.currentTarget.id == 'folders-menu') {
763 $('copy-from-folders-menu-command').canExecuteChange();
764 $('undo-delete-from-folders-menu-command').canExecuteChange();
765 } else {
766 $('copy-command').canExecuteChange();
767 }
768 }
769
770 function handleRename(e) {
771 var item = e.target;
772 var bookmarkNode = item.bookmarkNode;
773 chrome.bookmarks.update(bookmarkNode.id, {title: item.label});
774 performGlobalUndo = null; // This can't be undone, so disable global undo.
775 }
776
777 function handleEdit(e) {
778 var item = e.target;
779 var bookmarkNode = item.bookmarkNode;
780 var context = {
781 title: bookmarkNode.title
782 };
783 if (!bmm.isFolder(bookmarkNode))
784 context.url = bookmarkNode.url;
785
786 if (bookmarkNode.id == 'new') {
787 selectItemsAfterUserAction(/** @type {BookmarkList} */(bmm.list));
788
789 // New page
790 context.parentId = bookmarkNode.parentId;
791 chrome.bookmarks.create(context, function(node) {
792 // A new node was created and will get added to the list due to the
793 // handler.
794 var dataModel = bmm.list.dataModel;
795 var index = dataModel.indexOf(bookmarkNode);
796 dataModel.splice(index, 1);
797
798 // Select new item.
799 var newIndex = dataModel.findIndexById(node.id);
800 if (newIndex != -1) {
801 var sm = bmm.list.selectionModel;
802 bmm.list.scrollIndexIntoView(newIndex);
803 sm.leadIndex = sm.anchorIndex = sm.selectedIndex = newIndex;
804 } 763 }
805 }); 764 });
806 } else { 765 }
807 // Edit 766
808 chrome.bookmarks.update(bookmarkNode.id, context); 767 function handleChangeForTree(e) {
809 } 768 navigateTo(bmm.tree.selectedItem.bookmarkId);
810 performGlobalUndo = null; // This can't be undone, so disable global undo. 769 }
811 } 770
812 771 function handleMenuButtonClicked(e) {
813 function handleCancelEdit(e) { 772 updateEditingCommands();
814 var item = e.target; 773
815 var bookmarkNode = item.bookmarkNode; 774 if (e.currentTarget.id == 'folders-menu') {
816 if (bookmarkNode.id == 'new') { 775 $('copy-from-folders-menu-command').canExecuteChange();
817 var dataModel = bmm.list.dataModel; 776 $('undo-delete-from-folders-menu-command').canExecuteChange();
818 var index = dataModel.findIndexById('new'); 777 } else {
819 dataModel.splice(index, 1); 778 $('copy-command').canExecuteChange();
820 } 779 }
821 } 780 }
822 781
823 /** 782 function handleRename(e) {
824 * Navigates to the folder that the selected item is in and selects it. This is 783 var item = e.target;
825 * used for the show-in-folder command. 784 var bookmarkNode = item.bookmarkNode;
826 */ 785 chrome.bookmarks.update(bookmarkNode.id, {title: item.label});
827 function showInFolder() { 786 performGlobalUndo = null; // This can't be undone, so disable global undo.
828 var bookmarkNode = bmm.list.selectedItem; 787 }
829 if (!bookmarkNode) 788
830 return; 789 function handleEdit(e) {
831 var parentId = bookmarkNode.parentId; 790 var item = e.target;
832 791 var bookmarkNode = item.bookmarkNode;
833 // After the list is loaded we should select the revealed item. 792 var context = {title: bookmarkNode.title};
834 function selectItem() { 793 if (!bmm.isFolder(bookmarkNode))
835 var index = bmm.list.dataModel.findIndexById(bookmarkNode.id); 794 context.url = bookmarkNode.url;
836 if (index == -1) 795
796 if (bookmarkNode.id == 'new') {
797 selectItemsAfterUserAction(/** @type {BookmarkList} */ (bmm.list));
798
799 // New page
800 context.parentId = bookmarkNode.parentId;
801 chrome.bookmarks.create(context, function(node) {
802 // A new node was created and will get added to the list due to the
803 // handler.
804 var dataModel = bmm.list.dataModel;
805 var index = dataModel.indexOf(bookmarkNode);
806 dataModel.splice(index, 1);
807
808 // Select new item.
809 var newIndex = dataModel.findIndexById(node.id);
810 if (newIndex != -1) {
811 var sm = bmm.list.selectionModel;
812 bmm.list.scrollIndexIntoView(newIndex);
813 sm.leadIndex = sm.anchorIndex = sm.selectedIndex = newIndex;
814 }
815 });
816 } else {
817 // Edit
818 chrome.bookmarks.update(bookmarkNode.id, context);
819 }
820 performGlobalUndo = null; // This can't be undone, so disable global undo.
821 }
822
823 function handleCancelEdit(e) {
824 var item = e.target;
825 var bookmarkNode = item.bookmarkNode;
826 if (bookmarkNode.id == 'new') {
827 var dataModel = bmm.list.dataModel;
828 var index = dataModel.findIndexById('new');
829 dataModel.splice(index, 1);
830 }
831 }
832
833 /**
834 * Navigates to the folder that the selected item is in and selects it. This
835 * is
836 * used for the show-in-folder command.
837 */
838 function showInFolder() {
839 var bookmarkNode = bmm.list.selectedItem;
840 if (!bookmarkNode)
837 return; 841 return;
838 var sm = bmm.list.selectionModel; 842 var parentId = bookmarkNode.parentId;
839 sm.anchorIndex = sm.leadIndex = sm.selectedIndex = index; 843
840 bmm.list.scrollIndexIntoView(index); 844 // After the list is loaded we should select the revealed item.
841 } 845 function selectItem() {
842 846 var index = bmm.list.dataModel.findIndexById(bookmarkNode.id);
843 var treeItem = bmm.treeLookup[parentId]; 847 if (index == -1)
844 treeItem.reveal(); 848 return;
845 849 var sm = bmm.list.selectionModel;
846 navigateTo(parentId, selectItem); 850 sm.anchorIndex = sm.leadIndex = sm.selectedIndex = index;
847 } 851 bmm.list.scrollIndexIntoView(index);
848 852 }
849 /** 853
854 var treeItem = bmm.treeLookup[parentId];
855 treeItem.reveal();
856
857 navigateTo(parentId, selectItem);
858 }
859
860 /**
850 * @return {!cr.LinkController} The link controller used to open links based on 861 * @return {!cr.LinkController} The link controller used to open links based on
851 * user clicks and keyboard actions. 862 * user clicks and keyboard actions.
852 */ 863 */
853 function getLinkController() { 864 function getLinkController() {
854 return linkController || 865 return linkController ||
855 (linkController = new cr.LinkController(loadTimeData)); 866 (linkController = new cr.LinkController(loadTimeData));
856 } 867 }
857 868
858 /** 869 /**
859 * Returns the selected bookmark nodes of the provided tree or list. 870 * Returns the selected bookmark nodes of the provided tree or list.
860 * If |opt_target| is not provided or null the active element is used. 871 * If |opt_target| is not provided or null the active element is used.
861 * Only call this if the list or the tree is focused. 872 * Only call this if the list or the tree is focused.
862 * @param {EventTarget=} opt_target The target list or tree. 873 * @param {EventTarget=} opt_target The target list or tree.
863 * @return {!Array} Array of bookmark nodes. 874 * @return {!Array} Array of bookmark nodes.
864 */ 875 */
865 function getSelectedBookmarkNodes(opt_target) { 876 function getSelectedBookmarkNodes(opt_target) {
866 return (opt_target || document.activeElement) == bmm.tree ? 877 return (opt_target || document.activeElement) == bmm.tree ?
867 bmm.tree.selectedFolders : bmm.list.selectedItems; 878 bmm.tree.selectedFolders :
868 } 879 bmm.list.selectedItems;
869 880 }
870 /** 881
882 /**
871 * @param {EventTarget=} opt_target The target list or tree. 883 * @param {EventTarget=} opt_target The target list or tree.
872 * @return {!Array<string>} An array of the selected bookmark IDs. 884 * @return {!Array<string>} An array of the selected bookmark IDs.
873 */ 885 */
874 function getSelectedBookmarkIds(opt_target) { 886 function getSelectedBookmarkIds(opt_target) {
875 var selectedNodes = getSelectedBookmarkNodes(opt_target); 887 var selectedNodes = getSelectedBookmarkNodes(opt_target);
876 selectedNodes.sort(function(a, b) { return a.index - b.index }); 888 selectedNodes.sort(function(a, b) {
877 return selectedNodes.map(function(node) { 889 return a.index - b.index
878 return node.id; 890 });
879 }); 891 return selectedNodes.map(function(node) {
880 } 892 return node.id;
881 893 });
882 /** 894 }
895
896 /**
883 * @param {BookmarkTreeNode} node The node to test. 897 * @param {BookmarkTreeNode} node The node to test.
884 * @return {boolean} Whether the given node is unmodifiable. 898 * @return {boolean} Whether the given node is unmodifiable.
885 */ 899 */
886 function isUnmodifiable(node) { 900 function isUnmodifiable(node) {
887 return !!(node && node.unmodifiable); 901 return !!(node && node.unmodifiable);
888 } 902 }
889 903
890 /** 904 /**
891 * @param {Array<BookmarkTreeNode>} nodes A list of BookmarkTreeNodes. 905 * @param {Array<BookmarkTreeNode>} nodes A list of BookmarkTreeNodes.
892 * @return {boolean} Whether any of the nodes is managed. 906 * @return {boolean} Whether any of the nodes is managed.
893 */ 907 */
894 function hasUnmodifiable(nodes) { 908 function hasUnmodifiable(nodes) {
895 return nodes.some(isUnmodifiable); 909 return nodes.some(isUnmodifiable);
896 } 910 }
897 911
898 /** 912 /**
899 * Opens the selected bookmarks. 913 * Opens the selected bookmarks.
900 * @param {cr.LinkKind} kind The kind of link we want to open. 914 * @param {cr.LinkKind} kind The kind of link we want to open.
901 * @param {HTMLElement=} opt_eventTarget The target of the user initiated event. 915 * @param {HTMLElement=} opt_eventTarget The target of the user initiated
902 */ 916 * event.
903 function openBookmarks(kind, opt_eventTarget) { 917 */
904 // If we have selected any folders, we need to find all the bookmarks one 918 function openBookmarks(kind, opt_eventTarget) {
905 // level down. We use multiple async calls to getSubtree instead of getting 919 // If we have selected any folders, we need to find all the bookmarks one
906 // the whole tree since we would like to minimize the amount of data sent. 920 // level down. We use multiple async calls to getSubtree instead of getting
907 921 // the whole tree since we would like to minimize the amount of data sent.
908 var urlsP = getUrlsForOpenCommands(opt_eventTarget ? opt_eventTarget : null); 922
909 urlsP.then(function(urls) { 923 var urlsP =
910 getLinkController().openUrls(assert(urls), kind); 924 getUrlsForOpenCommands(opt_eventTarget ? opt_eventTarget : null);
911 chrome.bookmarkManagerPrivate.recordLaunch(); 925 urlsP.then(function(urls) {
912 }); 926 getLinkController().openUrls(assert(urls), kind);
913 } 927 chrome.bookmarkManagerPrivate.recordLaunch();
914 928 });
915 /** 929 }
916 * Opens an item in the list. 930
917 */ 931 /**
918 function openItem() { 932 * Opens an item in the list.
919 var bookmarkNodes = getSelectedBookmarkNodes(); 933 */
920 // If we double clicked or pressed enter on a single folder, navigate to it. 934 function openItem() {
921 if (bookmarkNodes.length == 1 && bmm.isFolder(bookmarkNodes[0])) 935 var bookmarkNodes = getSelectedBookmarkNodes();
922 navigateTo(bookmarkNodes[0].id); 936 // If we double clicked or pressed enter on a single folder, navigate to it.
923 else 937 if (bookmarkNodes.length == 1 && bmm.isFolder(bookmarkNodes[0]))
924 openBookmarks(LinkKind.FOREGROUND_TAB); 938 navigateTo(bookmarkNodes[0].id);
925 } 939 else
926 940 openBookmarks(LinkKind.FOREGROUND_TAB);
927 /** 941 }
928 * Refreshes search results after delete or undo-delete. 942
929 * This ensures children of deleted folders do not remain in results 943 /**
930 */ 944 * Refreshes search results after delete or undo-delete.
931 function updateSearchResults() { 945 * This ensures children of deleted folders do not remain in results
932 if (bmm.list.isSearch()) 946 */
933 bmm.list.reload(); 947 function updateSearchResults() {
934 } 948 if (bmm.list.isSearch())
935 949 bmm.list.reload();
936 /** 950 }
937 * Deletes the selected bookmarks. The bookmarks are saved in memory in case 951
938 * the user needs to undo the deletion. 952 /**
939 * @param {EventTarget=} opt_target The deleter of bookmarks. 953 * Deletes the selected bookmarks. The bookmarks are saved in memory in case
940 */ 954 * the user needs to undo the deletion.
941 function deleteBookmarks(opt_target) { 955 * @param {EventTarget=} opt_target The deleter of bookmarks.
942 var selectedIds = getSelectedBookmarkIds(opt_target); 956 */
943 if (!selectedIds.length) 957 function deleteBookmarks(opt_target) {
944 return; 958 var selectedIds = getSelectedBookmarkIds(opt_target);
945 959 if (!selectedIds.length)
946 var filteredIds = getFilteredSelectedBookmarkIds(opt_target); 960 return;
947 lastDeleted = {nodes: [], target: opt_target || document.activeElement}; 961
948 962 var filteredIds = getFilteredSelectedBookmarkIds(opt_target);
949 function performDelete() { 963 lastDeleted = {nodes: [], target: opt_target || document.activeElement};
950 // Only remove filtered ids. 964
951 chrome.bookmarkManagerPrivate.removeTrees(filteredIds); 965 function performDelete() {
966 // Only remove filtered ids.
967 chrome.bookmarkManagerPrivate.removeTrees(filteredIds);
968 $('undo-delete-command').canExecuteChange();
969 $('undo-delete-from-folders-menu-command').canExecuteChange();
970 performGlobalUndo = undoDelete;
971 }
972
973 // First, store information about the bookmarks being deleted.
974 // Store all selected ids.
975 selectedIds.forEach(function(id) {
976 chrome.bookmarks.getSubTree(id, function(results) {
977 lastDeleted.nodes.push(results);
978
979 // When all nodes have been saved, perform the deletion.
980 if (lastDeleted.nodes.length === selectedIds.length) {
981 performDelete();
982 updateSearchResults();
983 }
984 });
985 });
986 }
987
988 /**
989 * Restores a tree of bookmarks under a specified folder.
990 * @param {BookmarkTreeNode} node The node to restore.
991 * @param {(string|number)=} opt_parentId If a string is passed, it's the ID
992 * of
993 * the folder to restore under. If not specified or a number is passed,
994 * the
995 * original parentId of the node will be used.
996 */
997 function restoreTree(node, opt_parentId) {
998 var bookmarkInfo = {
999 parentId: typeof opt_parentId == 'string' ? opt_parentId : node.parentId,
1000 title: node.title,
1001 index: node.index,
1002 url: node.url
1003 };
1004
1005 chrome.bookmarks.create(bookmarkInfo, function(result) {
1006 if (!result) {
1007 console.error('Failed to restore bookmark.');
1008 return;
1009 }
1010
1011 if (node.children) {
1012 // Restore the children using the new ID for this node.
1013 node.children.forEach(function(child) {
1014 restoreTree(child, result.id);
1015 });
1016 }
1017
1018 updateSearchResults();
1019 });
1020 }
1021
1022 /**
1023 * Restores the last set of bookmarks that was deleted.
1024 */
1025 function undoDelete() {
1026 lastDeleted.nodes.forEach(function(arr) {
1027 arr.forEach(restoreTree);
1028 });
1029 lastDeleted = null;
952 $('undo-delete-command').canExecuteChange(); 1030 $('undo-delete-command').canExecuteChange();
953 $('undo-delete-from-folders-menu-command').canExecuteChange(); 1031 $('undo-delete-from-folders-menu-command').canExecuteChange();
954 performGlobalUndo = undoDelete; 1032
955 } 1033 // Only a single level of undo is supported, so disable global undo now.
956 1034 performGlobalUndo = null;
957 // First, store information about the bookmarks being deleted. 1035 }
958 // Store all selected ids. 1036
959 selectedIds.forEach(function(id) { 1037 /**
960 chrome.bookmarks.getSubTree(id, function(results) {
961 lastDeleted.nodes.push(results);
962
963 // When all nodes have been saved, perform the deletion.
964 if (lastDeleted.nodes.length === selectedIds.length) {
965 performDelete();
966 updateSearchResults();
967 }
968 });
969 });
970 }
971
972 /**
973 * Restores a tree of bookmarks under a specified folder.
974 * @param {BookmarkTreeNode} node The node to restore.
975 * @param {(string|number)=} opt_parentId If a string is passed, it's the ID of
976 * the folder to restore under. If not specified or a number is passed, the
977 * original parentId of the node will be used.
978 */
979 function restoreTree(node, opt_parentId) {
980 var bookmarkInfo = {
981 parentId: typeof opt_parentId == 'string' ? opt_parentId : node.parentId,
982 title: node.title,
983 index: node.index,
984 url: node.url
985 };
986
987 chrome.bookmarks.create(bookmarkInfo, function(result) {
988 if (!result) {
989 console.error('Failed to restore bookmark.');
990 return;
991 }
992
993 if (node.children) {
994 // Restore the children using the new ID for this node.
995 node.children.forEach(function(child) {
996 restoreTree(child, result.id);
997 });
998 }
999
1000 updateSearchResults();
1001 });
1002 }
1003
1004 /**
1005 * Restores the last set of bookmarks that was deleted.
1006 */
1007 function undoDelete() {
1008 lastDeleted.nodes.forEach(function(arr) {
1009 arr.forEach(restoreTree);
1010 });
1011 lastDeleted = null;
1012 $('undo-delete-command').canExecuteChange();
1013 $('undo-delete-from-folders-menu-command').canExecuteChange();
1014
1015 // Only a single level of undo is supported, so disable global undo now.
1016 performGlobalUndo = null;
1017 }
1018
1019 /**
1020 * Computes folder for "Add Page" and "Add Folder". 1038 * Computes folder for "Add Page" and "Add Folder".
1021 * @return {string} The id of folder node where we'll create new page/folder. 1039 * @return {string} The id of folder node where we'll create new page/folder.
1022 */ 1040 */
1023 function computeParentFolderForNewItem() { 1041 function computeParentFolderForNewItem() {
1024 if (document.activeElement == bmm.tree) 1042 if (document.activeElement == bmm.tree)
1025 return bmm.list.parentId; 1043 return bmm.list.parentId;
1026 var selectedItem = bmm.list.selectedItem; 1044 var selectedItem = bmm.list.selectedItem;
1027 return selectedItem && bmm.isFolder(selectedItem) ? 1045 return selectedItem && bmm.isFolder(selectedItem) ? selectedItem.id :
1028 selectedItem.id : bmm.list.parentId; 1046 bmm.list.parentId;
1029 } 1047 }
1030 1048
1031 /** 1049 /**
1032 * Callback for rename folder and edit command. This starts editing for 1050 * Callback for rename folder and edit command. This starts editing for
1033 * the passed in target, or the selected item. 1051 * the passed in target, or the selected item.
1034 * @param {EventTarget=} opt_target The target to start editing. If absent or 1052 * @param {EventTarget=} opt_target The target to start editing. If absent or
1035 * null, the selected item will be edited instead. 1053 * null, the selected item will be edited instead.
1036 */ 1054 */
1037 function editItem(opt_target) { 1055 function editItem(opt_target) {
1038 if ((opt_target || document.activeElement) == bmm.tree) { 1056 if ((opt_target || document.activeElement) == bmm.tree) {
1039 bmm.tree.selectedItem.editing = true; 1057 bmm.tree.selectedItem.editing = true;
1040 } else { 1058 } else {
1041 var li = bmm.list.getListItem(bmm.list.selectedItem); 1059 var li = bmm.list.getListItem(bmm.list.selectedItem);
1042 if (li) 1060 if (li)
1043 li.editing = true; 1061 li.editing = true;
1044 } 1062 }
1045 } 1063 }
1046 1064
1047 /** 1065 /**
1048 * Callback for the new folder command. This creates a new folder and starts 1066 * Callback for the new folder command. This creates a new folder and starts
1049 * a rename of it. 1067 * a rename of it.
1050 * @param {EventTarget=} opt_target The target to create a new folder in. 1068 * @param {EventTarget=} opt_target The target to create a new folder in.
1051 */ 1069 */
1052 function newFolder(opt_target) { 1070 function newFolder(opt_target) {
1053 performGlobalUndo = null; // This can't be undone, so disable global undo. 1071 performGlobalUndo = null; // This can't be undone, so disable global undo.
1054 1072
1055 var parentId = computeParentFolderForNewItem(); 1073 var parentId = computeParentFolderForNewItem();
1056 var selectedItems = bmm.list.selectedItems; 1074 var selectedItems = bmm.list.selectedItems;
1057 var newIndex; 1075 var newIndex;
1058 // Callback is called after tree and list data model updated. 1076 // Callback is called after tree and list data model updated.
1059 function createFolder(callback) { 1077 function createFolder(callback) {
1060 if (selectedItems.length == 1 && document.activeElement != bmm.tree && 1078 if (selectedItems.length == 1 && document.activeElement != bmm.tree &&
1061 !bmm.isFolder(selectedItems[0]) && selectedItems[0].id != 'new') { 1079 !bmm.isFolder(selectedItems[0]) && selectedItems[0].id != 'new') {
1062 newIndex = bmm.list.dataModel.indexOf(selectedItems[0]) + 1; 1080 newIndex = bmm.list.dataModel.indexOf(selectedItems[0]) + 1;
1063 } 1081 }
1064 chrome.bookmarks.create({ 1082 chrome.bookmarks.create(
1065 title: loadTimeData.getString('new_folder_name'), 1083 {
1066 parentId: parentId, 1084 title: loadTimeData.getString('new_folder_name'),
1067 index: newIndex 1085 parentId: parentId,
1068 }, callback); 1086 index: newIndex
1069 } 1087 },
1070 1088 callback);
1071 if ((opt_target || document.activeElement) == bmm.tree) { 1089 }
1072 createFolder(function(newNode) { 1090
1073 navigateTo(newNode.id, function() { 1091 if ((opt_target || document.activeElement) == bmm.tree) {
1074 bmm.treeLookup[newNode.id].editing = true; 1092 createFolder(function(newNode) {
1093 navigateTo(newNode.id, function() {
1094 bmm.treeLookup[newNode.id].editing = true;
1095 });
1075 }); 1096 });
1076 }); 1097 return;
1077 return; 1098 }
1078 } 1099
1079 1100 function editNewFolderInList() {
1080 function editNewFolderInList() { 1101 createFolder(function(newNode) {
1081 createFolder(function(newNode) { 1102 var index = newNode.index;
1082 var index = newNode.index; 1103 var sm = bmm.list.selectionModel;
1104 sm.anchorIndex = sm.leadIndex = sm.selectedIndex = index;
1105 scrollIntoViewAndMakeEditable(index);
1106 });
1107 }
1108
1109 navigateTo(parentId, editNewFolderInList);
1110 }
1111
1112 /**
1113 * Scrolls the list item into view and makes it editable.
1114 * @param {number} index The index of the item to make editable.
1115 */
1116 function scrollIntoViewAndMakeEditable(index) {
1117 bmm.list.scrollIndexIntoView(index);
1118 // onscroll is now dispatched asynchronously so we have to postpone
1119 // the rest.
1120 setTimeout(function() {
1121 var item = bmm.list.getListItemByIndex(index);
1122 if (item)
1123 item.editing = true;
1124 }, 0);
1125 }
1126
1127 /**
1128 * Adds a page to the current folder. This is called by the
1129 * add-new-bookmark-command handler.
1130 */
1131 function addPage() {
1132 var parentId = computeParentFolderForNewItem();
1133 var selectedItems = bmm.list.selectedItems;
1134 var newIndex;
1135 function editNewBookmark() {
1136 if (selectedItems.length == 1 && document.activeElement != bmm.tree &&
1137 !bmm.isFolder(selectedItems[0])) {
1138 newIndex = bmm.list.dataModel.indexOf(selectedItems[0]) + 1;
1139 }
1140
1141 var fakeNode =
1142 {title: '', url: '', parentId: parentId, index: newIndex, id: 'new'};
1143 var dataModel = bmm.list.dataModel;
1144 var index = dataModel.length;
1145 if (newIndex != undefined)
1146 index = newIndex;
1147 dataModel.splice(index, 0, fakeNode);
1083 var sm = bmm.list.selectionModel; 1148 var sm = bmm.list.selectionModel;
1084 sm.anchorIndex = sm.leadIndex = sm.selectedIndex = index; 1149 sm.anchorIndex = sm.leadIndex = sm.selectedIndex = index;
1085 scrollIntoViewAndMakeEditable(index); 1150 scrollIntoViewAndMakeEditable(index);
1086 });
1087 }
1088
1089 navigateTo(parentId, editNewFolderInList);
1090 }
1091
1092 /**
1093 * Scrolls the list item into view and makes it editable.
1094 * @param {number} index The index of the item to make editable.
1095 */
1096 function scrollIntoViewAndMakeEditable(index) {
1097 bmm.list.scrollIndexIntoView(index);
1098 // onscroll is now dispatched asynchronously so we have to postpone
1099 // the rest.
1100 setTimeout(function() {
1101 var item = bmm.list.getListItemByIndex(index);
1102 if (item)
1103 item.editing = true;
1104 }, 0);
1105 }
1106
1107 /**
1108 * Adds a page to the current folder. This is called by the
1109 * add-new-bookmark-command handler.
1110 */
1111 function addPage() {
1112 var parentId = computeParentFolderForNewItem();
1113 var selectedItems = bmm.list.selectedItems;
1114 var newIndex;
1115 function editNewBookmark() {
1116 if (selectedItems.length == 1 && document.activeElement != bmm.tree &&
1117 !bmm.isFolder(selectedItems[0])) {
1118 newIndex = bmm.list.dataModel.indexOf(selectedItems[0]) + 1;
1119 }
1120
1121 var fakeNode = {
1122 title: '',
1123 url: '',
1124 parentId: parentId,
1125 index: newIndex,
1126 id: 'new'
1127 }; 1151 };
1128 var dataModel = bmm.list.dataModel; 1152
1129 var index = dataModel.length; 1153 navigateTo(parentId, editNewBookmark);
1130 if (newIndex != undefined) 1154 }
1131 index = newIndex; 1155
1132 dataModel.splice(index, 0, fakeNode); 1156 /**
1133 var sm = bmm.list.selectionModel; 1157 * This function is used to select items after a user action such as paste,
1134 sm.anchorIndex = sm.leadIndex = sm.selectedIndex = index; 1158 * drop
1135 scrollIntoViewAndMakeEditable(index); 1159 * add page etc.
1136 }; 1160 * @param {BookmarkList|BookmarkTree} target The target of the user action.
1137 1161 * @param {string=} opt_selectedTreeId If provided, then select that tree id.
1138 navigateTo(parentId, editNewBookmark); 1162 */
1139 } 1163 function selectItemsAfterUserAction(target, opt_selectedTreeId) {
1140 1164 // We get one onCreated event per item so we delay the handling until we get
1141 /** 1165 // no more events coming.
1142 * This function is used to select items after a user action such as paste, drop 1166
1143 * add page etc. 1167 var ids = [];
1144 * @param {BookmarkList|BookmarkTree} target The target of the user action. 1168 var timer;
1145 * @param {string=} opt_selectedTreeId If provided, then select that tree id. 1169
1146 */ 1170 function handle(id, bookmarkNode) {
1147 function selectItemsAfterUserAction(target, opt_selectedTreeId) { 1171 clearTimeout(timer);
1148 // We get one onCreated event per item so we delay the handling until we get 1172 if (opt_selectedTreeId || bmm.list.parentId == bookmarkNode.parentId)
1149 // no more events coming. 1173 ids.push(id);
1150 1174 timer = setTimeout(handleTimeout, 50);
1151 var ids = []; 1175 }
1152 var timer; 1176
1153 1177 function handleTimeout() {
1154 function handle(id, bookmarkNode) { 1178 chrome.bookmarks.onCreated.removeListener(handle);
1155 clearTimeout(timer); 1179 chrome.bookmarks.onMoved.removeListener(handle);
1156 if (opt_selectedTreeId || bmm.list.parentId == bookmarkNode.parentId) 1180
1157 ids.push(id); 1181 if (opt_selectedTreeId && ids.indexOf(opt_selectedTreeId) != -1) {
1158 timer = setTimeout(handleTimeout, 50); 1182 var index = ids.indexOf(opt_selectedTreeId);
1159 } 1183 if (index != -1 && opt_selectedTreeId in bmm.treeLookup) {
1160 1184 bmm.tree.selectedItem = bmm.treeLookup[opt_selectedTreeId];
1161 function handleTimeout() { 1185 }
1162 chrome.bookmarks.onCreated.removeListener(handle); 1186 } else if (target == bmm.list) {
1163 chrome.bookmarks.onMoved.removeListener(handle); 1187 var dataModel = bmm.list.dataModel;
1164 1188 var firstIndex = dataModel.findIndexById(ids[0]);
1165 if (opt_selectedTreeId && ids.indexOf(opt_selectedTreeId) != -1) { 1189 var lastIndex = dataModel.findIndexById(ids[ids.length - 1]);
1166 var index = ids.indexOf(opt_selectedTreeId); 1190 if (firstIndex != -1 && lastIndex != -1) {
1167 if (index != -1 && opt_selectedTreeId in bmm.treeLookup) { 1191 var selectionModel = bmm.list.selectionModel;
1168 bmm.tree.selectedItem = bmm.treeLookup[opt_selectedTreeId]; 1192 selectionModel.selectedIndex = -1;
1193 selectionModel.selectRange(firstIndex, lastIndex);
1194 selectionModel.anchorIndex = selectionModel.leadIndex = lastIndex;
1195 bmm.list.focus();
1196 }
1169 } 1197 }
1170 } else if (target == bmm.list) { 1198
1171 var dataModel = bmm.list.dataModel; 1199 bmm.list.endBatchUpdates();
1172 var firstIndex = dataModel.findIndexById(ids[0]); 1200 }
1173 var lastIndex = dataModel.findIndexById(ids[ids.length - 1]); 1201
1174 if (firstIndex != -1 && lastIndex != -1) { 1202 bmm.list.startBatchUpdates();
1175 var selectionModel = bmm.list.selectionModel; 1203
1176 selectionModel.selectedIndex = -1; 1204 chrome.bookmarks.onCreated.addListener(handle);
1177 selectionModel.selectRange(firstIndex, lastIndex); 1205 chrome.bookmarks.onMoved.addListener(handle);
1178 selectionModel.anchorIndex = selectionModel.leadIndex = lastIndex; 1206 timer = setTimeout(handleTimeout, 300);
1179 bmm.list.focus(); 1207 }
1180 } 1208
1181 } 1209 /**
1182 1210 * Record user action.
1183 bmm.list.endBatchUpdates(); 1211 * @param {string} name An user action name.
1184 } 1212 */
1185 1213 function recordUserAction(name) {
1186 bmm.list.startBatchUpdates(); 1214 chrome.metricsPrivate.recordUserAction('BookmarkManager_Command_' + name);
1187 1215 }
1188 chrome.bookmarks.onCreated.addListener(handle); 1216
1189 chrome.bookmarks.onMoved.addListener(handle); 1217 /**
1190 timer = setTimeout(handleTimeout, 300);
1191 }
1192
1193 /**
1194 * Record user action.
1195 * @param {string} name An user action name.
1196 */
1197 function recordUserAction(name) {
1198 chrome.metricsPrivate.recordUserAction('BookmarkManager_Command_' + name);
1199 }
1200
1201 /**
1202 * The currently selected bookmark, based on where the user is clicking. 1218 * The currently selected bookmark, based on where the user is clicking.
1203 * @return {string} The ID of the currently selected bookmark (could be from 1219 * @return {string} The ID of the currently selected bookmark (could be from
1204 * tree view or list view). 1220 * tree view or list view).
1205 */ 1221 */
1206 function getSelectedId() { 1222 function getSelectedId() {
1207 if (document.activeElement == bmm.tree) 1223 if (document.activeElement == bmm.tree)
1208 return bmm.tree.selectedItem.bookmarkId; 1224 return bmm.tree.selectedItem.bookmarkId;
1209 var selectedItem = bmm.list.selectedItem; 1225 var selectedItem = bmm.list.selectedItem;
1210 return selectedItem && bmm.isFolder(selectedItem) ? 1226 return selectedItem && bmm.isFolder(selectedItem) ?
1211 selectedItem.id : bmm.tree.selectedItem.bookmarkId; 1227 selectedItem.id :
1212 } 1228 bmm.tree.selectedItem.bookmarkId;
1213 1229 }
1214 /** 1230
1215 * Pastes the copied/cutted bookmark into the right location depending whether 1231 /**
1216 * if it was called from Organize Menu or from Context Menu. 1232 * Pastes the copied/cutted bookmark into the right location depending whether
1217 * @param {string} id The id of the element being pasted from. 1233 * if it was called from Organize Menu or from Context Menu.
1218 */ 1234 * @param {string} id The id of the element being pasted from.
1219 function pasteBookmark(id) { 1235 */
1220 recordUserAction('Paste'); 1236 function pasteBookmark(id) {
1221 selectItemsAfterUserAction(/** @type {BookmarkList} */(bmm.list)); 1237 recordUserAction('Paste');
1222 chrome.bookmarkManagerPrivate.paste(id, getSelectedBookmarkIds()); 1238 selectItemsAfterUserAction(/** @type {BookmarkList} */ (bmm.list));
1223 } 1239 chrome.bookmarkManagerPrivate.paste(id, getSelectedBookmarkIds());
1224 1240 }
1225 /** 1241
1226 * Returns true if child is contained in another selected folder. 1242 /**
1227 * Traces parent nodes up the tree until a selected ancestor or root is found. 1243 * Returns true if child is contained in another selected folder.
1228 */ 1244 * Traces parent nodes up the tree until a selected ancestor or root is found.
1229 function hasSelectedAncestor(parentNode) { 1245 */
1230 function contains(arr, item) { 1246 function hasSelectedAncestor(parentNode) {
1231 for (var i = 0; i < arr.length; i++) 1247 function contains(arr, item) {
1248 for (var i = 0; i < arr.length; i++)
1232 if (arr[i] === item) 1249 if (arr[i] === item)
1233 return true; 1250 return true;
1234 return false; 1251 return false;
1235 } 1252 }
1236 1253
1237 // Don't search top level, cannot select permanent nodes in search. 1254 // Don't search top level, cannot select permanent nodes in search.
1238 if (parentNode == null || parentNode.id <= 2) 1255 if (parentNode == null || parentNode.id <= 2)
1239 return false; 1256 return false;
1240 1257
1241 // Found selected ancestor. 1258 // Found selected ancestor.
1242 if (contains(getSelectedBookmarkNodes(), parentNode)) 1259 if (contains(getSelectedBookmarkNodes(), parentNode))
1243 return true; 1260 return true;
1244 1261
1245 // Keep digging. 1262 // Keep digging.
1246 return hasSelectedAncestor( 1263 return hasSelectedAncestor(
1247 bmm.tree.getBookmarkNodeById(parentNode.parentId)); 1264 bmm.tree.getBookmarkNodeById(parentNode.parentId));
1248 } 1265 }
1249 1266
1250 /** 1267 /**
1251 * @param {EventTarget=} opt_target A target to get bookmark IDs from. 1268 * @param {EventTarget=} opt_target A target to get bookmark IDs from.
1252 * @return {Array<string>} An array of bookmarks IDs. 1269 * @return {Array<string>} An array of bookmarks IDs.
1253 */ 1270 */
1254 function getFilteredSelectedBookmarkIds(opt_target) { 1271 function getFilteredSelectedBookmarkIds(opt_target) {
1255 // Remove duplicates from filteredIds and return. 1272 // Remove duplicates from filteredIds and return.
1256 var filteredIds = []; 1273 var filteredIds = [];
1257 // Selected nodes to iterate through for matches. 1274 // Selected nodes to iterate through for matches.
1258 var nodes = getSelectedBookmarkNodes(opt_target); 1275 var nodes = getSelectedBookmarkNodes(opt_target);
1259 1276
1260 for (var i = 0; i < nodes.length; i++) 1277 for (var i = 0; i < nodes.length; i++)
1261 if (!hasSelectedAncestor(bmm.tree.getBookmarkNodeById(nodes[i].parentId))) 1278 if (!hasSelectedAncestor(bmm.tree.getBookmarkNodeById(nodes[i].parentId)))
1262 filteredIds.splice(0, 0, nodes[i].id); 1279 filteredIds.splice(0, 0, nodes[i].id);
1263 1280
1264 return filteredIds; 1281 return filteredIds;
1265 } 1282 }
1266 1283
1267 /** 1284 /**
1268 * Handler for the command event. This is used for context menu of list/tree 1285 * Handler for the command event. This is used for context menu of list/tree
1269 * and organized menu. 1286 * and organized menu.
1270 * @param {!Event} e The event object. 1287 * @param {!Event} e The event object.
1271 */ 1288 */
1272 function handleCommand(e) { 1289 function handleCommand(e) {
1273 var command = e.command; 1290 var command = e.command;
1274 var target = assertInstanceof(e.target, HTMLElement); 1291 var target = assertInstanceof(e.target, HTMLElement);
1275 switch (command.id) { 1292 switch (command.id) {
1276 case 'import-menu-command': 1293 case 'import-menu-command':
1277 recordUserAction('Import'); 1294 recordUserAction('Import');
1278 chrome.bookmarks.import(); 1295 chrome.bookmarks.import();
1279 break; 1296 break;
1280 1297
1281 case 'export-menu-command': 1298 case 'export-menu-command':
1282 recordUserAction('Export'); 1299 recordUserAction('Export');
1283 chrome.bookmarks.export(); 1300 chrome.bookmarks.export();
1284 break; 1301 break;
1285 1302
1286 case 'undo-command': 1303 case 'undo-command':
1287 if (performGlobalUndo) { 1304 if (performGlobalUndo) {
1288 recordUserAction('UndoGlobal'); 1305 recordUserAction('UndoGlobal');
1289 performGlobalUndo(); 1306 performGlobalUndo();
1290 } else { 1307 } else {
1291 recordUserAction('UndoNone'); 1308 recordUserAction('UndoNone');
1309 }
1310 break;
1311
1312 case 'show-in-folder-command':
1313 recordUserAction('ShowInFolder');
1314 showInFolder();
1315 break;
1316
1317 case 'open-in-new-tab-command':
1318 case 'open-in-background-tab-command':
1319 recordUserAction('OpenInNewTab');
1320 openBookmarks(LinkKind.BACKGROUND_TAB, target);
1321 break;
1322
1323 case 'open-in-new-window-command':
1324 recordUserAction('OpenInNewWindow');
1325 openBookmarks(LinkKind.WINDOW, target);
1326 break;
1327
1328 case 'open-incognito-window-command':
1329 recordUserAction('OpenIncognito');
1330 openBookmarks(LinkKind.INCOGNITO, target);
1331 break;
1332
1333 case 'delete-from-folders-menu-command':
1334 target = bmm.tree;
1335 case 'delete-command':
1336 recordUserAction('Delete');
1337 deleteBookmarks(target);
1338 break;
1339
1340 case 'copy-from-folders-menu-command':
1341 target = bmm.tree;
1342 case 'copy-command':
1343 recordUserAction('Copy');
1344 chrome.bookmarkManagerPrivate.copy(
1345 getSelectedBookmarkIds(target), updatePasteCommand);
1346 break;
1347
1348 case 'cut-from-folders-menu-command':
1349 target = bmm.tree;
1350 case 'cut-command':
1351 recordUserAction('Cut');
1352 chrome.bookmarkManagerPrivate.cut(
1353 getSelectedBookmarkIds(target), function() {
1354 updatePasteCommand();
1355 updateSearchResults();
1356 });
1357 break;
1358
1359 case 'paste-from-organize-menu-command':
1360 pasteBookmark(bmm.list.parentId);
1361 break;
1362
1363 case 'paste-from-folders-menu-command':
1364 pasteBookmark(bmm.tree.selectedItem.bookmarkId);
1365 break;
1366
1367 case 'paste-from-context-menu-command':
1368 pasteBookmark(getSelectedId());
1369 break;
1370
1371 case 'sort-command':
1372 recordUserAction('Sort');
1373 chrome.bookmarkManagerPrivate.sortChildren(bmm.list.parentId);
1374 break;
1375
1376
1377 case 'rename-folder-from-folders-menu-command':
1378 target = bmm.tree;
1379 case 'rename-folder-command':
1380 editItem(target);
1381 break;
1382
1383 case 'edit-command':
1384 recordUserAction('Edit');
1385 editItem();
1386 break;
1387
1388 case 'new-folder-from-folders-menu-command':
1389 target = bmm.tree;
1390 case 'new-folder-command':
1391 recordUserAction('NewFolder');
1392 newFolder(target);
1393 break;
1394
1395 case 'add-new-bookmark-command':
1396 recordUserAction('AddPage');
1397 addPage();
1398 break;
1399
1400 case 'open-in-same-window-command':
1401 recordUserAction('OpenInSame');
1402 openItem();
1403 break;
1404
1405 case 'undo-delete-command':
1406 case 'undo-delete-from-folders-menu-command':
1407 recordUserAction('UndoDelete');
1408 undoDelete();
1409 break;
1410 }
1411 }
1412
1413 // Execute the copy, cut and paste commands when those events are dispatched
1414 // by
1415 // the browser. This allows us to rely on the browser to handle the keyboard
1416 // shortcuts for these commands.
1417 function installEventHandlerForCommand(eventName, commandId) {
1418 function handle(e) {
1419 if (document.activeElement != bmm.list &&
1420 document.activeElement != bmm.tree)
1421 return;
1422 var command = $(commandId);
1423 if (!command.disabled) {
1424 command.execute();
1425 if (e)
1426 e.preventDefault(); // Prevent the system beep.
1292 } 1427 }
1293 break; 1428 }
1294 1429 if (eventName == 'paste') {
1295 case 'show-in-folder-command': 1430 // Paste is a bit special since we need to do an async call to see if we
1296 recordUserAction('ShowInFolder'); 1431 // can paste because the paste command might not be up to date.
1297 showInFolder(); 1432 document.addEventListener(eventName, function(e) {
1298 break; 1433 updatePasteCommand(handle);
1299 1434 });
1300 case 'open-in-new-tab-command': 1435 } else {
1301 case 'open-in-background-tab-command': 1436 document.addEventListener(eventName, handle);
1302 recordUserAction('OpenInNewTab'); 1437 }
1303 openBookmarks(LinkKind.BACKGROUND_TAB, target); 1438 }
1304 break; 1439
1305 1440 function initializeSplitter() {
1306 case 'open-in-new-window-command': 1441 var splitter = document.querySelector('.main > .splitter');
1307 recordUserAction('OpenInNewWindow'); 1442 Splitter.decorate(splitter);
1308 openBookmarks(LinkKind.WINDOW, target); 1443
1309 break; 1444 var splitterStyle = splitter.previousElementSibling.style;
1310 1445
1311 case 'open-incognito-window-command': 1446 // The splitter persists the size of the left component in the local store.
1312 recordUserAction('OpenIncognito'); 1447 if ('treeWidth' in window.localStorage)
1313 openBookmarks(LinkKind.INCOGNITO, target); 1448 splitterStyle.width = window.localStorage['treeWidth'];
1314 break; 1449
1315 1450 splitter.addEventListener('resize', function(e) {
1316 case 'delete-from-folders-menu-command': 1451 window.localStorage['treeWidth'] = splitterStyle.width;
1317 target = bmm.tree;
1318 case 'delete-command':
1319 recordUserAction('Delete');
1320 deleteBookmarks(target);
1321 break;
1322
1323 case 'copy-from-folders-menu-command':
1324 target = bmm.tree;
1325 case 'copy-command':
1326 recordUserAction('Copy');
1327 chrome.bookmarkManagerPrivate.copy(getSelectedBookmarkIds(target),
1328 updatePasteCommand);
1329 break;
1330
1331 case 'cut-from-folders-menu-command':
1332 target = bmm.tree;
1333 case 'cut-command':
1334 recordUserAction('Cut');
1335 chrome.bookmarkManagerPrivate.cut(getSelectedBookmarkIds(target),
1336 function() {
1337 updatePasteCommand();
1338 updateSearchResults();
1339 });
1340 break;
1341
1342 case 'paste-from-organize-menu-command':
1343 pasteBookmark(bmm.list.parentId);
1344 break;
1345
1346 case 'paste-from-folders-menu-command':
1347 pasteBookmark(bmm.tree.selectedItem.bookmarkId);
1348 break;
1349
1350 case 'paste-from-context-menu-command':
1351 pasteBookmark(getSelectedId());
1352 break;
1353
1354 case 'sort-command':
1355 recordUserAction('Sort');
1356 chrome.bookmarkManagerPrivate.sortChildren(bmm.list.parentId);
1357 break;
1358
1359
1360 case 'rename-folder-from-folders-menu-command':
1361 target = bmm.tree;
1362 case 'rename-folder-command':
1363 editItem(target);
1364 break;
1365
1366 case 'edit-command':
1367 recordUserAction('Edit');
1368 editItem();
1369 break;
1370
1371 case 'new-folder-from-folders-menu-command':
1372 target = bmm.tree;
1373 case 'new-folder-command':
1374 recordUserAction('NewFolder');
1375 newFolder(target);
1376 break;
1377
1378 case 'add-new-bookmark-command':
1379 recordUserAction('AddPage');
1380 addPage();
1381 break;
1382
1383 case 'open-in-same-window-command':
1384 recordUserAction('OpenInSame');
1385 openItem();
1386 break;
1387
1388 case 'undo-delete-command':
1389 case 'undo-delete-from-folders-menu-command':
1390 recordUserAction('UndoDelete');
1391 undoDelete();
1392 break;
1393 }
1394 }
1395
1396 // Execute the copy, cut and paste commands when those events are dispatched by
1397 // the browser. This allows us to rely on the browser to handle the keyboard
1398 // shortcuts for these commands.
1399 function installEventHandlerForCommand(eventName, commandId) {
1400 function handle(e) {
1401 if (document.activeElement != bmm.list &&
1402 document.activeElement != bmm.tree)
1403 return;
1404 var command = $(commandId);
1405 if (!command.disabled) {
1406 command.execute();
1407 if (e)
1408 e.preventDefault(); // Prevent the system beep.
1409 }
1410 }
1411 if (eventName == 'paste') {
1412 // Paste is a bit special since we need to do an async call to see if we
1413 // can paste because the paste command might not be up to date.
1414 document.addEventListener(eventName, function(e) {
1415 updatePasteCommand(handle);
1416 }); 1452 });
1417 } else { 1453 }
1418 document.addEventListener(eventName, handle); 1454
1419 } 1455 function initializeBookmarkManager() {
1420 } 1456 // Sometimes the extension API is not initialized.
1421 1457 if (!chrome.bookmarks)
1422 function initializeSplitter() { 1458 console.error('Bookmarks extension API is not available');
1423 var splitter = document.querySelector('.main > .splitter'); 1459
1424 Splitter.decorate(splitter); 1460 chrome.bookmarkManagerPrivate.getStrings(continueInitializeBookmarkManager);
1425 1461 }
1426 var splitterStyle = splitter.previousElementSibling.style; 1462
1427 1463 function continueInitializeBookmarkManager(localizedStrings) {
1428 // The splitter persists the size of the left component in the local store. 1464 loadLocalizedStrings(localizedStrings);
1429 if ('treeWidth' in window.localStorage) 1465
1430 splitterStyle.width = window.localStorage['treeWidth']; 1466 bmm.treeLookup[searchTreeItem.bookmarkId] = searchTreeItem;
1431 1467
1432 splitter.addEventListener('resize', function(e) { 1468 cr.ui.decorate('cr-menu', Menu);
1433 window.localStorage['treeWidth'] = splitterStyle.width; 1469 cr.ui.decorate('button[menu]', MenuButton);
1434 }); 1470 cr.ui.decorate('command', Command);
1435 } 1471 BookmarkList.decorate($('list'));
1436 1472 BookmarkTree.decorate($('tree'));
1437 function initializeBookmarkManager() { 1473
1438 // Sometimes the extension API is not initialized. 1474 bmm.list.addEventListener('canceledit', handleCancelEdit);
1439 if (!chrome.bookmarks) 1475 bmm.list.addEventListener('canExecute', handleCanExecuteForList);
1440 console.error('Bookmarks extension API is not available'); 1476 bmm.list.addEventListener('change', updateAllCommands);
1441 1477 bmm.list.addEventListener('contextmenu', updateEditingCommands);
1442 chrome.bookmarkManagerPrivate.getStrings(continueInitializeBookmarkManager); 1478 bmm.list.addEventListener('dblclick', handleDoubleClickForList);
1443 } 1479 bmm.list.addEventListener('edit', handleEdit);
1444 1480 bmm.list.addEventListener('rename', handleRename);
1445 function continueInitializeBookmarkManager(localizedStrings) { 1481 bmm.list.addEventListener('urlClicked', handleUrlClickedForList);
1446 loadLocalizedStrings(localizedStrings); 1482
1447 1483 bmm.tree.addEventListener('canExecute', handleCanExecuteForTree);
1448 bmm.treeLookup[searchTreeItem.bookmarkId] = searchTreeItem; 1484 bmm.tree.addEventListener('change', handleChangeForTree);
1449 1485 bmm.tree.addEventListener('contextmenu', updateEditingCommands);
1450 cr.ui.decorate('cr-menu', Menu); 1486 bmm.tree.addEventListener('rename', handleRename);
1451 cr.ui.decorate('button[menu]', MenuButton); 1487 bmm.tree.addEventListener('load', handleLoadForTree);
1452 cr.ui.decorate('command', Command); 1488
1453 BookmarkList.decorate($('list')); 1489 cr.ui.contextMenuHandler.addContextMenuProperty(
1454 BookmarkTree.decorate($('tree')); 1490 /** @type {!Element} */ (bmm.tree));
1455 1491 bmm.list.contextMenu = $('context-menu');
1456 bmm.list.addEventListener('canceledit', handleCancelEdit); 1492 bmm.tree.contextMenu = $('context-menu');
1457 bmm.list.addEventListener('canExecute', handleCanExecuteForList); 1493
1458 bmm.list.addEventListener('change', updateAllCommands); 1494 // We listen to hashchange so that we can update the currently shown folder
1459 bmm.list.addEventListener('contextmenu', updateEditingCommands); 1495 // when // the user goes back and forward in the history.
1460 bmm.list.addEventListener('dblclick', handleDoubleClickForList); 1496 window.addEventListener('hashchange', processHash);
1461 bmm.list.addEventListener('edit', handleEdit); 1497
1462 bmm.list.addEventListener('rename', handleRename); 1498 document.querySelector('header form').onsubmit =
1463 bmm.list.addEventListener('urlClicked', handleUrlClickedForList); 1499 /** @type {function(Event=)} */ (function(e) {
1464 1500 setSearch($('term').value);
1465 bmm.tree.addEventListener('canExecute', handleCanExecuteForTree); 1501 e.preventDefault();
1466 bmm.tree.addEventListener('change', handleChangeForTree); 1502 });
1467 bmm.tree.addEventListener('contextmenu', updateEditingCommands); 1503
1468 bmm.tree.addEventListener('rename', handleRename); 1504 $('term').addEventListener('search', handleSearch);
1469 bmm.tree.addEventListener('load', handleLoadForTree); 1505 $('term').addEventListener('canExecute', handleCanExecuteForSearchBox);
1470 1506
1471 cr.ui.contextMenuHandler.addContextMenuProperty( 1507 $('folders-button').addEventListener('click', handleMenuButtonClicked);
1472 /** @type {!Element} */(bmm.tree)); 1508 $('organize-button').addEventListener('click', handleMenuButtonClicked);
1473 bmm.list.contextMenu = $('context-menu'); 1509
1474 bmm.tree.contextMenu = $('context-menu'); 1510 document.addEventListener('canExecute', handleCanExecuteForDocument);
1475 1511 document.addEventListener('command', handleCommand);
1476 // We listen to hashchange so that we can update the currently shown folder 1512
1477 // when // the user goes back and forward in the history. 1513 // Listen to copy, cut and paste events and execute the associated commands.
1478 window.addEventListener('hashchange', processHash); 1514 installEventHandlerForCommand('copy', 'copy-command');
1479 1515 installEventHandlerForCommand('cut', 'cut-command');
1480 document.querySelector('header form').onsubmit = 1516 installEventHandlerForCommand('paste', 'paste-from-organize-menu-command');
1481 /** @type {function(Event=)} */(function(e) { 1517
1482 setSearch($('term').value); 1518 // Install shortcuts
1483 e.preventDefault(); 1519 for (var name in commandShortcutMap) {
1484 }); 1520 $(name + '-command').shortcut = commandShortcutMap[name];
1485 1521 }
1486 $('term').addEventListener('search', handleSearch); 1522
1487 $('term').addEventListener('canExecute', handleCanExecuteForSearchBox); 1523 // Disable almost all commands at startup.
1488 1524 var commands = document.querySelectorAll('command');
1489 $('folders-button').addEventListener('click', handleMenuButtonClicked); 1525 for (var i = 0, command; command = commands[i]; ++i) {
1490 $('organize-button').addEventListener('click', handleMenuButtonClicked); 1526 if (command.id != 'import-menu-command' &&
1491 1527 command.id != 'export-menu-command') {
1492 document.addEventListener('canExecute', handleCanExecuteForDocument); 1528 command.disabled = true;
1493 document.addEventListener('command', handleCommand); 1529 }
1494 1530 }
1495 // Listen to copy, cut and paste events and execute the associated commands. 1531
1496 installEventHandlerForCommand('copy', 'copy-command'); 1532 chrome.bookmarkManagerPrivate.canEdit(function(result) {
1497 installEventHandlerForCommand('cut', 'cut-command'); 1533 canEdit = result;
1498 installEventHandlerForCommand('paste', 'paste-from-organize-menu-command'); 1534 });
1499 1535
1500 // Install shortcuts 1536 chrome.systemPrivate.getIncognitoModeAvailability(function(result) {
1501 for (var name in commandShortcutMap) { 1537 // TODO(rustema): propagate policy value to the bookmark manager when it
1502 $(name + '-command').shortcut = commandShortcutMap[name]; 1538 // changes.
1503 } 1539 incognitoModeAvailability = result;
1504 1540 });
1505 // Disable almost all commands at startup. 1541
1506 var commands = document.querySelectorAll('command'); 1542 chrome.bookmarkManagerPrivate.canOpenNewWindows(function(result) {
1507 for (var i = 0, command; command = commands[i]; ++i) { 1543 canOpenNewWindows = result;
1508 if (command.id != 'import-menu-command' && 1544 });
1509 command.id != 'export-menu-command') { 1545
1510 command.disabled = true; 1546 cr.ui.FocusOutlineManager.forDocument(document);
1511 } 1547 initializeSplitter();
1512 } 1548 bmm.addBookmarkModelListeners();
1513 1549 dnd.init(selectItemsAfterUserAction);
1514 chrome.bookmarkManagerPrivate.canEdit(function(result) { 1550 bmm.tree.reload();
1515 canEdit = result; 1551 }
1516 }); 1552
1517 1553 initializeBookmarkManager();
1518 chrome.systemPrivate.getIncognitoModeAvailability(function(result) {
1519 // TODO(rustema): propagate policy value to the bookmark manager when it
1520 // changes.
1521 incognitoModeAvailability = result;
1522 });
1523
1524 chrome.bookmarkManagerPrivate.canOpenNewWindows(function(result) {
1525 canOpenNewWindows = result;
1526 });
1527
1528 cr.ui.FocusOutlineManager.forDocument(document);
1529 initializeSplitter();
1530 bmm.addBookmarkModelListeners();
1531 dnd.init(selectItemsAfterUserAction);
1532 bmm.tree.reload();
1533 }
1534
1535 initializeBookmarkManager();
1536 })(); 1554 })();
OLDNEW
« no previous file with comments | « chrome/browser/resources/bookmark_manager/js/dnd.js ('k') | chrome/browser/resources/cast/cast.js » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698