OLD | NEW |
---|---|
1 // Copyright (c) 2012 The Chromium Authors. All rights reserved. | 1 // Copyright (c) 2012 The Chromium Authors. All rights reserved. |
2 // Use of this source code is governed by a BSD-style license that can be | 2 // Use of this source code is governed by a BSD-style license that can be |
3 // found in the LICENSE file. | 3 // found in the LICENSE file. |
4 | 4 |
5 <include src="../uber/uber_utils.js"> | 5 <include src="../uber/uber_utils.js"> |
6 | 6 |
7 /////////////////////////////////////////////////////////////////////////////// | 7 /////////////////////////////////////////////////////////////////////////////// |
8 // Globals: | 8 // Globals: |
9 /** @const */ var RESULTS_PER_PAGE = 150; | 9 /** @const */ var RESULTS_PER_PAGE = 150; |
10 /** @const */ var MAX_SEARCH_DEPTH_MONTHS = 18; | 10 /** @const */ var MAX_SEARCH_DEPTH_MONTHS = 18; |
(...skipping 23 matching lines...) Expand all Loading... | |
34 return null; | 34 return null; |
35 } | 35 } |
36 | 36 |
37 // TODO(glen): Get rid of these global references, replace with a controller | 37 // TODO(glen): Get rid of these global references, replace with a controller |
38 // or just make the classes own more of the page. | 38 // or just make the classes own more of the page. |
39 var historyModel; | 39 var historyModel; |
40 var historyView; | 40 var historyView; |
41 var pageState; | 41 var pageState; |
42 var deleteQueue = []; | 42 var deleteQueue = []; |
43 var selectionAnchor = -1; | 43 var selectionAnchor = -1; |
44 var activePage = null; | 44 var activeVisit = null; |
45 | 45 |
46 /** @const */ var Command = cr.ui.Command; | 46 /** @const */ var Command = cr.ui.Command; |
47 /** @const */ var Menu = cr.ui.Menu; | 47 /** @const */ var Menu = cr.ui.Menu; |
48 /** @const */ var MenuButton = cr.ui.MenuButton; | 48 /** @const */ var MenuButton = cr.ui.MenuButton; |
49 | 49 |
50 MenuButton.createDropDownArrows(); | 50 MenuButton.createDropDownArrows(); |
51 | 51 |
52 /////////////////////////////////////////////////////////////////////////////// | 52 /////////////////////////////////////////////////////////////////////////////// |
53 // Page: | 53 // Visit: |
54 | 54 |
55 /** | 55 /** |
56 * Class to hold all the information about an entry in our model. | 56 * Class to hold all the information about an entry in our model. |
57 * @param {Object} result An object containing the page's data. | 57 * @param {Object} result An object containing the visit's data. |
58 * @param {boolean} continued Whether this page is on the same day as the | 58 * @param {boolean} continued Whether this visit is on the same day as the |
59 * page before it | 59 * visit before it. |
60 * @param {HistoryModel} model The model object this entry belongs to. | 60 * @param {HistoryModel} model The model object this entry belongs to. |
61 * @param {Number} id The identifier for the entry. | 61 * @param {Number} id The identifier for the entry. |
62 * @constructor | 62 * @constructor |
63 */ | 63 */ |
64 function Page(result, continued, model, id) { | 64 function Visit(result, continued, model, id) { |
65 this.model_ = model; | 65 this.model_ = model; |
66 this.title_ = result.title; | 66 this.title_ = result.title; |
67 this.url_ = result.url; | 67 this.url_ = result.url; |
68 this.domain_ = this.getDomainFromURL_(this.url_); | |
69 this.starred_ = result.starred; | 68 this.starred_ = result.starred; |
70 this.snippet_ = result.snippet || ''; | 69 this.snippet_ = result.snippet || ''; |
71 this.id_ = id; | 70 this.id_ = id; |
72 | 71 |
73 this.changed = false; | 72 this.changed = false; |
74 | 73 |
75 this.isRendered = false; | 74 this.isRendered = false; |
76 | 75 |
77 // All the date information is public so that owners can compare properties of | 76 // All the date information is public so that owners can compare properties of |
78 // two items easily. | 77 // two items easily. |
79 | 78 |
80 // We get the time in seconds, but we want it in milliseconds. | 79 // We get the time in seconds, but we want it in milliseconds. |
81 this.time = new Date(result.time * 1000); | 80 this.time = new Date(result.time * 1000); |
82 | 81 |
83 // See comment in BrowsingHistoryHandler::QueryComplete - we won't always | 82 // See comment in BrowsingHistoryHandler::QueryComplete - we won't always |
84 // get all of these. | 83 // get all of these. |
85 this.dateRelativeDay = result.dateRelativeDay || ''; | 84 this.dateRelativeDay = result.dateRelativeDay || ''; |
86 this.dateTimeOfDay = result.dateTimeOfDay || ''; | 85 this.dateTimeOfDay = result.dateTimeOfDay || ''; |
87 this.dateShort = result.dateShort || ''; | 86 this.dateShort = result.dateShort || ''; |
88 | 87 |
89 // Whether this is the continuation of a previous day. | 88 // Whether this is the continuation of a previous day. |
90 this.continued = continued; | 89 this.continued = continued; |
91 } | 90 } |
92 | 91 |
93 // Page, Public: -------------------------------------------------------------- | 92 // Visit, public: ------------------------------------------------------------- |
94 | 93 |
95 /** | 94 /** |
96 * Returns a dom structure for a browse page result or a search page result. | 95 * Returns a dom structure for a browse page result or a search page result. |
97 * @param {boolean} searchResultFlag Indicates whether the result is a search | 96 * @param {boolean} searchResultFlag Indicates whether the result is a search |
98 * result or not. | 97 * result or not. |
99 * @return {Node} A DOM node to represent the history entry or search result. | 98 * @return {Node} A DOM node to represent the history entry or search result. |
100 */ | 99 */ |
101 Page.prototype.getResultDOM = function(searchResultFlag) { | 100 Visit.prototype.getResultDOM = function(searchResultFlag) { |
102 var node = createElementWithClassName('li', 'entry'); | 101 var node = createElementWithClassName('li', 'entry'); |
103 var time = createElementWithClassName('div', 'time'); | 102 var time = createElementWithClassName('div', 'time'); |
104 var entryBox = createElementWithClassName('label', 'entry-box'); | 103 var entryBox = createElementWithClassName('label', 'entry-box'); |
105 var domain = createElementWithClassName('div', 'domain'); | 104 var domain = createElementWithClassName('div', 'domain'); |
106 | 105 |
107 var dropDown = createElementWithClassName('button', 'drop-down'); | 106 var dropDown = createElementWithClassName('button', 'drop-down'); |
108 dropDown.value = 'Open action menu'; | 107 dropDown.value = 'Open action menu'; |
109 dropDown.title = loadTimeData.getString('actionMenuDescription'); | 108 dropDown.title = loadTimeData.getString('actionMenuDescription'); |
110 dropDown.setAttribute('menu', '#action-menu'); | 109 dropDown.setAttribute('menu', '#action-menu'); |
111 cr.ui.decorate(dropDown, MenuButton); | 110 cr.ui.decorate(dropDown, MenuButton); |
112 | 111 |
113 // Checkbox is always created, but only visible on hover & when checked. | 112 // Checkbox is always created, but only visible on hover & when checked. |
114 var checkbox = document.createElement('input'); | 113 var checkbox = document.createElement('input'); |
115 checkbox.type = 'checkbox'; | 114 checkbox.type = 'checkbox'; |
116 checkbox.id = 'checkbox-' + this.id_; | 115 checkbox.id = 'checkbox-' + this.id_; |
117 checkbox.time = this.time.getTime(); | 116 checkbox.time = this.time.getTime(); |
118 checkbox.addEventListener('click', checkboxClicked); | 117 checkbox.addEventListener('click', checkboxClicked); |
119 time.appendChild(checkbox); | 118 time.appendChild(checkbox); |
120 | 119 |
121 // Keep track of the drop down that triggered the menu, so we know | 120 // Keep track of the drop down that triggered the menu, so we know |
122 // which element to apply the command to. | 121 // which element to apply the command to. |
123 // TODO(dubroy): Ideally we'd use 'activate', but MenuButton swallows it. | 122 // TODO(dubroy): Ideally we'd use 'activate', but MenuButton swallows it. |
124 var self = this; | 123 var self = this; |
125 var setActivePage = function(e) { | 124 var setActiveVisit = function(e) { |
126 activePage = self; | 125 activeVisit = self; |
127 }; | 126 }; |
128 dropDown.addEventListener('mousedown', setActivePage); | 127 dropDown.addEventListener('mousedown', setActiveVisit); |
129 dropDown.addEventListener('focus', setActivePage); | 128 dropDown.addEventListener('focus', setActiveVisit); |
130 | 129 |
131 domain.textContent = this.domain_; | 130 domain.textContent = this.getDomainFromURL_(this.url_); |
132 | 131 |
133 // Clicking anywhere in the entryBox will check/uncheck the checkbox. | 132 // Clicking anywhere in the entryBox will check/uncheck the checkbox. |
134 entryBox.setAttribute('for', checkbox.id); | 133 entryBox.setAttribute('for', checkbox.id); |
135 entryBox.addEventListener('mousedown', entryBoxMousedown); | 134 entryBox.addEventListener('mousedown', entryBoxMousedown); |
136 | 135 |
137 // Prevent clicks on the drop down from affecting the checkbox. | 136 // Prevent clicks on the drop down from affecting the checkbox. |
138 dropDown.addEventListener('click', function(e) { e.preventDefault(); }); | 137 dropDown.addEventListener('click', function(e) { e.preventDefault(); }); |
139 | 138 |
140 // We use a wrapper div so that the entry contents will be shinkwrapped. | 139 // We use a wrapper div so that the entry contents will be shinkwrapped. |
141 entryBox.appendChild(time); | 140 entryBox.appendChild(time); |
(...skipping 15 matching lines...) Expand all Loading... | |
157 time.appendChild(document.createTextNode(this.dateShort)); | 156 time.appendChild(document.createTextNode(this.dateShort)); |
158 var snippet = createElementWithClassName('div', 'snippet'); | 157 var snippet = createElementWithClassName('div', 'snippet'); |
159 this.addHighlightedText_(snippet, | 158 this.addHighlightedText_(snippet, |
160 this.snippet_, | 159 this.snippet_, |
161 this.model_.getSearchText()); | 160 this.model_.getSearchText()); |
162 node.appendChild(snippet); | 161 node.appendChild(snippet); |
163 } else { | 162 } else { |
164 time.appendChild(document.createTextNode(this.dateTimeOfDay)); | 163 time.appendChild(document.createTextNode(this.dateTimeOfDay)); |
165 } | 164 } |
166 | 165 |
167 if (typeof this.domNode_ != 'undefined') { | |
168 console.error('Already generated node for page.'); | |
Patrick Dubroy
2012/05/10 16:52:01
This is not actually an error -- it happens whenev
| |
169 } | |
170 this.domNode_ = node; | 166 this.domNode_ = node; |
171 | 167 |
172 return node; | 168 return node; |
173 }; | 169 }; |
174 | 170 |
175 // Page, private: ------------------------------------------------------------- | 171 // Visit, private: ------------------------------------------------------------ |
176 | 172 |
177 /** | 173 /** |
178 * Extracts and returns the domain (and subdomains) from a URL. | 174 * Extracts and returns the domain (and subdomains) from a URL. |
179 * @param {string} url The url. | 175 * @param {string} url The url. |
180 * @return {string} The domain. An empty string is returned if no domain can | 176 * @return {string} The domain. An empty string is returned if no domain can |
181 * be found. | 177 * be found. |
182 * @private | 178 * @private |
183 */ | 179 */ |
184 Page.prototype.getDomainFromURL_ = function(url) { | 180 Visit.prototype.getDomainFromURL_ = function(url) { |
185 var domain = url.replace(/^.+:\/\//, '').match(/[^/]+/); | 181 var domain = url.replace(/^.+:\/\//, '').match(/[^/]+/); |
186 return domain ? domain[0] : ''; | 182 return domain ? domain[0] : ''; |
187 }; | 183 }; |
188 | 184 |
189 /** | 185 /** |
190 * Add child text nodes to a node such that occurrences of the specified text is | 186 * Add child text nodes to a node such that occurrences of the specified text is |
191 * highlighted. | 187 * highlighted. |
192 * @param {Node} node The node under which new text nodes will be made as | 188 * @param {Node} node The node under which new text nodes will be made as |
193 * children. | 189 * children. |
194 * @param {string} content Text to be added beneath |node| as one or more | 190 * @param {string} content Text to be added beneath |node| as one or more |
195 * text nodes. | 191 * text nodes. |
196 * @param {string} highlightText Occurences of this text inside |content| will | 192 * @param {string} highlightText Occurences of this text inside |content| will |
197 * be highlighted. | 193 * be highlighted. |
198 * @private | 194 * @private |
199 */ | 195 */ |
200 Page.prototype.addHighlightedText_ = function(node, content, highlightText) { | 196 Visit.prototype.addHighlightedText_ = function(node, content, highlightText) { |
201 var i = 0; | 197 var i = 0; |
202 if (highlightText) { | 198 if (highlightText) { |
203 var re = new RegExp(Page.pregQuote_(highlightText), 'gim'); | 199 var re = new RegExp(Visit.pregQuote_(highlightText), 'gim'); |
204 var match; | 200 var match; |
205 while (match = re.exec(content)) { | 201 while (match = re.exec(content)) { |
206 if (match.index > i) | 202 if (match.index > i) |
207 node.appendChild(document.createTextNode(content.slice(i, | 203 node.appendChild(document.createTextNode(content.slice(i, |
208 match.index))); | 204 match.index))); |
209 i = re.lastIndex; | 205 i = re.lastIndex; |
210 // Mark the highlighted text in bold. | 206 // Mark the highlighted text in bold. |
211 var b = document.createElement('b'); | 207 var b = document.createElement('b'); |
212 b.textContent = content.substring(match.index, i); | 208 b.textContent = content.substring(match.index, i); |
213 node.appendChild(b); | 209 node.appendChild(b); |
214 } | 210 } |
215 } | 211 } |
216 if (i < content.length) | 212 if (i < content.length) |
217 node.appendChild(document.createTextNode(content.slice(i))); | 213 node.appendChild(document.createTextNode(content.slice(i))); |
218 }; | 214 }; |
219 | 215 |
220 /** | 216 /** |
221 * @return {DOMObject} DOM representation for the title block. | 217 * @return {DOMObject} DOM representation for the title block. |
222 * @private | 218 * @private |
223 */ | 219 */ |
224 Page.prototype.getTitleDOM_ = function() { | 220 Visit.prototype.getTitleDOM_ = function() { |
225 var node = createElementWithClassName('div', 'title'); | 221 var node = createElementWithClassName('div', 'title'); |
226 node.style.backgroundImage = | 222 node.style.backgroundImage = |
227 'url(chrome://favicon/' + encodeURIForCSS(this.url_) + ')'; | 223 'url(chrome://favicon/' + encodeURIForCSS(this.url_) + ')'; |
228 | 224 |
229 var link = document.createElement('a'); | 225 var link = document.createElement('a'); |
230 link.href = this.url_; | 226 link.href = this.url_; |
231 link.id = 'id-' + this.id_; | 227 link.id = 'id-' + this.id_; |
232 link.target = '_top'; | 228 link.target = '_top'; |
233 | 229 |
234 // Add a tooltip, since it might be ellipsized. | 230 // Add a tooltip, since it might be ellipsized. |
235 // TODO(dubroy): Find a way to show the tooltip only when necessary. | 231 // TODO(dubroy): Find a way to show the tooltip only when necessary. |
236 link.title = this.title_; | 232 link.title = this.title_; |
237 | 233 |
238 this.addHighlightedText_(link, this.title_, this.model_.getSearchText()); | 234 this.addHighlightedText_(link, this.title_, this.model_.getSearchText()); |
239 node.appendChild(link); | 235 node.appendChild(link); |
240 | 236 |
241 if (this.starred_) { | 237 if (this.starred_) { |
242 var star = createElementWithClassName('div', 'starred'); | 238 var star = createElementWithClassName('div', 'starred'); |
243 node.appendChild(star); | 239 node.appendChild(star); |
244 star.addEventListener('click', this.starClicked_.bind(this)); | 240 star.addEventListener('click', this.starClicked_.bind(this)); |
245 } | 241 } |
246 | 242 |
247 return node; | 243 return node; |
248 }; | 244 }; |
249 | 245 |
250 /** | 246 /** |
251 * Launch a search for more history entries from the same domain. | 247 * Launch a search for more history entries from the same domain. |
252 * @private | 248 * @private |
253 */ | 249 */ |
254 Page.prototype.showMoreFromSite_ = function() { | 250 Visit.prototype.showMoreFromSite_ = function() { |
255 setSearch(this.domain_); | 251 setSearch(this.getDomainFromURL_(this.url_)); |
256 }; | 252 }; |
257 | 253 |
258 /** | 254 /** |
259 * Remove a single entry from the history. | 255 * Remove a single entry from the history. |
260 * @private | 256 * @private |
261 */ | 257 */ |
262 Page.prototype.removeFromHistory_ = function() { | 258 Visit.prototype.removeFromHistory_ = function() { |
263 var self = this; | 259 var self = this; |
264 var onSuccessCallback = function() { | 260 var onSuccessCallback = function() { |
265 removeEntryFromView(self.domNode_); | 261 removeEntryFromView(self.domNode_); |
266 }; | 262 }; |
267 queueURLsForDeletion(this.time, [this.url_], onSuccessCallback); | 263 queueURLsForDeletion(this.time, [this.url_], onSuccessCallback); |
268 deleteNextInQueue(); | 264 deleteNextInQueue(); |
269 }; | 265 }; |
270 | 266 |
271 /** | 267 /** |
272 * Click event handler for the star icon that appears beside bookmarked URLs. | 268 * Click event handler for the star icon that appears beside bookmarked URLs. |
273 * When clicked, the bookmark is removed for that URL. | 269 * When clicked, the bookmark is removed for that URL. |
274 * @param {Event} event The click event. | 270 * @param {Event} event The click event. |
275 * @private | 271 * @private |
276 */ | 272 */ |
277 Page.prototype.starClicked_ = function(event) { | 273 Visit.prototype.starClicked_ = function(event) { |
278 chrome.send('removeBookmark', [this.url_]); | 274 chrome.send('removeBookmark', [this.url_]); |
279 event.currentTarget.hidden = true; | 275 event.currentTarget.hidden = true; |
280 event.preventDefault(); | 276 event.preventDefault(); |
281 }; | 277 }; |
282 | 278 |
283 // Page, private, static: ----------------------------------------------------- | 279 // Visit, private, static: ---------------------------------------------------- |
284 | 280 |
285 /** | 281 /** |
286 * Quote a string so it can be used in a regular expression. | 282 * Quote a string so it can be used in a regular expression. |
287 * @param {string} str The source string | 283 * @param {string} str The source string |
288 * @return {string} The escaped string | 284 * @return {string} The escaped string |
289 * @private | 285 * @private |
290 */ | 286 */ |
291 Page.pregQuote_ = function(str) { | 287 Visit.pregQuote_ = function(str) { |
292 return str.replace(/([\\\.\+\*\?\[\^\]\$\(\)\{\}\=\!\<\>\|\:])/g, '\\$1'); | 288 return str.replace(/([\\\.\+\*\?\[\^\]\$\(\)\{\}\=\!\<\>\|\:])/g, '\\$1'); |
293 }; | 289 }; |
294 | 290 |
295 /////////////////////////////////////////////////////////////////////////////// | 291 /////////////////////////////////////////////////////////////////////////////// |
296 // HistoryModel: | 292 // HistoryModel: |
297 | 293 |
298 /** | 294 /** |
299 * Global container for history data. Future optimizations might include | 295 * Global container for history data. Future optimizations might include |
300 * allowing the creation of a HistoryModel for each search string, allowing | 296 * allowing the creation of a HistoryModel for each search string, allowing |
301 * quick flips back and forth between results. | 297 * quick flips back and forth between results. |
(...skipping 71 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
373 this.inFlight_ = false; | 369 this.inFlight_ = false; |
374 if (info.term != this.searchText_) { | 370 if (info.term != this.searchText_) { |
375 // If our results aren't for our current search term, they're rubbish. | 371 // If our results aren't for our current search term, they're rubbish. |
376 return; | 372 return; |
377 } | 373 } |
378 | 374 |
379 // Currently we assume we're getting things in date order. This needs to | 375 // Currently we assume we're getting things in date order. This needs to |
380 // be updated if that ever changes. | 376 // be updated if that ever changes. |
381 if (results) { | 377 if (results) { |
382 var lastURL, lastDay; | 378 var lastURL, lastDay; |
383 var oldLength = this.pages_.length; | 379 var oldLength = this.visits_.length; |
384 if (oldLength) { | 380 if (oldLength) { |
385 var oldPage = this.pages_[oldLength - 1]; | 381 var oldVisit = this.visits_[oldLength - 1]; |
386 lastURL = oldPage.url; | 382 lastURL = oldVisit.url; |
387 lastDay = oldPage.dateRelativeDay; | 383 lastDay = oldVisit.dateRelativeDay; |
388 } | 384 } |
389 | 385 |
390 for (var i = 0, thisResult; thisResult = results[i]; i++) { | 386 for (var i = 0, thisResult; thisResult = results[i]; i++) { |
391 var thisURL = thisResult.url; | 387 var thisURL = thisResult.url; |
392 var thisDay = thisResult.dateRelativeDay; | 388 var thisDay = thisResult.dateRelativeDay; |
393 | 389 |
394 // Remove adjacent duplicates. | 390 // Remove adjacent duplicates. |
395 if (!lastURL || lastURL != thisURL) { | 391 if (!lastURL || lastURL != thisURL) { |
396 // Figure out if this page is in the same day as the previous page, | 392 // Figure out if this visit is in the same day as the previous visit. |
397 // this is used to determine how day headers should be drawn. | 393 // This is used to determine how day headers should be drawn. |
398 this.pages_.push(new Page(thisResult, thisDay == lastDay, this, | 394 this.visits_.push( |
399 this.last_id_++)); | 395 new Visit(thisResult, thisDay == lastDay, this, this.last_id_++)); |
400 lastDay = thisDay; | 396 lastDay = thisDay; |
401 lastURL = thisURL; | 397 lastURL = thisURL; |
402 } | 398 } |
403 } | 399 } |
404 if (results.length) | 400 if (results.length) |
405 this.changed = true; | 401 this.changed = true; |
406 } | 402 } |
407 | 403 |
408 this.updateSearch_(info.finished); | 404 this.updateSearch_(info.finished); |
409 }; | 405 }; |
410 | 406 |
411 /** | 407 /** |
412 * @return {Number} The number of pages in the model. | 408 * @return {Number} The number of visits in the model. |
413 */ | 409 */ |
414 HistoryModel.prototype.getSize = function() { | 410 HistoryModel.prototype.getSize = function() { |
415 return this.pages_.length; | 411 return this.visits_.length; |
416 }; | 412 }; |
417 | 413 |
418 /** | 414 /** |
419 * @return {boolean} Whether our history query has covered all of | 415 * Get a list of visits between specified index positions. |
420 * the user's history | |
421 */ | |
422 HistoryModel.prototype.isComplete = function() { | |
423 return this.complete_; | |
424 }; | |
425 | |
426 /** | |
427 * Get a list of pages between specified index positions. | |
428 * @param {Number} start The start index | 416 * @param {Number} start The start index |
429 * @param {Number} end The end index | 417 * @param {Number} end The end index |
430 * @return {Array} A list of pages | 418 * @return {Array.<Visit>} A list of visits |
431 */ | 419 */ |
432 HistoryModel.prototype.getNumberedRange = function(start, end) { | 420 HistoryModel.prototype.getNumberedRange = function(start, end) { |
433 if (start >= this.getSize()) | 421 if (start >= this.getSize()) |
434 return []; | 422 return []; |
435 | 423 |
436 var end = end > this.getSize() ? this.getSize() : end; | 424 var end = end > this.getSize() ? this.getSize() : end; |
437 return this.pages_.slice(start, end); | 425 return this.visits_.slice(start, end); |
438 }; | 426 }; |
439 | 427 |
440 // HistoryModel, Private: ----------------------------------------------------- | 428 // HistoryModel, Private: ----------------------------------------------------- |
441 | 429 |
442 /** | 430 /** |
443 * Clear the history model. | 431 * Clear the history model. |
444 * @private | 432 * @private |
445 */ | 433 */ |
446 HistoryModel.prototype.clearModel_ = function() { | 434 HistoryModel.prototype.clearModel_ = function() { |
447 this.inFlight_ = false; // Whether a query is inflight. | 435 this.inFlight_ = false; // Whether a query is inflight. |
448 this.searchText_ = ''; | 436 this.searchText_ = ''; |
449 this.searchDepth_ = 0; | 437 this.searchDepth_ = 0; |
450 this.pages_ = []; // Date-sorted list of pages. | 438 this.visits_ = []; // Date-sorted list of visits. |
451 this.last_id_ = 0; | 439 this.last_id_ = 0; |
452 selectionAnchor = -1; | 440 selectionAnchor = -1; |
453 | 441 |
454 // The page that the view wants to see - we only fetch slightly past this | 442 // The page that the view wants to see - we only fetch slightly past this |
455 // point. If the view requests a page that we don't have data for, we try | 443 // point. If the view requests a page that we don't have data for, we try |
456 // to fetch it and call back when we're done. | 444 // to fetch it and call back when we're done. |
457 this.requestedPage_ = 0; | 445 this.requestedPage_ = 0; |
458 | 446 |
459 this.complete_ = false; | 447 if (this.view_) |
460 | |
461 if (this.view_) { | |
462 this.view_.clear_(); | 448 this.view_.clear_(); |
463 } | |
464 }; | 449 }; |
465 | 450 |
466 /** | 451 /** |
467 * Figure out if we need to do more searches to fill the currently requested | 452 * Figure out if we need to do more searches to fill the currently requested |
468 * page. If we think we can fill the page, call the view and let it know | 453 * page. If we think we can fill the page, call the view and let it know |
469 * we're ready to show something. | 454 * we're ready to show something. |
470 * @param {boolean} finished Indicates if there is any more data to come. | 455 * @param {boolean} finished Indicates if there is any more data to come. |
471 * @private | 456 * @private |
472 */ | 457 */ |
473 HistoryModel.prototype.updateSearch_ = function(finished) { | 458 HistoryModel.prototype.updateSearch_ = function(finished) { |
(...skipping 26 matching lines...) Expand all Loading... | |
500 * used. | 485 * used. |
501 * | 486 * |
502 * TODO: Fix this for when the user's clock goes across month boundaries. | 487 * TODO: Fix this for when the user's clock goes across month boundaries. |
503 * @param {number=} depth How many days (or months, if the search text is | 488 * @param {number=} depth How many days (or months, if the search text is |
504 * non-empty) back to do the search. | 489 * non-empty) back to do the search. |
505 * @private | 490 * @private |
506 */ | 491 */ |
507 HistoryModel.prototype.getSearchResults_ = function(depth) { | 492 HistoryModel.prototype.getSearchResults_ = function(depth) { |
508 this.searchDepth_ = depth || 0; | 493 this.searchDepth_ = depth || 0; |
509 | 494 |
510 if (this.searchText_ == '') { | 495 if (this.searchText_) { |
496 chrome.send('searchHistory', | |
497 [this.searchText_, String(this.searchDepth_)]); | |
498 } else { | |
511 chrome.send('getHistory', | 499 chrome.send('getHistory', |
512 [String(this.searchDepth_)]); | 500 [String(this.searchDepth_)]); |
513 } else { | |
514 chrome.send('searchHistory', | |
515 [this.searchText_, String(this.searchDepth_)]); | |
516 } | 501 } |
517 | 502 |
518 this.inFlight_ = true; | 503 this.inFlight_ = true; |
519 }; | 504 }; |
520 | 505 |
521 /** | 506 /** |
522 * Check to see if we have data for a given page. | 507 * Check to see if we have data for a given page. |
523 * @param {number} page The page number | 508 * @param {number} page The page number |
524 * @return {boolean} Whether we have any data for the given page. | 509 * @return {boolean} Whether we have any data for the given page. |
525 * @private | 510 * @private |
(...skipping 26 matching lines...) Expand all Loading... | |
552 this.editButtonTd_ = $('edit-button'); | 537 this.editButtonTd_ = $('edit-button'); |
553 this.editingControlsDiv_ = $('editing-controls'); | 538 this.editingControlsDiv_ = $('editing-controls'); |
554 this.resultDiv_ = $('results-display'); | 539 this.resultDiv_ = $('results-display'); |
555 this.pageDiv_ = $('results-pagination'); | 540 this.pageDiv_ = $('results-pagination'); |
556 this.model_ = model; | 541 this.model_ = model; |
557 this.pageIndex_ = 0; | 542 this.pageIndex_ = 0; |
558 this.lastDisplayed_ = []; | 543 this.lastDisplayed_ = []; |
559 | 544 |
560 this.model_.setView(this); | 545 this.model_.setView(this); |
561 | 546 |
562 this.currentPages_ = []; | 547 this.currentVisits_ = []; |
563 | 548 |
564 var self = this; | 549 var self = this; |
565 window.onresize = function() { | 550 window.onresize = function() { |
566 self.updateEntryAnchorWidth_(); | 551 self.updateEntryAnchorWidth_(); |
567 }; | 552 }; |
568 | 553 |
569 $('clear-browsing-data').addEventListener('click', openClearBrowsingData); | 554 $('clear-browsing-data').addEventListener('click', openClearBrowsingData); |
570 $('remove-selected').addEventListener('click', removeItems); | 555 $('remove-selected').addEventListener('click', removeItems); |
571 } | 556 } |
572 | 557 |
(...skipping 57 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
630 // HistoryView, private: ------------------------------------------------------ | 615 // HistoryView, private: ------------------------------------------------------ |
631 | 616 |
632 /** | 617 /** |
633 * Clear the results in the view. Since we add results piecemeal, we need | 618 * Clear the results in the view. Since we add results piecemeal, we need |
634 * to clear them out when we switch to a new page or reload. | 619 * to clear them out when we switch to a new page or reload. |
635 * @private | 620 * @private |
636 */ | 621 */ |
637 HistoryView.prototype.clear_ = function() { | 622 HistoryView.prototype.clear_ = function() { |
638 this.resultDiv_.textContent = ''; | 623 this.resultDiv_.textContent = ''; |
639 | 624 |
640 var pages = this.currentPages_; | 625 this.currentVisits_.forEach(function(visit) { |
641 for (var i = 0; i < pages.length; i++) { | 626 visit.isRendered = false; |
642 pages[i].isRendered = false; | 627 }); |
643 } | 628 this.currentVisits_ = []; |
644 this.currentPages_ = []; | |
645 }; | 629 }; |
646 | 630 |
647 /** | 631 /** |
648 * Record that the given page has been rendered. | 632 * Record that the given visit has been rendered. |
649 * @param {Page} page The page that was rendered. | 633 * @param {Visit} visit The visit that was rendered. |
650 * @private | 634 * @private |
651 */ | 635 */ |
652 HistoryView.prototype.setPageRendered_ = function(page) { | 636 HistoryView.prototype.setVisitRendered_ = function(visit) { |
653 page.isRendered = true; | 637 visit.isRendered = true; |
654 this.currentPages_.push(page); | 638 this.currentVisits_.push(visit); |
655 }; | 639 }; |
656 | 640 |
657 /** | 641 /** |
658 * Update the page with results. | 642 * Update the page with results. |
659 * @private | 643 * @private |
660 */ | 644 */ |
661 HistoryView.prototype.displayResults_ = function() { | 645 HistoryView.prototype.displayResults_ = function() { |
662 var results = this.model_.getNumberedRange( | 646 var results = this.model_.getNumberedRange( |
663 this.pageIndex_ * RESULTS_PER_PAGE, | 647 this.pageIndex_ * RESULTS_PER_PAGE, |
664 this.pageIndex_ * RESULTS_PER_PAGE + RESULTS_PER_PAGE); | 648 this.pageIndex_ * RESULTS_PER_PAGE + RESULTS_PER_PAGE); |
665 | 649 |
666 var searchText = this.model_.getSearchText(); | 650 var searchText = this.model_.getSearchText(); |
667 if (searchText) { | 651 if (searchText) { |
668 // Add a header for the search results, if there isn't already one. | 652 // Add a header for the search results, if there isn't already one. |
669 if (!this.resultDiv_.querySelector('h3')) { | 653 if (!this.resultDiv_.querySelector('h3')) { |
670 var header = document.createElement('h3'); | 654 var header = document.createElement('h3'); |
671 header.textContent = loadTimeData.getStringF('searchresultsfor', | 655 header.textContent = loadTimeData.getStringF('searchresultsfor', |
672 searchText); | 656 searchText); |
673 this.resultDiv_.appendChild(header); | 657 this.resultDiv_.appendChild(header); |
674 } | 658 } |
675 | 659 |
676 var searchResults = createElementWithClassName('ol', 'search-results'); | 660 var searchResults = createElementWithClassName('ol', 'search-results'); |
677 for (var i = 0, page; page = results[i]; i++) { | 661 for (var i = 0, visit; visit = results[i]; i++) { |
678 if (!page.isRendered) { | 662 if (!visit.isRendered) { |
679 searchResults.appendChild(page.getResultDOM(true)); | 663 searchResults.appendChild(visit.getResultDOM(true)); |
680 this.setPageRendered_(page); | 664 this.setVisitRendered_(visit); |
681 } | 665 } |
682 } | 666 } |
683 this.resultDiv_.appendChild(searchResults); | 667 this.resultDiv_.appendChild(searchResults); |
684 } else { | 668 } else { |
685 var resultsFragment = document.createDocumentFragment(); | 669 var resultsFragment = document.createDocumentFragment(); |
686 var lastTime = Math.infinity; | 670 var lastTime = Math.infinity; |
687 var dayResults; | 671 var dayResults; |
688 for (var i = 0, page; page = results[i]; i++) { | 672 for (var i = 0, visit; visit = results[i]; i++) { |
689 if (page.isRendered) { | 673 if (visit.isRendered) { |
690 continue; | 674 continue; |
691 } | 675 } |
692 // Break across day boundaries and insert gaps for browsing pauses. | 676 // Break across day boundaries and insert gaps for browsing pauses. |
693 // Create a dayResults element to contain results for each day | 677 // Create a dayResults element to contain results for each day |
694 var thisTime = page.time.getTime(); | 678 var thisTime = visit.time.getTime(); |
695 | 679 |
696 if ((i == 0 && page.continued) || !page.continued) { | 680 if ((i == 0 && visit.continued) || !visit.continued) { |
697 var day = createElementWithClassName('h3', 'day'); | 681 var day = createElementWithClassName('h3', 'day'); |
698 day.appendChild(document.createTextNode(page.dateRelativeDay)); | 682 day.appendChild(document.createTextNode(visit.dateRelativeDay)); |
699 if (i == 0 && page.continued) { | 683 if (i == 0 && visit.continued) { |
700 day.appendChild(document.createTextNode(' ' + | 684 day.appendChild(document.createTextNode(' ' + |
701 loadTimeData.getString('cont'))); | 685 loadTimeData.getString('cont'))); |
702 } | 686 } |
703 | 687 |
704 // If there is an existing dayResults element, append it. | 688 // If there is an existing dayResults element, append it. |
705 if (dayResults) { | 689 if (dayResults) { |
706 resultsFragment.appendChild(dayResults); | 690 resultsFragment.appendChild(dayResults); |
707 } | 691 } |
708 resultsFragment.appendChild(day); | 692 resultsFragment.appendChild(day); |
709 dayResults = createElementWithClassName('ol', 'day-results'); | 693 dayResults = createElementWithClassName('ol', 'day-results'); |
710 } else if (lastTime - thisTime > BROWSING_GAP_TIME) { | 694 } else if (lastTime - thisTime > BROWSING_GAP_TIME) { |
711 if (dayResults) { | 695 if (dayResults) { |
712 dayResults.appendChild(createElementWithClassName('li', 'gap')); | 696 dayResults.appendChild(createElementWithClassName('li', 'gap')); |
713 } | 697 } |
714 } | 698 } |
715 lastTime = thisTime; | 699 lastTime = thisTime; |
716 // Add entry. | 700 // Add entry. |
717 if (dayResults) { | 701 if (dayResults) { |
718 dayResults.appendChild(page.getResultDOM(false)); | 702 dayResults.appendChild(visit.getResultDOM(false)); |
719 this.setPageRendered_(page); | 703 this.setVisitRendered_(visit); |
720 } | 704 } |
721 } | 705 } |
722 // Add final dayResults element. | 706 // Add final dayResults element. |
723 if (dayResults) { | 707 if (dayResults) { |
724 resultsFragment.appendChild(dayResults); | 708 resultsFragment.appendChild(dayResults); |
725 } | 709 } |
726 this.resultDiv_.appendChild(resultsFragment); | 710 this.resultDiv_.appendChild(resultsFragment); |
727 } | 711 } |
728 this.displayNavBar_(); | 712 this.displayNavBar_(); |
729 this.updateEntryAnchorWidth_(); | 713 this.updateEntryAnchorWidth_(); |
(...skipping 185 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
915 | 899 |
916 // Create default view. | 900 // Create default view. |
917 var hashData = pageState.getHashData(); | 901 var hashData = pageState.getHashData(); |
918 historyView.setSearch(hashData.q, hashData.p); | 902 historyView.setSearch(hashData.q, hashData.p); |
919 | 903 |
920 $('search-form').onsubmit = function() { | 904 $('search-form').onsubmit = function() { |
921 setSearch(searchField.value); | 905 setSearch(searchField.value); |
922 return false; | 906 return false; |
923 }; | 907 }; |
924 | 908 |
925 $('remove-page').addEventListener('activate', function(e) { | 909 $('remove-visit').addEventListener('activate', function(e) { |
926 activePage.removeFromHistory_(); | 910 activeVisit.removeFromHistory_(); |
927 activePage = null; | 911 activeVisit = null; |
928 }); | 912 }); |
929 $('more-from-site').addEventListener('activate', function(e) { | 913 $('more-from-site').addEventListener('activate', function(e) { |
930 activePage.showMoreFromSite_(); | 914 activeVisit.showMoreFromSite_(); |
931 activePage = null; | 915 activeVisit = null; |
932 }); | 916 }); |
933 | 917 |
934 var title = loadTimeData.getString('title'); | 918 var title = loadTimeData.getString('title'); |
935 uber.invokeMethodOnParent('setTitle', {title: title}); | 919 uber.invokeMethodOnParent('setTitle', {title: title}); |
936 | 920 |
937 window.addEventListener('message', function(e) { | 921 window.addEventListener('message', function(e) { |
938 if (e.data.method == 'frameSelected') | 922 if (e.data.method == 'frameSelected') |
939 searchField.focus(); | 923 searchField.focus(); |
940 }); | 924 }); |
941 } | 925 } |
(...skipping 231 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
1173 historyView.reload(); | 1157 historyView.reload(); |
1174 } | 1158 } |
1175 | 1159 |
1176 // Add handlers to HTML elements. | 1160 // Add handlers to HTML elements. |
1177 document.addEventListener('DOMContentLoaded', load); | 1161 document.addEventListener('DOMContentLoaded', load); |
1178 | 1162 |
1179 // This event lets us enable and disable menu items before the menu is shown. | 1163 // This event lets us enable and disable menu items before the menu is shown. |
1180 document.addEventListener('canExecute', function(e) { | 1164 document.addEventListener('canExecute', function(e) { |
1181 e.canExecute = true; | 1165 e.canExecute = true; |
1182 }); | 1166 }); |
OLD | NEW |