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 var RESULTS_PER_PAGE = 150; | 9 var RESULTS_PER_PAGE = 150; |
10 var MAX_SEARCH_DEPTH_MONTHS = 18; | 10 var MAX_SEARCH_DEPTH_MONTHS = 18; |
11 | 11 |
12 // Amount of time between pageviews that we consider a 'break' in browsing, | 12 // Amount of time between pageviews that we consider a 'break' in browsing, |
13 // measured in milliseconds. | 13 // measured in milliseconds. |
14 var BROWSING_GAP_TIME = 15 * 60 * 1000; | 14 var BROWSING_GAP_TIME = 15 * 60 * 1000; |
15 | 15 |
16 function $(o) {return document.getElementById(o);} | 16 function $(o) {return document.getElementById(o);} |
17 | 17 |
18 function createElementWithClassName(type, className) { | 18 function createElementWithClassName(type, className) { |
19 var elm = document.createElement(type); | 19 var elm = document.createElement(type); |
20 elm.className = className; | 20 elm.className = className; |
21 return elm; | 21 return elm; |
22 } | 22 } |
23 | 23 |
24 // Escapes a URI as appropriate for CSS. | 24 // Escapes a URI as appropriate for CSS. |
25 function encodeURIForCSS(uri) { | 25 function encodeURIForCSS(uri) { |
26 // CSS URIs need to have '(' and ')' escaped. | 26 // CSS URIs need to have '(' and ')' escaped. |
27 return uri.replace(/\(/g, "\\(").replace(/\)/g, "\\)"); | 27 return uri.replace(/\(/g, '\\(').replace(/\)/g, '\\)'); |
28 } | 28 } |
29 | 29 |
30 function findAncestorWithClass(node, className) { | 30 function findAncestorWithClass(node, className) { |
31 while ((node = node.parentNode)) { | 31 while ((node = node.parentNode)) { |
32 if (node.classList.contains(className)) return node; | 32 if (node.classList.contains(className)) return node; |
33 } | 33 } |
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 localStrings; | 41 var localStrings; |
42 var pageState; | 42 var pageState; |
43 var deleteQueue = []; | 43 var deleteQueue = []; |
44 var selectionAnchor = -1; | 44 var selectionAnchor = -1; |
45 var activePage = null; | 45 var activePage = null; |
46 | 46 |
47 const MenuButton = cr.ui.MenuButton; | 47 var MenuButton = cr.ui.MenuButton; |
48 const Command = cr.ui.Command; | 48 var Command = cr.ui.Command; |
49 const Menu = cr.ui.Menu; | 49 var Menu = cr.ui.Menu; |
Tyler Breisacher (Chromium)
2012/04/20 17:07:59
You can mark these /** @const */ if you want, but
Patrick Dubroy
2012/04/23 16:35:06
Done.
| |
50 | 50 |
51 function createDropDownBgImage(canvasName, colorSpec) { | 51 function createDropDownBgImage(canvasName, colorSpec) { |
52 var ctx = document.getCSSCanvasContext('2d', canvasName, 6, 4); | 52 var ctx = document.getCSSCanvasContext('2d', canvasName, 6, 4); |
53 ctx.fillStyle = ctx.strokeStyle = colorSpec; | 53 ctx.fillStyle = ctx.strokeStyle = colorSpec; |
54 ctx.beginPath(); | 54 ctx.beginPath(); |
55 ctx.moveTo(0, 0); | 55 ctx.moveTo(0, 0); |
56 ctx.lineTo(6, 0); | 56 ctx.lineTo(6, 0); |
57 ctx.lineTo(3, 3); | 57 ctx.lineTo(3, 3); |
58 ctx.closePath(); | 58 ctx.closePath(); |
59 ctx.fill(); | 59 ctx.fill(); |
60 ctx.stroke(); | 60 ctx.stroke(); |
61 return ctx; | 61 return ctx; |
62 } | 62 } |
63 | 63 |
64 // Create the canvases to be used as the drop down button background images. | 64 // Create the canvases to be used as the drop down button background images. |
65 var arrow = createDropDownBgImage('drop-down-arrow', 'rgb(192, 195, 198)'); | 65 var arrow = createDropDownBgImage('drop-down-arrow', 'rgb(192, 195, 198)'); |
66 var hoverArrow = createDropDownBgImage('drop-down-arrow-hover', | 66 var hoverArrow = createDropDownBgImage('drop-down-arrow-hover', |
67 'rgb(48, 57, 66)'); | 67 'rgb(48, 57, 66)'); |
68 var activeArrow = createDropDownBgImage('drop-down-arrow-active', 'white'); | 68 var activeArrow = createDropDownBgImage('drop-down-arrow-active', 'white'); |
69 | 69 |
70 /////////////////////////////////////////////////////////////////////////////// | 70 /////////////////////////////////////////////////////////////////////////////// |
71 // Page: | 71 // Page: |
72 | |
72 /** | 73 /** |
73 * Class to hold all the information about an entry in our model. | 74 * Class to hold all the information about an entry in our model. |
74 * @param {Object} result An object containing the page's data. | 75 * @param {Object} result An object containing the page's data. |
75 * @param {boolean} continued Whether this page is on the same day as the | 76 * @param {boolean} continued Whether this page is on the same day as the |
76 * page before it | 77 * page before it |
78 * @param {HistoryModel} model The model object this entry belongs to. | |
79 * @param {Number} id The identifier for the entry. | |
80 * @constructor | |
77 */ | 81 */ |
78 function Page(result, continued, model, id) { | 82 function Page(result, continued, model, id) { |
79 this.model_ = model; | 83 this.model_ = model; |
80 this.title_ = result.title; | 84 this.title_ = result.title; |
81 this.url_ = result.url; | 85 this.url_ = result.url; |
82 this.domain_ = this.getDomainFromURL_(this.url_); | 86 this.domain_ = this.getDomainFromURL_(this.url_); |
83 this.starred_ = result.starred; | 87 this.starred_ = result.starred; |
84 this.snippet_ = result.snippet || ""; | 88 this.snippet_ = result.snippet || ''; |
85 this.id_ = id; | 89 this.id_ = id; |
86 | 90 |
87 this.changed = false; | 91 this.changed = false; |
88 | 92 |
89 this.isRendered = false; | 93 this.isRendered = false; |
90 | 94 |
91 // All the date information is public so that owners can compare properties of | 95 // All the date information is public so that owners can compare properties of |
92 // two items easily. | 96 // two items easily. |
93 | 97 |
94 // We get the time in seconds, but we want it in milliseconds. | 98 // We get the time in seconds, but we want it in milliseconds. |
95 this.time = new Date(result.time * 1000); | 99 this.time = new Date(result.time * 1000); |
96 | 100 |
97 // See comment in BrowsingHistoryHandler::QueryComplete - we won't always | 101 // See comment in BrowsingHistoryHandler::QueryComplete - we won't always |
98 // get all of these. | 102 // get all of these. |
99 this.dateRelativeDay = result.dateRelativeDay || ""; | 103 this.dateRelativeDay = result.dateRelativeDay || ''; |
100 this.dateTimeOfDay = result.dateTimeOfDay || ""; | 104 this.dateTimeOfDay = result.dateTimeOfDay || ''; |
101 this.dateShort = result.dateShort || ""; | 105 this.dateShort = result.dateShort || ''; |
102 | 106 |
103 // Whether this is the continuation of a previous day. | 107 // Whether this is the continuation of a previous day. |
104 this.continued = continued; | 108 this.continued = continued; |
105 } | 109 } |
106 | 110 |
107 // Page, Public: -------------------------------------------------------------- | 111 // Page, Public: -------------------------------------------------------------- |
112 | |
108 /** | 113 /** |
109 * Returns a dom structure for a browse page result or a search page result. | 114 * Returns a dom structure for a browse page result or a search page result. |
110 * @param {boolean} Flag to indicate if result is a search result. | 115 * @param {boolean} searchResultFlag Indicates whether the result is a search |
111 * @return {Element} The dom structure. | 116 * result or not. |
117 * @return {Node} A DOM node to represent the history entry or search result. | |
112 */ | 118 */ |
113 Page.prototype.getResultDOM = function(searchResultFlag) { | 119 Page.prototype.getResultDOM = function(searchResultFlag) { |
114 var node = createElementWithClassName('li', 'entry'); | 120 var node = createElementWithClassName('li', 'entry'); |
115 var time = createElementWithClassName('div', 'time'); | 121 var time = createElementWithClassName('div', 'time'); |
116 var entryBox = createElementWithClassName('label', 'entry-box'); | 122 var entryBox = createElementWithClassName('label', 'entry-box'); |
117 var domain = createElementWithClassName('div', 'domain'); | 123 var domain = createElementWithClassName('div', 'domain'); |
118 | 124 |
119 var dropDown = createElementWithClassName('button', 'drop-down'); | 125 var dropDown = createElementWithClassName('button', 'drop-down'); |
120 dropDown.value = 'Open action menu'; | 126 dropDown.value = 'Open action menu'; |
121 dropDown.title = localStrings.getString('actionMenuDescription'); | 127 dropDown.title = localStrings.getString('actionMenuDescription'); |
(...skipping 15 matching lines...) Expand all Loading... | |
137 var setActivePage = function(e) { | 143 var setActivePage = function(e) { |
138 activePage = self; | 144 activePage = self; |
139 }; | 145 }; |
140 dropDown.addEventListener('mousedown', setActivePage); | 146 dropDown.addEventListener('mousedown', setActivePage); |
141 dropDown.addEventListener('focus', setActivePage); | 147 dropDown.addEventListener('focus', setActivePage); |
142 | 148 |
143 domain.textContent = this.domain_; | 149 domain.textContent = this.domain_; |
144 | 150 |
145 // Clicking anywhere in the entryBox will check/uncheck the checkbox. | 151 // Clicking anywhere in the entryBox will check/uncheck the checkbox. |
146 entryBox.setAttribute('for', checkbox.id); | 152 entryBox.setAttribute('for', checkbox.id); |
147 entryBox.addEventListener('mousedown', entryBoxMousedown, false); | 153 entryBox.addEventListener('mousedown', entryBoxMousedown); |
148 | 154 |
149 // Prevent clicks on the drop down from affecting the checkbox. | 155 // Prevent clicks on the drop down from affecting the checkbox. |
150 dropDown.addEventListener('click', function(e) { e.preventDefault(); }); | 156 dropDown.addEventListener('click', function(e) { e.preventDefault(); }); |
151 | 157 |
152 // We use a wrapper div so that the entry contents will be shinkwrapped. | 158 // We use a wrapper div so that the entry contents will be shinkwrapped. |
153 entryBox.appendChild(time); | 159 entryBox.appendChild(time); |
154 entryBox.appendChild(this.getTitleDOM_()); | 160 entryBox.appendChild(this.getTitleDOM_()); |
155 entryBox.appendChild(domain); | 161 entryBox.appendChild(domain); |
156 entryBox.appendChild(dropDown); | 162 entryBox.appendChild(dropDown); |
157 | 163 |
(...skipping 20 matching lines...) Expand all Loading... | |
178 | 184 |
179 if (typeof this.domNode_ != 'undefined') { | 185 if (typeof this.domNode_ != 'undefined') { |
180 console.error('Already generated node for page.'); | 186 console.error('Already generated node for page.'); |
181 } | 187 } |
182 this.domNode_ = node; | 188 this.domNode_ = node; |
183 | 189 |
184 return node; | 190 return node; |
185 }; | 191 }; |
186 | 192 |
187 // Page, private: ------------------------------------------------------------- | 193 // Page, private: ------------------------------------------------------------- |
194 | |
188 /** | 195 /** |
189 * Extracts and returns the domain (and subdomains) from a URL. | 196 * Extracts and returns the domain (and subdomains) from a URL. |
190 * @param {string} The url | 197 * @param {string} url The url. |
191 * @return (string) The domain. An empty string is returned if no domain can | 198 * @return {string} The domain. An empty string is returned if no domain can |
192 * be found. | 199 * be found. |
200 * @private | |
193 */ | 201 */ |
194 Page.prototype.getDomainFromURL_ = function(url) { | 202 Page.prototype.getDomainFromURL_ = function(url) { |
195 var domain = url.replace(/^.+:\/\//, '').match(/[^/]+/); | 203 var domain = url.replace(/^.+:\/\//, '').match(/[^/]+/); |
196 return domain ? domain[0] : ''; | 204 return domain ? domain[0] : ''; |
197 }; | 205 }; |
198 | 206 |
199 /** | 207 /** |
200 * Add child text nodes to a node such that occurrences of the specified text is | 208 * Add child text nodes to a node such that occurrences of the specified text is |
201 * highlighted. | 209 * highlighted. |
202 * @param {Node} node The node under which new text nodes will be made as | 210 * @param {Node} node The node under which new text nodes will be made as |
203 * children. | 211 * children. |
204 * @param {string} content Text to be added beneath |node| as one or more | 212 * @param {string} content Text to be added beneath |node| as one or more |
205 * text nodes. | 213 * text nodes. |
206 * @param {string} highlightText Occurences of this text inside |content| will | 214 * @param {string} highlightText Occurences of this text inside |content| will |
207 * be highlighted. | 215 * be highlighted. |
216 * @private | |
208 */ | 217 */ |
209 Page.prototype.addHighlightedText_ = function(node, content, highlightText) { | 218 Page.prototype.addHighlightedText_ = function(node, content, highlightText) { |
210 var i = 0; | 219 var i = 0; |
211 if (highlightText) { | 220 if (highlightText) { |
212 var re = new RegExp(Page.pregQuote_(highlightText), 'gim'); | 221 var re = new RegExp(Page.pregQuote_(highlightText), 'gim'); |
213 var match; | 222 var match; |
214 while (match = re.exec(content)) { | 223 while (match = re.exec(content)) { |
215 if (match.index > i) | 224 if (match.index > i) |
216 node.appendChild(document.createTextNode(content.slice(i, | 225 node.appendChild(document.createTextNode(content.slice(i, |
217 match.index))); | 226 match.index))); |
218 i = re.lastIndex; | 227 i = re.lastIndex; |
219 // Mark the highlighted text in bold. | 228 // Mark the highlighted text in bold. |
220 var b = document.createElement('b'); | 229 var b = document.createElement('b'); |
221 b.textContent = content.substring(match.index, i); | 230 b.textContent = content.substring(match.index, i); |
222 node.appendChild(b); | 231 node.appendChild(b); |
223 } | 232 } |
224 } | 233 } |
225 if (i < content.length) | 234 if (i < content.length) |
226 node.appendChild(document.createTextNode(content.slice(i))); | 235 node.appendChild(document.createTextNode(content.slice(i))); |
227 }; | 236 }; |
228 | 237 |
229 /** | 238 /** |
230 * @return {DOMObject} DOM representation for the title block. | 239 * @return {DOMObject} DOM representation for the title block. |
240 * @private | |
231 */ | 241 */ |
232 Page.prototype.getTitleDOM_ = function() { | 242 Page.prototype.getTitleDOM_ = function() { |
233 var node = createElementWithClassName('div', 'title'); | 243 var node = createElementWithClassName('div', 'title'); |
234 node.style.backgroundImage = | 244 node.style.backgroundImage = |
235 'url(chrome://favicon/' + encodeURIForCSS(this.url_) + ')'; | 245 'url(chrome://favicon/' + encodeURIForCSS(this.url_) + ')'; |
236 | 246 |
237 var link = document.createElement('a'); | 247 var link = document.createElement('a'); |
238 link.href = this.url_; | 248 link.href = this.url_; |
239 link.id = "id-" + this.id_; | 249 link.id = 'id-' + this.id_; |
240 link.target = "_top"; | 250 link.target = '_top'; |
241 | 251 |
242 // Add a tooltip, since it might be ellipsized. | 252 // Add a tooltip, since it might be ellipsized. |
243 // TODO(dubroy): Find a way to show the tooltip only when necessary. | 253 // TODO(dubroy): Find a way to show the tooltip only when necessary. |
244 link.title = this.title_; | 254 link.title = this.title_; |
245 | 255 |
246 this.addHighlightedText_(link, this.title_, this.model_.getSearchText()); | 256 this.addHighlightedText_(link, this.title_, this.model_.getSearchText()); |
247 node.appendChild(link); | 257 node.appendChild(link); |
248 | 258 |
249 if (this.starred_) { | 259 if (this.starred_) { |
250 var star = createElementWithClassName('div', 'starred'); | 260 var star = createElementWithClassName('div', 'starred'); |
251 node.appendChild(star); | 261 node.appendChild(star); |
252 star.addEventListener('click', this.starClicked_.bind(this)); | 262 star.addEventListener('click', this.starClicked_.bind(this)); |
253 } | 263 } |
254 | 264 |
255 return node; | 265 return node; |
256 }; | 266 }; |
257 | 267 |
258 /** | 268 /** |
259 * Launch a search for more history entries from the same domain. | 269 * Launch a search for more history entries from the same domain. |
270 * @private | |
260 */ | 271 */ |
261 Page.prototype.showMoreFromSite_ = function() { | 272 Page.prototype.showMoreFromSite_ = function() { |
262 setSearch(this.domain_); | 273 setSearch(this.domain_); |
263 }; | 274 }; |
264 | 275 |
265 /** | 276 /** |
266 * Remove a single entry from the history. | 277 * Remove a single entry from the history. |
278 * @private | |
267 */ | 279 */ |
268 Page.prototype.removeFromHistory_ = function() { | 280 Page.prototype.removeFromHistory_ = function() { |
269 var self = this; | 281 var self = this; |
270 var onSuccessCallback = function() { | 282 var onSuccessCallback = function() { |
271 removeEntryFromView(self.domNode_); | 283 removeEntryFromView(self.domNode_); |
272 }; | 284 }; |
273 queueURLsForDeletion(this.time, [this.url_], onSuccessCallback); | 285 queueURLsForDeletion(this.time, [this.url_], onSuccessCallback); |
274 deleteNextInQueue(); | 286 deleteNextInQueue(); |
275 }; | 287 }; |
276 | 288 |
277 /** | 289 /** |
278 * Click event handler for the star icon that appears beside bookmarked URLs. | 290 * Click event handler for the star icon that appears beside bookmarked URLs. |
279 * When clicked, the bookmark is removed for that URL. | 291 * When clicked, the bookmark is removed for that URL. |
292 * @param {Event} event The click event. | |
280 * @private | 293 * @private |
281 */ | 294 */ |
282 Page.prototype.starClicked_ = function(event) { | 295 Page.prototype.starClicked_ = function(event) { |
283 chrome.send('removeBookmark', [this.url_]); | 296 chrome.send('removeBookmark', [this.url_]); |
284 event.currentTarget.hidden = true; | 297 event.currentTarget.hidden = true; |
285 event.preventDefault(); | 298 event.preventDefault(); |
286 } | 299 }; |
287 | 300 |
288 // Page, private, static: ----------------------------------------------------- | 301 // Page, private, static: ----------------------------------------------------- |
289 | 302 |
290 /** | 303 /** |
291 * Quote a string so it can be used in a regular expression. | 304 * Quote a string so it can be used in a regular expression. |
292 * @param {string} str The source string | 305 * @param {string} str The source string |
293 * @return {string} The escaped string | 306 * @return {string} The escaped string |
307 * @private | |
294 */ | 308 */ |
295 Page.pregQuote_ = function(str) { | 309 Page.pregQuote_ = function(str) { |
296 return str.replace(/([\\\.\+\*\?\[\^\]\$\(\)\{\}\=\!\<\>\|\:])/g, "\\$1"); | 310 return str.replace(/([\\\.\+\*\?\[\^\]\$\(\)\{\}\=\!\<\>\|\:])/g, '\\$1'); |
297 }; | 311 }; |
298 | 312 |
299 /////////////////////////////////////////////////////////////////////////////// | 313 /////////////////////////////////////////////////////////////////////////////// |
300 // HistoryModel: | 314 // HistoryModel: |
315 | |
301 /** | 316 /** |
302 * Global container for history data. Future optimizations might include | 317 * Global container for history data. Future optimizations might include |
303 * allowing the creation of a HistoryModel for each search string, allowing | 318 * allowing the creation of a HistoryModel for each search string, allowing |
304 * quick flips back and forth between results. | 319 * quick flips back and forth between results. |
305 * | 320 * |
306 * The history model is based around pages, and only fetching the data to | 321 * The history model is based around pages, and only fetching the data to |
307 * fill the currently requested page. This is somewhat dependent on the view, | 322 * fill the currently requested page. This is somewhat dependent on the view, |
308 * and so future work may wish to change history model to operate on | 323 * and so future work may wish to change history model to operate on |
309 * timeframe (day or week) based containers. | 324 * timeframe (day or week) based containers. |
325 * | |
326 * @constructor | |
310 */ | 327 */ |
311 function HistoryModel() { | 328 function HistoryModel() { |
312 this.clearModel_(); | 329 this.clearModel_(); |
313 } | 330 } |
314 | 331 |
315 // HistoryModel, Public: ------------------------------------------------------ | 332 // HistoryModel, Public: ------------------------------------------------------ |
333 | |
316 /** | 334 /** |
317 * Sets our current view that is called when the history model changes. | 335 * Sets our current view that is called when the history model changes. |
318 * @param {HistoryView} view The view to set our current view to. | 336 * @param {HistoryView} view The view to set our current view to. |
319 */ | 337 */ |
320 HistoryModel.prototype.setView = function(view) { | 338 HistoryModel.prototype.setView = function(view) { |
321 this.view_ = view; | 339 this.view_ = view; |
322 }; | 340 }; |
323 | 341 |
324 /** | 342 /** |
325 * Start a new search - this will clear out our model. | 343 * Start a new search - this will clear out our model. |
(...skipping 23 matching lines...) Expand all Loading... | |
349 /** | 367 /** |
350 * @return {String} The current search text. | 368 * @return {String} The current search text. |
351 */ | 369 */ |
352 HistoryModel.prototype.getSearchText = function() { | 370 HistoryModel.prototype.getSearchText = function() { |
353 return this.searchText_; | 371 return this.searchText_; |
354 }; | 372 }; |
355 | 373 |
356 /** | 374 /** |
357 * Tell the model that the view will want to see the current page. When | 375 * Tell the model that the view will want to see the current page. When |
358 * the data becomes available, the model will call the view back. | 376 * the data becomes available, the model will call the view back. |
359 * @page {Number} page The page we want to view. | 377 * @param {Number} page The page we want to view. |
360 */ | 378 */ |
361 HistoryModel.prototype.requestPage = function(page) { | 379 HistoryModel.prototype.requestPage = function(page) { |
362 this.requestedPage_ = page; | 380 this.requestedPage_ = page; |
363 this.changed = true; | 381 this.changed = true; |
364 this.updateSearch_(false); | 382 this.updateSearch_(false); |
365 }; | 383 }; |
366 | 384 |
367 /** | 385 /** |
368 * Receiver for history query. | 386 * Receiver for history query. |
369 * @param {String} term The search term that the results are for. | 387 * @param {Object} info An object containing information about the query. |
370 * @param {Array} results A list of results | 388 * @param {Array} results A list of results. |
371 */ | 389 */ |
372 HistoryModel.prototype.addResults = function(info, results) { | 390 HistoryModel.prototype.addResults = function(info, results) { |
373 this.inFlight_ = false; | 391 this.inFlight_ = false; |
374 if (info.term != this.searchText_) { | 392 if (info.term != this.searchText_) { |
375 // If our results aren't for our current search term, they're rubbish. | 393 // If our results aren't for our current search term, they're rubbish. |
376 return; | 394 return; |
377 } | 395 } |
378 | 396 |
379 // Currently we assume we're getting things in date order. This needs to | 397 // Currently we assume we're getting things in date order. This needs to |
380 // be updated if that ever changes. | 398 // be updated if that ever changes. |
(...skipping 50 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
431 */ | 449 */ |
432 HistoryModel.prototype.getNumberedRange = function(start, end) { | 450 HistoryModel.prototype.getNumberedRange = function(start, end) { |
433 if (start >= this.getSize()) | 451 if (start >= this.getSize()) |
434 return []; | 452 return []; |
435 | 453 |
436 var end = end > this.getSize() ? this.getSize() : end; | 454 var end = end > this.getSize() ? this.getSize() : end; |
437 return this.pages_.slice(start, end); | 455 return this.pages_.slice(start, end); |
438 }; | 456 }; |
439 | 457 |
440 // HistoryModel, Private: ----------------------------------------------------- | 458 // HistoryModel, Private: ----------------------------------------------------- |
459 | |
460 /** | |
461 * Clear the history model. | |
462 * @private | |
463 */ | |
441 HistoryModel.prototype.clearModel_ = function() { | 464 HistoryModel.prototype.clearModel_ = function() { |
442 this.inFlight_ = false; // Whether a query is inflight. | 465 this.inFlight_ = false; // Whether a query is inflight. |
443 this.searchText_ = ''; | 466 this.searchText_ = ''; |
444 this.searchDepth_ = 0; | 467 this.searchDepth_ = 0; |
445 this.pages_ = []; // Date-sorted list of pages. | 468 this.pages_ = []; // Date-sorted list of pages. |
446 this.last_id_ = 0; | 469 this.last_id_ = 0; |
447 selectionAnchor = -1; | 470 selectionAnchor = -1; |
448 | 471 |
449 // The page that the view wants to see - we only fetch slightly past this | 472 // The page that the view wants to see - we only fetch slightly past this |
450 // point. If the view requests a page that we don't have data for, we try | 473 // point. If the view requests a page that we don't have data for, we try |
451 // to fetch it and call back when we're done. | 474 // to fetch it and call back when we're done. |
452 this.requestedPage_ = 0; | 475 this.requestedPage_ = 0; |
453 | 476 |
454 this.complete_ = false; | 477 this.complete_ = false; |
455 | 478 |
456 if (this.view_) { | 479 if (this.view_) { |
457 this.view_.clear_(); | 480 this.view_.clear_(); |
458 } | 481 } |
459 }; | 482 }; |
460 | 483 |
461 /** | 484 /** |
462 * Figure out if we need to do more searches to fill the currently requested | 485 * Figure out if we need to do more searches to fill the currently requested |
463 * page. If we think we can fill the page, call the view and let it know | 486 * page. If we think we can fill the page, call the view and let it know |
464 * we're ready to show something. | 487 * we're ready to show something. |
488 * @param {boolean} finished Indicates if there is any more data to come. | |
489 * @private | |
465 */ | 490 */ |
466 HistoryModel.prototype.updateSearch_ = function(finished) { | 491 HistoryModel.prototype.updateSearch_ = function(finished) { |
467 if ((this.searchText_ && this.searchDepth_ >= MAX_SEARCH_DEPTH_MONTHS) || | 492 if ((this.searchText_ && this.searchDepth_ >= MAX_SEARCH_DEPTH_MONTHS) || |
468 finished) { | 493 finished) { |
469 // We have maxed out. There will be no more data. | 494 // We have maxed out. There will be no more data. |
470 this.complete_ = true; | 495 this.complete_ = true; |
471 this.view_.onModelReady(); | 496 this.view_.onModelReady(); |
472 this.changed = false; | 497 this.changed = false; |
473 } else { | 498 } else { |
474 // If we can't fill the requested page, ask for more data unless a request | 499 // If we can't fill the requested page, ask for more data unless a request |
(...skipping 11 matching lines...) Expand all Loading... | |
486 }; | 511 }; |
487 | 512 |
488 /** | 513 /** |
489 * Get search results for a selected depth. Our history system is optimized | 514 * Get search results for a selected depth. Our history system is optimized |
490 * for queries that don't cross month boundaries, but an entire month's | 515 * for queries that don't cross month boundaries, but an entire month's |
491 * worth of data is huge. When we're in browse mode (searchText is empty) | 516 * worth of data is huge. When we're in browse mode (searchText is empty) |
492 * we request the data a day at a time. When we're searching, a month is | 517 * we request the data a day at a time. When we're searching, a month is |
493 * used. | 518 * used. |
494 * | 519 * |
495 * TODO: Fix this for when the user's clock goes across month boundaries. | 520 * TODO: Fix this for when the user's clock goes across month boundaries. |
496 * @param {number} opt_day How many days back to do the search. | 521 * @param {number} depth How many days back to do the search. |
Tyler Breisacher (Chromium)
2012/04/20 17:07:59
It looks like this is an optional parameter, so we
Patrick Dubroy
2012/04/23 16:35:06
Done.
| |
522 * @private | |
497 */ | 523 */ |
498 HistoryModel.prototype.getSearchResults_ = function(depth) { | 524 HistoryModel.prototype.getSearchResults_ = function(depth) { |
499 this.searchDepth_ = depth || 0; | 525 this.searchDepth_ = depth || 0; |
500 | 526 |
501 if (this.searchText_ == "") { | 527 if (this.searchText_ == '') { |
Tyler Breisacher (Chromium)
2012/04/20 17:07:59
Not something we should fix in this CL, but it mig
Patrick Dubroy
2012/04/23 16:35:06
You mean "if (!this.searchText_)" :-)
I'll fix in
| |
502 chrome.send('getHistory', | 528 chrome.send('getHistory', |
503 [String(this.searchDepth_)]); | 529 [String(this.searchDepth_)]); |
504 } else { | 530 } else { |
505 chrome.send('searchHistory', | 531 chrome.send('searchHistory', |
506 [this.searchText_, String(this.searchDepth_)]); | 532 [this.searchText_, String(this.searchDepth_)]); |
507 } | 533 } |
508 | 534 |
509 this.inFlight_ = true; | 535 this.inFlight_ = true; |
510 }; | 536 }; |
511 | 537 |
512 /** | 538 /** |
513 * Check to see if we have data for a given page. | 539 * Check to see if we have data for a given page. |
514 * @param {number} page The page number | 540 * @param {number} page The page number |
515 * @return {boolean} Whether we have any data for the given page. | 541 * @return {boolean} Whether we have any data for the given page. |
542 * @private | |
516 */ | 543 */ |
517 HistoryModel.prototype.haveDataForPage_ = function(page) { | 544 HistoryModel.prototype.haveDataForPage_ = function(page) { |
518 return (page * RESULTS_PER_PAGE < this.getSize()); | 545 return (page * RESULTS_PER_PAGE < this.getSize()); |
519 }; | 546 }; |
520 | 547 |
521 /** | 548 /** |
522 * Check to see if we have data to fill a page. | 549 * Check to see if we have data to fill a page. |
523 * @param {number} page The page number. | 550 * @param {number} page The page number. |
524 * @return {boolean} Whether we have data to fill the page. | 551 * @return {boolean} Whether we have data to fill the page. |
552 * @private | |
525 */ | 553 */ |
526 HistoryModel.prototype.canFillPage_ = function(page) { | 554 HistoryModel.prototype.canFillPage_ = function(page) { |
527 return ((page + 1) * RESULTS_PER_PAGE <= this.getSize()); | 555 return ((page + 1) * RESULTS_PER_PAGE <= this.getSize()); |
528 }; | 556 }; |
529 | 557 |
530 /////////////////////////////////////////////////////////////////////////////// | 558 /////////////////////////////////////////////////////////////////////////////// |
531 // HistoryView: | 559 // HistoryView: |
560 | |
532 /** | 561 /** |
533 * Functions and state for populating the page with HTML. This should one-day | 562 * Functions and state for populating the page with HTML. This should one-day |
534 * contain the view and use event handlers, rather than pushing HTML out and | 563 * contain the view and use event handlers, rather than pushing HTML out and |
535 * getting called externally. | 564 * getting called externally. |
536 * @param {HistoryModel} model The model backing this view. | 565 * @param {HistoryModel} model The model backing this view. |
566 * @constructor | |
537 */ | 567 */ |
538 function HistoryView(model) { | 568 function HistoryView(model) { |
539 this.editButtonTd_ = $('edit-button'); | 569 this.editButtonTd_ = $('edit-button'); |
540 this.editingControlsDiv_ = $('editing-controls'); | 570 this.editingControlsDiv_ = $('editing-controls'); |
541 this.resultDiv_ = $('results-display'); | 571 this.resultDiv_ = $('results-display'); |
542 this.pageDiv_ = $('results-pagination'); | 572 this.pageDiv_ = $('results-pagination'); |
543 this.model_ = model | 573 this.model_ = model; |
544 this.pageIndex_ = 0; | 574 this.pageIndex_ = 0; |
545 this.lastDisplayed_ = []; | 575 this.lastDisplayed_ = []; |
546 | 576 |
547 this.model_.setView(this); | 577 this.model_.setView(this); |
548 | 578 |
549 this.currentPages_ = []; | 579 this.currentPages_ = []; |
550 | 580 |
551 var self = this; | 581 var self = this; |
552 window.onresize = function() { | 582 window.onresize = function() { |
553 self.updateEntryAnchorWidth_(); | 583 self.updateEntryAnchorWidth_(); |
(...skipping 51 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
605 HistoryView.prototype.onModelReady = function() { | 635 HistoryView.prototype.onModelReady = function() { |
606 this.displayResults_(); | 636 this.displayResults_(); |
607 }; | 637 }; |
608 | 638 |
609 /** | 639 /** |
610 * Enables or disables the 'Remove selected items' button as appropriate. | 640 * Enables or disables the 'Remove selected items' button as appropriate. |
611 */ | 641 */ |
612 HistoryView.prototype.updateRemoveButton = function() { | 642 HistoryView.prototype.updateRemoveButton = function() { |
613 var anyChecked = document.querySelector('.entry input:checked') != null; | 643 var anyChecked = document.querySelector('.entry input:checked') != null; |
614 $('remove-selected').disabled = !anyChecked; | 644 $('remove-selected').disabled = !anyChecked; |
615 } | 645 }; |
616 | 646 |
617 // HistoryView, private: ------------------------------------------------------ | 647 // HistoryView, private: ------------------------------------------------------ |
648 | |
618 /** | 649 /** |
619 * Clear the results in the view. Since we add results piecemeal, we need | 650 * Clear the results in the view. Since we add results piecemeal, we need |
620 * to clear them out when we switch to a new page or reload. | 651 * to clear them out when we switch to a new page or reload. |
652 * @private | |
621 */ | 653 */ |
622 HistoryView.prototype.clear_ = function() { | 654 HistoryView.prototype.clear_ = function() { |
623 this.resultDiv_.textContent = ''; | 655 this.resultDiv_.textContent = ''; |
624 | 656 |
625 var pages = this.currentPages_; | 657 var pages = this.currentPages_; |
626 for (var i = 0; i < pages.length; i++) { | 658 for (var i = 0; i < pages.length; i++) { |
627 pages[i].isRendered = false; | 659 pages[i].isRendered = false; |
628 } | 660 } |
629 this.currentPages_ = []; | 661 this.currentPages_ = []; |
630 }; | 662 }; |
631 | 663 |
664 /** | |
665 * Record that the given page has been rendered. | |
666 * @param {Page} page The page that was rendered. | |
667 * @private | |
668 */ | |
632 HistoryView.prototype.setPageRendered_ = function(page) { | 669 HistoryView.prototype.setPageRendered_ = function(page) { |
633 page.isRendered = true; | 670 page.isRendered = true; |
634 this.currentPages_.push(page); | 671 this.currentPages_.push(page); |
635 }; | 672 }; |
636 | 673 |
637 /** | 674 /** |
638 * Update the page with results. | 675 * Update the page with results. |
676 * @private | |
639 */ | 677 */ |
640 HistoryView.prototype.displayResults_ = function() { | 678 HistoryView.prototype.displayResults_ = function() { |
641 var results = this.model_.getNumberedRange( | 679 var results = this.model_.getNumberedRange( |
642 this.pageIndex_ * RESULTS_PER_PAGE, | 680 this.pageIndex_ * RESULTS_PER_PAGE, |
643 this.pageIndex_ * RESULTS_PER_PAGE + RESULTS_PER_PAGE); | 681 this.pageIndex_ * RESULTS_PER_PAGE + RESULTS_PER_PAGE); |
644 | 682 |
645 var searchText = this.model_.getSearchText(); | 683 var searchText = this.model_.getSearchText(); |
646 if (searchText) { | 684 if (searchText) { |
647 // Add a header for the search results, if there isn't already one. | 685 // Add a header for the search results, if there isn't already one. |
648 if (!this.resultDiv_.querySelector('h3')) { | 686 if (!this.resultDiv_.querySelector('h3')) { |
(...skipping 54 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
703 resultsFragment.appendChild(dayResults); | 741 resultsFragment.appendChild(dayResults); |
704 } | 742 } |
705 this.resultDiv_.appendChild(resultsFragment); | 743 this.resultDiv_.appendChild(resultsFragment); |
706 } | 744 } |
707 this.displayNavBar_(); | 745 this.displayNavBar_(); |
708 this.updateEntryAnchorWidth_(); | 746 this.updateEntryAnchorWidth_(); |
709 }; | 747 }; |
710 | 748 |
711 /** | 749 /** |
712 * Update the pagination tools. | 750 * Update the pagination tools. |
751 * @private | |
713 */ | 752 */ |
714 HistoryView.prototype.displayNavBar_ = function() { | 753 HistoryView.prototype.displayNavBar_ = function() { |
715 this.pageDiv_.textContent = ''; | 754 this.pageDiv_.textContent = ''; |
716 | 755 |
717 if (this.pageIndex_ > 0) { | 756 if (this.pageIndex_ > 0) { |
718 this.pageDiv_.appendChild( | 757 this.pageDiv_.appendChild( |
719 this.createPageNav_(0, localStrings.getString('newest'))); | 758 this.createPageNav_(0, localStrings.getString('newest'))); |
720 this.pageDiv_.appendChild( | 759 this.pageDiv_.appendChild( |
721 this.createPageNav_(this.pageIndex_ - 1, | 760 this.createPageNav_(this.pageIndex_ - 1, |
722 localStrings.getString('newer'))); | 761 localStrings.getString('newer'))); |
723 } | 762 } |
724 | 763 |
725 // TODO(feldstein): this causes the navbar to not show up when your first | 764 // TODO(feldstein): this causes the navbar to not show up when your first |
726 // page has the exact amount of results as RESULTS_PER_PAGE. | 765 // page has the exact amount of results as RESULTS_PER_PAGE. |
727 if (this.model_.getSize() > (this.pageIndex_ + 1) * RESULTS_PER_PAGE) { | 766 if (this.model_.getSize() > (this.pageIndex_ + 1) * RESULTS_PER_PAGE) { |
728 this.pageDiv_.appendChild( | 767 this.pageDiv_.appendChild( |
729 this.createPageNav_(this.pageIndex_ + 1, | 768 this.createPageNav_(this.pageIndex_ + 1, |
730 localStrings.getString('older'))); | 769 localStrings.getString('older'))); |
731 } | 770 } |
732 }; | 771 }; |
733 | 772 |
734 /** | 773 /** |
735 * Make a DOM object representation of a page navigation link. | 774 * Make a DOM object representation of a page navigation link. |
736 * @param {number} page The page index the navigation element should link to | 775 * @param {number} page The page index the navigation element should link to |
737 * @param {string} name The text content of the link | 776 * @param {string} name The text content of the link |
738 * @return {HTMLAnchorElement} the pagination link | 777 * @return {HTMLAnchorElement} the pagination link |
778 * @private | |
739 */ | 779 */ |
740 HistoryView.prototype.createPageNav_ = function(page, name) { | 780 HistoryView.prototype.createPageNav_ = function(page, name) { |
741 var navButton = createElementWithClassName('button', 'link-button'); | 781 var navButton = createElementWithClassName('button', 'link-button'); |
742 navButton.textContent = name; | 782 navButton.textContent = name; |
743 navButton.onclick = function() { | 783 navButton.onclick = function() { |
744 setPage(page); | 784 setPage(page); |
745 }; | 785 }; |
746 return navButton; | 786 return navButton; |
747 }; | 787 }; |
748 | 788 |
(...skipping 26 matching lines...) Expand all Loading... | |
775 // var anchorMaxWith = titleElement.offsetWidth; | 815 // var anchorMaxWith = titleElement.offsetWidth; |
776 // this.entryAnchorRule_.style.maxWidth = anchorMaxWith + 'px'; | 816 // this.entryAnchorRule_.style.maxWidth = anchorMaxWith + 'px'; |
777 // // Adjust by the width of star plus its margin. | 817 // // Adjust by the width of star plus its margin. |
778 // this.entryAnchorStarredRule_.style.maxWidth = anchorMaxWith - 23 + 'px'; | 818 // this.entryAnchorStarredRule_.style.maxWidth = anchorMaxWith - 23 + 'px'; |
779 }; | 819 }; |
780 | 820 |
781 /////////////////////////////////////////////////////////////////////////////// | 821 /////////////////////////////////////////////////////////////////////////////// |
782 // State object: | 822 // State object: |
783 /** | 823 /** |
784 * An 'AJAX-history' implementation. | 824 * An 'AJAX-history' implementation. |
785 * @param {HistoryModel} model The model we're representing | 825 * @param {HistoryModel} model The model we're representing. |
786 * @param {HistoryView} view The view we're representing | 826 * @param {HistoryView} view The view we're representing. |
827 * @constructor | |
787 */ | 828 */ |
788 function PageState(model, view) { | 829 function PageState(model, view) { |
789 // Enforce a singleton. | 830 // Enforce a singleton. |
790 if (PageState.instance) { | 831 if (PageState.instance) { |
791 return PageState.instance; | 832 return PageState.instance; |
792 } | 833 } |
793 | 834 |
794 this.model = model; | 835 this.model = model; |
795 this.view = view; | 836 this.view = view; |
796 | 837 |
797 if (typeof this.checker_ != 'undefined' && this.checker_) { | 838 if (typeof this.checker_ != 'undefined' && this.checker_) { |
798 clearInterval(this.checker_); | 839 clearInterval(this.checker_); |
799 } | 840 } |
800 | 841 |
801 // TODO(glen): Replace this with a bound method so we don't need | 842 // TODO(glen): Replace this with a bound method so we don't need |
802 // public model and view. | 843 // public model and view. |
803 this.checker_ = setInterval((function(state_obj) { | 844 this.checker_ = setInterval((function(state_obj) { |
804 var hashData = state_obj.getHashData(); | 845 var hashData = state_obj.getHashData(); |
805 if (hashData.q != state_obj.model.getSearchText()) { | 846 if (hashData.q != state_obj.model.getSearchText()) { |
806 state_obj.view.setSearch(hashData.q, parseInt(hashData.p, 10)); | 847 state_obj.view.setSearch(hashData.q, parseInt(hashData.p, 10)); |
807 } else if (parseInt(hashData.p, 10) != state_obj.view.getPage()) { | 848 } else if (parseInt(hashData.p, 10) != state_obj.view.getPage()) { |
808 state_obj.view.setPage(hashData.p); | 849 state_obj.view.setPage(hashData.p); |
809 } | 850 } |
810 }), 50, this); | 851 }), 50, this); |
811 } | 852 } |
812 | 853 |
854 /** | |
855 * Holds the singleton instance. | |
856 */ | |
813 PageState.instance = null; | 857 PageState.instance = null; |
814 | 858 |
815 /** | 859 /** |
816 * @return {Object} An object containing parameters from our window hash. | 860 * @return {Object} An object containing parameters from our window hash. |
817 */ | 861 */ |
818 PageState.prototype.getHashData = function() { | 862 PageState.prototype.getHashData = function() { |
819 var result = { | 863 var result = { |
820 e : 0, | 864 e: 0, |
821 q : '', | 865 q: '', |
822 p : 0 | 866 p: 0 |
823 }; | 867 }; |
824 | 868 |
825 if (!window.location.hash) { | 869 if (!window.location.hash) { |
826 return result; | 870 return result; |
827 } | 871 } |
828 | 872 |
829 var hashSplit = window.location.hash.substr(1).split('&'); | 873 var hashSplit = window.location.hash.substr(1).split('&'); |
830 for (var i = 0; i < hashSplit.length; i++) { | 874 for (var i = 0; i < hashSplit.length; i++) { |
831 var pair = hashSplit[i].split('='); | 875 var pair = hashSplit[i].split('='); |
832 if (pair.length > 1) { | 876 if (pair.length > 1) { |
(...skipping 111 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
944 // Call the native function to remove history entries. | 988 // Call the native function to remove history entries. |
945 // First arg is a time in seconds (passed as String) identifying the day. | 989 // First arg is a time in seconds (passed as String) identifying the day. |
946 // Remaining args are URLs of history entries from that day to delete. | 990 // Remaining args are URLs of history entries from that day to delete. |
947 var timeInSeconds = Math.floor(deleteQueue[0].date.getTime() / 1000); | 991 var timeInSeconds = Math.floor(deleteQueue[0].date.getTime() / 1000); |
948 chrome.send('removeURLsOnOneDay', | 992 chrome.send('removeURLsOnOneDay', |
949 [String(timeInSeconds)].concat(deleteQueue[0].urls)); | 993 [String(timeInSeconds)].concat(deleteQueue[0].urls)); |
950 } | 994 } |
951 } | 995 } |
952 | 996 |
953 /** | 997 /** |
954 * Open the clear browsing data dialog. | 998 * Click handler for the 'Clear browsing data' dialog. |
999 * @param {Event} e The click event. | |
955 */ | 1000 */ |
956 function openClearBrowsingData() { | 1001 function openClearBrowsingData(e) { |
957 chrome.send('clearBrowsingData', []); | 1002 chrome.send('clearBrowsingData'); |
958 return false; | |
959 } | 1003 } |
960 | 1004 |
961 /** | 1005 /** |
962 * Queue a set of URLs from the same day for deletion. | 1006 * Queue a set of URLs from the same day for deletion. |
963 * @param {Date} date A date indicating the day the URLs were visited. | 1007 * @param {Date} date A date indicating the day the URLs were visited. |
964 * @param {Array} urls Array of URLs from the same day to be deleted. | 1008 * @param {Array} urls Array of URLs from the same day to be deleted. |
965 * @param {Function} opt_callback An optional callback to be executed when | 1009 * @param {Function} opt_callback An optional callback to be executed when |
966 * the deletion is complete. | 1010 * the deletion is complete. |
967 */ | 1011 */ |
968 function queueURLsForDeletion(date, urls, opt_callback) { | 1012 function queueURLsForDeletion(date, urls, opt_callback) { |
969 deleteQueue.push({ 'date': date, 'urls': urls, 'callback': opt_callback }); | 1013 deleteQueue.push({ 'date': date, 'urls': urls, 'callback': opt_callback }); |
970 } | 1014 } |
971 | 1015 |
972 function reloadHistory() { | 1016 function reloadHistory() { |
973 historyView.reload(); | 1017 historyView.reload(); |
974 } | 1018 } |
975 | 1019 |
976 /** | 1020 /** |
977 * Collect IDs from checked checkboxes and send to Chrome for deletion. | 1021 * Click handler for the 'Remove selected items' button. |
1022 * Collect IDs from the checked checkboxes and send to Chrome for deletion. | |
978 */ | 1023 */ |
979 function removeItems() { | 1024 function removeItems() { |
980 var checked = document.querySelectorAll( | 1025 var checked = document.querySelectorAll( |
981 'input[type=checkbox]:checked:not([disabled])'); | 1026 'input[type=checkbox]:checked:not([disabled])'); |
982 var urls = []; | 1027 var urls = []; |
983 var disabledItems = []; | 1028 var disabledItems = []; |
984 var queue = []; | 1029 var queue = []; |
985 var date = new Date(); | 1030 var date = new Date(); |
986 | 1031 |
987 for (var i = 0; i < checked.length; i++) { | 1032 for (var i = 0; i < checked.length; i++) { |
(...skipping 29 matching lines...) Expand all Loading... | |
1017 // If the remove is cancelled, return the checkboxes to their | 1062 // If the remove is cancelled, return the checkboxes to their |
1018 // enabled, non-line-through state. | 1063 // enabled, non-line-through state. |
1019 for (var i = 0; i < disabledItems.length; i++) { | 1064 for (var i = 0; i < disabledItems.length; i++) { |
1020 var checkbox = disabledItems[i]; | 1065 var checkbox = disabledItems[i]; |
1021 var link = findAncestorWithClass( | 1066 var link = findAncestorWithClass( |
1022 checkbox, 'entry-box').querySelector('a'); | 1067 checkbox, 'entry-box').querySelector('a'); |
1023 checkbox.disabled = false; | 1068 checkbox.disabled = false; |
1024 link.classList.remove('to-be-removed'); | 1069 link.classList.remove('to-be-removed'); |
1025 } | 1070 } |
1026 } | 1071 } |
1027 return false; | |
1028 } | 1072 } |
1029 | 1073 |
1030 /** | 1074 /** |
1031 * Toggle state of checkbox and handle Shift modifier. | 1075 * Handler for the 'click' event on a checkbox. |
1076 * @param {Event} e The click event. | |
1032 */ | 1077 */ |
1033 function checkboxClicked(event) { | 1078 function checkboxClicked(e) { |
1034 var id = Number(this.id.slice("checkbox-".length)); | 1079 var checkbox = e.currentTarget; |
1080 var id = Number(checkbox.id.slice('checkbox-'.length)); | |
1081 // Handle multi-select if shift was pressed. | |
1035 if (event.shiftKey && (selectionAnchor != -1)) { | 1082 if (event.shiftKey && (selectionAnchor != -1)) { |
1036 var checked = this.checked; | 1083 var checked = checkbox.checked; |
1037 // Set all checkboxes from the anchor up to the clicked checkbox to the | 1084 // Set all checkboxes from the anchor up to the clicked checkbox to the |
1038 // state of the clicked one. | 1085 // state of the clicked one. |
1039 var begin = Math.min(id, selectionAnchor); | 1086 var begin = Math.min(id, selectionAnchor); |
1040 var end = Math.max(id, selectionAnchor); | 1087 var end = Math.max(id, selectionAnchor); |
1041 for (var i = begin; i <= end; i++) { | 1088 for (var i = begin; i <= end; i++) { |
1042 var checkbox = document.querySelector('#checkbox-' + i); | 1089 var checkbox = document.querySelector('#checkbox-' + i); |
1043 if (checkbox) | 1090 if (checkbox) |
1044 checkbox.checked = checked; | 1091 checkbox.checked = checked; |
1045 } | 1092 } |
1046 } | 1093 } |
(...skipping 14 matching lines...) Expand all Loading... | |
1061 | 1108 |
1062 // Delete the node when the animation is complete. | 1109 // Delete the node when the animation is complete. |
1063 node.addEventListener('webkitTransitionEnd', function() { | 1110 node.addEventListener('webkitTransitionEnd', function() { |
1064 node.parentNode.removeChild(node); | 1111 node.parentNode.removeChild(node); |
1065 }); | 1112 }); |
1066 } | 1113 } |
1067 | 1114 |
1068 /** | 1115 /** |
1069 * Removes a single entry from the view. Also removes gaps before and after | 1116 * Removes a single entry from the view. Also removes gaps before and after |
1070 * entry if necessary. | 1117 * entry if necessary. |
1118 * @param {Node} entry The DOM node representing the entry to be removed. | |
1071 */ | 1119 */ |
1072 function removeEntryFromView(entry) { | 1120 function removeEntryFromView(entry) { |
1073 var nextEntry = entry.nextSibling; | 1121 var nextEntry = entry.nextSibling; |
1074 var previousEntry = entry.previousSibling; | 1122 var previousEntry = entry.previousSibling; |
1075 | 1123 |
1076 removeNode(entry); | 1124 removeNode(entry); |
1077 | 1125 |
1078 // if there is no previous entry, and the next entry is a gap, remove it | 1126 // if there is no previous entry, and the next entry is a gap, remove it |
1079 if (!previousEntry && nextEntry && nextEntry.className == 'gap') { | 1127 if (!previousEntry && nextEntry && nextEntry.className == 'gap') { |
1080 removeNode(nextEntry); | 1128 removeNode(nextEntry); |
1081 } | 1129 } |
1082 | 1130 |
1083 // if there is no next entry, and the previous entry is a gap, remove it | 1131 // if there is no next entry, and the previous entry is a gap, remove it |
1084 if (!nextEntry && previousEntry && previousEntry.className == 'gap') { | 1132 if (!nextEntry && previousEntry && previousEntry.className == 'gap') { |
1085 removeNode(previousEntry); | 1133 removeNode(previousEntry); |
1086 } | 1134 } |
1087 | 1135 |
1088 // if both the next and previous entries are gaps, remove one | 1136 // if both the next and previous entries are gaps, remove one |
1089 if (nextEntry && nextEntry.className == 'gap' && | 1137 if (nextEntry && nextEntry.className == 'gap' && |
1090 previousEntry && previousEntry.className == 'gap') { | 1138 previousEntry && previousEntry.className == 'gap') { |
1091 removeNode(nextEntry); | 1139 removeNode(nextEntry); |
1092 } | 1140 } |
1093 } | 1141 } |
1094 | 1142 |
1095 /////////////////////////////////////////////////////////////////////////////// | 1143 /////////////////////////////////////////////////////////////////////////////// |
1096 // Chrome callbacks: | 1144 // Chrome callbacks: |
1145 | |
1097 /** | 1146 /** |
1098 * Our history system calls this function with results from searches. | 1147 * Our history system calls this function with results from searches. |
1148 * @param {Object} info An object containing information about the query. | |
1149 * @param {Array} results A list of results. | |
1099 */ | 1150 */ |
1100 function historyResult(info, results) { | 1151 function historyResult(info, results) { |
1101 historyModel.addResults(info, results); | 1152 historyModel.addResults(info, results); |
1102 } | 1153 } |
1103 | 1154 |
1104 /** | 1155 /** |
1105 * Our history system calls this function when a deletion has finished. | 1156 * Our history system calls this function when a deletion has finished. |
1106 */ | 1157 */ |
1107 function deleteComplete() { | 1158 function deleteComplete() { |
1108 if (deleteQueue.length > 0) { | 1159 if (deleteQueue.length > 0) { |
(...skipping 31 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
1140 historyView.reload(); | 1191 historyView.reload(); |
1141 } | 1192 } |
1142 | 1193 |
1143 // Add handlers to HTML elements. | 1194 // Add handlers to HTML elements. |
1144 document.addEventListener('DOMContentLoaded', load); | 1195 document.addEventListener('DOMContentLoaded', load); |
1145 | 1196 |
1146 // This event lets us enable and disable menu items before the menu is shown. | 1197 // This event lets us enable and disable menu items before the menu is shown. |
1147 document.addEventListener('canExecute', function(e) { | 1198 document.addEventListener('canExecute', function(e) { |
1148 e.canExecute = true; | 1199 e.canExecute = true; |
1149 }); | 1200 }); |
OLD | NEW |