OLD | NEW |
---|---|
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 <include src="../uber/uber_utils.js"> | 5 <include src="../uber/uber_utils.js"> |
6 <include src="history_focus_manager.js"> | 6 <include src="history_focus_manager.js"> |
7 | 7 |
8 /////////////////////////////////////////////////////////////////////////////// | 8 /////////////////////////////////////////////////////////////////////////////// |
9 // Globals: | 9 // Globals: |
10 /** @const */ var RESULTS_PER_PAGE = 150; | 10 /** @const */ var RESULTS_PER_PAGE = 150; |
(...skipping 232 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
243 chrome.send('removeBookmark', [self.url_]); | 243 chrome.send('removeBookmark', [self.url_]); |
244 | 244 |
245 this.model_.getView().onBeforeUnstarred(this); | 245 this.model_.getView().onBeforeUnstarred(this); |
246 bookmarkSection.classList.remove('starred'); | 246 bookmarkSection.classList.remove('starred'); |
247 this.model_.getView().onAfterUnstarred(this); | 247 this.model_.getView().onAfterUnstarred(this); |
248 | 248 |
249 bookmarkSection.removeEventListener('click', f); | 249 bookmarkSection.removeEventListener('click', f); |
250 e.preventDefault(); | 250 e.preventDefault(); |
251 }.bind(this)); | 251 }.bind(this)); |
252 } | 252 } |
253 | |
254 if (focusless) | |
255 bookmarkSection.tabIndex = -1; | |
256 | |
253 entryBox.appendChild(bookmarkSection); | 257 entryBox.appendChild(bookmarkSection); |
254 | 258 |
255 var visitEntryWrapper = /** @type {HTMLElement} */( | 259 var visitEntryWrapper = /** @type {HTMLElement} */( |
256 entryBox.appendChild(document.createElement('div'))); | 260 entryBox.appendChild(document.createElement('div'))); |
257 if (addTitleFavicon || this.blockedVisit) | 261 if (addTitleFavicon || this.blockedVisit) |
258 visitEntryWrapper.classList.add('visit-entry'); | 262 visitEntryWrapper.classList.add('visit-entry'); |
259 if (this.blockedVisit) { | 263 if (this.blockedVisit) { |
260 visitEntryWrapper.classList.add('blocked-indicator'); | 264 visitEntryWrapper.classList.add('blocked-indicator'); |
261 visitEntryWrapper.appendChild(this.getVisitAttemptDOM_()); | 265 visitEntryWrapper.appendChild(this.getVisitAttemptDOM_()); |
262 } else { | 266 } else { |
(...skipping 614 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
877 | 881 |
878 /** | 882 /** |
879 * Gets whether we are grouped by domain. | 883 * Gets whether we are grouped by domain. |
880 * @return {boolean} Whether the results are grouped by domain. | 884 * @return {boolean} Whether the results are grouped by domain. |
881 */ | 885 */ |
882 HistoryModel.prototype.getGroupByDomain = function() { | 886 HistoryModel.prototype.getGroupByDomain = function() { |
883 return this.groupByDomain_; | 887 return this.groupByDomain_; |
884 }; | 888 }; |
885 | 889 |
886 /////////////////////////////////////////////////////////////////////////////// | 890 /////////////////////////////////////////////////////////////////////////////// |
887 // HistoryFocusObserver: | 891 // HistoryFocusRow: |
888 | 892 |
889 /** | 893 /** |
894 * Provides an implementation for a single column grid. | |
890 * @constructor | 895 * @constructor |
891 * @implements {cr.ui.FocusRow.Observer} | 896 * @extends {cr.ui.FocusRow} |
892 */ | 897 */ |
893 function HistoryFocusObserver() {} | 898 function HistoryFocusRow() {} |
894 | 899 |
895 HistoryFocusObserver.prototype = { | 900 /** |
901 * Decorates |rowElement| so that it can be treated as a HistoryFocusRow item. | |
902 * @param {Element} rowElement The element representing this row. | |
903 * @param {Node} boundary Focus events are ignored outside of this node. | |
904 */ | |
905 HistoryFocusRow.decorate = function(rowElement, boundary) { | |
906 rowElement.__proto__ = HistoryFocusRow.prototype; | |
907 rowElement.decorate(boundary); | |
908 | |
909 /** | |
910 * Both of these elements are checkboxes and a history focusRow will only | |
911 * have 1 of the two. By giving them both the same type we can ensure | |
912 * that a checkbox remains focused when focus changes. | |
Dan Beam
2015/01/29 18:46:46
use
// comments
instead of
/** comment */
Dan Beam
2015/01/29 18:46:46
i don't think this comment is all that helpful. i
hcarmona
2015/01/31 02:45:49
Done.
hcarmona
2015/01/31 02:45:49
Done.
| |
913 */ | |
914 rowElement.addFocusRow_('.entry-box input', 'checkbox'); | |
915 rowElement.addFocusRow_('.domain-checkbox', 'checkbox'); | |
916 | |
917 // Add the remaining elements. | |
Dan Beam
2015/01/29 18:46:46
not useful
hcarmona
2015/01/31 02:45:49
Done.
| |
918 rowElement.addFocusRow_('.bookmark-section.starred', 'star'); | |
919 rowElement.addFocusRow_('[is="action-link"]', 'domain'); | |
920 rowElement.addFocusRow_('.title a', 'title'); | |
921 rowElement.addFocusRow_('.drop-down', 'menu'); | |
922 }; | |
923 | |
924 HistoryFocusRow.prototype = { | |
925 __proto__: cr.ui.FocusRow.prototype, | |
926 | |
896 /** @override */ | 927 /** @override */ |
897 onActivate: function(row) { | 928 onActiveStateChanged: function(state) { |
898 this.getActiveRowElement_(row).classList.add('active'); | 929 this.classList.toggle('active', state); |
899 }, | 930 }, |
900 | 931 |
901 /** @override */ | 932 /** @override */ |
902 onDeactivate: function(row) { | 933 getEquivalentElement: function(element) { |
903 this.getActiveRowElement_(row).classList.remove('active'); | 934 if (this.contains(element)) |
935 return element; | |
936 | |
937 // All elements default to another element with the same type. | |
938 var ret = this.getColumn_(element.getAttribute('column-type')); | |
Dan Beam
2015/01/29 18:46:46
s/ret/equivalent or el
hcarmona
2015/01/31 02:45:49
Done.
| |
939 | |
940 if (!ret) { | |
941 switch (element.getAttribute('column-type')) { | |
942 case 'star': | |
943 ret = this.getColumn_('title') || this.getColumn_('domain'); | |
944 break; | |
945 case 'domain': | |
946 ret = this.getColumn_('title'); | |
947 break; | |
948 case 'title': | |
949 ret = this.getColumn_('domain'); | |
950 break; | |
951 case 'menu': | |
952 ret = this.focusableElements[this.focusableElements.length - 1]; | |
953 } | |
954 } | |
955 | |
956 return ret || this.focusableElements[0]; | |
904 }, | 957 }, |
905 | 958 |
906 /** | 959 /** |
907 * @param {cr.ui.FocusRow} row The row to find an element for. | 960 * @param {string} type The type of column to return. |
908 * @return {Element} |row|'s "active" element. | 961 * @return {?Element} The column matching the type. |
909 * @private | 962 * @private |
910 */ | 963 */ |
911 getActiveRowElement_: function(row) { | 964 getColumn_: function(type) { |
912 return findAncestorByClass(row.items[0], 'entry') || | 965 return this.querySelector('[column-type=' + type + ']'); |
913 findAncestorByClass(row.items[0], 'site-domain-wrapper'); | 966 }, |
967 | |
968 /** | |
969 * Add a focusable element if it exists in this FocusRow. | |
970 * @param {string} query A query to select the appropriate element. | |
971 * @param {string} type The type to use for the element. | |
972 * @private | |
973 */ | |
974 addFocusRow_: function(query, type) { | |
Dan Beam
2015/01/29 18:46:46
shouldn't this be addFocusItem_ or something? thi
hcarmona
2015/01/31 02:45:49
Done.
| |
975 var element = this.querySelector(query); | |
976 if (element) { | |
977 this.addFocusableElement(element); | |
978 element.setAttribute('column-type', type); | |
979 } | |
914 }, | 980 }, |
915 }; | 981 }; |
916 | 982 |
917 /////////////////////////////////////////////////////////////////////////////// | |
918 // HistoryFocusGrid: | |
919 | |
920 /** | |
921 * @param {Node=} opt_boundary | |
922 * @param {cr.ui.FocusRow.Observer=} opt_observer | |
923 * @constructor | |
924 * @extends {cr.ui.FocusGrid} | |
925 */ | |
926 function HistoryFocusGrid(opt_boundary, opt_observer) { | |
927 cr.ui.FocusGrid.apply(this, arguments); | |
928 } | |
929 | |
930 HistoryFocusGrid.prototype = { | |
931 __proto__: cr.ui.FocusGrid.prototype, | |
932 | |
933 /** @override */ | |
934 onMousedown: function(row, e) { | |
935 // TODO(dbeam): Can cr.ui.FocusGrid know about cr.ui.MenuButton? If so, bake | |
936 // this logic into the base class directly. | |
937 var menuButton = findAncestorByClass(e.target, 'menu-button'); | |
938 if (menuButton) { | |
939 // Deactivate any other active row. | |
940 this.rows.some(function(r) { | |
941 if (r.activeIndex >= 0 && r != row) { | |
942 r.activeIndex = -1; | |
943 return true; | |
944 } | |
945 }); | |
946 // Activate only the row with a pressed menu button. | |
947 row.activeIndex = row.items.indexOf(menuButton); | |
948 } | |
949 return !!menuButton; | |
950 }, | |
951 }; | |
952 | |
953 /////////////////////////////////////////////////////////////////////////////// | 983 /////////////////////////////////////////////////////////////////////////////// |
954 // HistoryView: | 984 // HistoryView: |
955 | 985 |
956 /** | 986 /** |
957 * Functions and state for populating the page with HTML. This should one-day | 987 * Functions and state for populating the page with HTML. This should one-day |
958 * contain the view and use event handlers, rather than pushing HTML out and | 988 * contain the view and use event handlers, rather than pushing HTML out and |
959 * getting called externally. | 989 * getting called externally. |
960 * @param {HistoryModel} model The model backing this view. | 990 * @param {HistoryModel} model The model backing this view. |
961 * @constructor | 991 * @constructor |
962 */ | 992 */ |
963 function HistoryView(model) { | 993 function HistoryView(model) { |
964 this.editButtonTd_ = $('edit-button'); | 994 this.editButtonTd_ = $('edit-button'); |
965 this.editingControlsDiv_ = $('editing-controls'); | 995 this.editingControlsDiv_ = $('editing-controls'); |
966 this.resultDiv_ = $('results-display'); | 996 this.resultDiv_ = $('results-display'); |
967 this.focusGrid_ = new HistoryFocusGrid(this.resultDiv_, | 997 this.focusGrid_ = new cr.ui.FocusGrid(); |
968 new HistoryFocusObserver); | |
969 this.pageDiv_ = $('results-pagination'); | 998 this.pageDiv_ = $('results-pagination'); |
970 this.model_ = model; | 999 this.model_ = model; |
971 this.pageIndex_ = 0; | 1000 this.pageIndex_ = 0; |
972 this.lastDisplayed_ = []; | 1001 this.lastDisplayed_ = []; |
973 | 1002 |
974 this.model_.setView(this); | 1003 this.model_.setView(this); |
975 | 1004 |
976 this.currentVisits_ = []; | 1005 this.currentVisits_ = []; |
977 | 1006 |
978 // If there is no search button, use the search button label as placeholder | 1007 // If there is no search button, use the search button label as placeholder |
(...skipping 211 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
1190 | 1219 |
1191 this.positionNotificationBar(); | 1220 this.positionNotificationBar(); |
1192 }; | 1221 }; |
1193 | 1222 |
1194 /** | 1223 /** |
1195 * @param {Visit} visit The visit about to be removed from this view. | 1224 * @param {Visit} visit The visit about to be removed from this view. |
1196 */ | 1225 */ |
1197 HistoryView.prototype.onBeforeRemove = function(visit) { | 1226 HistoryView.prototype.onBeforeRemove = function(visit) { |
1198 assert(this.currentVisits_.indexOf(visit) >= 0); | 1227 assert(this.currentVisits_.indexOf(visit) >= 0); |
1199 | 1228 |
1200 var pos = this.focusGrid_.getPositionForTarget(document.activeElement); | 1229 var rowIndex = this.focusGrid_.getRowIndexForTarget(document.activeElement); |
1201 if (!pos) | 1230 if (rowIndex == -1) |
1202 return; | 1231 return; |
1203 | 1232 |
1204 var row = this.focusGrid_.rows[pos.row + 1] || | 1233 var rowToFocus = this.focusGrid_.rows[rowIndex + 1] || |
1205 this.focusGrid_.rows[pos.row - 1]; | 1234 this.focusGrid_.rows[rowIndex - 1]; |
1206 if (row) | 1235 if (rowToFocus) |
1207 row.focusIndex(Math.min(pos.col, row.items.length - 1)); | 1236 rowToFocus.getEquivalentElement(document.activeElement).focus(); |
1208 }; | 1237 }; |
1209 | 1238 |
1210 /** @param {Visit} visit The visit about to be unstarred. */ | 1239 /** @param {Visit} visit The visit about to be unstarred. */ |
1211 HistoryView.prototype.onBeforeUnstarred = function(visit) { | 1240 HistoryView.prototype.onBeforeUnstarred = function(visit) { |
1212 assert(this.currentVisits_.indexOf(visit) >= 0); | 1241 assert(this.currentVisits_.indexOf(visit) >= 0); |
1213 assert(visit.bookmarkStar == document.activeElement); | 1242 assert(visit.bookmarkStar == document.activeElement); |
1214 | 1243 |
1215 var pos = this.focusGrid_.getPositionForTarget(document.activeElement); | 1244 var rowIndex = this.focusGrid_.getRowIndexForTarget(document.activeElement); |
1216 var row = this.focusGrid_.rows[pos.row]; | 1245 var row = this.focusGrid_.rows[rowIndex]; |
1217 row.focusIndex(Math.min(pos.col + 1, row.items.length - 1)); | 1246 |
1247 // Focus the title or domain when the bookmarked star is removed because the | |
1248 // star will no longer be focusable. | |
1249 row.querySelector('[column-type=title], [column-type=domain]').focus(); | |
1218 }; | 1250 }; |
1219 | 1251 |
1220 /** @param {Visit} visit The visit that was just unstarred. */ | 1252 /** @param {Visit} visit The visit that was just unstarred. */ |
1221 HistoryView.prototype.onAfterUnstarred = function(visit) { | 1253 HistoryView.prototype.onAfterUnstarred = function(visit) { |
1222 this.updateFocusGrid_(); | 1254 this.updateFocusGrid_(); |
1223 }; | 1255 }; |
1224 | 1256 |
1225 /** | 1257 /** |
1226 * Removes a single entry from the view. Also removes gaps before and after | 1258 * Removes a single entry from the view. Also removes gaps before and after |
1227 * entry if necessary. | 1259 * entry if necessary. |
(...skipping 77 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
1305 } else { | 1337 } else { |
1306 bar.classList.remove('alone'); | 1338 bar.classList.remove('alone'); |
1307 } | 1339 } |
1308 }; | 1340 }; |
1309 | 1341 |
1310 /** | 1342 /** |
1311 * @param {Element} el An element to look for. | 1343 * @param {Element} el An element to look for. |
1312 * @return {boolean} Whether |el| is in |this.focusGrid_|. | 1344 * @return {boolean} Whether |el| is in |this.focusGrid_|. |
1313 */ | 1345 */ |
1314 HistoryView.prototype.isInFocusGrid = function(el) { | 1346 HistoryView.prototype.isInFocusGrid = function(el) { |
1315 return !!this.focusGrid_.getPositionForTarget(el); | 1347 return this.focusGrid_.getRowIndexForTarget(el) != -1; |
1316 }; | 1348 }; |
1317 | 1349 |
1318 // HistoryView, private: ------------------------------------------------------ | 1350 // HistoryView, private: ------------------------------------------------------ |
1319 | 1351 |
1320 /** | 1352 /** |
1321 * Clear the results in the view. Since we add results piecemeal, we need | 1353 * Clear the results in the view. Since we add results piecemeal, we need |
1322 * to clear them out when we switch to a new page or reload. | 1354 * to clear them out when we switch to a new page or reload. |
1323 * @private | 1355 * @private |
1324 */ | 1356 */ |
1325 HistoryView.prototype.clear_ = function() { | 1357 HistoryView.prototype.clear_ = function() { |
(...skipping 341 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
1667 // time column. | 1699 // time column. |
1668 this.setTimeColumnWidth_(); | 1700 this.setTimeColumnWidth_(); |
1669 }; | 1701 }; |
1670 | 1702 |
1671 var focusGridRowSelector = [ | 1703 var focusGridRowSelector = [ |
1672 ':-webkit-any(.day-results, .search-results) > .entry:not(.fade-out)', | 1704 ':-webkit-any(.day-results, .search-results) > .entry:not(.fade-out)', |
1673 '.expand .grouped .entry:not(.fade-out)', | 1705 '.expand .grouped .entry:not(.fade-out)', |
1674 '.site-domain-wrapper' | 1706 '.site-domain-wrapper' |
1675 ].join(', '); | 1707 ].join(', '); |
1676 | 1708 |
1677 var focusGridColumnSelector = [ | |
1678 '.entry-box input', | |
1679 '.bookmark-section.starred', | |
1680 '.title a', | |
1681 '.drop-down', | |
1682 '.domain-checkbox', | |
1683 '[is="action-link"]', | |
1684 ].join(', '); | |
1685 | |
1686 /** @private */ | 1709 /** @private */ |
1687 HistoryView.prototype.updateFocusGrid_ = function() { | 1710 HistoryView.prototype.updateFocusGrid_ = function() { |
1688 var rows = this.resultDiv_.querySelectorAll(focusGridRowSelector); | 1711 var rows = this.resultDiv_.querySelectorAll(focusGridRowSelector); |
1689 var grid = []; | 1712 this.focusGrid_.destroy(); |
1690 | 1713 |
1691 for (var i = 0; i < rows.length; ++i) { | 1714 for (var i = 0; i < rows.length; ++i) { |
1692 assert(rows[i].parentNode); | 1715 assert(rows[i].parentNode); |
1693 grid.push(rows[i].querySelectorAll(focusGridColumnSelector)); | 1716 HistoryFocusRow.decorate(rows[i], this.resultDiv_); |
1717 this.focusGrid_.addRow(rows[i]); | |
1694 } | 1718 } |
1695 | |
1696 this.focusGrid_.setGrid(grid); | |
1697 }; | 1719 }; |
1698 | 1720 |
1699 /** | 1721 /** |
1700 * Update the visibility of the page navigation buttons. | 1722 * Update the visibility of the page navigation buttons. |
1701 * @private | 1723 * @private |
1702 */ | 1724 */ |
1703 HistoryView.prototype.updateNavBar_ = function() { | 1725 HistoryView.prototype.updateNavBar_ = function() { |
1704 this.updateRangeButtons_(); | 1726 this.updateRangeButtons_(); |
1705 | 1727 |
1706 // If grouping by domain is enabled, there's a control bar on top, don't show | 1728 // If grouping by domain is enabled, there's a control bar on top, don't show |
(...skipping 674 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
2381 historyView.reload(); | 2403 historyView.reload(); |
2382 } | 2404 } |
2383 | 2405 |
2384 // Add handlers to HTML elements. | 2406 // Add handlers to HTML elements. |
2385 document.addEventListener('DOMContentLoaded', load); | 2407 document.addEventListener('DOMContentLoaded', load); |
2386 | 2408 |
2387 // This event lets us enable and disable menu items before the menu is shown. | 2409 // This event lets us enable and disable menu items before the menu is shown. |
2388 document.addEventListener('canExecute', function(e) { | 2410 document.addEventListener('canExecute', function(e) { |
2389 e.canExecute = true; | 2411 e.canExecute = true; |
2390 }); | 2412 }); |
OLD | NEW |