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