| OLD | NEW |
| (Empty) | |
| 1 <!DOCTYPE HTML> |
| 2 <html id="t"> |
| 3 <head> |
| 4 <meta charset="utf-8"> |
| 5 <title jscontent="title"></title> |
| 6 <script type="text/javascript"> |
| 7 /////////////////////////////////////////////////////////////////////////////// |
| 8 // Globals: |
| 9 var RESULTS_PER_PAGE = 60; |
| 10 var MAX_SEARCH_DEPTH = 18; |
| 11 |
| 12 // Amount of time between pageviews that we consider a 'break' in browsing, |
| 13 // measured in milliseconds. |
| 14 var BROWSING_GAP_TIME = 15 * 60 * 1000; |
| 15 |
| 16 function $(o) {return document.getElementById(o);} |
| 17 |
| 18 // TODO(glen): Get rid of these global references, replace with a controller |
| 19 // or just make the classes own more of the page. |
| 20 var historyModel; |
| 21 var historyView; |
| 22 var localStrings; |
| 23 var pageState; |
| 24 |
| 25 /////////////////////////////////////////////////////////////////////////////// |
| 26 // localStrings: |
| 27 /** |
| 28 * We get strings into the page by using JSTemplate to populate some elements |
| 29 * with localized content, then reading the content of those elements into |
| 30 * this global strings object. |
| 31 * @param {Node} node The DOM node containing all our strings. |
| 32 */ |
| 33 function LocalStrings(node) { |
| 34 this.strings_ = {}; |
| 35 |
| 36 var children = node.childNodes; |
| 37 for (var i = 0, child; child = children[i]; i++) { |
| 38 var id = child.id; |
| 39 if (id) { |
| 40 this.strings_[id] = child.innerHTML; |
| 41 } |
| 42 } |
| 43 } |
| 44 |
| 45 /** |
| 46 * Gets a localized string by its id. |
| 47 * @param {string} s The id of the string we want |
| 48 * @return {string} The localized string |
| 49 */ |
| 50 LocalStrings.prototype.getString = function(s) { |
| 51 return (s in this.strings_) ? this.strings_[s] : ''; |
| 52 } |
| 53 |
| 54 /** |
| 55 * Returns a formatted localized string (where all %s contents are replaced |
| 56 * by the second argument). |
| 57 * @param {string} s The id of the string we want |
| 58 * @param {string} d The string to include in the formatted string |
| 59 * @return {string} The formatted string. |
| 60 */ |
| 61 LocalStrings.prototype.formatString = function(s, d) { |
| 62 return (s in this.strings_) ? this.strings_[s].replace(/\%s/, d) : ''; |
| 63 } |
| 64 |
| 65 /////////////////////////////////////////////////////////////////////////////// |
| 66 // Page: |
| 67 /** |
| 68 * Class to hold all the information about an entry in our model. |
| 69 * @param {Object} result An object containing the page's data. |
| 70 * @param {boolean} continued Whether this page is on the same day as the |
| 71 * page before it |
| 72 */ |
| 73 function Page(result, continued, model) { |
| 74 this.model_ = model; |
| 75 this.title_ = result.title; |
| 76 this.url_ = result.url; |
| 77 this.snippet_ = result.snippet || ""; |
| 78 |
| 79 // All the date information is public so that owners can compare properties of |
| 80 // two items easily. |
| 81 |
| 82 // We get the time in seconds, but we want it in milliseconds. |
| 83 this.time = new Date(result.time * 1000); |
| 84 |
| 85 // See comment in BrowsingHistoryHandler::QueryComplete - we won't always |
| 86 // get all of these. |
| 87 this.dateRelativeDay = result.dateRelativeDay || ""; |
| 88 this.dateTimeOfDay = result.dateTimeOfDay || ""; |
| 89 this.dateShort = result.dateShort || ""; |
| 90 |
| 91 // Whether this is the continuation of a previous day. |
| 92 this.continued = continued; |
| 93 } |
| 94 |
| 95 // Page, Public: -------------------------------------------------------------- |
| 96 /** |
| 97 * @return {string} Gets the HTML representation of the page |
| 98 * for use in browse results. |
| 99 */ |
| 100 Page.prototype.getBrowseResultHTML = function() { |
| 101 return '<div class="entry">' + |
| 102 '<div class="time">' + |
| 103 this.dateTimeOfDay + |
| 104 '</div>' + |
| 105 this.getTitleHTML_() + |
| 106 '</div>'; |
| 107 } |
| 108 |
| 109 /** |
| 110 * @return {string} Gets the HTML representation of the page for |
| 111 * use in search results. |
| 112 */ |
| 113 Page.prototype.getSearchResultHTML = function() { |
| 114 return ['<tr class="entry"><td valign="top">', |
| 115 '<div class="time">', |
| 116 this.dateShort, |
| 117 '</div>', |
| 118 '</td><td valign="top">', |
| 119 this.getTitleHTML_(), |
| 120 '<div class="snippet">', |
| 121 this.getHighlightedSnippet_(), |
| 122 '</div>', |
| 123 '</td></tr>'].join(""); |
| 124 } |
| 125 |
| 126 // Page, private: ------------------------------------------------------------- |
| 127 /** |
| 128 * @return {string} The page's snippet highlighted with the model's |
| 129 * current search term. |
| 130 */ |
| 131 Page.prototype.getHighlightedSnippet_ = function() { |
| 132 return Page.getHighlightedText_(this.snippet_, this.model_.getSearchText()); |
| 133 } |
| 134 |
| 135 /** |
| 136 * @return {string} Gets the page's title highlighted with the |
| 137 * model's current search term. |
| 138 */ |
| 139 Page.prototype.getHighlightedTitle_ = function() { |
| 140 return Page.getHighlightedText_(this.title_, this.model_.getSearchText()); |
| 141 } |
| 142 |
| 143 /** |
| 144 * @return {string} HTML for the title block. |
| 145 */ |
| 146 Page.prototype.getTitleHTML_ = function() { |
| 147 return '<div class="title">' + |
| 148 '<a ' + |
| 149 'href="' + this.url_ + '" ' + |
| 150 'style="background-image:url(chrome://favicon/' + |
| 151 this.url_ + ')" ' + |
| 152 '>' + |
| 153 this.getHighlightedTitle_() + |
| 154 '</a>' + |
| 155 '</div>'; |
| 156 } |
| 157 |
| 158 // Page, private, static: ----------------------------------------------------- |
| 159 /** |
| 160 * Case-insensitively highlights a string. |
| 161 * @param {string} str The source string |
| 162 * @param {string} opt_highlight The string to highlight with |
| 163 * @return {string} The highlighted string |
| 164 */ |
| 165 Page.getHighlightedText_ = function(str, opt_highlight ) { |
| 166 if (!opt_highlight) return str; |
| 167 |
| 168 var r = new RegExp(Page.pregQuote_(opt_highlight), 'gim'); |
| 169 return str.replace(r, "<b>\$&</b>"); |
| 170 } |
| 171 |
| 172 /** |
| 173 * Quote a string so it can be used in a regular expression. |
| 174 * @param {string} str The source string |
| 175 * @return {string} The escaped string |
| 176 */ |
| 177 Page.pregQuote_ = function(str) { |
| 178 return str.replace(/([\\\.\+\*\?\[\^\]\$\(\)\{\}\=\!\<\>\|\:])/g, "\\$1"); |
| 179 } |
| 180 |
| 181 /////////////////////////////////////////////////////////////////////////////// |
| 182 // HistoryModel: |
| 183 /** |
| 184 * Global container for history data. Future optimizations might include |
| 185 * allowing the creation of a HistoryModel for each search string, allowing |
| 186 * quick flips back and forth between results. |
| 187 * |
| 188 * The history model is based around pages, and only fetching the data to |
| 189 * fill the currently requested page. This is somewhat dependent on the view, |
| 190 * and so future work may wish to change history model to operate on |
| 191 * timeframe (day or week) based containers. |
| 192 */ |
| 193 function HistoryModel() { |
| 194 this.clearModel_(); |
| 195 this.view_; |
| 196 } |
| 197 |
| 198 // HistoryModel, Public: ------------------------------------------------------ |
| 199 /** |
| 200 * Sets our current view that is called when the history model changes. |
| 201 * @param {HistoryView} view The view to set our current view to. |
| 202 */ |
| 203 HistoryModel.prototype.setView = function(view) { |
| 204 this.view_ = view; |
| 205 } |
| 206 |
| 207 /** |
| 208 * Start a new search - this will clear out our model. |
| 209 * @param {String} searchText The text to search for |
| 210 * @param {Number} opt_page The page to view - this is mostly used when setting |
| 211 * up an initial view, use #requestPage otherwise. |
| 212 */ |
| 213 HistoryModel.prototype.setSearchText = function(searchText, opt_page) { |
| 214 this.clearModel_(); |
| 215 this.searchText_ = searchText; |
| 216 this.requestedPage_ = opt_page ? opt_page : 0; |
| 217 this.getSearchResults_(); |
| 218 } |
| 219 |
| 220 /** |
| 221 * @return {String} The current search text. |
| 222 */ |
| 223 HistoryModel.prototype.getSearchText = function() { |
| 224 return this.searchText_; |
| 225 } |
| 226 |
| 227 /** |
| 228 * Tell the model that the view will want to see the current page. When |
| 229 * the data becomes available, the model will call the view back. |
| 230 * @page {Number} page The page we want to view. |
| 231 */ |
| 232 HistoryModel.prototype.requestPage = function(page) { |
| 233 this.requestedPage_ = page; |
| 234 this.updateSearch_(); |
| 235 } |
| 236 |
| 237 /** |
| 238 * Receiver for history query. |
| 239 * @param {String} term The search term that the results are for. |
| 240 * @param {Array} results A list of results |
| 241 */ |
| 242 HistoryModel.prototype.addResults = function(term, results) { |
| 243 this.inFlight_ = false; |
| 244 if (term != this.searchText_) { |
| 245 // If our results aren't for our current search term, they're rubbish. |
| 246 return; |
| 247 } |
| 248 |
| 249 // Currently we assume we're getting things in date order. This needs to |
| 250 // be updated if that ever changes. |
| 251 if (results) { |
| 252 var lastURL, lastDay; |
| 253 var oldLength = this.pages_.length; |
| 254 if (oldLength) { |
| 255 var oldPage = this.pages_[oldLength - 1]; |
| 256 lastURL = oldPage.url; |
| 257 lastDay = oldPage.dateRelativeDay; |
| 258 } |
| 259 |
| 260 for (var i = 0, thisResult; thisResult = results[i]; i++) { |
| 261 var thisURL = thisResult.url; |
| 262 var thisDay = thisResult.dateRelativeDay; |
| 263 |
| 264 // Remove adjacent duplicates. |
| 265 if (!lastURL || lastURL != thisURL) { |
| 266 // Figure out if this page is in the same day as the previous page, |
| 267 // this is used to determine how day headers should be drawn. |
| 268 this.pages_.push(new Page(thisResult, thisDay == lastDay, this)); |
| 269 lastDay = thisDay; |
| 270 lastURL = thisURL; |
| 271 } |
| 272 } |
| 273 } |
| 274 |
| 275 this.updateSearch_(); |
| 276 } |
| 277 |
| 278 /** |
| 279 * @return {Number} The number of pages in the model. |
| 280 */ |
| 281 HistoryModel.prototype.getSize = function() { |
| 282 return this.pages_.length; |
| 283 } |
| 284 |
| 285 /** |
| 286 * @return {boolean} Whether our history query has covered all of |
| 287 * the user's history |
| 288 */ |
| 289 HistoryModel.prototype.isComplete = function() { |
| 290 return this.complete_; |
| 291 } |
| 292 |
| 293 /** |
| 294 * Get a list of pages between specified index positions. |
| 295 * @param {Number} start The start index |
| 296 * @param {Number} end The end index |
| 297 * @return {Array} A list of pages |
| 298 */ |
| 299 HistoryModel.prototype.getNumberedRange = function(start, end) { |
| 300 if (start >= this.getSize()) |
| 301 return []; |
| 302 |
| 303 var end = end > this.getSize() ? this.getSize() : end; |
| 304 return this.pages_.slice(start, end); |
| 305 } |
| 306 |
| 307 // HistoryModel, Private: ----------------------------------------------------- |
| 308 HistoryModel.prototype.clearModel_ = function() { |
| 309 this.inFlight_ = false; // Whether a query is inflight. |
| 310 this.searchText_ = ''; |
| 311 this.searchMonth_ = 0; |
| 312 this.pages_ = []; // Date-sorted list of pages. |
| 313 |
| 314 // The page that the view wants to see - we only fetch slightly past this |
| 315 // point. If the view requests a page that we don't have data for, we try |
| 316 // to fetch it and call back when we're done. |
| 317 this.requestedPage_ = 0; |
| 318 |
| 319 this.complete_ = false; |
| 320 } |
| 321 |
| 322 /** |
| 323 * Figure out if we need to do more searches to fill the currently requested |
| 324 * page. If we think we can fill the page, call the view and let it know |
| 325 * we're ready to show something. |
| 326 */ |
| 327 HistoryModel.prototype.updateSearch_ = function() { |
| 328 if (this.searchMonth_ >= MAX_SEARCH_DEPTH) { |
| 329 // We have maxed out. There will be no more data. |
| 330 this.complete_ = true; |
| 331 this.view_.onModelReady(); |
| 332 } else { |
| 333 // If we can't fill the requested page, ask for more data unless a request |
| 334 // is still in-flight. |
| 335 if (!this.canFillPage_(this.requestedPage_) && !this.inFlight_) { |
| 336 this.getSearchResults_(this.searchMonth_ + 1); |
| 337 } |
| 338 |
| 339 // If we have any data for the requested page, show it. |
| 340 if (this.haveDataForPage_(this.requestedPage_)) { |
| 341 this.view_.onModelReady(); |
| 342 } |
| 343 } |
| 344 } |
| 345 |
| 346 /** |
| 347 * Get search results for a selected month. Our history system is optimized |
| 348 * for queries that don't cross month boundaries. |
| 349 * |
| 350 * TODO: Fix this for when the user's clock goes across month boundaries. |
| 351 * @param {number} opt_month How many months back to do the search. |
| 352 */ |
| 353 HistoryModel.prototype.getSearchResults_ = function(opt_month) { |
| 354 this.searchMonth_ = opt_month || 0; |
| 355 chrome.send('getHistory', |
| 356 [this.searchText_, String(this.searchMonth_)]); |
| 357 this.inFlight_ = true; |
| 358 } |
| 359 |
| 360 /** |
| 361 * Check to see if we have data for a given page. |
| 362 * @param {number} page The page number |
| 363 * @return {boolean} Whether we have any data for the given page. |
| 364 */ |
| 365 HistoryModel.prototype.haveDataForPage_ = function(page) { |
| 366 return (page * RESULTS_PER_PAGE < this.getSize()); |
| 367 } |
| 368 |
| 369 /** |
| 370 * Check to see if we have data to fill a page. |
| 371 * @param {number} page The page number. |
| 372 * @return {boolean} Whether we have data to fill the page. |
| 373 */ |
| 374 HistoryModel.prototype.canFillPage_ = function(page) { |
| 375 return ((page + 1) * RESULTS_PER_PAGE <= this.getSize()); |
| 376 } |
| 377 |
| 378 /////////////////////////////////////////////////////////////////////////////// |
| 379 // HistoryView: |
| 380 /** |
| 381 * Functions and state for populating the page with HTML. This should one-day |
| 382 * contain the view and use event handlers, rather than pushing HTML out and |
| 383 * getting called externally. |
| 384 * @param {HistoryModel} model The model backing this view. |
| 385 */ |
| 386 function HistoryView(model) { |
| 387 this.summaryDiv_ = $('results-summary'); |
| 388 this.summaryDiv_.innerHTML = localStrings.getString('loading'); |
| 389 this.resultDiv_ = $('results-display'); |
| 390 this.pageDiv_ = $('results-pagination'); |
| 391 this.model_ = model |
| 392 this.pageIndex_ = 0; |
| 393 |
| 394 this.model_.setView(this); |
| 395 } |
| 396 |
| 397 // HistoryView, public: ------------------------------------------------------- |
| 398 /** |
| 399 * Do a search and optionally view a certain page. |
| 400 * @param {string} term The string to search for. |
| 401 * @param {number} opt_page The page we wish to view, only use this for |
| 402 * setting up initial views, as this triggers a search. |
| 403 */ |
| 404 HistoryView.prototype.setSearch = function(term, opt_page) { |
| 405 this.pageIndex_ = parseInt(opt_page || 0, 10); |
| 406 window.scrollTo(0, 0); |
| 407 this.model_.setSearchText(term, this.pageIndex_); |
| 408 pageState.setUIState(term, this.pageIndex_); |
| 409 } |
| 410 |
| 411 /** |
| 412 * Switch to a specified page. |
| 413 * @param {string} term The string to search for. |
| 414 * @param {number} opt_page The page we wish to view. |
| 415 */ |
| 416 HistoryView.prototype.setPage = function(page) { |
| 417 this.pageIndex_ = parseInt(page); |
| 418 window.scrollTo(0, 0); |
| 419 this.model_.requestPage(page); |
| 420 pageState.setUIState(this.model_.getSearchText(), this.pageIndex_); |
| 421 } |
| 422 |
| 423 /** |
| 424 * @return {number} The page number being viewed. |
| 425 */ |
| 426 HistoryView.prototype.getPage = function() { |
| 427 return this.pageIndex_; |
| 428 } |
| 429 |
| 430 /** |
| 431 * Callback for the history model to let it know that it has data ready for us |
| 432 * to view. |
| 433 */ |
| 434 HistoryView.prototype.onModelReady = function() { |
| 435 this.displayResults_(); |
| 436 } |
| 437 |
| 438 // HistoryView, private: ------------------------------------------------------ |
| 439 /** |
| 440 * Update the page with results. |
| 441 */ |
| 442 HistoryView.prototype.displayResults_ = function() { |
| 443 var output = []; |
| 444 var results = this.model_.getNumberedRange( |
| 445 this.pageIndex_ * RESULTS_PER_PAGE, |
| 446 this.pageIndex_ * RESULTS_PER_PAGE + RESULTS_PER_PAGE); |
| 447 |
| 448 if (this.model_.getSearchText()) { |
| 449 output.push('<table class="results" cellspacing="0" ', |
| 450 'cellpadding="0" border="0">'); |
| 451 for (var i = 0, page; page = results[i]; i++) { |
| 452 output.push(page.getSearchResultHTML()); |
| 453 } |
| 454 output.push('</table>'); |
| 455 } else { |
| 456 var lastTime = Math.infinity; |
| 457 for (var i = 0, page; page = results[i]; i++) { |
| 458 // Break across day boundaries and insert gaps for browsing pauses. |
| 459 var thisTime = page.time.getTime(); |
| 460 if (page.continued && i == 0) { |
| 461 output.push('<div class="day">' + page.dateRelativeDay + ' ' + |
| 462 localStrings.getString('cont') + '</div>'); |
| 463 } else if (!page.continued) { |
| 464 output.push('<div class="day">' + page.dateRelativeDay + '</div>'); |
| 465 } else if (lastTime - thisTime > BROWSING_GAP_TIME) { |
| 466 output.push('<div class="gap"></div>'); |
| 467 } |
| 468 lastTime = thisTime; |
| 469 |
| 470 // Draw entry. |
| 471 output.push(page.getBrowseResultHTML()); |
| 472 } |
| 473 } |
| 474 this.resultDiv_.innerHTML = output.join(""); |
| 475 |
| 476 this.displaySummaryBar_(); |
| 477 this.displayNavBar_(); |
| 478 } |
| 479 |
| 480 /** |
| 481 * Update the summary bar with descriptive text. |
| 482 */ |
| 483 HistoryView.prototype.displaySummaryBar_ = function() { |
| 484 var searchText = this.model_.getSearchText(); |
| 485 if (searchText != '') { |
| 486 this.summaryDiv_.innerHTML = localStrings.formatString('searchresultsfor', |
| 487 searchText); |
| 488 } else { |
| 489 this.summaryDiv_.innerHTML = localStrings.getString('history'); |
| 490 } |
| 491 } |
| 492 |
| 493 /** |
| 494 * Update the pagination tools. |
| 495 */ |
| 496 HistoryView.prototype.displayNavBar_ = function() { |
| 497 var navOutput = []; |
| 498 if (this.pageIndex_ > 0) { |
| 499 navOutput.push(this.createPageNavHTML_(0, |
| 500 localStrings.getString('newest'))); |
| 501 navOutput.push(this.createPageNavHTML_( |
| 502 this.pageIndex_ - 1, localStrings.getString('newer'))); |
| 503 } |
| 504 if (this.model_.getSize() > (this.pageIndex_ + 1) * RESULTS_PER_PAGE) { |
| 505 navOutput.push(this.createPageNavHTML_( |
| 506 this.pageIndex_ + 1, localStrings.getString('older'))); |
| 507 } |
| 508 this.pageDiv_.innerHTML = navOutput.join(""); |
| 509 } |
| 510 |
| 511 /** |
| 512 * Get the HTML representation of a page navigation link. |
| 513 * @param {number} page The page index the navigation element should link to |
| 514 * @param {string} name The text content of the link |
| 515 * @return {string} HTML representation of the pagination link |
| 516 */ |
| 517 HistoryView.prototype.createPageNavHTML_ = function(page, name) { |
| 518 var hashString = PageState.getHashString(this.model_.getSearchText(), page); |
| 519 return '<a href="chrome://history/' + |
| 520 (hashString ? '#' + hashString : '') + |
| 521 '"' + |
| 522 'class="page-navigation"' + |
| 523 'onclick="setPage(' + page + '); return false;"' + |
| 524 '>' + name + '</a>'; |
| 525 } |
| 526 |
| 527 /////////////////////////////////////////////////////////////////////////////// |
| 528 // State object: |
| 529 /** |
| 530 * An 'AJAX-history' implementation. |
| 531 * @param {HistoryModel} model The model we're representing |
| 532 * @param {HistoryView} view The view we're representing |
| 533 */ |
| 534 function PageState(model, view) { |
| 535 // Enforce a singleton. |
| 536 if (PageState.instance) { |
| 537 return PageState.instance; |
| 538 } |
| 539 |
| 540 this.model = model; |
| 541 this.view = view; |
| 542 |
| 543 if (typeof this.checker_ != 'undefined' && this.checker_) { |
| 544 clearInterval(this.checker_); |
| 545 } |
| 546 |
| 547 // TODO(glen): Replace this with a bound method so we don't need |
| 548 // public model and view. |
| 549 this.checker_ = setInterval((function(state_obj) { |
| 550 var hashData = state_obj.getHashData(); |
| 551 |
| 552 if (hashData.q != state_obj.model.getSearchText(term)) { |
| 553 state_obj.view.setSearch(hashData.q, parseInt(hashData.p)); |
| 554 } else if (parseInt(hashData.p) != state_obj.view.getPage()) { |
| 555 state_obj.view.setPage(hashData.p); |
| 556 } |
| 557 }), 50, this); |
| 558 } |
| 559 |
| 560 PageState.instance = null; |
| 561 |
| 562 /** |
| 563 * @return {Object} An object containing parameters from our window hash. |
| 564 */ |
| 565 PageState.prototype.getHashData = function() { |
| 566 var result = { |
| 567 q : '', |
| 568 p : 0 |
| 569 }; |
| 570 |
| 571 if (!window.location.hash) { |
| 572 return result; |
| 573 } |
| 574 |
| 575 var hashSplit = window.location.hash.substr(1).split("&"); |
| 576 for (var i = 0; i < hashSplit.length; i++) { |
| 577 var pair = hashSplit[i].split("="); |
| 578 if (pair.length > 1) { |
| 579 result[pair[0]] = unescape(pair[1]); |
| 580 } |
| 581 } |
| 582 |
| 583 return result; |
| 584 } |
| 585 |
| 586 /** |
| 587 * Set the hash to a specified state, this will create an entry in the |
| 588 * session history so the back button cycles through hash states, which |
| 589 * are then picked up by our listener. |
| 590 * @param {string} term The current search string. |
| 591 * @param {string} page The page currently being viewed. |
| 592 */ |
| 593 PageState.prototype.setUIState = function(term, page) { |
| 594 // Make sure the form looks pretty. |
| 595 document.forms[0].term.value = term; |
| 596 |
| 597 var hash = PageState.getHashString(term, page); |
| 598 if (window.location.hash.substr(1) != hash) { |
| 599 window.location.hash = hash; |
| 600 } |
| 601 } |
| 602 |
| 603 /** |
| 604 * Static method to get the hash string for a specified state |
| 605 * @param {string} term The current search string. |
| 606 * @param {string} page The page currently being viewed. |
| 607 * @return {string} The string to be used in a hash. |
| 608 */ |
| 609 PageState.getHashString = function(term, page) { |
| 610 var newHash = []; |
| 611 if (term) { |
| 612 newHash.push("q=" + escape(term)); |
| 613 } |
| 614 if (page) { |
| 615 newHash.push("p=" + page); |
| 616 } |
| 617 |
| 618 return newHash.join("&"); |
| 619 } |
| 620 |
| 621 /////////////////////////////////////////////////////////////////////////////// |
| 622 // Document Functions: |
| 623 /** |
| 624 * Window onload handler, sets up the page. |
| 625 */ |
| 626 function load() { |
| 627 localStrings = new LocalStrings($('l10n')); |
| 628 historyModel = new HistoryModel(); |
| 629 historyView = new HistoryView(historyModel); |
| 630 pageState = new PageState(historyModel, historyView); |
| 631 |
| 632 // Create default view. |
| 633 var hashData = pageState.getHashData(); |
| 634 historyView.setSearch(hashData.q, hashData.p); |
| 635 } |
| 636 |
| 637 /** |
| 638 * TODO(glen): Get rid of this function. |
| 639 * Set the history view to a specified page. |
| 640 * @param {String} term The string to search for |
| 641 */ |
| 642 function setSearch(term) { |
| 643 if (historyView) { |
| 644 historyView.setSearch(term); |
| 645 } |
| 646 } |
| 647 |
| 648 /** |
| 649 * TODO(glen): Get rid of this function. |
| 650 * Set the history view to a specified page. |
| 651 * @param {number} page The page to set the view to. |
| 652 */ |
| 653 function setPage(page) { |
| 654 if (historyView) { |
| 655 historyView.setPage(page); |
| 656 } |
| 657 } |
| 658 |
| 659 /////////////////////////////////////////////////////////////////////////////// |
| 660 // Chrome callbacks: |
| 661 /** |
| 662 * Our history system calls this function with results from searches. |
| 663 */ |
| 664 function historyResult(term, results) { |
| 665 historyModel.addResults(term, results); |
| 666 } |
| 667 </script> |
| 668 <style type="text/css"> |
| 669 body { |
| 670 font-family:arial; |
| 671 background-color:white; |
| 672 color:black; |
| 673 font-size:84%; |
| 674 margin:10px; |
| 675 } |
| 676 .header { |
| 677 overflow:auto; |
| 678 clear:both; |
| 679 } |
| 680 .header .logo { |
| 681 float:left; |
| 682 } |
| 683 .header .form { |
| 684 float:left; |
| 685 margin-top:22px; |
| 686 margin-left:12px; |
| 687 } |
| 688 #results-summary { |
| 689 margin-top:12px; |
| 690 border-top:1px solid #9cc2ef; |
| 691 background-color:#ebeff9; |
| 692 font-weight:bold; |
| 693 padding:3px; |
| 694 margin-bottom:-8px; |
| 695 } |
| 696 #results-display { |
| 697 max-width:740px; |
| 698 } |
| 699 .day { |
| 700 margin-top:18px; |
| 701 margin-left:3px; |
| 702 } |
| 703 .gap { |
| 704 margin-left:18px; |
| 705 width:15px; |
| 706 border-right:1px solid #ddd; |
| 707 height:14px; |
| 708 } |
| 709 .entry { |
| 710 margin-left:18px; |
| 711 margin-top:6px; |
| 712 overflow:auto; |
| 713 } |
| 714 table.results { |
| 715 margin-left:4px; |
| 716 } |
| 717 .entry .time { |
| 718 color:#888; |
| 719 float:left; |
| 720 min-width:56px; |
| 721 margin-right:5px; |
| 722 padding-top:1px; |
| 723 } |
| 724 .entry .title { |
| 725 max-width:600px; |
| 726 overflow: hidden; |
| 727 white-space: nowrap; |
| 728 text-overflow: ellipsis; |
| 729 } |
| 730 .results .time, .results .title { |
| 731 margin-top:18px; |
| 732 } |
| 733 .entry .title a { |
| 734 background-repeat:no-repeat; |
| 735 background-size:16px; |
| 736 background-position:0px 1px; |
| 737 padding:1px 0px 4px 22px; |
| 738 } |
| 739 html[dir='rtl'] .entry .title a { |
| 740 background-position:right; |
| 741 padding-left:0px; |
| 742 padding-right:22px; |
| 743 } |
| 744 #results-pagination { |
| 745 padding-top:24px; |
| 746 margin-left:18px; |
| 747 } |
| 748 .page-navigation { |
| 749 padding:8px; |
| 750 background-color:#ebeff9; |
| 751 margin-right:4px; |
| 752 } |
| 753 .footer { |
| 754 height:24px; |
| 755 } |
| 756 </style> |
| 757 </head> |
| 758 <body onload="load();"> |
| 759 <div class="header"> |
| 760 <a href="" onclick="setSearch(''); return false;"> |
| 761 <img src="../../app/theme/history_section.png" |
| 762 width="67" height="67" class="logo" border="0" /></a> |
| 763 <form method="post" action="" |
| 764 onsubmit="setSearch(this.term.value); return false;" |
| 765 class="form"> |
| 766 <input type="text" name="term" id="term" /> |
| 767 <input type="submit" name="submit" jsvalues="value:searchbutton" /> |
| 768 </form> |
| 769 </div> |
| 770 <div class="main"> |
| 771 <div id="results-summary"></div> |
| 772 <div id="results-display"></div> |
| 773 <div id="results-pagination"></div> |
| 774 </div> |
| 775 <div class="footer"> |
| 776 </div> |
| 777 <div id="l10n" style="display:none;"> |
| 778 <span id="loading" jscontent="loading">Loading...</span> |
| 779 <span id="newest" jscontent="newest">« Newest</span> |
| 780 <span id="newer" jscontent="newer">‹ Newer</span> |
| 781 <span id="older" jscontent="older">Older ›</span> |
| 782 <span id="searchresultsfor" jscontent="searchresultsfor">Search results for '%
s'</span> |
| 783 <span id="history" jscontent="history">History</span> |
| 784 <span id="cont" jscontent="cont">(cont.)</span> |
| 785 <span id="noresults" jscontent="noresults">No results</span> |
| 786 <span id="noitems" jscontent="noitems">No items</span> |
| 787 <span id="delete" jscontent="delete">delete</span> |
| 788 </div> |
| 789 </body> |
| 790 </html> |
| OLD | NEW |