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

Side by Side Diff: chrome/browser/resources/md_history/history_list.js

Issue 2684693004: MD History: Remove list-container and list-behavior (Closed)
Patch Set: Rename listeners Created 3 years, 10 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 2015 The Chromium Authors. All rights reserved. 1 // Copyright 2015 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: 'history-list', 6 is: 'history-list',
7 7
8 behaviors: [HistoryListBehavior],
9
10 properties: { 8 properties: {
11 // The search term for the current query. Set when the query returns. 9 // The search term for the current query. Set when the query returns.
12 searchedTerm: { 10 searchedTerm: {
13 type: String, 11 type: String,
14 value: '', 12 value: '',
15 }, 13 },
16 14
17 resultLoadingDisabled_: { 15 resultLoadingDisabled_: {
18 type: Boolean, 16 type: Boolean,
19 value: false, 17 value: false,
20 }, 18 },
21 19
20 /**
21 * Indexes into historyData_ of selected items.
22 * @type {!Set<number>}
23 */
24 selectedItems: {
25 type: Object,
26 value: /** @return {!Set<string>} */ function() {
27 return new Set();
28 },
29 },
30
31 canDeleteHistory_: {
32 type: Boolean,
33 value: loadTimeData.getBoolean('allowDeletingHistory'),
34 },
35
22 // An array of history entries in reverse chronological order. 36 // An array of history entries in reverse chronological order.
23 historyData_: Array, 37 historyData_: Array,
24 38
25 lastFocused_: Object, 39 lastFocused_: Object,
26 40
27 querying: Boolean, 41 lastSelectedIndex: Number,
42
43 /** @type {!QueryState} */
44 queryState: Object,
45
46 /**
47 * @private {?{
48 * index: number,
49 * item: !HistoryEntry,
50 * path: string,
51 * target: !HTMLElement
52 * }}
53 */
54 actionMenuModel_: Object,
28 }, 55 },
29 56
30 listeners: { 57 listeners: {
31 'remove-bookmark-stars': 'removeBookmarkStars_', 58 'history-checkbox-select': 'onItemSelected_',
32 'open-menu': 'onOpenMenu_', 59 'open-menu': 'onOpenMenu_',
60 'remove-bookmark-stars': 'onRemoveBookmarkStars_',
33 }, 61 },
34 62
35 /** @override */ 63 /** @override */
36 attached: function() { 64 attached: function() {
37 // It is possible (eg, when middle clicking the reload button) for all other 65 // It is possible (eg, when middle clicking the reload button) for all other
38 // resize events to fire before the list is attached and can be measured. 66 // resize events to fire before the list is attached and can be measured.
39 // Adding another resize here ensures it will get sized correctly. 67 // Adding another resize here ensures it will get sized correctly.
40 /** @type {IronListElement} */ (this.$['infinite-list']).notifyResize(); 68 /** @type {IronListElement} */ (this.$['infinite-list']).notifyResize();
41 this.$['infinite-list'].scrollTarget = this; 69 this.$['infinite-list'].scrollTarget = this;
42 this.$['scroll-threshold'].scrollTarget = this; 70 this.$['scroll-threshold'].scrollTarget = this;
43 }, 71 },
44 72
73 /////////////////////////////////////////////////////////////////////////////
74 // Public methods:
75
45 /** 76 /**
46 * Remove bookmark star for history items with matching URLs. 77 * @param {HistoryQuery} info An object containing information about the
47 * @param {{detail: !string}} e 78 * query.
48 * @private 79 * @param {!Array<!HistoryEntry>} results A list of results.
49 */ 80 */
50 removeBookmarkStars_: function(e) { 81 historyResult: function(info, results) {
51 var url = e.detail; 82 this.initializeResults_(info, results);
83 this.closeMenu_();
52 84
53 if (this.historyData_ === undefined) 85 if (info.term && !this.queryState.incremental) {
54 return; 86 Polymer.IronA11yAnnouncer.requestAvailability();
87 this.fire('iron-announce', {
88 text:
89 md_history.HistoryItem.searchResultsTitle(results.length, info.term)
90 });
91 }
55 92
56 for (var i = 0; i < this.historyData_.length; i++) { 93 this.addNewResults(results, this.queryState.incremental, info.finished);
57 if (this.historyData_[i].url == url)
58 this.set('historyData_.' + i + '.starred', false);
59 }
60 }, 94 },
61 95
62 /** 96 /**
63 * Adds the newly updated history results into historyData_. Adds new fields 97 * Adds the newly updated history results into historyData_. Adds new fields
64 * for each result. 98 * for each result.
65 * @param {!Array<!HistoryEntry>} historyResults The new history results. 99 * @param {!Array<!HistoryEntry>} historyResults The new history results.
66 * @param {boolean} incremental Whether the result is from loading more 100 * @param {boolean} incremental Whether the result is from loading more
67 * history, or a new search/list reload. 101 * history, or a new search/list reload.
68 * @param {boolean} finished True if there are no more results available and 102 * @param {boolean} finished True if there are no more results available and
69 * result loading should be disabled. 103 * result loading should be disabled.
(...skipping 17 matching lines...) Expand all
87 this.push.apply(this, results); 121 this.push.apply(this, results);
88 } else { 122 } else {
89 // The first time we receive data, use set() to ensure the iron-list is 123 // The first time we receive data, use set() to ensure the iron-list is
90 // initialized correctly. 124 // initialized correctly.
91 this.set('historyData_', results); 125 this.set('historyData_', results);
92 } 126 }
93 127
94 this.resultLoadingDisabled_ = finished; 128 this.resultLoadingDisabled_ = finished;
95 }, 129 },
96 130
131 historyDeleted: function() {
132 // Do not reload the list when there are items checked.
133 if (this.getSelectedItemCount() > 0)
134 return;
135
136 // Reload the list with current search state.
137 this.fire('query-history', false);
138 },
139
140 /**
141 * Deselect each item in |selectedItems|.
142 */
143 unselectAllItems: function() {
144 this.selectedItems.forEach(function(index) {
145 this.changeSelection_(index, false);
146 }.bind(this));
147
148 assert(this.selectedItems.size == 0);
149 },
150
151 /** @return {number} */
152 getSelectedItemCount: function() {
153 return this.selectedItems.size;
154 },
155
156 /**
157 * Delete all the currently selected history items. Will prompt the user with
158 * a dialog to confirm that the deletion should be performed.
159 */
160 deleteSelectedWithPrompt: function() {
161 if (!this.canDeleteHistory_)
162 return;
163
164 var browserService = md_history.BrowserService.getInstance();
165 browserService.recordAction('RemoveSelected');
166 if (this.queryState.searchTerm != '')
167 browserService.recordAction('SearchResultRemove');
168 this.$.dialog.get().showModal();
169
170 // TODO(dbeam): remove focus flicker caused by showModal() + focus().
171 this.$$('.action-button').focus();
172 },
173
174 /////////////////////////////////////////////////////////////////////////////
175 // Private methods:
176
177 /**
178 * Set the selection status for an item at a particular index.
179 * @param {number} index
180 * @param {boolean} selected
181 * @private
182 */
183 changeSelection_: function(index, selected) {
184 this.set('historyData_.' + index + '.selected', selected);
185 if (selected)
186 this.selectedItems.add(index);
187 else
188 this.selectedItems.delete(index);
189 },
190
191 /**
192 * Performs a request to the backend to delete all selected items. If
193 * successful, removes them from the view. Does not prompt the user before
194 * deleting -- see deleteSelectedWithPrompt for a version of this method which
195 * does prompt.
196 * @private
197 */
198 deleteSelected_: function() {
199 var toBeRemoved =
200 Array.from(this.selectedItems.values()).map(function(index) {
201 return this.get('historyData_.' + index);
202 }.bind(this));
203
204 md_history.BrowserService.getInstance()
205 .deleteItems(toBeRemoved)
206 .then(function(items) {
207 this.removeItemsByIndex_(Array.from(this.selectedItems));
208 this.fire('unselect-all');
209 }.bind(this));
210 },
211
212 /**
213 * Remove all |indices| from the history list. Uses notifySplices to send a
214 * single large notification to Polymer, rather than many small notifications,
215 * which greatly improves performance.
216 * @param {!Array<number>} indices
217 * @private
218 */
219 removeItemsByIndex_: function(indices) {
220 var splices = [];
221 indices.sort(function(a, b) {
222 // Sort in reverse numerical order.
223 return b - a;
224 });
225 indices.forEach(function(index) {
226 var item = this.historyData_.splice(index, 1);
227 splices.push({
228 index: index,
229 removed: [item],
230 addedCount: 0,
231 object: this.historyData_,
232 type: 'splice'
233 });
234 }.bind(this));
235 this.notifySplices('historyData_', splices);
236 },
237
238 /**
239 * Closes the overflow menu.
240 * @private
241 */
242 closeMenu_: function() {
243 var menu = this.$.sharedMenu.getIfExists();
244 if (menu && menu.open) {
245 this.actionMenuModel_ = null;
246 menu.close();
247 }
248 },
249
250 /////////////////////////////////////////////////////////////////////////////
251 // Event listeners:
252
253 /** @private */
254 onDialogConfirmTap_: function() {
255 md_history.BrowserService.getInstance().recordAction(
256 'ConfirmRemoveSelected');
257
258 this.deleteSelected_();
259 var dialog = assert(this.$.dialog.getIfExists());
260 dialog.close();
261 },
262
263 /** @private */
264 onDialogCancelTap_: function() {
265 md_history.BrowserService.getInstance().recordAction(
266 'CancelRemoveSelected');
267
268 var dialog = assert(this.$.dialog.getIfExists());
269 dialog.close();
270 },
271
272 /**
273 * Remove bookmark star for history items with matching URLs.
274 * @param {{detail: !string}} e
275 * @private
276 */
277 onRemoveBookmarkStars_: function(e) {
278 var url = e.detail;
279
280 if (this.historyData_ === undefined)
281 return;
282
283 for (var i = 0; i < this.historyData_.length; i++) {
284 if (this.historyData_[i].url == url)
285 this.set('historyData_.' + i + '.starred', false);
286 }
287 },
288
97 /** 289 /**
98 * Called when the page is scrolled to near the bottom of the list. 290 * Called when the page is scrolled to near the bottom of the list.
99 * @private 291 * @private
100 */ 292 */
101 loadMoreData_: function() { 293 onScrollToBottom_: function() {
102 if (this.resultLoadingDisabled_ || this.querying) 294 if (this.resultLoadingDisabled_ || this.queryState.querying)
103 return; 295 return;
104 296
105 this.fire('query-history', true); 297 this.fire('query-history', true);
106 }, 298 },
107 299
108 /** 300 /**
109 * Ensure that the item is visible in the scroll pane when its menu is 301 * Open the overflow menu and ensure that the item is visible in the scroll
110 * opened (it is possible to open off-screen items using keyboard shortcuts). 302 * pane when its menu is opened (it is possible to open off-screen items using
111 * @param {Event} e 303 * keyboard shortcuts).
304 * @param {{detail: {
305 * index: number, item: !HistoryEntry,
306 * path: string, target: !HTMLElement
307 * }}} e
112 * @private 308 * @private
113 */ 309 */
114 onOpenMenu_: function(e) { 310 onOpenMenu_: function(e) {
115 var index = e.detail.index; 311 var index = e.detail.index;
116 var list = /** @type {IronListElement} */ (this.$['infinite-list']); 312 var list = /** @type {IronListElement} */ (this.$['infinite-list']);
117 if (index < list.firstVisibleIndex || index > list.lastVisibleIndex) 313 if (index < list.firstVisibleIndex || index > list.lastVisibleIndex)
118 list.scrollToIndex(index); 314 list.scrollToIndex(index);
315
316 var target = e.detail.target;
317 this.actionMenuModel_ = e.detail;
318 var menu = /** @type {CrSharedMenuElement} */ this.$.sharedMenu.get();
319 menu.showAt(target);
119 }, 320 },
120 321
322 /** @private */
323 onMoreFromSiteTap_: function() {
324 md_history.BrowserService.getInstance().recordAction(
325 'EntryMenuShowMoreFromSite');
326
327 var menu = assert(this.$.sharedMenu.getIfExists());
328 this.fire('change-query', {search: this.actionMenuModel_.item.domain});
329 this.actionMenuModel_ = null;
330 this.closeMenu_();
331 },
332
333 /** @private */
334 onRemoveFromHistoryTap_: function() {
335 var browserService = md_history.BrowserService.getInstance();
336 browserService.recordAction('EntryMenuRemoveFromHistory');
337 var menu = assert(this.$.sharedMenu.getIfExists());
338 var itemData = this.actionMenuModel_;
339 browserService.deleteItems([itemData.item]).then(function(items) {
340 // This unselect-all resets the toolbar when deleting a selected item
341 // and clears selection state which can be invalid if items move
342 // around during deletion.
343 // TODO(tsergeant): Make this automatic based on observing list
344 // modifications.
345 this.fire('unselect-all');
346 this.removeItemsByIndex_([itemData.index]);
347
348 var index = itemData.index;
349 if (index == undefined)
350 return;
351
352 var browserService = md_history.BrowserService.getInstance();
353 browserService.recordHistogram(
354 'HistoryPage.RemoveEntryPosition',
355 Math.min(index, UMA_MAX_BUCKET_VALUE), UMA_MAX_BUCKET_VALUE);
356 if (index <= UMA_MAX_SUBSET_BUCKET_VALUE) {
357 browserService.recordHistogram(
358 'HistoryPage.RemoveEntryPositionSubset', index,
359 UMA_MAX_SUBSET_BUCKET_VALUE);
360 }
361 }.bind(this));
362 this.closeMenu_();
363 },
364
365 /**
366 * @param {Event} e
367 * @private
368 */
369 onItemSelected_: function(e) {
370 var index = e.detail.index;
371 var indices = [];
372
373 // Handle shift selection. Change the selection state of all items between
374 // |path| and |lastSelected| to the selection state of |item|.
375 if (e.detail.shiftKey && this.lastSelectedIndex != undefined) {
376 for (var i = Math.min(index, this.lastSelectedIndex);
377 i <= Math.max(index, this.lastSelectedIndex); i++) {
378 indices.push(i);
379 }
380 }
381
382 if (indices.length == 0)
383 indices.push(index);
384
385 var selected = !this.selectedItems.has(index);
386
387 indices.forEach(function(index) {
388 this.changeSelection_(index, selected);
389 }.bind(this));
390
391 this.lastSelectedIndex = index;
392 },
393
394 /////////////////////////////////////////////////////////////////////////////
395 // Template helpers:
396
121 /** 397 /**
122 * Check whether the time difference between the given history item and the 398 * Check whether the time difference between the given history item and the
123 * next one is large enough for a spacer to be required. 399 * next one is large enough for a spacer to be required.
124 * @param {HistoryEntry} item 400 * @param {HistoryEntry} item
125 * @param {number} index The index of |item| in |historyData_|. 401 * @param {number} index The index of |item| in |historyData_|.
126 * @param {number} length The length of |historyData_|. 402 * @param {number} length The length of |historyData_|.
127 * @return {boolean} Whether or not time gap separator is required. 403 * @return {boolean} Whether or not time gap separator is required.
128 * @private 404 * @private
129 */ 405 */
130 needsTimeGap_: function(item, index, length) { 406 needsTimeGap_: function(item, index, length) {
131 return md_history.HistoryItem.needsTimeGap( 407 if (index >= length - 1 || length == 0)
132 this.historyData_, index, this.searchedTerm); 408 return false;
409
410 var currentItem = this.historyData_[index];
411 var nextItem = this.historyData_[index + 1];
412
413 if (this.searchedTerm)
414 return currentItem.dateShort != nextItem.dateShort;
415
416 return currentItem.time - nextItem.time > BROWSING_GAP_TIME &&
417 currentItem.dateRelativeDay == nextItem.dateRelativeDay;
133 }, 418 },
134 419
135 /** 420 /**
136 * True if the given item is the beginning of a new card. 421 * True if the given item is the beginning of a new card.
137 * @param {HistoryEntry} item 422 * @param {HistoryEntry} item
138 * @param {number} i Index of |item| within |historyData_|. 423 * @param {number} i Index of |item| within |historyData_|.
139 * @param {number} length 424 * @param {number} length
140 * @return {boolean} 425 * @return {boolean}
141 * @private 426 * @private
142 */ 427 */
(...skipping 15 matching lines...) Expand all
158 */ 443 */
159 isCardEnd_: function(item, i, length) { 444 isCardEnd_: function(item, i, length) {
160 if (length == 0 || i > length - 1) 445 if (length == 0 || i > length - 1)
161 return false; 446 return false;
162 return i == length - 1 || 447 return i == length - 1 ||
163 this.historyData_[i].dateRelativeDay != 448 this.historyData_[i].dateRelativeDay !=
164 this.historyData_[i + 1].dateRelativeDay; 449 this.historyData_[i + 1].dateRelativeDay;
165 }, 450 },
166 451
167 /** 452 /**
168 * @param {number} index 453 * @param {number} historyDataLength
454 * @return {boolean}
455 * @private
456 */
457 hasResults_: function(historyDataLength) {
458 return historyDataLength > 0;
459 },
460
461 /**
462 * @param {string} searchedTerm
463 * @param {boolean} isLoading
169 * @return {string} 464 * @return {string}
170 * @private 465 * @private
171 */ 466 */
172 pathForItem_: function(index) { 467 noResultsMessage_: function(searchedTerm, isLoading) {
173 return 'historyData_.' + index; 468 if (isLoading)
469 return '';
470
471 var messageId = searchedTerm !== '' ? 'noSearchResults' : 'noResults';
472 return loadTimeData.getString(messageId);
473 },
474
475 /**
476 * @param {HistoryQuery} info
477 * @param {!Array<HistoryEntry>} results
478 * @private
479 */
480 initializeResults_: function(info, results) {
481 if (results.length == 0)
482 return;
483
484 var currentDate = results[0].dateRelativeDay;
485
486 for (var i = 0; i < results.length; i++) {
487 // Sets the default values for these fields to prevent undefined types.
488 results[i].selected = false;
489 results[i].readableTimestamp =
490 info.term == '' ? results[i].dateTimeOfDay : results[i].dateShort;
491
492 if (results[i].dateRelativeDay != currentDate) {
493 currentDate = results[i].dateRelativeDay;
494 }
495 }
174 }, 496 },
175 }); 497 });
OLDNEW
« no previous file with comments | « chrome/browser/resources/md_history/history_list.html ('k') | chrome/browser/resources/md_history/history_list_behavior.html » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698