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

Unified Diff: chrome/browser/resources/history.js

Issue 8511055: Remove old history UI, and replace with history2. (Closed) Base URL: http://git.chromium.org/chromium/src.git@master
Patch Set: Suppress bidichecker test failure. Created 9 years, 1 month 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 side-by-side diff with in-line comments
Download patch
« no previous file with comments | « chrome/browser/resources/history.html ('k') | chrome/browser/resources/history2.html » ('j') | no next file with comments »
Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
Index: chrome/browser/resources/history.js
diff --git a/chrome/browser/resources/history.js b/chrome/browser/resources/history.js
index b37495fe3074fbb4194239ab2e81eb9e9e5ab330..2c70f571f58bd1c48bf27411e1a0344157c69103 100644
--- a/chrome/browser/resources/history.js
+++ b/chrome/browser/resources/history.js
@@ -32,10 +32,30 @@ var historyView;
var localStrings;
var pageState;
var deleteQueue = [];
-var deleteInFlight = false;
var selectionAnchor = -1;
-var id2checkbox = [];
+var activePage = null;
+
+const MenuButton = cr.ui.MenuButton;
+const Command = cr.ui.Command;
+const Menu = cr.ui.Menu;
+
+function createDropDownBgImage(canvasName, colorSpec) {
+ var ctx = document.getCSSCanvasContext('2d', canvasName, 6, 4);
+ ctx.fillStyle = ctx.strokeStyle = colorSpec;
+ ctx.beginPath();
+ ctx.moveTo(0, 0);
+ ctx.lineTo(6, 0);
+ ctx.lineTo(3, 3);
+ ctx.closePath();
+ ctx.fill();
+ ctx.stroke();
+ return ctx;
+}
+// Create the canvases to be used as the drop down button background images.
+var arrow = createDropDownBgImage('drop-down-arrow', 'hsl(214, 91%, 85%)');
+var hoverArrow = createDropDownBgImage('drop-down-arrow-hover', '#6A86DE');
+var activeArrow = createDropDownBgImage('drop-down-arrow-active', 'white');
///////////////////////////////////////////////////////////////////////////////
// Page:
@@ -49,6 +69,7 @@ function Page(result, continued, model, id) {
this.model_ = model;
this.title_ = result.title;
this.url_ = result.url;
+ this.domain_ = this.getDomainFromURL_(this.url_);
this.starred_ = result.starred;
this.snippet_ = result.snippet || "";
this.id_ = id;
@@ -75,63 +96,101 @@ function Page(result, continued, model, id) {
// Page, Public: --------------------------------------------------------------
/**
- * @return {DOMObject} Gets the DOM representation of the page
- * for use in browse results.
+ * Returns a dom structure for a browse page result or a search page result.
+ * @param {boolean} Flag to indicate if result is a search result.
+ * @return {Element} The dom structure.
*/
-Page.prototype.getBrowseResultDOM = function() {
- var node = createElementWithClassName('div', 'entry');
+Page.prototype.getResultDOM = function(searchResultFlag) {
+ var node = createElementWithClassName('li', 'entry');
var time = createElementWithClassName('div', 'time');
- if (this.model_.getEditMode()) {
- var checkbox = document.createElement('input');
- checkbox.type = "checkbox";
- checkbox.name = this.id_;
- checkbox.time = this.time.toString();
- checkbox.addEventListener("click", checkboxClicked, false);
- id2checkbox[this.id_] = checkbox;
- time.appendChild(checkbox);
+ var entryBox = createElementWithClassName('label', 'entry-box');
+ var domain = createElementWithClassName('div', 'domain');
+
+ var dropDown = createElementWithClassName('button', 'drop-down');
+ dropDown.value = 'Open action menu';
+ dropDown.title = localStrings.getString('actionMenuDescription');
+ dropDown.setAttribute('menu', '#action-menu');
+ cr.ui.decorate(dropDown, MenuButton);
+
+ // Checkbox is always created, but only visible on hover & when checked.
+ var checkbox = document.createElement('input');
+ checkbox.type = 'checkbox';
+ checkbox.id = 'checkbox-' + this.id_;
+ checkbox.time = this.time.getTime();
+ checkbox.addEventListener('click', checkboxClicked);
+ time.appendChild(checkbox);
+
+ // Keep track of the drop down that triggered the menu, so we know
+ // which element to apply the command to.
+ // TODO(dubroy): Ideally we'd use 'activate', but MenuButton swallows it.
+ var self = this;
+ var setActivePage = function(e) {
+ activePage = self;
+ };
+ dropDown.addEventListener('mousedown', setActivePage);
+ dropDown.addEventListener('focus', setActivePage);
+
+ domain.style.backgroundImage =
+ 'url(chrome://favicon/' + encodeURIForCSS(this.url_) + ')';
+ domain.textContent = this.domain_;
+
+ // Clicking anywhere in the entryBox will check/uncheck the checkbox.
+ entryBox.setAttribute('for', checkbox.id);
+ entryBox.addEventListener('mousedown', entryBoxMousedown, false);
+
+ // Prevent clicks on the drop down from affecting the checkbox.
+ dropDown.addEventListener('click', function(e) { e.preventDefault(); });
+
+ // We use a wrapper div so that the entry contents will be shinkwrapped.
+ entryBox.appendChild(time);
+ entryBox.appendChild(domain);
+ entryBox.appendChild(this.getTitleDOM_());
+ entryBox.appendChild(dropDown);
+
+ // Let the entryBox be styled appropriately when it contains keyboard focus.
+ entryBox.addEventListener('focus', function() {
+ this.classList.add('contains-focus');
+ }, true);
+ entryBox.addEventListener('blur', function() {
+ this.classList.remove('contains-focus');
+ }, true);
+
+ node.appendChild(entryBox);
+
+ if (searchResultFlag) {
+ time.textContent = this.dateShort;
+ var snippet = createElementWithClassName('div', 'snippet');
+ this.addHighlightedText_(snippet,
+ this.snippet_,
+ this.model_.getSearchText());
+ node.appendChild(snippet);
+ } else {
+ time.appendChild(document.createTextNode(this.dateTimeOfDay));
}
- time.appendChild(document.createTextNode(this.dateTimeOfDay));
- node.appendChild(time);
- node.appendChild(this.getTitleDOM_());
+
+ if (typeof this.domNode_ != 'undefined') {
+ console.error('Already generated node for page.');
+ }
+ this.domNode_ = node;
+
return node;
};
+// Page, private: -------------------------------------------------------------
/**
- * @return {DOMObject} Gets the DOM representation of the page for
- * use in search results.
+ * Extracts and returns the domain (and subdomains) from a URL.
+ * @param {string} The url
+ * @return (string) The domain. An empty string is returned if no domain can
+ * be found.
*/
-Page.prototype.getSearchResultDOM = function() {
- var row = createElementWithClassName('tr', 'entry');
- var datecell = createElementWithClassName('td', 'time');
- datecell.appendChild(document.createTextNode(this.dateShort));
- row.appendChild(datecell);
- if (this.model_.getEditMode()) {
- var checkbox = document.createElement('input');
- checkbox.type = "checkbox";
- checkbox.name = this.id_;
- checkbox.time = this.time.toString();
- checkbox.addEventListener("click", checkboxClicked, false);
- id2checkbox[this.id_] = checkbox;
- datecell.appendChild(checkbox);
- }
-
- var titleCell = document.createElement('td');
- titleCell.valign = 'top';
- titleCell.appendChild(this.getTitleDOM_());
- var snippet = createElementWithClassName('div', 'snippet');
- this.addHighlightedText_(snippet,
- this.snippet_,
- this.model_.getSearchText());
- titleCell.appendChild(snippet);
- row.appendChild(titleCell);
-
- return row;
+Page.prototype.getDomainFromURL_ = function(url) {
+ var domain = url.replace(/^.+:\/\//, '').match(/[^/]+/);
+ return domain ? domain[0] : '';
};
-// Page, private: -------------------------------------------------------------
/**
- * Add child text nodes to a node such that occurrences of the spcified text is
- * highligted.
+ * Add child text nodes to a node such that occurrences of the specified text is
+ * highlighted.
* @param {Node} node The node under which new text nodes will be made as
* children.
* @param {string} content Text to be added beneath |node| as one or more
@@ -163,16 +222,16 @@ Page.prototype.addHighlightedText_ = function(node, content, highlightText) {
* @return {DOMObject} DOM representation for the title block.
*/
Page.prototype.getTitleDOM_ = function() {
- var node = document.createElement('div');
- node.className = 'title';
+ var node = createElementWithClassName('div', 'title');
var link = document.createElement('a');
link.href = this.url_;
-
- link.style.backgroundImage =
- 'url(chrome://favicon/' + encodeURIForCSS(this.url_) + ')';
link.id = "id-" + this.id_;
- this.addHighlightedText_(link, this.title_, this.model_.getSearchText());
+ // Add a tooltip, since it might be ellipsized.
+ // TODO(dubroy): Find a way to show the tooltip only when necessary.
+ link.title = this.title_;
+
+ this.addHighlightedText_(link, this.title_, this.model_.getSearchText());
node.appendChild(link);
if (this.starred_) {
@@ -183,6 +242,26 @@ Page.prototype.getTitleDOM_ = function() {
return node;
};
+/**
+ * Launch a search for more history entries from the same domain.
+ */
+Page.prototype.showMoreFromSite_ = function() {
+ setSearch(this.domain_);
+};
+
+/**
+ * Remove a single entry from the history.
+ */
+Page.prototype.removeFromHistory_ = function() {
+ var self = this;
+ var onSuccessCallback = function() {
+ removeEntryFromView(self.domNode_);
+ };
+ queueURLsForDeletion(this.time, [this.url_], onSuccessCallback);
+ deleteNextInQueue();
+};
+
+
// Page, private, static: -----------------------------------------------------
/**
@@ -208,8 +287,6 @@ Page.pregQuote_ = function(str) {
*/
function HistoryModel() {
this.clearModel_();
- this.setEditMode(false);
- this.view_;
}
// HistoryModel, Public: ------------------------------------------------------
@@ -337,21 +414,6 @@ HistoryModel.prototype.getNumberedRange = function(start, end) {
return this.pages_.slice(start, end);
};
-/**
- * @return {boolean} Whether we are in edit mode where history items can be
- * deleted
- */
-HistoryModel.prototype.getEditMode = function() {
- return this.editMode_;
-};
-
-/**
- * @param {boolean} edit_mode Control whether we are in edit mode.
- */
-HistoryModel.prototype.setEditMode = function(edit_mode) {
- this.editMode_ = edit_mode;
-};
-
// HistoryModel, Private: -----------------------------------------------------
HistoryModel.prototype.clearModel_ = function() {
this.inFlight_ = false; // Whether a query is inflight.
@@ -360,7 +422,6 @@ HistoryModel.prototype.clearModel_ = function() {
this.pages_ = []; // Date-sorted list of pages.
this.last_id_ = 0;
selectionAnchor = -1;
- id2checkbox = [];
// The page that the view wants to see - we only fetch slightly past this
// point. If the view requests a page that we don't have data for, we try
@@ -389,7 +450,7 @@ HistoryModel.prototype.updateSearch_ = function(finished) {
} else {
// If we can't fill the requested page, ask for more data unless a request
// is still in-flight.
- if (!this.inFlight_ && !this.canFillPage_(this.requestedPage_)) {
+ if (!this.canFillPage_(this.requestedPage_) && !this.inFlight_) {
this.getSearchResults_(this.searchDepth_ + 1);
}
@@ -470,12 +531,9 @@ function HistoryView(model) {
window.onresize = function() {
self.updateEntryAnchorWidth_();
};
- this.updateEditControls_();
- this.editButtonTd_.hidden = true;
- this.boundUpdateRemoveButton_ = function(e) {
- return self.updateRemoveButton_(e);
- };
+ $('clear-browsing-data').addEventListener('click', openClearBrowsingData);
+ $('remove-selected').addEventListener('click', removeItems);
}
// HistoryView, public: -------------------------------------------------------
@@ -489,35 +547,7 @@ HistoryView.prototype.setSearch = function(term, opt_page) {
this.pageIndex_ = parseInt(opt_page || 0, 10);
window.scrollTo(0, 0);
this.model_.setSearchText(term, this.pageIndex_);
- this.updateEditControls_();
- pageState.setUIState(this.model_.getEditMode(), term, this.pageIndex_);
-};
-
-/**
- * Controls edit mode where history can be deleted.
- * @param {boolean} edit_mode Whether to enable edit mode.
- */
-HistoryView.prototype.setEditMode = function(edit_mode) {
- this.model_.setEditMode(edit_mode);
- pageState.setUIState(this.model_.getEditMode(), this.model_.getSearchText(),
- this.pageIndex_);
-};
-
-/**
- * Toggles the edit mode and triggers UI update.
- */
-HistoryView.prototype.toggleEditMode = function() {
- var editMode = !this.model_.getEditMode();
- this.setEditMode(editMode);
- this.updateEditControls_();
-};
-
-/**
- * @return {boolean} Whether we are in edit mode where history items can be
- * deleted
- */
-HistoryView.prototype.getEditMode = function() {
- return this.model_.getEditMode();
+ pageState.setUIState(term, this.pageIndex_);
};
/**
@@ -525,6 +555,7 @@ HistoryView.prototype.getEditMode = function() {
*/
HistoryView.prototype.reload = function() {
this.model_.reload();
+ this.updateRemoveButton();
};
/**
@@ -536,8 +567,7 @@ HistoryView.prototype.setPage = function(page) {
this.pageIndex_ = parseInt(page, 10);
window.scrollTo(0, 0);
this.model_.requestPage(page);
- pageState.setUIState(this.model_.getEditMode(), this.model_.getSearchText(),
- this.pageIndex_);
+ pageState.setUIState(this.model_.getSearchText(), this.pageIndex_);
};
/**
@@ -555,6 +585,14 @@ HistoryView.prototype.onModelReady = function() {
this.displayResults_();
};
+/**
+ * Enables or disables the 'Remove selected items' button as appropriate.
+ */
+HistoryView.prototype.updateRemoveButton = function() {
+ var anyChecked = document.querySelector('.entry input:checked') != null;
+ $('remove-selected').disabled = !anyChecked;
+}
+
// HistoryView, private: ------------------------------------------------------
/**
* Clear the results in the view. Since we add results piecemeal, we need
@@ -579,54 +617,62 @@ HistoryView.prototype.setPageRendered_ = function(page) {
* Update the page with results.
*/
HistoryView.prototype.displayResults_ = function() {
- // Hide the Edit Button if there are no history results to display.
- this.editButtonTd_.hidden = !this.model_.getSize();
-
var results = this.model_.getNumberedRange(
this.pageIndex_ * RESULTS_PER_PAGE,
this.pageIndex_ * RESULTS_PER_PAGE + RESULTS_PER_PAGE);
if (this.model_.getSearchText()) {
- var resultTable = createElementWithClassName('table', 'results');
- resultTable.cellSpacing = 0;
- resultTable.cellPadding = 0;
- resultTable.border = 0;
-
+ var searchResults = createElementWithClassName('ol', 'search-results');
for (var i = 0, page; page = results[i]; i++) {
if (!page.isRendered) {
- resultTable.appendChild(page.getSearchResultDOM());
+ searchResults.appendChild(page.getResultDOM(true));
this.setPageRendered_(page);
}
}
- this.resultDiv_.appendChild(resultTable);
+ this.resultDiv_.appendChild(searchResults);
} else {
+ var resultsFragment = document.createDocumentFragment();
var lastTime = Math.infinity;
+ var dayResults;
for (var i = 0, page; page = results[i]; i++) {
if (page.isRendered) {
continue;
}
// Break across day boundaries and insert gaps for browsing pauses.
+ // Create a dayResults element to contain results for each day
var thisTime = page.time.getTime();
if ((i == 0 && page.continued) || !page.continued) {
- var day = createElementWithClassName('div', 'day');
+ var day = createElementWithClassName('h2', 'day');
day.appendChild(document.createTextNode(page.dateRelativeDay));
-
if (i == 0 && page.continued) {
day.appendChild(document.createTextNode(' ' +
localStrings.getString('cont')));
}
- this.resultDiv_.appendChild(day);
+ // If there is an existing dayResults element, append it.
+ if (dayResults) {
+ resultsFragment.appendChild(dayResults);
+ }
+ resultsFragment.appendChild(day);
+ dayResults = createElementWithClassName('ol', 'day-results');
} else if (lastTime - thisTime > BROWSING_GAP_TIME) {
- this.resultDiv_.appendChild(createElementWithClassName('div', 'gap'));
+ if (dayResults) {
+ dayResults.appendChild(createElementWithClassName('li', 'gap'));
+ }
}
lastTime = thisTime;
-
// Add entry.
- this.resultDiv_.appendChild(page.getBrowseResultDOM());
- this.setPageRendered_(page);
+ if (dayResults) {
+ dayResults.appendChild(page.getResultDOM(false));
+ this.setPageRendered_(page);
+ }
}
+ // Add final dayResults element.
+ if (dayResults) {
+ resultsFragment.appendChild(dayResults);
+ }
+ this.resultDiv_.appendChild(resultsFragment);
}
this.displaySummaryBar_();
@@ -648,58 +694,6 @@ HistoryView.prototype.displaySummaryBar_ = function() {
};
/**
- * Update the widgets related to edit mode.
- */
-HistoryView.prototype.updateEditControls_ = function() {
- // Display a button (looking like a link) to enable/disable edit mode.
- var oldButton = this.editButtonTd_.firstChild;
- var editMode = this.model_.getEditMode();
- var button = createElementWithClassName('button', 'edit-button');
- button.onclick = toggleEditMode;
- button.textContent = localStrings.getString(editMode ?
- 'doneediting' : 'edithistory');
- this.editButtonTd_.replaceChild(button, oldButton);
-
- this.editingControlsDiv_.textContent = '';
-
- if (editMode) {
- // Button to delete the selected items.
- button = document.createElement('button');
- button.onclick = removeItems;
- button.textContent = localStrings.getString('removeselected');
- button.disabled = true;
- this.editingControlsDiv_.appendChild(button);
- this.removeButton_ = button;
-
- // Button that opens up the clear browsing data dialog.
- button = document.createElement('button');
- button.onclick = openClearBrowsingData;
- button.textContent = localStrings.getString('clearallhistory');
- this.editingControlsDiv_.appendChild(button);
-
- // Listen for clicks in the page to sync the disabled state.
- document.addEventListener('click', this.boundUpdateRemoveButton_);
- } else {
- this.removeButton_ = null;
- document.removeEventListener('click', this.boundUpdateRemoveButton_);
- }
-};
-
-/**
- * Updates the disabled state of the remove button when in editing mode.
- * @param {!Event} e The click event object.
- * @private
- */
-HistoryView.prototype.updateRemoveButton_ = function(e) {
- if (e.target.tagName != 'INPUT')
- return;
-
- var anyChecked = document.querySelector('.entry input:checked') != null;
- if (this.removeButton_)
- this.removeButton_.disabled = !anyChecked;
-};
-
-/**
* Update the pagination tools.
*/
HistoryView.prototype.displayNavBar_ = function() {
@@ -732,8 +726,7 @@ HistoryView.prototype.createPageNav_ = function(page, name) {
anchor = document.createElement('a');
anchor.className = 'page-navigation';
anchor.textContent = name;
- var hashString = PageState.getHashString(this.model_.getEditMode(),
- this.model_.getSearchText(), page);
+ var hashString = PageState.getHashString(this.model_.getSearchText(), page);
var link = 'chrome://history/' + (hashString ? '#' + hashString : '');
anchor.href = link;
anchor.onclick = function() {
@@ -755,23 +748,24 @@ HistoryView.prototype.updateEntryAnchorWidth_ = function() {
return;
// Create new CSS rules and add them last to the last stylesheet.
- if (!this.entryAnchorRule_) {
- var styleSheets = document.styleSheets;
- var styleSheet = styleSheets[styleSheets.length - 1];
- var rules = styleSheet.cssRules;
- var createRule = function(selector) {
- styleSheet.insertRule(selector + '{}', rules.length);
- return rules[rules.length - 1];
- };
- this.entryAnchorRule_ = createRule('.entry .title > a');
- // The following rule needs to be more specific to have higher priority.
- this.entryAnchorStarredRule_ = createRule('.entry .title.starred > a');
- }
-
- var anchorMaxWith = titleElement.offsetWidth;
- this.entryAnchorRule_.style.maxWidth = anchorMaxWith + 'px';
- // Adjust by the width of star plus its margin.
- this.entryAnchorStarredRule_.style.maxWidth = anchorMaxWith - 23 + 'px';
+ // TODO(jochen): The following code does not work due to WebKit bug #32309
+ // if (!this.entryAnchorRule_) {
+ // var styleSheets = document.styleSheets;
+ // var styleSheet = styleSheets[styleSheets.length - 1];
+ // var rules = styleSheet.cssRules;
+ // var createRule = function(selector) {
+ // styleSheet.insertRule(selector + '{}', rules.length);
+ // return rules[rules.length - 1];
+ // };
+ // this.entryAnchorRule_ = createRule('.entry .title > a');
+ // // The following rule needs to be more specific to have higher priority.
+ // this.entryAnchorStarredRule_ = createRule('.entry .title.starred > a');
+ // }
+ //
+ // var anchorMaxWith = titleElement.offsetWidth;
+ // this.entryAnchorRule_.style.maxWidth = anchorMaxWith + 'px';
+ // // Adjust by the width of star plus its margin.
+ // this.entryAnchorStarredRule_.style.maxWidth = anchorMaxWith - 23 + 'px';
};
///////////////////////////////////////////////////////////////////////////////
@@ -841,13 +835,12 @@ PageState.prototype.getHashData = function() {
* @param {string} term The current search string.
* @param {string} page The page currently being viewed.
*/
-PageState.prototype.setUIState = function(editMode, term, page) {
+PageState.prototype.setUIState = function(term, page) {
// Make sure the form looks pretty.
document.forms[0].term.value = term;
var currentHash = this.getHashData();
- if (Boolean(currentHash.e) != editMode || currentHash.q != term ||
- currentHash.p != page) {
- window.location.hash = PageState.getHashString(editMode, term, page);
+ if (currentHash.q != term || currentHash.p != page) {
+ window.location.hash = PageState.getHashString(term, page);
}
};
@@ -857,11 +850,8 @@ PageState.prototype.setUIState = function(editMode, term, page) {
* @param {string} page The page currently being viewed.
* @return {string} The string to be used in a hash.
*/
-PageState.getHashString = function(editMode, term, page) {
+PageState.getHashString = function(term, page) {
var newHash = [];
- if (editMode) {
- newHash.push('e=1');
- }
if (term) {
newHash.push('q=' + encodeURIComponent(term));
}
@@ -887,12 +877,9 @@ function load() {
// Create default view.
var hashData = pageState.getHashData();
- if (Boolean(hashData.e)) {
- historyView.toggleEditMode();
- }
historyView.setSearch(hashData.q, hashData.p);
- // Add handlers to HTML elements.
+ // Setup click handlers.
$('history-section').onclick = function () {
setSearch('');
return false;
@@ -901,6 +888,15 @@ function load() {
setSearch(this.term.value);
return false;
};
+
+ $('remove-page').addEventListener('activate', function(e) {
+ activePage.removeFromHistory_();
+ activePage = null;
+ });
+ $('more-from-site').addEventListener('activate', function(e) {
+ activePage.showMoreFromSite_();
+ activePage = null;
+ });
}
/**
@@ -926,24 +922,16 @@ function setPage(page) {
}
/**
- * TODO(glen): Get rid of this function.
- * Toggles edit mode.
- */
-function toggleEditMode() {
- if (historyView) {
- historyView.toggleEditMode();
- historyView.reload();
- }
-}
-
-/**
* Delete the next item in our deletion queue.
*/
function deleteNextInQueue() {
- if (!deleteInFlight && deleteQueue.length) {
- deleteInFlight = true;
+ if (deleteQueue.length > 0) {
+ // Call the native function to remove history entries.
+ // First arg is a time in seconds (passed as String) identifying the day.
+ // Remaining args are URLs of history entries from that day to delete.
+ var timeInSeconds = Math.floor(deleteQueue[0].date.getTime() / 1000);
chrome.send('removeURLsOnOneDay',
- [String(deleteQueue[0])].concat(deleteQueue[1]));
+ [String(timeInSeconds)].concat(deleteQueue[0].urls));
}
}
@@ -956,52 +944,68 @@ function openClearBrowsingData() {
}
/**
+ * Queue a set of URLs from the same day for deletion.
+ * @param {Date} date A date indicating the day the URLs were visited.
+ * @param {Array} urls Array of URLs from the same day to be deleted.
+ * @param {Function} opt_callback An optional callback to be executed when
+ * the deletion is complete.
+ */
+function queueURLsForDeletion(date, urls, opt_callback) {
+ deleteQueue.push({ 'date': date, 'urls': urls, 'callback': opt_callback });
+}
+
+function reloadHistory() {
+ historyView.reload();
+}
+
+/**
* Collect IDs from checked checkboxes and send to Chrome for deletion.
*/
function removeItems() {
- var checkboxes = document.getElementsByTagName('input');
- var ids = [];
+ var checked = document.querySelectorAll(
+ 'input[type=checkbox]:checked:not([disabled])');
+ var urls = [];
var disabledItems = [];
var queue = [];
var date = new Date();
- for (var i = 0; i < checkboxes.length; i++) {
- if (checkboxes[i].type == 'checkbox' && checkboxes[i].checked &&
- !checkboxes[i].disabled) {
- var cbDate = new Date(checkboxes[i].time);
- if (date.getFullYear() != cbDate.getFullYear() ||
- date.getMonth() != cbDate.getMonth() ||
- date.getDate() != cbDate.getDate()) {
- if (ids.length > 0) {
- queue.push(date.valueOf() / 1000);
- queue.push(ids);
- }
- ids = [];
- date = cbDate;
+
+ for (var i = 0; i < checked.length; i++) {
+ var checkbox = checked[i];
+ var cbDate = new Date(checkbox.time);
+ if (date.getFullYear() != cbDate.getFullYear() ||
+ date.getMonth() != cbDate.getMonth() ||
+ date.getDate() != cbDate.getDate()) {
+ if (urls.length > 0) {
+ queue.push([date, urls]);
}
- var link = $('id-' + checkboxes[i].name);
- checkboxes[i].disabled = true;
- link.style.textDecoration = 'line-through';
- disabledItems.push(checkboxes[i]);
- ids.push(link.href);
+ urls = [];
+ date = cbDate;
}
+ var link = checkbox.parentNode.parentNode.querySelector('a');
+ checkbox.disabled = true;
+ link.classList.add('to-be-removed');
+ disabledItems.push(checkbox);
+ urls.push(link.href);
}
- if (ids.length > 0) {
- queue.push(date.valueOf() / 1000);
- queue.push(ids);
+ if (urls.length > 0) {
+ queue.push([date, urls]);
}
- if (queue.length > 0) {
- if (confirm(localStrings.getString('deletewarning'))) {
- deleteQueue = deleteQueue.concat(queue);
- deleteNextInQueue();
- historyView.removeButton_.disabled = true;
- } else {
- // If the remove is cancelled, return the checkboxes to their
- // enabled, non-line-through state.
- for (var i = 0; i < disabledItems.length; i++) {
- var link = $('id-' + disabledItems[i].name);
- disabledItems[i].disabled = false;
- link.style.textDecoration = '';
- }
+ if (checked.length > 0 && confirm(localStrings.getString('deletewarning'))) {
+ for (var i = 0; i < queue.length; i++) {
+ // Reload the page when the final entry has been deleted.
+ var callback = i == 0 ? reloadHistory : null;
+
+ queueURLsForDeletion(queue[i][0], queue[i][1], callback);
+ }
+ deleteNextInQueue();
+ } else {
+ // If the remove is cancelled, return the checkboxes to their
+ // enabled, non-line-through state.
+ for (var i = 0; i < disabledItems.length; i++) {
+ var checkbox = disabledItems[i];
+ var link = checkbox.parentNode.parentNode.querySelector('a');
+ checkbox.disabled = false;
+ link.classList.remove('to-be-removed');
}
}
return false;
@@ -1011,18 +1015,65 @@ function removeItems() {
* Toggle state of checkbox and handle Shift modifier.
*/
function checkboxClicked(event) {
+ var id = Number(this.id.slice("checkbox-".length));
if (event.shiftKey && (selectionAnchor != -1)) {
var checked = this.checked;
// Set all checkboxes from the anchor up to the clicked checkbox to the
// state of the clicked one.
- var begin = Math.min(this.name, selectionAnchor);
- var end = Math.max(this.name, selectionAnchor);
+ var begin = Math.min(id, selectionAnchor);
+ var end = Math.max(id, selectionAnchor);
for (var i = begin; i <= end; i++) {
- id2checkbox[i].checked = checked;
+ var checkbox = document.querySelector('#checkbox-' + i);
+ if (checkbox)
+ checkbox.checked = checked;
}
}
- selectionAnchor = this.name;
- this.focus();
+ selectionAnchor = id;
+
+ historyView.updateRemoveButton();
+}
+
+function entryBoxMousedown(event) {
+ // Prevent text selection when shift-clicking to select multiple entries.
+ if (event.shiftKey) {
+ event.preventDefault();
+ }
+}
+
+function removeNode(node) {
+ node.classList.add('fade-out'); // Trigger CSS fade out animation.
+
+ // Delete the node when the animation is complete.
+ node.addEventListener('webkitTransitionEnd', function() {
+ node.parentNode.removeChild(node);
+ });
+}
+
+/**
+ * Removes a single entry from the view. Also removes gaps before and after
+ * entry if necessary.
+ */
+function removeEntryFromView(entry) {
+ var nextEntry = entry.nextSibling;
+ var previousEntry = entry.previousSibling;
+
+ removeNode(entry);
+
+ // if there is no previous entry, and the next entry is a gap, remove it
+ if (!previousEntry && nextEntry && nextEntry.className == 'gap') {
+ removeNode(nextEntry);
+ }
+
+ // if there is no next entry, and the previous entry is a gap, remove it
+ if (!nextEntry && previousEntry && previousEntry.className == 'gap') {
+ removeNode(previousEntry);
+ }
+
+ // if both the next and previous entries are gaps, remove one
+ if (nextEntry && nextEntry.className == 'gap' &&
+ previousEntry && previousEntry.className == 'gap') {
+ removeNode(nextEntry);
+ }
}
///////////////////////////////////////////////////////////////////////////////
@@ -1038,13 +1089,14 @@ function historyResult(info, results) {
* Our history system calls this function when a deletion has finished.
*/
function deleteComplete() {
- window.console.log('Delete complete');
- deleteInFlight = false;
- if (deleteQueue.length > 2) {
- deleteQueue = deleteQueue.slice(2);
+ if (deleteQueue.length > 0) {
+ // Remove the successfully deleted entry from the queue.
+ if (deleteQueue[0].callback)
+ deleteQueue[0].callback.apply();
+ deleteQueue.splice(0, 1);
deleteNextInQueue();
} else {
- deleteQueue = [];
+ console.error('Received deleteComplete but queue is empty.');
}
}
@@ -1054,20 +1106,28 @@ function deleteComplete() {
*/
function deleteFailed() {
window.console.log('Delete failed');
+
// The deletion failed - try again later.
- deleteInFlight = false;
+ // TODO(dubroy): We should probably give up at some point.
setTimeout(deleteNextInQueue, 500);
}
/**
- * We're called when something is deleted (either by us or by someone
- * else).
+ * Called when the history is deleted by someone else.
*/
function historyDeleted() {
window.console.log('History deleted');
var anyChecked = document.querySelector('.entry input:checked') != null;
- if (!(historyView.getEditMode() && anyChecked))
+ // Reload the page, unless the user has any items checked.
+ // TODO(dubroy): We should just reload the page & restore the checked items.
+ if (!anyChecked)
historyView.reload();
}
+// Add handlers to HTML elements.
document.addEventListener('DOMContentLoaded', load);
+
+// This event lets us enable and disable menu items before the menu is shown.
+document.addEventListener('canExecute', function(e) {
+ e.canExecute = true;
+});
« no previous file with comments | « chrome/browser/resources/history.html ('k') | chrome/browser/resources/history2.html » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698