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

Side by Side Diff: chrome/browser/resources/md_bookmarks/command_manager.js

Issue 2864993002: MD Bookmarks: Add commands for 'Open in <x>' menu items (Closed)
Patch Set: Closure Created 3 years, 7 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 2017 The Chromium Authors. All rights reserved. 1 // Copyright 2017 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 Polymer({ 5 Polymer({
6 is: 'bookmarks-command-manager', 6 is: 'bookmarks-command-manager',
7 7
8 behaviors: [ 8 behaviors: [
9 bookmarks.StoreClient, 9 bookmarks.StoreClient,
10 ], 10 ],
11 11
12 properties: { 12 properties: {
13 /** @private {!Array<Command>} */
14 menuCommands_: {
15 type: Array,
16 value: function() {
17 return [
18 Command.EDIT,
19 Command.COPY,
20 Command.DELETE,
21 // <hr>
22 Command.OPEN_NEW_TAB,
23 Command.OPEN_NEW_WINDOW,
24 Command.OPEN_INCOGNITO,
25 ];
26 },
27 },
28
13 /** @type {Set<string>} */ 29 /** @type {Set<string>} */
14 menuIds_: Object, 30 menuIds_: Object,
15 }, 31 },
16 32
17 attached: function() { 33 attached: function() {
18 /** @private {function(!Event)} */ 34 /** @private {function(!Event)} */
19 this.boundOnOpenItemMenu_ = this.onOpenItemMenu_.bind(this); 35 this.boundOnOpenItemMenu_ = this.onOpenItemMenu_.bind(this);
20 document.addEventListener('open-item-menu', this.boundOnOpenItemMenu_); 36 document.addEventListener('open-item-menu', this.boundOnOpenItemMenu_);
21 37
22 /** @private {function(!Event)} */ 38 /** @private {function(!Event)} */
23 this.boundOnKeydown_ = this.onKeydown_.bind(this); 39 this.boundOnKeydown_ = this.onKeydown_.bind(this);
24 document.addEventListener('keydown', this.boundOnKeydown_); 40 document.addEventListener('keydown', this.boundOnKeydown_);
25 41
26 /** @private {Object<Command, string>} */ 42 /** @private {Object<Command, string>} */
27 this.shortcuts_ = {}; 43 this.shortcuts_ = {};
28 this.shortcuts_[Command.EDIT] = cr.isMac ? 'enter' : 'f2'; 44 this.shortcuts_[Command.EDIT] = cr.isMac ? 'enter' : 'f2';
29 this.shortcuts_[Command.COPY] = cr.isMac ? 'meta+c' : 'ctrl+c'; 45 this.shortcuts_[Command.COPY] = cr.isMac ? 'meta+c' : 'ctrl+c';
30 this.shortcuts_[Command.DELETE] = cr.isMac ? 'delete backspace' : 'delete'; 46 this.shortcuts_[Command.DELETE] = cr.isMac ? 'delete backspace' : 'delete';
47 this.shortcuts_[Command.OPEN_NEW_TAB] =
48 cr.isMac ? 'meta+enter' : 'ctrl+enter';
49 this.shortcuts_[Command.OPEN_NEW_WINDOW] = 'shift+enter';
31 }, 50 },
32 51
33 detached: function() { 52 detached: function() {
34 document.removeEventListener('open-item-menu', this.boundOnOpenItemMenu_); 53 document.removeEventListener('open-item-menu', this.boundOnOpenItemMenu_);
35 document.removeEventListener('keydown', this.boundOnKeydown_); 54 document.removeEventListener('keydown', this.boundOnKeydown_);
36 }, 55 },
37 56
38 /** 57 /**
39 * Display the command context menu at (|x|, |y|) in window co-ordinates. 58 * Display the command context menu at (|x|, |y|) in window co-ordinates.
40 * Commands will execute on the currently selected items. 59 * Commands will execute on the currently selected items.
(...skipping 17 matching lines...) Expand all
58 }, 77 },
59 78
60 closeCommandMenu: function() { 79 closeCommandMenu: function() {
61 /** @type {!CrActionMenuElement} */ (this.$.dropdown).close(); 80 /** @type {!CrActionMenuElement} */ (this.$.dropdown).close();
62 }, 81 },
63 82
64 //////////////////////////////////////////////////////////////////////////// 83 ////////////////////////////////////////////////////////////////////////////
65 // Command handlers: 84 // Command handlers:
66 85
67 /** 86 /**
87 * Determine if the |command| can be executed with the given |itemIds|.
88 * Commands which appear in the context menu should be implemented separately
89 * using `isCommandVisible_` and `isCommandEnabled_`.
68 * @param {Command} command 90 * @param {Command} command
69 * @param {!Set<string>} itemIds 91 * @param {!Set<string>} itemIds
70 * @return {boolean} 92 * @return {boolean}
71 */ 93 */
72 canExecute: function(command, itemIds) { 94 canExecute: function(command, itemIds) {
95 return this.isCommandVisible_(command, itemIds) &&
96 this.isCommandEnabled_(command, itemIds);
97 },
98
99 /**
100 * @param {Command} command
101 * @param {!Set<string>} itemIds
102 * @return {boolean} True if the command should be visible in the context
103 * menu.
104 */
105 isCommandVisible_: function(command, itemIds) {
73 switch (command) { 106 switch (command) {
74 case Command.EDIT: 107 case Command.EDIT:
75 return itemIds.size == 1; 108 return itemIds.size == 1;
76 case Command.COPY: 109 case Command.COPY:
77 return itemIds.size == 1 && 110 return itemIds.size == 1 &&
78 this.containsMatchingNode_(itemIds, function(node) { 111 this.containsMatchingNode_(itemIds, function(node) {
79 return !!node.url; 112 return !!node.url;
80 }); 113 });
81 case Command.DELETE: 114 case Command.DELETE:
115 case Command.OPEN_NEW_TAB:
116 case Command.OPEN_NEW_WINDOW:
117 case Command.OPEN_INCOGNITO:
82 return itemIds.size > 0; 118 return itemIds.size > 0;
83 default: 119 default:
84 return false; 120 return false;
85 } 121 }
86 }, 122 },
87 123
88 /** 124 /**
89 * @param {Command} command 125 * @param {Command} command
90 * @param {!Set<string>} itemIds 126 * @param {!Set<string>} itemIds
127 * @return {boolean} True if the command should be clickable in the context
128 * menu.
129 */
130 isCommandEnabled_: function(command, itemIds) {
131 switch (command) {
132 case Command.OPEN_NEW_TAB:
133 case Command.OPEN_NEW_WINDOW:
134 case Command.OPEN_INCOGNITO:
135 return this.expandUrls_(itemIds).length > 0;
136 default:
137 return true;
138 }
139 },
140
141 /**
142 * @param {Command} command
143 * @param {!Set<string>} itemIds
91 */ 144 */
92 handle: function(command, itemIds) { 145 handle: function(command, itemIds) {
93 switch (command) { 146 switch (command) {
94 case Command.EDIT: 147 case Command.EDIT:
95 var id = Array.from(itemIds)[0]; 148 var id = Array.from(itemIds)[0];
96 /** @type {!BookmarksEditDialogElement} */ (this.$.editDialog.get()) 149 /** @type {!BookmarksEditDialogElement} */ (this.$.editDialog.get())
97 .showEditDialog(this.getState().nodes[id]); 150 .showEditDialog(this.getState().nodes[id]);
98 break; 151 break;
99 case Command.COPY: 152 case Command.COPY:
100 var idList = Array.from(itemIds); 153 var idList = Array.from(itemIds);
101 chrome.bookmarkManagerPrivate.copy(idList, function() { 154 chrome.bookmarkManagerPrivate.copy(idList, function() {
102 // TODO(jiaxi): Add toast later. 155 // TODO(jiaxi): Add toast later.
103 }); 156 });
104 break; 157 break;
105 case Command.DELETE: 158 case Command.DELETE:
106 chrome.bookmarkManagerPrivate.removeTrees( 159 chrome.bookmarkManagerPrivate.removeTrees(
107 Array.from(this.minimizeDeletionSet_(itemIds)), function() { 160 Array.from(this.minimizeDeletionSet_(itemIds)), function() {
108 // TODO(jiaxi): Add toast later. 161 // TODO(jiaxi): Add toast later.
109 }); 162 });
110 break; 163 break;
164 case Command.OPEN_NEW_TAB:
165 case Command.OPEN_NEW_WINDOW:
166 case Command.OPEN_INCOGNITO:
167 this.openUrls_(this.expandUrls_(itemIds), command);
168 break;
111 } 169 }
112 }, 170 },
113 171
114 //////////////////////////////////////////////////////////////////////////// 172 ////////////////////////////////////////////////////////////////////////////
115 // Private functions: 173 // Private functions:
116 174
117 /** 175 /**
118 * Minimize the set of |itemIds| by removing any node which has an ancestor 176 * Minimize the set of |itemIds| by removing any node which has an ancestor
119 * node already in the set. This ensures that instead of trying to delete both 177 * node already in the set. This ensures that instead of trying to delete both
120 * a node and its descendant, we will only try to delete the topmost node, 178 * a node and its descendant, we will only try to delete the topmost node,
(...skipping 10 matching lines...) Expand all
131 currentId = assert(nodes[currentId].parentId); 189 currentId = assert(nodes[currentId].parentId);
132 if (itemIds.has(currentId)) 190 if (itemIds.has(currentId))
133 return; 191 return;
134 } 192 }
135 minimizedSet.add(itemId); 193 minimizedSet.add(itemId);
136 }); 194 });
137 return minimizedSet; 195 return minimizedSet;
138 }, 196 },
139 197
140 /** 198 /**
199 * @param {!Array<string>} urls
200 * @param {Command} command
201 * @private
202 */
203 openUrls_: function(urls, command) {
204 assert(
205 command == Command.OPEN_NEW_TAB || command == Command.OPEN_NEW_WINDOW ||
206 command == Command.OPEN_INCOGNITO);
207
208 if (urls.length == 0)
209 return;
210
211 var incognito = command == Command.OPEN_INCOGNITO;
212 if (command == Command.OPEN_NEW_WINDOW || incognito) {
213 chrome.windows.create({url: urls, incognito: incognito});
214 } else {
215 urls.forEach(function(url) {
216 chrome.tabs.create({url: url, active: false});
217 });
218 }
219 },
220
221 /**
222 * Returns all URLs in the given set of nodes and their immediate children.
223 * Note that these will be ordered by insertion order into the |itemIds| set.
calamity 2017/05/12 03:22:52 Mention that this assumes itemIds are all at the s
tsergeant 2017/05/12 05:15:27 As discussed, it doesn't assume that, but I've exp
224 * @param {!Set<string>} itemIds
225 * @return {!Array<string>}
226 * @private
227 */
228 expandUrls_: function(itemIds) {
229 var urls = [];
230 var nodes = this.getState().nodes;
231
232 itemIds.forEach(function(id) {
233 var node = nodes[id];
234 if (node.url) {
235 urls.push(node.url);
236 } else {
237 node.children.forEach(function(childId) {
238 var childNode = nodes[childId];
239 if (childNode.url)
240 urls.push(childNode.url);
241 });
242 }
243 });
244
245 return urls;
246 },
247
248 /**
141 * @param {!Set<string>} itemIds 249 * @param {!Set<string>} itemIds
142 * @param {function(BookmarkNode):boolean} predicate 250 * @param {function(BookmarkNode):boolean} predicate
143 * @return {boolean} True if any node in |itemIds| returns true for 251 * @return {boolean} True if any node in |itemIds| returns true for
144 * |predicate|. 252 * |predicate|.
145 */ 253 */
146 containsMatchingNode_: function(itemIds, predicate) { 254 containsMatchingNode_: function(itemIds, predicate) {
147 var nodes = this.getState().nodes; 255 var nodes = this.getState().nodes;
148 256
149 return Array.from(itemIds).some(function(id) { 257 return Array.from(itemIds).some(function(id) {
150 return predicate(nodes[id]); 258 return predicate(nodes[id]);
(...skipping 49 matching lines...) Expand 10 before | Expand all | Expand 10 after
200 * @param {Event} e 308 * @param {Event} e
201 * @private 309 * @private
202 */ 310 */
203 onMenuMousedown_: function(e) { 311 onMenuMousedown_: function(e) {
204 if (e.path[0] != this.$.dropdown) 312 if (e.path[0] != this.$.dropdown)
205 return; 313 return;
206 314
207 this.$.dropdown.close(); 315 this.$.dropdown.close();
208 }, 316 },
209 317
210 /** @private */ 318 /**
211 getEditActionLabel_: function() { 319 * @param {Command} command
212 if (this.menuIds_.size > 1) 320 * @return {string}
213 return; 321 * @private
322 */
323 getCommandLabel_: function(command) {
324 var multipleNodes = this.menuIds_.size > 1 ||
325 this.containsMatchingNode_(this.menuIds_, function(node) {
326 return !node.url;
327 });
328 var label;
329 switch (command) {
330 case Command.EDIT:
331 if (this.menuIds_.size > 1)
332 return '';
214 333
215 var id = Array.from(this.menuIds_)[0]; 334 var id = Array.from(this.menuIds_)[0];
216 var itemUrl = this.getState().nodes[id].url; 335 var itemUrl = this.getState().nodes[id].url;
217 var label = itemUrl ? 'menuEdit' : 'menuRename'; 336 label = itemUrl ? 'menuEdit' : 'menuRename';
218 return loadTimeData.getString(label); 337 break;
338 case Command.COPY:
339 label = 'menuCopyURL';
340 break;
341 case Command.DELETE:
342 label = 'menuDelete';
343 break;
344 case Command.OPEN_NEW_TAB:
345 label = multipleNodes ? 'menuOpenAllNewTab' : 'menuOpenNewTab';
346 break;
347 case Command.OPEN_NEW_WINDOW:
348 label = multipleNodes ? 'menuOpenAllNewWindow' : 'menuOpenNewWindow';
349 break;
350 case Command.OPEN_INCOGNITO:
351 label = multipleNodes ? 'menuOpenAllIncognito' : 'menuOpenIncognito';
352 break;
353 }
354
355 return loadTimeData.getString(assert(label));
356 },
357
358 /**
359 * @param {Command} command
360 * @return {boolean}
361 * @private
362 */
363 showDividerAfter_: function(command) {
364 return command == Command.DELETE;
219 }, 365 },
220 }); 366 });
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698