Chromium Code Reviews| 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 |