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

Side by Side Diff: chrome/browser/resources/history2.js

Issue 8079010: Change history2 to allow multi-deletion without entering a separate mode. (Closed) Base URL: svn://svn.chromium.org/chrome/trunk/src
Patch Set: Address arv's comments. Created 9 years, 2 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 | Annotate | Revision Log
OLDNEW
1 // Copyright (c) 2011 The Chromium Authors. All rights reserved. 1 // Copyright (c) 2011 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 /////////////////////////////////////////////////////////////////////////////// 5 ///////////////////////////////////////////////////////////////////////////////
6 // Globals: 6 // Globals:
7 var RESULTS_PER_PAGE = 150; 7 var RESULTS_PER_PAGE = 150;
8 var MAX_SEARCH_DEPTH_MONTHS = 18; 8 var MAX_SEARCH_DEPTH_MONTHS = 18;
9 9
10 // Amount of time between pageviews that we consider a 'break' in browsing, 10 // Amount of time between pageviews that we consider a 'break' in browsing,
(...skipping 16 matching lines...) Expand all
27 27
28 // TODO(glen): Get rid of these global references, replace with a controller 28 // TODO(glen): Get rid of these global references, replace with a controller
29 // or just make the classes own more of the page. 29 // or just make the classes own more of the page.
30 var historyModel; 30 var historyModel;
31 var historyView; 31 var historyView;
32 var localStrings; 32 var localStrings;
33 var pageState; 33 var pageState;
34 var deleteQueue = []; 34 var deleteQueue = [];
35 var selectionAnchor = -1; 35 var selectionAnchor = -1;
36 var activePage = null; 36 var activePage = null;
37 var idToCheckbox = [];
38 37
39 const MenuButton = cr.ui.MenuButton; 38 const MenuButton = cr.ui.MenuButton;
40 const Command = cr.ui.Command; 39 const Command = cr.ui.Command;
41 const Menu = cr.ui.Menu; 40 const Menu = cr.ui.Menu;
42 41
43 function createDropDownBgImage(canvasName, colorSpec) { 42 function createDropDownBgImage(canvasName, colorSpec) {
44 var ctx = document.getCSSCanvasContext('2d', canvasName, 6, 4); 43 var ctx = document.getCSSCanvasContext('2d', canvasName, 6, 4);
45 ctx.fillStyle = ctx.strokeStyle = colorSpec; 44 ctx.fillStyle = ctx.strokeStyle = colorSpec;
46 ctx.beginPath(); 45 ctx.beginPath();
47 ctx.moveTo(0, 0); 46 ctx.moveTo(0, 0);
(...skipping 49 matching lines...) Expand 10 before | Expand all | Expand 10 after
97 96
98 // Page, Public: -------------------------------------------------------------- 97 // Page, Public: --------------------------------------------------------------
99 /** 98 /**
100 * Returns a dom structure for a browse page result or a search page result. 99 * Returns a dom structure for a browse page result or a search page result.
101 * @param {boolean} Flag to indicate if result is a search result. 100 * @param {boolean} Flag to indicate if result is a search result.
102 * @return {Element} The dom structure. 101 * @return {Element} The dom structure.
103 */ 102 */
104 Page.prototype.getResultDOM = function(searchResultFlag) { 103 Page.prototype.getResultDOM = function(searchResultFlag) {
105 var node = createElementWithClassName('li', 'entry'); 104 var node = createElementWithClassName('li', 'entry');
106 var time = createElementWithClassName('div', 'time'); 105 var time = createElementWithClassName('div', 'time');
107 var entryBox = createElementWithClassName('div', 'entry-box'); 106 var entryBox = createElementWithClassName('label', 'entry-box');
108 var domain = createElementWithClassName('div', 'domain'); 107 var domain = createElementWithClassName('div', 'domain');
108
109 var dropDown = createElementWithClassName('button', 'drop-down'); 109 var dropDown = createElementWithClassName('button', 'drop-down');
110 dropDown.value = 'Open action menu'; 110 dropDown.value = 'Open action menu';
111 dropDown.title = localStrings.getString('actionMenuDescription'); 111 dropDown.title = localStrings.getString('actionMenuDescription');
112 dropDown.setAttribute('menu', '#action-menu'); 112 dropDown.setAttribute('menu', '#action-menu');
113 cr.ui.decorate(dropDown, MenuButton); 113 cr.ui.decorate(dropDown, MenuButton);
114 114
115 // Checkbox is always created, but only visible on hover & when checked.
116 var checkbox = document.createElement('input');
117 checkbox.type = 'checkbox';
118 checkbox.id = 'checkbox-' + this.id_;
119 checkbox.time = this.time.getTime();
120 checkbox.addEventListener('click', checkboxClicked);
121 time.appendChild(checkbox);
122
115 // Keep track of the drop down that triggered the menu, so we know 123 // Keep track of the drop down that triggered the menu, so we know
116 // which element to apply the command to. 124 // which element to apply the command to.
117 // TODO(dubroy): Ideally we'd use 'activate', but MenuButton swallows it. 125 // TODO(dubroy): Ideally we'd use 'activate', but MenuButton swallows it.
118 var self = this; 126 var self = this;
119 var setActivePage = function(e) { 127 var setActivePage = function(e) {
120 activePage = self; 128 activePage = self;
121 }; 129 };
122 dropDown.addEventListener('mousedown', setActivePage); 130 dropDown.addEventListener('mousedown', setActivePage);
123 dropDown.addEventListener('focus', setActivePage); 131 dropDown.addEventListener('focus', setActivePage);
124 132
125 domain.style.backgroundImage = 133 domain.style.backgroundImage =
126 'url(chrome://favicon/' + encodeURIForCSS(this.url_) + ')'; 134 'url(chrome://favicon/' + encodeURIForCSS(this.url_) + ')';
127 domain.textContent = this.domain_; 135 domain.textContent = this.domain_;
128 // Use a wrapper div so that the entry contents will be shinkwrapped. 136
137 // Clicking anywhere in the entryBox will check/uncheck the checkbox.
138 entryBox.setAttribute('for', checkbox.id);
139 entryBox.addEventListener('mousedown', entryBoxMousedown, false);
140
141 // Prevent clicks on the drop down from affecting the checkbox.
142 dropDown.addEventListener('click', function(e) { e.preventDefault(); });
143
144 // We use a wrapper div so that the entry contents will be shinkwrapped.
129 entryBox.appendChild(time); 145 entryBox.appendChild(time);
130 entryBox.appendChild(domain); 146 entryBox.appendChild(domain);
131 entryBox.appendChild(this.getTitleDOM_()); 147 entryBox.appendChild(this.getTitleDOM_());
132 entryBox.appendChild(dropDown); 148 entryBox.appendChild(dropDown);
149
150 // Let the entryBox be styled appropriately when it contains keyboard focus.
151 entryBox.addEventListener('focus', function() {
152 this.classList.add('contains-focus');
153 }, true);
154 entryBox.addEventListener('blur', function() {
155 this.classList.remove('contains-focus');
156 }, true);
157
133 node.appendChild(entryBox); 158 node.appendChild(entryBox);
134 159
135 if (searchResultFlag) { 160 if (searchResultFlag) {
136 time.textContent = this.dateShort; 161 time.textContent = this.dateShort;
137 var snippet = createElementWithClassName('div', 'snippet'); 162 var snippet = createElementWithClassName('div', 'snippet');
138 this.addHighlightedText_(snippet, 163 this.addHighlightedText_(snippet,
139 this.snippet_, 164 this.snippet_,
140 this.model_.getSearchText()); 165 this.model_.getSearchText());
141 node.appendChild(snippet); 166 node.appendChild(snippet);
142 } else { 167 } else {
143 if (this.model_.getEditMode()) {
144 var checkbox = document.createElement('input');
145 checkbox.type = 'checkbox';
146 checkbox.name = this.id_;
147 checkbox.time = this.time.toString();
148 checkbox.addEventListener("click", checkboxClicked);
149 idToCheckbox[this.id_] = checkbox;
150 time.appendChild(checkbox);
151 }
152 time.appendChild(document.createTextNode(this.dateTimeOfDay)); 168 time.appendChild(document.createTextNode(this.dateTimeOfDay));
153 } 169 }
154 170
155 if (typeof this.domNode_ != 'undefined') { 171 if (typeof this.domNode_ != 'undefined') {
156 console.error('Already generated node for page.'); 172 console.error('Already generated node for page.');
157 } 173 }
158 this.domNode_ = node; 174 this.domNode_ = node;
159 175
160 return node; 176 return node;
161 }; 177 };
(...skipping 65 matching lines...) Expand 10 before | Expand all | Expand 10 after
227 }; 243 };
228 244
229 /** 245 /**
230 * Launch a search for more history entries from the same domain. 246 * Launch a search for more history entries from the same domain.
231 */ 247 */
232 Page.prototype.showMoreFromSite_ = function() { 248 Page.prototype.showMoreFromSite_ = function() {
233 setSearch(this.domain_); 249 setSearch(this.domain_);
234 }; 250 };
235 251
236 /** 252 /**
237 * Remove the page from the history. 253 * Remove a single entry from the history.
238 */ 254 */
239 Page.prototype.removeFromHistory_ = function() { 255 Page.prototype.removeFromHistory_ = function() {
240 var self = this; 256 var self = this;
241 var onSuccessCallback = function() { 257 var onSuccessCallback = function() {
242 removeEntryFromView(self.domNode_); 258 removeEntryFromView(self.domNode_);
243 }; 259 };
244 queueURLsForDeletion(this.time, [this.url_], onSuccessCallback); 260 queueURLsForDeletion(this.time, [this.url_], onSuccessCallback);
245 deleteNextInQueue(); 261 deleteNextInQueue();
246 }; 262 };
247 263
(...skipping 16 matching lines...) Expand all
264 * allowing the creation of a HistoryModel for each search string, allowing 280 * allowing the creation of a HistoryModel for each search string, allowing
265 * quick flips back and forth between results. 281 * quick flips back and forth between results.
266 * 282 *
267 * The history model is based around pages, and only fetching the data to 283 * The history model is based around pages, and only fetching the data to
268 * fill the currently requested page. This is somewhat dependent on the view, 284 * fill the currently requested page. This is somewhat dependent on the view,
269 * and so future work may wish to change history model to operate on 285 * and so future work may wish to change history model to operate on
270 * timeframe (day or week) based containers. 286 * timeframe (day or week) based containers.
271 */ 287 */
272 function HistoryModel() { 288 function HistoryModel() {
273 this.clearModel_(); 289 this.clearModel_();
274 this.setEditMode(false);
275 this.view_;
276 } 290 }
277 291
278 // HistoryModel, Public: ------------------------------------------------------ 292 // HistoryModel, Public: ------------------------------------------------------
279 /** 293 /**
280 * Sets our current view that is called when the history model changes. 294 * Sets our current view that is called when the history model changes.
281 * @param {HistoryView} view The view to set our current view to. 295 * @param {HistoryView} view The view to set our current view to.
282 */ 296 */
283 HistoryModel.prototype.setView = function(view) { 297 HistoryModel.prototype.setView = function(view) {
284 this.view_ = view; 298 this.view_ = view;
285 }; 299 };
(...skipping 107 matching lines...) Expand 10 before | Expand all | Expand 10 after
393 * @return {Array} A list of pages 407 * @return {Array} A list of pages
394 */ 408 */
395 HistoryModel.prototype.getNumberedRange = function(start, end) { 409 HistoryModel.prototype.getNumberedRange = function(start, end) {
396 if (start >= this.getSize()) 410 if (start >= this.getSize())
397 return []; 411 return [];
398 412
399 var end = end > this.getSize() ? this.getSize() : end; 413 var end = end > this.getSize() ? this.getSize() : end;
400 return this.pages_.slice(start, end); 414 return this.pages_.slice(start, end);
401 }; 415 };
402 416
403 /**
404 * @return {boolean} Whether we are in edit mode where history items can be
405 * deleted
406 */
407 HistoryModel.prototype.getEditMode = function() {
408 return this.editMode_;
409 };
410
411 /**
412 * @param {boolean} edit_mode Control whether we are in edit mode.
413 */
414 HistoryModel.prototype.setEditMode = function(edit_mode) {
415 this.editMode_ = edit_mode;
416 };
417
418 // HistoryModel, Private: ----------------------------------------------------- 417 // HistoryModel, Private: -----------------------------------------------------
419 HistoryModel.prototype.clearModel_ = function() { 418 HistoryModel.prototype.clearModel_ = function() {
420 this.inFlight_ = false; // Whether a query is inflight. 419 this.inFlight_ = false; // Whether a query is inflight.
421 this.searchText_ = ''; 420 this.searchText_ = '';
422 this.searchDepth_ = 0; 421 this.searchDepth_ = 0;
423 this.pages_ = []; // Date-sorted list of pages. 422 this.pages_ = []; // Date-sorted list of pages.
424 this.last_id_ = 0; 423 this.last_id_ = 0;
425 selectionAnchor = -1; 424 selectionAnchor = -1;
426 idToCheckbox = [];
427 425
428 // The page that the view wants to see - we only fetch slightly past this 426 // The page that the view wants to see - we only fetch slightly past this
429 // point. If the view requests a page that we don't have data for, we try 427 // point. If the view requests a page that we don't have data for, we try
430 // to fetch it and call back when we're done. 428 // to fetch it and call back when we're done.
431 this.requestedPage_ = 0; 429 this.requestedPage_ = 0;
432 430
433 this.complete_ = false; 431 this.complete_ = false;
434 432
435 if (this.view_) { 433 if (this.view_) {
436 this.view_.clear_(); 434 this.view_.clear_();
(...skipping 89 matching lines...) Expand 10 before | Expand all | Expand 10 after
526 this.lastDisplayed_ = []; 524 this.lastDisplayed_ = [];
527 525
528 this.model_.setView(this); 526 this.model_.setView(this);
529 527
530 this.currentPages_ = []; 528 this.currentPages_ = [];
531 529
532 var self = this; 530 var self = this;
533 window.onresize = function() { 531 window.onresize = function() {
534 self.updateEntryAnchorWidth_(); 532 self.updateEntryAnchorWidth_();
535 }; 533 };
536 self.updateEditControls_();
537 534
538 this.boundUpdateRemoveButton_ = function(e) { 535 $('clear-browsing-data').addEventListener('click', openClearBrowsingData);
539 return self.updateRemoveButton_(e); 536 $('remove-selected').addEventListener('click', removeItems);
540 };
541 } 537 }
542 538
543 // HistoryView, public: ------------------------------------------------------- 539 // HistoryView, public: -------------------------------------------------------
544 /** 540 /**
545 * Do a search and optionally view a certain page. 541 * Do a search and optionally view a certain page.
546 * @param {string} term The string to search for. 542 * @param {string} term The string to search for.
547 * @param {number} opt_page The page we wish to view, only use this for 543 * @param {number} opt_page The page we wish to view, only use this for
548 * setting up initial views, as this triggers a search. 544 * setting up initial views, as this triggers a search.
549 */ 545 */
550 HistoryView.prototype.setSearch = function(term, opt_page) { 546 HistoryView.prototype.setSearch = function(term, opt_page) {
551 this.pageIndex_ = parseInt(opt_page || 0, 10); 547 this.pageIndex_ = parseInt(opt_page || 0, 10);
552 window.scrollTo(0, 0); 548 window.scrollTo(0, 0);
553 this.model_.setSearchText(term, this.pageIndex_); 549 this.model_.setSearchText(term, this.pageIndex_);
554 if (term) { 550 pageState.setUIState(term, this.pageIndex_);
555 this.setEditMode(false);
556 }
557 this.updateEditControls_();
558 pageState.setUIState(this.model_.getEditMode(), term, this.pageIndex_);
559 }; 551 };
560 552
561 /** 553 /**
562 * Controls edit mode where history can be deleted.
563 * @param {boolean} edit_mode Whether to enable edit mode.
564 */
565 HistoryView.prototype.setEditMode = function(edit_mode) {
566 this.model_.setEditMode(edit_mode);
567 pageState.setUIState(this.model_.getEditMode(), this.model_.getSearchText(),
568 this.pageIndex_);
569 };
570
571 /**
572 * Toggles the edit mode and triggers UI update.
573 */
574 HistoryView.prototype.toggleEditMode = function() {
575 var editMode = !this.model_.getEditMode();
576 this.setEditMode(editMode);
577 this.updateEditControls_();
578 };
579
580 /**
581 * @return {boolean} Whether we are in edit mode where history items can be
582 * deleted
583 */
584 HistoryView.prototype.getEditMode = function() {
585 return this.model_.getEditMode();
586 };
587
588 /**
589 * Reload the current view. 554 * Reload the current view.
590 */ 555 */
591 HistoryView.prototype.reload = function() { 556 HistoryView.prototype.reload = function() {
592 this.model_.reload(); 557 this.model_.reload();
558 this.updateRemoveButton();
593 }; 559 };
594 560
595 /** 561 /**
596 * Switch to a specified page. 562 * Switch to a specified page.
597 * @param {number} page The page we wish to view. 563 * @param {number} page The page we wish to view.
598 */ 564 */
599 HistoryView.prototype.setPage = function(page) { 565 HistoryView.prototype.setPage = function(page) {
600 this.clear_(); 566 this.clear_();
601 this.pageIndex_ = parseInt(page, 10); 567 this.pageIndex_ = parseInt(page, 10);
602 window.scrollTo(0, 0); 568 window.scrollTo(0, 0);
603 this.model_.requestPage(page); 569 this.model_.requestPage(page);
604 pageState.setUIState(this.model_.getEditMode(), this.model_.getSearchText(), 570 pageState.setUIState(this.model_.getSearchText(), this.pageIndex_);
605 this.pageIndex_);
606 }; 571 };
607 572
608 /** 573 /**
609 * @return {number} The page number being viewed. 574 * @return {number} The page number being viewed.
610 */ 575 */
611 HistoryView.prototype.getPage = function() { 576 HistoryView.prototype.getPage = function() {
612 return this.pageIndex_; 577 return this.pageIndex_;
613 }; 578 };
614 579
615 /** 580 /**
616 * Callback for the history model to let it know that it has data ready for us 581 * Callback for the history model to let it know that it has data ready for us
617 * to view. 582 * to view.
618 */ 583 */
619 HistoryView.prototype.onModelReady = function() { 584 HistoryView.prototype.onModelReady = function() {
620 this.displayResults_(); 585 this.displayResults_();
621 }; 586 };
622 587
588 /**
589 * Enables or disables the 'Remove selected items' button as appropriate.
590 */
591 HistoryView.prototype.updateRemoveButton = function() {
592 var anyChecked = document.querySelector('.entry input:checked') != null;
593 $('remove-selected').disabled = !anyChecked;
594 }
595
623 // HistoryView, private: ------------------------------------------------------ 596 // HistoryView, private: ------------------------------------------------------
624 /** 597 /**
625 * Clear the results in the view. Since we add results piecemeal, we need 598 * Clear the results in the view. Since we add results piecemeal, we need
626 * to clear them out when we switch to a new page or reload. 599 * to clear them out when we switch to a new page or reload.
627 */ 600 */
628 HistoryView.prototype.clear_ = function() { 601 HistoryView.prototype.clear_ = function() {
629 this.resultDiv_.textContent = ''; 602 this.resultDiv_.textContent = '';
630 603
631 var pages = this.currentPages_; 604 var pages = this.currentPages_;
632 for (var i = 0; i < pages.length; i++) { 605 for (var i = 0; i < pages.length; i++) {
(...skipping 81 matching lines...) Expand 10 before | Expand all | Expand 10 after
714 var searchText = this.model_.getSearchText(); 687 var searchText = this.model_.getSearchText();
715 if (searchText != '') { 688 if (searchText != '') {
716 this.summaryTd_.textContent = localStrings.getStringF('searchresultsfor', 689 this.summaryTd_.textContent = localStrings.getStringF('searchresultsfor',
717 searchText); 690 searchText);
718 } else { 691 } else {
719 this.summaryTd_.textContent = localStrings.getString('history'); 692 this.summaryTd_.textContent = localStrings.getString('history');
720 } 693 }
721 }; 694 };
722 695
723 /** 696 /**
724 * Update the widgets related to edit mode.
725 */
726 HistoryView.prototype.updateEditControls_ = function() {
727 // Display a button (looking like a link) to enable/disable edit mode.
728 var oldButton = this.editButtonTd_.firstChild;
729 if (this.model_.getSearchText()) {
730 this.editButtonTd_.replaceChild(document.createElement('p'), oldButton);
731 this.editingControlsDiv_.textContent = '';
732 return;
733 }
734
735 var editMode = this.model_.getEditMode();
736 var button = createElementWithClassName('button', 'edit-button');
737 button.onclick = toggleEditMode;
738 button.textContent = localStrings.getString(editMode ?
739 'doneediting' : 'edithistory');
740 this.editButtonTd_.replaceChild(button, oldButton);
741
742 this.editingControlsDiv_.textContent = '';
743
744 if (editMode) {
745 // Button to delete the selected items.
746 button = document.createElement('button');
747 button.onclick = removeItems;
748 button.textContent = localStrings.getString('removeselected');
749 button.disabled = true;
750 this.editingControlsDiv_.appendChild(button);
751 this.removeButton_ = button;
752
753 // Button that opens up the clear browsing data dialog.
754 button = document.createElement('button');
755 button.onclick = openClearBrowsingData;
756 button.textContent = localStrings.getString('clearallhistory');
757 this.editingControlsDiv_.appendChild(button);
758
759 // Listen for clicks in the page to sync the disabled state.
760 document.addEventListener('click', this.boundUpdateRemoveButton_);
761 } else {
762 this.removeButton_ = null;
763 document.removeEventListener('click', this.boundUpdateRemoveButton_);
764 }
765 };
766
767 /**
768 * Updates the disabled state of the remove button when in editing mode.
769 * @param {!Event} e The click event object.
770 * @private
771 */
772 HistoryView.prototype.updateRemoveButton_ = function(e) {
773 if (e.target.tagName != 'INPUT')
774 return;
775
776 var anyChecked = document.querySelector('.entry input:checked') != null;
777 if (this.removeButton_)
778 this.removeButton_.disabled = !anyChecked;
779 };
780
781 /**
782 * Update the pagination tools. 697 * Update the pagination tools.
783 */ 698 */
784 HistoryView.prototype.displayNavBar_ = function() { 699 HistoryView.prototype.displayNavBar_ = function() {
785 this.pageDiv_.textContent = ''; 700 this.pageDiv_.textContent = '';
786 701
787 if (this.pageIndex_ > 0) { 702 if (this.pageIndex_ > 0) {
788 this.pageDiv_.appendChild( 703 this.pageDiv_.appendChild(
789 this.createPageNav_(0, localStrings.getString('newest'))); 704 this.createPageNav_(0, localStrings.getString('newest')));
790 this.pageDiv_.appendChild( 705 this.pageDiv_.appendChild(
791 this.createPageNav_(this.pageIndex_ - 1, 706 this.createPageNav_(this.pageIndex_ - 1,
(...skipping 12 matching lines...) Expand all
804 /** 719 /**
805 * Make a DOM object representation of a page navigation link. 720 * Make a DOM object representation of a page navigation link.
806 * @param {number} page The page index the navigation element should link to 721 * @param {number} page The page index the navigation element should link to
807 * @param {string} name The text content of the link 722 * @param {string} name The text content of the link
808 * @return {HTMLAnchorElement} the pagination link 723 * @return {HTMLAnchorElement} the pagination link
809 */ 724 */
810 HistoryView.prototype.createPageNav_ = function(page, name) { 725 HistoryView.prototype.createPageNav_ = function(page, name) {
811 anchor = document.createElement('a'); 726 anchor = document.createElement('a');
812 anchor.className = 'page-navigation'; 727 anchor.className = 'page-navigation';
813 anchor.textContent = name; 728 anchor.textContent = name;
814 var hashString = PageState.getHashString(this.model_.getEditMode(), 729 var hashString = PageState.getHashString(this.model_.getSearchText(), page);
815 this.model_.getSearchText(), page);
816 var link = 'chrome://history2/' + (hashString ? '#' + hashString : ''); 730 var link = 'chrome://history2/' + (hashString ? '#' + hashString : '');
817 anchor.href = link; 731 anchor.href = link;
818 anchor.onclick = function() { 732 anchor.onclick = function() {
819 setPage(page); 733 setPage(page);
820 return false; 734 return false;
821 }; 735 };
822 return anchor; 736 return anchor;
823 }; 737 };
824 738
825 /** 739 /**
(...skipping 88 matching lines...) Expand 10 before | Expand all | Expand 10 after
914 return result; 828 return result;
915 }; 829 };
916 830
917 /** 831 /**
918 * Set the hash to a specified state, this will create an entry in the 832 * Set the hash to a specified state, this will create an entry in the
919 * session history so the back button cycles through hash states, which 833 * session history so the back button cycles through hash states, which
920 * are then picked up by our listener. 834 * are then picked up by our listener.
921 * @param {string} term The current search string. 835 * @param {string} term The current search string.
922 * @param {string} page The page currently being viewed. 836 * @param {string} page The page currently being viewed.
923 */ 837 */
924 PageState.prototype.setUIState = function(editMode, term, page) { 838 PageState.prototype.setUIState = function(term, page) {
925 // Make sure the form looks pretty. 839 // Make sure the form looks pretty.
926 document.forms[0].term.value = term; 840 document.forms[0].term.value = term;
927 var currentHash = this.getHashData(); 841 var currentHash = this.getHashData();
928 if (Boolean(currentHash.e) != editMode || currentHash.q != term || 842 if (currentHash.q != term || currentHash.p != page) {
929 currentHash.p != page) { 843 window.location.hash = PageState.getHashString(term, page);
930 window.location.hash = PageState.getHashString(editMode, term, page);
931 } 844 }
932 }; 845 };
933 846
934 /** 847 /**
935 * Static method to get the hash string for a specified state 848 * Static method to get the hash string for a specified state
936 * @param {string} term The current search string. 849 * @param {string} term The current search string.
937 * @param {string} page The page currently being viewed. 850 * @param {string} page The page currently being viewed.
938 * @return {string} The string to be used in a hash. 851 * @return {string} The string to be used in a hash.
939 */ 852 */
940 PageState.getHashString = function(editMode, term, page) { 853 PageState.getHashString = function(term, page) {
941 var newHash = []; 854 var newHash = [];
942 if (editMode) {
943 newHash.push('e=1');
944 }
945 if (term) { 855 if (term) {
946 newHash.push('q=' + encodeURIComponent(term)); 856 newHash.push('q=' + encodeURIComponent(term));
947 } 857 }
948 if (page != undefined) { 858 if (page != undefined) {
949 newHash.push('p=' + page); 859 newHash.push('p=' + page);
950 } 860 }
951 861
952 return newHash.join('&'); 862 return newHash.join('&');
953 }; 863 };
954 864
955 /////////////////////////////////////////////////////////////////////////////// 865 ///////////////////////////////////////////////////////////////////////////////
956 // Document Functions: 866 // Document Functions:
957 /** 867 /**
958 * Window onload handler, sets up the page. 868 * Window onload handler, sets up the page.
959 */ 869 */
960 function load() { 870 function load() {
961 $('term').focus(); 871 $('term').focus();
962 872
963 localStrings = new LocalStrings(); 873 localStrings = new LocalStrings();
964 historyModel = new HistoryModel(); 874 historyModel = new HistoryModel();
965 historyView = new HistoryView(historyModel); 875 historyView = new HistoryView(historyModel);
966 pageState = new PageState(historyModel, historyView); 876 pageState = new PageState(historyModel, historyView);
967 877
968 // Create default view. 878 // Create default view.
969 var hashData = pageState.getHashData(); 879 var hashData = pageState.getHashData();
970 if (Boolean(hashData.e)) {
971 historyView.toggleEditMode();
972 }
973 historyView.setSearch(hashData.q, hashData.p); 880 historyView.setSearch(hashData.q, hashData.p);
974 881
975 // Setup click handlers. 882 // Setup click handlers.
976 $('history-section').onclick = function () { 883 $('history-section').onclick = function () {
977 setSearch(''); 884 setSearch('');
978 return false; 885 return false;
979 }; 886 };
980 $('search-form').onsubmit = function () { 887 $('search-form').onsubmit = function () {
981 setSearch(this.term.value); 888 setSearch(this.term.value);
982 return false; 889 return false;
(...skipping 25 matching lines...) Expand all
1008 * Set the history view to a specified page. 915 * Set the history view to a specified page.
1009 * @param {number} page The page to set the view to. 916 * @param {number} page The page to set the view to.
1010 */ 917 */
1011 function setPage(page) { 918 function setPage(page) {
1012 if (historyView) { 919 if (historyView) {
1013 historyView.setPage(page); 920 historyView.setPage(page);
1014 } 921 }
1015 } 922 }
1016 923
1017 /** 924 /**
1018 * TODO(glen): Get rid of this function.
1019 * Toggles edit mode.
1020 */
1021 function toggleEditMode() {
1022 if (historyView) {
1023 historyView.toggleEditMode();
1024 historyView.reload();
1025 }
1026 }
1027
1028 /**
1029 * Delete the next item in our deletion queue. 925 * Delete the next item in our deletion queue.
1030 */ 926 */
1031 function deleteNextInQueue() { 927 function deleteNextInQueue() {
1032 if (deleteQueue.length > 0) { 928 if (deleteQueue.length > 0) {
1033 // Call the native function to remove history entries. 929 // Call the native function to remove history entries.
1034 // First arg is a time in seconds (passed as String) identifying the day. 930 // First arg is a time in seconds (passed as String) identifying the day.
1035 // Remaining args are URLs of history entries from that day to delete. 931 // Remaining args are URLs of history entries from that day to delete.
1036 var timeInSeconds = Math.floor(deleteQueue[0].date.getTime() / 1000); 932 var timeInSeconds = Math.floor(deleteQueue[0].date.getTime() / 1000);
1037 chrome.send('removeURLsOnOneDay', 933 chrome.send('removeURLsOnOneDay',
1038 [String(timeInSeconds)].concat(deleteQueue[0].urls)); 934 [String(timeInSeconds)].concat(deleteQueue[0].urls));
(...skipping 12 matching lines...) Expand all
1051 * Queue a set of URLs from the same day for deletion. 947 * Queue a set of URLs from the same day for deletion.
1052 * @param {Date} date A date indicating the day the URLs were visited. 948 * @param {Date} date A date indicating the day the URLs were visited.
1053 * @param {Array} urls Array of URLs from the same day to be deleted. 949 * @param {Array} urls Array of URLs from the same day to be deleted.
1054 * @param {Function} opt_callback An optional callback to be executed when 950 * @param {Function} opt_callback An optional callback to be executed when
1055 * the deletion is complete. 951 * the deletion is complete.
1056 */ 952 */
1057 function queueURLsForDeletion(date, urls, opt_callback) { 953 function queueURLsForDeletion(date, urls, opt_callback) {
1058 deleteQueue.push({ 'date': date, 'urls': urls, 'callback': opt_callback }); 954 deleteQueue.push({ 'date': date, 'urls': urls, 'callback': opt_callback });
1059 } 955 }
1060 956
957 function reloadHistory() {
958 historyView.reload();
959 }
960
1061 /** 961 /**
1062 * Collect IDs from checked checkboxes and send to Chrome for deletion. 962 * Collect IDs from checked checkboxes and send to Chrome for deletion.
1063 */ 963 */
1064 function removeItems() { 964 function removeItems() {
1065 var checked = document.querySelectorAll( 965 var checked = document.querySelectorAll(
1066 'input[type=checkbox]:checked:not([disabled])'); 966 'input[type=checkbox]:checked:not([disabled])');
1067 var urls = []; 967 var urls = [];
1068 var disabledItems = []; 968 var disabledItems = [];
1069 var queue = []; 969 var queue = [];
1070 var date = new Date(); 970 var date = new Date();
1071 971
1072 for (var i = 0; i < checked.length; i++) { 972 for (var i = 0; i < checked.length; i++) {
1073 var checkbox = checked[i]; 973 var checkbox = checked[i];
1074 var cbDate = new Date(checkbox.time); 974 var cbDate = new Date(checkbox.time);
1075 if (date.getFullYear() != cbDate.getFullYear() || 975 if (date.getFullYear() != cbDate.getFullYear() ||
1076 date.getMonth() != cbDate.getMonth() || 976 date.getMonth() != cbDate.getMonth() ||
1077 date.getDate() != cbDate.getDate()) { 977 date.getDate() != cbDate.getDate()) {
1078 if (urls.length > 0) { 978 if (urls.length > 0) {
1079 queue.push([date, urls]); 979 queue.push([date, urls]);
1080 } 980 }
1081 urls = []; 981 urls = [];
1082 date = cbDate; 982 date = cbDate;
1083 } 983 }
1084 var link = $('id-' + checkbox.name); 984 var link = checkbox.parentNode.parentNode.querySelector('a');
1085 checkbox.disabled = true; 985 checkbox.disabled = true;
1086 link.classList.add('to-be-removed'); 986 link.classList.add('to-be-removed');
1087 disabledItems.push(checkbox); 987 disabledItems.push(checkbox);
1088 urls.push(link.href); 988 urls.push(link.href);
1089 } 989 }
1090 if (urls.length > 0) { 990 if (urls.length > 0) {
1091 queue.push([date, urls]); 991 queue.push([date, urls]);
1092 } 992 }
1093 if (checked.length > 0 && confirm(localStrings.getString('deletewarning'))) { 993 if (checked.length > 0 && confirm(localStrings.getString('deletewarning'))) {
1094 for (var i = 0; i < queue.length; i++) { 994 for (var i = 0; i < queue.length; i++) {
1095 queueURLsForDeletion(queue[i][0], queue[i][1]); 995 // Reload the page when the final entry has been deleted.
996 var callback = i == 0 ? reloadHistory : null;
997
998 queueURLsForDeletion(queue[i][0], queue[i][1], callback);
1096 } 999 }
1097 deleteNextInQueue(); 1000 deleteNextInQueue();
1098 } else { 1001 } else {
1099 // If the remove is cancelled, return the checkboxes to their 1002 // If the remove is cancelled, return the checkboxes to their
1100 // enabled, non-line-through state. 1003 // enabled, non-line-through state.
1101 for (var i = 0; i < disabledItems.length; i++) { 1004 for (var i = 0; i < disabledItems.length; i++) {
1102 var link = $('id-' + disabledItems[i].name); 1005 var checkbox = disabledItems[i];
1103 disabledItems[i].disabled = false; 1006 var link = checkbox.parentNode.parentNode.querySelector('a');
1007 checkbox.disabled = false;
1104 link.classList.remove('to-be-removed'); 1008 link.classList.remove('to-be-removed');
1105 } 1009 }
1106 } 1010 }
1107 return false; 1011 return false;
1108 } 1012 }
1109 1013
1110 /** 1014 /**
1111 * Toggle state of checkbox and handle Shift modifier. 1015 * Toggle state of checkbox and handle Shift modifier.
1112 */ 1016 */
1113 function checkboxClicked(event) { 1017 function checkboxClicked(event) {
1018 var id = Number(this.id.slice("checkbox-".length));
1114 if (event.shiftKey && (selectionAnchor != -1)) { 1019 if (event.shiftKey && (selectionAnchor != -1)) {
1115 var checked = this.checked; 1020 var checked = this.checked;
1116 // Set all checkboxes from the anchor up to the clicked checkbox to the 1021 // Set all checkboxes from the anchor up to the clicked checkbox to the
1117 // state of the clicked one. 1022 // state of the clicked one.
1118 var begin = Math.min(this.name, selectionAnchor); 1023 var begin = Math.min(id, selectionAnchor);
1119 var end = Math.max(this.name, selectionAnchor); 1024 var end = Math.max(id, selectionAnchor);
1120 for (var i = begin; i <= end; i++) { 1025 for (var i = begin; i <= end; i++) {
1121 idToCheckbox[i].checked = checked; 1026 var checkbox = document.querySelector('#checkbox-' + i);
1027 if (checkbox)
1028 checkbox.checked = checked;
1122 } 1029 }
1123 } 1030 }
1124 selectionAnchor = this.name; 1031 selectionAnchor = id;
1125 this.focus(); 1032
1033 historyView.updateRemoveButton();
1034 }
1035
1036 function entryBoxMousedown(event) {
1037 // Prevent text selection when shift-clicking to select multiple entries.
1038 if (event.shiftKey) {
1039 event.preventDefault();
1040 }
1041 }
1042
1043 function removeNode(node) {
1044 node.classList.add('fade-out'); // Trigger CSS fade out animation.
1045
1046 // Delete the node when the animation is complete.
1047 setTimeout(function() {
arv (Not doing code reviews) 2011/10/05 21:30:08 This can be done using the webkitTransitionEnd eve
Patrick Dubroy 2011/10/06 09:38:45 Excellent! Didn't know about that.
1048 node.parentNode.removeChild(node);
1049 }, 200);
1126 } 1050 }
1127 1051
1128 /** 1052 /**
1129 * Removes a single entry from the view. Also removes gaps before and after 1053 * Removes a single entry from the view. Also removes gaps before and after
1130 * entry if necessary. 1054 * entry if necessary.
1131 */ 1055 */
1132 function removeEntryFromView(entry) { 1056 function removeEntryFromView(entry) {
1133 var nextEntry = entry.nextSibling; 1057 var nextEntry = entry.nextSibling;
1134 var previousEntry = entry.previousSibling; 1058 var previousEntry = entry.previousSibling;
1135 entry.parentNode.removeChild(entry); 1059
1060 removeNode(entry);
1136 1061
1137 // if there is no previous entry, and the next entry is a gap, remove it 1062 // if there is no previous entry, and the next entry is a gap, remove it
1138 if (!previousEntry && nextEntry && nextEntry.className == 'gap') { 1063 if (!previousEntry && nextEntry && nextEntry.className == 'gap') {
1139 nextEntry.parentNode.removeChild(nextEntry); 1064 removeNode(nextEntry);
1140 } 1065 }
1141 1066
1142 // if there is no next entry, and the previous entry is a gap, remove it 1067 // if there is no next entry, and the previous entry is a gap, remove it
1143 if (!nextEntry && previousEntry && previousEntry.className == 'gap') { 1068 if (!nextEntry && previousEntry && previousEntry.className == 'gap') {
1144 previousEntry.parentNode.removeChild(previousEntry); 1069 removeNode(previousEntry);
1145 } 1070 }
1146 1071
1147 // if both the next and previous entries are gaps, remove one 1072 // if both the next and previous entries are gaps, remove one
1148 if (nextEntry && nextEntry.className == 'gap' && 1073 if (nextEntry && nextEntry.className == 'gap' &&
1149 previousEntry && previousEntry.className == 'gap') { 1074 previousEntry && previousEntry.className == 'gap') {
1150 nextEntry.parentNode.removeChild(nextEntry); 1075 removeNode(nextEntry);
1151 } 1076 }
1152 } 1077 }
1153 1078
1154 /////////////////////////////////////////////////////////////////////////////// 1079 ///////////////////////////////////////////////////////////////////////////////
1155 // Chrome callbacks: 1080 // Chrome callbacks:
1156 /** 1081 /**
1157 * Our history system calls this function with results from searches. 1082 * Our history system calls this function with results from searches.
1158 */ 1083 */
1159 function historyResult(info, results) { 1084 function historyResult(info, results) {
1160 historyModel.addResults(info, results); 1085 historyModel.addResults(info, results);
1161 } 1086 }
1162 1087
1163 /** 1088 /**
1164 * Our history system calls this function when a deletion has finished. 1089 * Our history system calls this function when a deletion has finished.
1165 */ 1090 */
1166 function deleteComplete() { 1091 function deleteComplete() {
1167 if (deleteQueue.length > 0) { 1092 if (deleteQueue.length > 0) {
1168 // Remove the successfully deleted entry from the queue. 1093 // Remove the successfully deleted entry from the queue.
1169 if (deleteQueue[0].callback) 1094 if (deleteQueue[0].callback)
1170 deleteQueue[0].callback(); 1095 deleteQueue[0].callback.apply();
1171 deleteQueue.splice(0, 1); 1096 deleteQueue.splice(0, 1);
1172 deleteNextInQueue(); 1097 deleteNextInQueue();
1173 } else { 1098 } else {
1174 console.error('Received deleteComplete but queue is empty.'); 1099 console.error('Received deleteComplete but queue is empty.');
1175 } 1100 }
1176 } 1101 }
1177 1102
1178 /** 1103 /**
1179 * Our history system calls this function if a delete is not ready (e.g. 1104 * Our history system calls this function if a delete is not ready (e.g.
1180 * another delete is in-progress). 1105 * another delete is in-progress).
1181 */ 1106 */
1182 function deleteFailed() { 1107 function deleteFailed() {
1183 window.console.log('Delete failed'); 1108 window.console.log('Delete failed');
1184 1109
1185 // The deletion failed - try again later. 1110 // The deletion failed - try again later.
1186 // TODO(dubroy): We should probably give up at some point. 1111 // TODO(dubroy): We should probably give up at some point.
1187 setTimeout(deleteNextInQueue, 500); 1112 setTimeout(deleteNextInQueue, 500);
1188 } 1113 }
1189 1114
1190 /** 1115 /**
1191 * Called when the history is deleted by someone else. 1116 * Called when the history is deleted by someone else.
1192 */ 1117 */
1193 function historyDeleted() { 1118 function historyDeleted() {
1194 window.console.log('History deleted'); 1119 window.console.log('History deleted');
1195 var anyChecked = document.querySelector('.entry input:checked') != null; 1120 var anyChecked = document.querySelector('.entry input:checked') != null;
1196 // Reload the page, unless the user is actively editing. 1121 // Reload the page, unless the user has any items checked.
1197 // TODO(dubroy): We should just reload the page & restore the checked items. 1122 // TODO(dubroy): We should just reload the page & restore the checked items.
1198 if (!(historyView.getEditMode() && anyChecked)) 1123 if (!anyChecked)
1199 historyView.reload(); 1124 historyView.reload();
1200 } 1125 }
1201 1126
1202 // Add handlers to HTML elements. 1127 // Add handlers to HTML elements.
1203 document.addEventListener('DOMContentLoaded', load); 1128 document.addEventListener('DOMContentLoaded', load);
1204 1129
1205 // This event lets us enable and disable menu items before the menu is shown. 1130 // This event lets us enable and disable menu items before the menu is shown.
1206 document.addEventListener('canExecute', function(e) { 1131 document.addEventListener('canExecute', function(e) {
1207 e.canExecute = true; 1132 e.canExecute = true;
1208 }); 1133 });
OLDNEW
« chrome/browser/resources/history2.html ('K') | « chrome/browser/resources/history2.html ('k') | no next file » | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698