summaryrefslogtreecommitdiffstats
path: root/chrome/browser/resources
diff options
context:
space:
mode:
authorjochen@chromium.org <jochen@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98>2010-03-05 08:15:53 +0000
committerjochen@chromium.org <jochen@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98>2010-03-05 08:15:53 +0000
commit21f4d251210a4408da8a3279510ef7eb44cc1e1a (patch)
treea235ca4b807641d671b244fc78ed9bb64205b4ac /chrome/browser/resources
parent0c86dbf56c6f3e82ee748f34dca48aedf962dec2 (diff)
downloadchromium_src-21f4d251210a4408da8a3279510ef7eb44cc1e1a.zip
chromium_src-21f4d251210a4408da8a3279510ef7eb44cc1e1a.tar.gz
chromium_src-21f4d251210a4408da8a3279510ef7eb44cc1e1a.tar.bz2
Implement edit mode for history page.
BUG=35338 TEST=none Review URL: http://codereview.chromium.org/660283 git-svn-id: svn://svn.chromium.org/chrome/trunk/src@40722 0039d316-1c4b-4281-b951-d872f2087c98
Diffstat (limited to 'chrome/browser/resources')
-rw-r--r--chrome/browser/resources/history.html335
1 files changed, 276 insertions, 59 deletions
diff --git a/chrome/browser/resources/history.html b/chrome/browser/resources/history.html
index f32ef0b..39a23d8 100644
--- a/chrome/browser/resources/history.html
+++ b/chrome/browser/resources/history.html
@@ -37,6 +37,9 @@ var localStrings;
var pageState;
var deleteQueue = [];
var deleteInFlight = false;
+var selectionAnchor = -1;
+var selectionEnd = -1;
+var id2checkbox = [];
///////////////////////////////////////////////////////////////////////////////
@@ -47,12 +50,13 @@ var deleteInFlight = false;
* @param {boolean} continued Whether this page is on the same day as the
* page before it
*/
-function Page(result, continued, model) {
+function Page(result, continued, model, id) {
this.model_ = model;
this.title_ = result.title;
this.url_ = result.url;
this.starred_ = result.starred;
this.snippet_ = result.snippet || "";
+ this.id_ = id;
this.changed = false;
@@ -82,6 +86,15 @@ function Page(result, continued, model) {
Page.prototype.getBrowseResultDOM = function() {
var node = createElementWithClassName('div', '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);
+ }
time.appendChild(document.createTextNode(this.dateTimeOfDay));
node.appendChild(time);
node.appendChild(this.getTitleDOM_());
@@ -131,6 +144,7 @@ Page.prototype.getTitleDOM_ = function() {
link.style.backgroundImage =
'url(chrome://favicon/' + encodeURIForCSS(this.url_) + ')';
link.appendChild(document.createTextNode(this.title_));
+ link.id = "id-" + this.id_;
this.highlightNodeContent_(link);
node.appendChild(link);
@@ -180,6 +194,7 @@ Page.pregQuote_ = function(str) {
*/
function HistoryModel() {
this.clearModel_();
+ this.setEditMode(false);
this.view_;
}
@@ -266,7 +281,8 @@ HistoryModel.prototype.addResults = function(info, results) {
if (!lastURL || lastURL != thisURL) {
// Figure out if this page is in the same day as the previous page,
// this is used to determine how day headers should be drawn.
- this.pages_.push(new Page(thisResult, thisDay == lastDay, this));
+ this.pages_.push(new Page(thisResult, thisDay == lastDay, this,
+ this.last_id_++));
lastDay = thisDay;
lastURL = thisURL;
}
@@ -307,12 +323,31 @@ 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.
this.searchText_ = '';
this.searchDepth_ = 0;
this.pages_ = []; // Date-sorted list of pages.
+ this.last_id_ = 0;
+ selectionAnchor = -1;
+ selectionEnd = -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
@@ -404,8 +439,10 @@ HistoryModel.prototype.canFillPage_ = function(page) {
* @param {HistoryModel} model The model backing this view.
*/
function HistoryView(model) {
- this.summaryDiv_ = $('results-summary');
- this.summaryDiv_.innerHTML = localStrings.getString('loading');
+ this.summaryTd_ = $('results-summary');
+ this.summaryTd_.innerHTML = localStrings.getString('loading');
+ this.editButtonTd_ = $('edit-button');
+ this.editingControlsDiv_ = $('editing-controls');
this.resultDiv_ = $('results-display');
this.pageDiv_ = $('results-pagination');
this.model_ = model
@@ -420,6 +457,7 @@ function HistoryView(model) {
window.onresize = function() {
self.updateEntryAnchorWidth_();
};
+ self.updateEditControls_();
}
// HistoryView, public: -------------------------------------------------------
@@ -433,7 +471,30 @@ HistoryView.prototype.setSearch = function(term, opt_page) {
this.pageIndex_ = parseInt(opt_page || 0, 10);
window.scrollTo(0, 0);
this.model_.setSearchText(term, this.pageIndex_);
- pageState.setUIState(term, this.pageIndex_);
+ if (term) {
+ this.setEditMode(false);
+ }
+ 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_();
}
/**
@@ -452,7 +513,8 @@ HistoryView.prototype.setPage = function(page) {
this.pageIndex_ = parseInt(page, 10);
window.scrollTo(0, 0);
this.model_.requestPage(page);
- pageState.setUIState(this.model_.getSearchText(), this.pageIndex_);
+ pageState.setUIState(this.model_.getEditMode(), this.model_.getSearchText(),
+ this.pageIndex_);
}
/**
@@ -528,13 +590,6 @@ HistoryView.prototype.displayResults_ = function() {
localStrings.getString('cont')));
}
- var link = createElementWithClassName('button', 'delete-day');
- link.time = page.time.toString();
- link.onclick = deleteDay;
- link.appendChild(
- document.createTextNode(localStrings.getString("deleteday")));
-
- day.appendChild(link);
this.resultDiv_.appendChild(day);
} else if (lastTime - thisTime > BROWSING_GAP_TIME) {
this.resultDiv_.appendChild(createElementWithClassName('div', 'gap'));
@@ -558,10 +613,51 @@ HistoryView.prototype.displayResults_ = function() {
HistoryView.prototype.displaySummaryBar_ = function() {
var searchText = this.model_.getSearchText();
if (searchText != '') {
- this.summaryDiv_.textContent = localStrings.formatString('searchresultsfor',
+ this.summaryTd_.textContent = localStrings.formatString('searchresultsfor',
searchText);
} else {
- this.summaryDiv_.innerHTML = localStrings.getString('history');
+ this.summaryTd_.innerHTML = localStrings.getString('history');
+ }
+}
+
+/**
+ * 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;
+ if (this.model_.getSearchText()) {
+ this.editButtonTd_.replaceChild(document.createElement('p'), oldButton);
+ while (this.editingControlsDiv_.hasChildNodes()) {
+ this.editingControlsDiv_.removeChild(this.editingControlsDiv_.firstChild);
+ }
+ return;
+ }
+
+ var editMode = this.model_.getEditMode();
+ var button = createElementWithClassName('button', 'edit-button');
+ button.onclick = toggleEditMode;
+ button.appendChild(document.createTextNode(editMode ?
+ localStrings.getString("doneediting") :
+ localStrings.getString("edithistory")));
+ this.editButtonTd_.replaceChild(button, oldButton);
+ if (editMode) {
+ // Button to delete the selected items.
+ button = document.createElement('button');
+ button.onclick = removeItems;
+ button.appendChild(document.createTextNode(
+ localStrings.getString("removeselected")));
+ this.editingControlsDiv_.appendChild(button);
+ // Button that opens up the clear browsing data dialog.
+ button = document.createElement('button');
+ button.onclick = openClearBrowsingData;
+ button.appendChild(document.createTextNode(
+ localStrings.getString("clearallhistory")));
+ this.editingControlsDiv_.appendChild(button);
+ } else {
+ while (this.editingControlsDiv_.hasChildNodes()) {
+ this.editingControlsDiv_.removeChild(this.editingControlsDiv_.firstChild);
+ }
}
}
@@ -592,7 +688,8 @@ HistoryView.prototype.displayNavBar_ = function() {
* @return {string} HTML representation of the pagination link
*/
HistoryView.prototype.createPageNavHTML_ = function(page, name) {
- var hashString = PageState.getHashString(this.model_.getSearchText(), page);
+ var hashString = PageState.getHashString(this.model_.getEditMode(),
+ this.model_.getSearchText(), page);
return '<a href="chrome://history/' +
(hashString ? '#' + hashString : '') +
'"' +
@@ -613,23 +710,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';
};
///////////////////////////////////////////////////////////////////////////////
@@ -672,6 +770,7 @@ PageState.instance = null;
*/
PageState.prototype.getHashData = function() {
var result = {
+ e : 0,
q : '',
p : 0
};
@@ -698,12 +797,13 @@ PageState.prototype.getHashData = function() {
* @param {string} term The current search string.
* @param {string} page The page currently being viewed.
*/
-PageState.prototype.setUIState = function(term, page) {
+PageState.prototype.setUIState = function(editMode, term, page) {
// Make sure the form looks pretty.
document.forms[0].term.value = term;
var currentHash = this.getHashData();
- if (currentHash.q != term || currentHash.p != page) {
- window.location.hash = PageState.getHashString(term, page);
+ if (Boolean(currentHash.e) != editMode || currentHash.q != term ||
+ currentHash.p != page) {
+ window.location.hash = PageState.getHashString(editMode, term, page);
}
}
@@ -713,8 +813,11 @@ PageState.prototype.setUIState = function(term, page) {
* @param {string} page The page currently being viewed.
* @return {string} The string to be used in a hash.
*/
-PageState.getHashString = function(term, page) {
+PageState.getHashString = function(editMode, term, page) {
var newHash = [];
+ if (editMode) {
+ newHash.push("e=1");
+ }
if (term) {
newHash.push("q=" + encodeURIComponent(term));
}
@@ -740,6 +843,9 @@ function load() {
// Create default view.
var hashData = pageState.getHashData();
+ if (Boolean(hashData.e)) {
+ historyView.toggleEditMode();
+ }
historyView.setSearch(hashData.q, hashData.p);
}
@@ -766,25 +872,14 @@ function setPage(page) {
}
/**
- * Delete a day from history.
- * TODO: Add UI to indicate that something is happening.
- * @param {number} time A time from the day we wish to delete.
+ * TODO(glen): Get rid of this function.
+ * Toggles edit mode.
*/
-function deleteDay() {
- var time = this.time;
-
- // Check to see if item is already being deleted.
- for (var i = 0, deleting; deleting = deleteQueue[i]; i++) {
- if (deleting == time)
- return false;
- }
-
- if (confirm(localStrings.getString("deletedaywarning"))) {
- deleteQueue.push(time);
- deleteNextInQueue();
+function toggleEditMode() {
+ if (historyView) {
+ historyView.toggleEditMode();
+ historyView.reload();
}
-
- return false;
}
/**
@@ -793,7 +888,101 @@ function deleteDay() {
function deleteNextInQueue() {
if (!deleteInFlight && deleteQueue.length) {
deleteInFlight = true;
- chrome.send("deleteDay", [deleteQueue[0]]);
+ chrome.send("removeURLsOnOneDay",
+ [String(deleteQueue[0])].concat(deleteQueue[1]));
+ }
+}
+
+/**
+ * Open the clear browsing data dialog.
+ */
+function openClearBrowsingData() {
+ chrome.send("clearBrowsingData", []);
+ return false;
+}
+
+/**
+ * Collect IDs from checked checkboxes and send to Chrome for deletion.
+ */
+function removeItems() {
+ var checkboxes = document.getElementsByTagName('input');
+ var ids = [];
+ 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;
+ }
+ var link = $("id-" + checkboxes[i].name);
+ checkboxes[i].disabled = true;
+ link.style.setProperty("text-decoration", "line-through");
+ ids.push(link.href);
+ }
+ }
+ if (ids.length > 0) {
+ queue.push(date.valueOf() / 1000);
+ queue.push(ids);
+ }
+ if (queue.length > 0) {
+ if (confirm(localStrings.getString("deletewarning"))) {
+ deleteQueue = deleteQueue.concat(queue);
+ deleteNextInQueue();
+ }
+ }
+ return false;
+}
+
+/**
+ * Toggle state of checkbox and handle CTRL and Shift modifiers
+ */
+function checkboxClicked(event) {
+ if ((selectionAnchor == -1) || !event.shiftKey) {
+ selectionAnchor = this.name;
+ selectionEnd = this.name;
+ }
+ if (event.shiftKey) {
+ if (((selectionAnchor >= selectionEnd) && (selectionAnchor >= this.name)) ||
+ ((selectionAnchor <= selectionEnd) && (selectionAnchor <= this.name))) {
+ // If the click was on the same side of the anchor as the last click,
+ // extend the current selection.
+ var begin = selectionEnd < this.name ? selectionEnd : this.name;
+ var end = selectionEnd < this.name ? this.name : selectionEnd;
+ var checked = true;
+ if (((selectionAnchor <= this.name) && (this.name <= selectionEnd)) ||
+ ((selectionEnd <= this.name) && (this.name <= selectionAnchor))) {
+ checked = false;
+ }
+ for (var i = begin; i <= end; i++) {
+ if (!id2checkbox[i].disabled) {
+ id2checkbox[i].checked = checked;
+ }
+ }
+ this.checked = true;
+ selectionEnd = this.name;
+ } else {
+ // Otherwise, the last clicked checkbox becomes the new anchor, and
+ // everything between the last anchor and the newly clicked checkbox is
+ // enabled.
+ var begin = selectionAnchor < this.name ? selectionAnchor : this.name;
+ var end = selectionAnchor < this.name ? this.name : selectionAnchor;
+ for (var i = begin; i <= end; i++) {
+ if (!id2checkbox[i].disabled) {
+ id2checkbox[i].checked = true;
+ }
+ }
+ selectionAnchor = selectionEnd;
+ selectionEnd = this.name;
+ }
}
}
@@ -813,8 +1002,11 @@ function deleteComplete() {
window.console.log("Delete complete");
deleteInFlight = false;
if (deleteQueue.length > 1) {
- deleteQueue = deleteQueue.slice(1, deleteQueue.length);
+ deleteQueue = deleteQueue.slice(2);
deleteNextInQueue();
+ } else {
+ deleteQueue = [];
+ historyView.reload();
}
}
@@ -840,7 +1032,7 @@ function historyDeleted() {
</script>
<link rel="stylesheet" href="dom_ui.css">
<style>
-#results-summary {
+#results-separator {
margin-top:12px;
border-top:1px solid #9cc2ef;
background-color:#ebeff9;
@@ -848,6 +1040,26 @@ function historyDeleted() {
padding:3px;
margin-bottom:-8px;
}
+#results-separator table {
+ width: 100%;
+}
+#results-summary {
+ overflow: hidden;
+ white-space: nowrap;
+ text-overflow: ellipsis;
+ width: 50%;
+}
+#edit-button {
+ text-align: right;
+ overflow: hidden;
+ white-space: nowrap;
+ text-overflow: ellipsis;
+ width: 50%;
+}
+#editing-controls button {
+ margin-top: 18px;
+ margin-bottom: -8px;
+}
#results-display {
max-width:740px;
}
@@ -856,7 +1068,7 @@ function historyDeleted() {
padding:0px 3px;
display:inline-block;
}
-.delete-day {
+.edit-button {
display: inline;
-webkit-appearance: none;
background: none;
@@ -952,7 +1164,12 @@ html[dir='rtl'] .entry .title > a {
</form>
</div>
<div class="main">
- <div id="results-summary"></div>
+ <div id="results-separator">
+ <table border="0" cellPadding="0" cellSpacing="0">
+ <tr><td id="results-summary"></td><td id="edit-button"><p></p></td></tr>
+ </table>
+ </div>
+ <div id="editing-controls"></div>
<div id="results-display"></div>
<div id="results-pagination"></div>
</div>