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 // TODO(jhawkins): Use hidden instead of showInline* and display:none. | 5 // TODO(jhawkins): Use hidden instead of showInline* and display:none. |
6 | 6 |
7 /** | 7 /** |
8 * The type of the download object. The definition is based on | 8 * The type of the download object. The definition is based on |
9 * chrome/browser/ui/webui/downloads_dom_handler.cc:CreateDownloadItemValue() | 9 * chrome/browser/ui/webui/downloads_dom_handler.cc:CreateDownloadItemValue() |
10 * @typedef {{by_ext_id: (string|undefined), | 10 * @typedef {{by_ext_id: (string|undefined), |
(...skipping 73 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
84 */ | 84 */ |
85 function Downloads() { | 85 function Downloads() { |
86 /** | 86 /** |
87 * @type {!Object.<string, Download>} | 87 * @type {!Object.<string, Download>} |
88 * @private | 88 * @private |
89 */ | 89 */ |
90 this.downloads_ = {}; | 90 this.downloads_ = {}; |
91 this.node_ = $('downloads-display'); | 91 this.node_ = $('downloads-display'); |
92 this.summary_ = $('downloads-summary-text'); | 92 this.summary_ = $('downloads-summary-text'); |
93 this.searchText_ = ''; | 93 this.searchText_ = ''; |
94 this.focusGrid_ = new cr.ui.FocusGrid(this.node_, | |
95 new DownloadFocusObserver()); | |
94 | 96 |
95 // Keep track of the dates of the newest and oldest downloads so that we | 97 // Keep track of the dates of the newest and oldest downloads so that we |
96 // know where to insert them. | 98 // know where to insert them. |
97 this.newestTime_ = -1; | 99 this.newestTime_ = -1; |
98 | 100 |
99 // Icon load request queue. | 101 // Icon load request queue. |
100 this.iconLoadQueue_ = []; | 102 this.iconLoadQueue_ = []; |
101 this.isIconLoading_ = false; | 103 this.isIconLoading_ = false; |
102 | 104 |
103 this.progressForeground1_ = new Image(); | 105 this.progressForeground1_ = new Image(); |
(...skipping 65 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
169 var noDownloadsOrResults = $('no-downloads-or-results'); | 171 var noDownloadsOrResults = $('no-downloads-or-results'); |
170 noDownloadsOrResults.textContent = loadTimeData.getString( | 172 noDownloadsOrResults.textContent = loadTimeData.getString( |
171 this.searchText_ ? 'no_search_results' : 'no_downloads'); | 173 this.searchText_ ? 'no_search_results' : 'no_downloads'); |
172 | 174 |
173 var hasDownloads = this.size() > 0; | 175 var hasDownloads = this.size() > 0; |
174 this.node_.hidden = !hasDownloads; | 176 this.node_.hidden = !hasDownloads; |
175 noDownloadsOrResults.hidden = hasDownloads; | 177 noDownloadsOrResults.hidden = hasDownloads; |
176 | 178 |
177 if (loadTimeData.getBoolean('allow_deleting_history')) | 179 if (loadTimeData.getBoolean('allow_deleting_history')) |
178 $('clear-all').hidden = !hasDownloads || this.searchText_.length > 0; | 180 $('clear-all').hidden = !hasDownloads || this.searchText_.length > 0; |
181 | |
182 var grid = []; | |
183 for (var node = this.node_.firstChild; node; node = node.nextSibling) { | |
184 // Add all clickable elements as a row into the grid. | |
185 grid.push(node.getElementsByClassName('row-item')); | |
Dan Beam
2014/12/22 23:19:59
use querySelectorAll('.row-item') unless you want
hcarmona
2015/01/13 00:04:40
Elements are now added explicitly, so there's no n
| |
186 } | |
187 this.focusGrid_.setGrid(grid); | |
179 }; | 188 }; |
180 | 189 |
181 /** | 190 /** |
191 * Decorate elem so that it can be added to the focusGrid_. | |
192 * @param {!Element} elem The element that should be decorated. | |
193 */ | |
194 Downloads.makeFocusable = function(elem) { | |
dmazzoni
2014/12/22 23:14:07
Maybe makePartOfFocusGrid or addFocusGridItemClass
Dan Beam
2014/12/22 23:19:59
what happens if this class is added after the focu
hcarmona
2015/01/13 00:04:40
Created the rebuildFocusGrid_ method to make it mo
hcarmona
2015/01/13 00:04:40
Done.
| |
195 elem.classList.add('row-item'); | |
196 }; | |
197 | |
198 /** | |
182 * Returns the number of downloads in the model. Used by tests. | 199 * Returns the number of downloads in the model. Used by tests. |
183 * @return {number} Returns the number of downloads shown on the page. | 200 * @return {number} Returns the number of downloads shown on the page. |
184 */ | 201 */ |
185 Downloads.prototype.size = function() { | 202 Downloads.prototype.size = function() { |
186 return Object.keys(this.downloads_).length; | 203 return Object.keys(this.downloads_).length; |
187 }; | 204 }; |
188 | 205 |
189 /** | 206 /** |
190 * Called whenever the downloads lists items have changed (either by being | 207 * Called whenever the downloads lists items have changed (either by being |
191 * updated, added, or removed). | 208 * updated, added, or removed). |
(...skipping 105 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
297 * @private | 314 * @private |
298 */ | 315 */ |
299 Downloads.prototype.onCommand_ = function(e) { | 316 Downloads.prototype.onCommand_ = function(e) { |
300 if (e.command.id == 'undo-command') | 317 if (e.command.id == 'undo-command') |
301 chrome.send('undo'); | 318 chrome.send('undo'); |
302 else if (e.command.id == 'clear-all-command') | 319 else if (e.command.id == 'clear-all-command') |
303 clearAll(); | 320 clearAll(); |
304 }; | 321 }; |
305 | 322 |
306 /////////////////////////////////////////////////////////////////////////////// | 323 /////////////////////////////////////////////////////////////////////////////// |
324 // DownloadFocusObserver | |
325 | |
326 /** | |
327 * @constructor | |
328 * @implements {cr.ui.FocusRow.Observer} | |
329 */ | |
330 function DownloadFocusObserver() {} | |
Dan Beam
2014/12/22 23:19:59
you could probably make the HistoryFocusObserver i
hcarmona
2015/01/13 00:04:40
Observer will now handle the case when a column mi
| |
331 | |
332 DownloadFocusObserver.prototype = { | |
333 /** @override */ | |
334 onActivate: function(row) { | |
335 this.getActiveRowElement_(row).classList.add('active'); | |
336 }, | |
337 | |
338 /** @override */ | |
339 onDeactivate: function(row) { | |
340 this.getActiveRowElement_(row).classList.remove('active'); | |
341 }, | |
342 | |
343 /** | |
344 * @param {cr.ui.FocusRow} row The row to find an element for. | |
345 * @return {Element} |row|'s "active" element. | |
346 * @private | |
347 */ | |
348 getActiveRowElement_: function(row) { | |
349 return findAncestorByClass(row.items[0], 'download'); | |
350 }, | |
351 }; | |
352 | |
353 /////////////////////////////////////////////////////////////////////////////// | |
307 // Download | 354 // Download |
308 /** | 355 /** |
309 * A download and the DOM representation for that download. | 356 * A download and the DOM representation for that download. |
310 * @param {DownloadItem} download Info about the download. | 357 * @param {DownloadItem} download Info about the download. |
311 * @constructor | 358 * @constructor |
312 */ | 359 */ |
313 function Download(download) { | 360 function Download(download) { |
314 // Create DOM | 361 // Create DOM |
315 this.node = createElementWithClassName( | 362 this.node = createElementWithClassName( |
316 'div', 'download' + (download.otr ? ' otr' : '')); | 363 'div', 'download' + (download.otr ? ' otr' : '')); |
(...skipping 30 matching lines...) Expand all Loading... | |
347 this.nodeImg_.alt = ''; | 394 this.nodeImg_.alt = ''; |
348 this.safe_.appendChild(this.nodeImg_); | 395 this.safe_.appendChild(this.nodeImg_); |
349 | 396 |
350 // FileLink is used for completed downloads, otherwise we show FileName. | 397 // FileLink is used for completed downloads, otherwise we show FileName. |
351 this.nodeTitleArea_ = createElementWithClassName('div', 'title-area'); | 398 this.nodeTitleArea_ = createElementWithClassName('div', 'title-area'); |
352 this.safe_.appendChild(this.nodeTitleArea_); | 399 this.safe_.appendChild(this.nodeTitleArea_); |
353 | 400 |
354 this.nodeFileLink_ = createActionLink(this.openFile_.bind(this)); | 401 this.nodeFileLink_ = createActionLink(this.openFile_.bind(this)); |
355 this.nodeFileLink_.className = 'name'; | 402 this.nodeFileLink_.className = 'name'; |
356 this.nodeFileLink_.style.display = 'none'; | 403 this.nodeFileLink_.style.display = 'none'; |
404 Downloads.makeFocusable(this.nodeFileLink_); | |
357 this.nodeTitleArea_.appendChild(this.nodeFileLink_); | 405 this.nodeTitleArea_.appendChild(this.nodeFileLink_); |
358 | 406 |
359 this.nodeFileName_ = createElementWithClassName('span', 'name'); | 407 this.nodeFileName_ = createElementWithClassName('span', 'name'); |
360 this.nodeFileName_.style.display = 'none'; | 408 this.nodeFileName_.style.display = 'none'; |
361 this.nodeTitleArea_.appendChild(this.nodeFileName_); | 409 this.nodeTitleArea_.appendChild(this.nodeFileName_); |
362 | 410 |
363 this.nodeStatus_ = createElementWithClassName('span', 'status'); | 411 this.nodeStatus_ = createElementWithClassName('span', 'status'); |
364 this.nodeTitleArea_.appendChild(this.nodeStatus_); | 412 this.nodeTitleArea_.appendChild(this.nodeStatus_); |
365 | 413 |
366 var nodeURLDiv = createElementWithClassName('div', 'url-container'); | 414 var nodeURLDiv = createElementWithClassName('div', 'url-container'); |
367 this.safe_.appendChild(nodeURLDiv); | 415 this.safe_.appendChild(nodeURLDiv); |
368 | 416 |
369 this.nodeURL_ = createElementWithClassName('a', 'src-url'); | 417 this.nodeURL_ = createElementWithClassName('a', 'src-url'); |
370 this.nodeURL_.target = '_blank'; | 418 this.nodeURL_.target = '_blank'; |
419 Downloads.makeFocusable(this.nodeURL_); | |
371 nodeURLDiv.appendChild(this.nodeURL_); | 420 nodeURLDiv.appendChild(this.nodeURL_); |
372 | 421 |
373 // Controls. | 422 // Controls. |
374 this.nodeControls_ = createElementWithClassName('div', 'controls'); | 423 this.nodeControls_ = createElementWithClassName('div', 'controls'); |
375 this.safe_.appendChild(this.nodeControls_); | 424 this.safe_.appendChild(this.nodeControls_); |
376 | 425 |
377 // We don't need 'show in folder' in chromium os. See download_ui.cc and | 426 // We don't need 'show in folder' in chromium os. See download_ui.cc and |
378 // http://code.google.com/p/chromium-os/issues/detail?id=916. | 427 // http://code.google.com/p/chromium-os/issues/detail?id=916. |
379 if (loadTimeData.valueExists('control_showinfolder')) { | 428 if (loadTimeData.valueExists('control_showinfolder')) { |
380 this.controlShow_ = createActionLink(this.show_.bind(this), | 429 this.controlShow_ = createActionLink(this.show_.bind(this), |
381 loadTimeData.getString('control_showinfolder')); | 430 loadTimeData.getString('control_showinfolder')); |
431 Downloads.makeFocusable(this.controlShow_); | |
382 this.nodeControls_.appendChild(this.controlShow_); | 432 this.nodeControls_.appendChild(this.controlShow_); |
383 } else { | 433 } else { |
384 this.controlShow_ = null; | 434 this.controlShow_ = null; |
385 } | 435 } |
386 | 436 |
387 this.controlRetry_ = document.createElement('a'); | 437 this.controlRetry_ = document.createElement('a'); |
388 this.controlRetry_.download = ''; | 438 this.controlRetry_.download = ''; |
389 this.controlRetry_.textContent = loadTimeData.getString('control_retry'); | 439 this.controlRetry_.textContent = loadTimeData.getString('control_retry'); |
440 Downloads.makeFocusable(this.controlRetry_); | |
390 this.nodeControls_.appendChild(this.controlRetry_); | 441 this.nodeControls_.appendChild(this.controlRetry_); |
391 | 442 |
392 // Pause/Resume are a toggle. | 443 // Pause/Resume are a toggle. |
393 this.controlPause_ = createActionLink(this.pause_.bind(this), | 444 this.controlPause_ = createActionLink(this.pause_.bind(this), |
394 loadTimeData.getString('control_pause')); | 445 loadTimeData.getString('control_pause')); |
446 Downloads.makeFocusable(this.controlPause_); | |
395 this.nodeControls_.appendChild(this.controlPause_); | 447 this.nodeControls_.appendChild(this.controlPause_); |
396 | 448 |
397 this.controlResume_ = createActionLink(this.resume_.bind(this), | 449 this.controlResume_ = createActionLink(this.resume_.bind(this), |
398 loadTimeData.getString('control_resume')); | 450 loadTimeData.getString('control_resume')); |
451 Downloads.makeFocusable(this.controlResume_); | |
399 this.nodeControls_.appendChild(this.controlResume_); | 452 this.nodeControls_.appendChild(this.controlResume_); |
400 | 453 |
401 if (loadTimeData.getBoolean('allow_deleting_history')) { | 454 if (loadTimeData.getBoolean('allow_deleting_history')) { |
402 this.controlRemove_ = createActionLink(this.remove_.bind(this), | 455 this.controlRemove_ = createActionLink(this.remove_.bind(this), |
403 loadTimeData.getString('control_removefromlist')); | 456 loadTimeData.getString('control_removefromlist')); |
404 this.controlRemove_.classList.add('control-remove-link'); | 457 this.controlRemove_.classList.add('control-remove-link'); |
458 Downloads.makeFocusable(this.controlRemove_); | |
405 this.nodeControls_.appendChild(this.controlRemove_); | 459 this.nodeControls_.appendChild(this.controlRemove_); |
406 } | 460 } |
407 | 461 |
408 this.controlCancel_ = createActionLink(this.cancel_.bind(this), | 462 this.controlCancel_ = createActionLink(this.cancel_.bind(this), |
409 loadTimeData.getString('control_cancel')); | 463 loadTimeData.getString('control_cancel')); |
464 Downloads.makeFocusable(this.controlCancel_); | |
410 this.nodeControls_.appendChild(this.controlCancel_); | 465 this.nodeControls_.appendChild(this.controlCancel_); |
411 | 466 |
412 this.controlByExtension_ = document.createElement('span'); | 467 this.controlByExtension_ = document.createElement('span'); |
413 this.nodeControls_.appendChild(this.controlByExtension_); | 468 this.nodeControls_.appendChild(this.controlByExtension_); |
414 | 469 |
415 // Container for 'unsafe download' UI. | 470 // Container for 'unsafe download' UI. |
416 this.danger_ = createElementWithClassName('div', 'show-dangerous'); | 471 this.danger_ = createElementWithClassName('div', 'show-dangerous'); |
417 this.node.appendChild(this.danger_); | 472 this.node.appendChild(this.danger_); |
418 | 473 |
419 this.dangerNodeImg_ = createElementWithClassName('img', 'icon'); | 474 this.dangerNodeImg_ = createElementWithClassName('img', 'icon'); |
420 this.dangerNodeImg_.alt = ''; | 475 this.dangerNodeImg_.alt = ''; |
421 this.danger_.appendChild(this.dangerNodeImg_); | 476 this.danger_.appendChild(this.dangerNodeImg_); |
422 | 477 |
423 this.dangerDesc_ = document.createElement('div'); | 478 this.dangerDesc_ = document.createElement('div'); |
424 this.danger_.appendChild(this.dangerDesc_); | 479 this.danger_.appendChild(this.dangerDesc_); |
425 | 480 |
426 // Buttons for the malicious case. | 481 // Buttons for the malicious case. |
427 this.malwareNodeControls_ = createElementWithClassName('div', 'controls'); | 482 this.malwareNodeControls_ = createElementWithClassName('div', 'controls'); |
428 this.malwareSave_ = createActionLink( | 483 this.malwareSave_ = createActionLink( |
429 this.saveDangerous_.bind(this), | 484 this.saveDangerous_.bind(this), |
430 loadTimeData.getString('danger_restore')); | 485 loadTimeData.getString('danger_restore')); |
486 Downloads.makeFocusable(this.malwareSave_); | |
431 this.malwareNodeControls_.appendChild(this.malwareSave_); | 487 this.malwareNodeControls_.appendChild(this.malwareSave_); |
432 this.malwareDiscard_ = createActionLink( | 488 this.malwareDiscard_ = createActionLink( |
433 this.discardDangerous_.bind(this), | 489 this.discardDangerous_.bind(this), |
434 loadTimeData.getString('control_removefromlist')); | 490 loadTimeData.getString('control_removefromlist')); |
491 Downloads.makeFocusable(this.malwareDiscard_); | |
435 this.malwareNodeControls_.appendChild(this.malwareDiscard_); | 492 this.malwareNodeControls_.appendChild(this.malwareDiscard_); |
436 this.danger_.appendChild(this.malwareNodeControls_); | 493 this.danger_.appendChild(this.malwareNodeControls_); |
437 | 494 |
438 // Buttons for the dangerous but not malicious case. | 495 // Buttons for the dangerous but not malicious case. |
439 this.dangerSave_ = createButton( | 496 this.dangerSave_ = createButton( |
440 this.saveDangerous_.bind(this), | 497 this.saveDangerous_.bind(this), |
441 loadTimeData.getString('danger_save')); | 498 loadTimeData.getString('danger_save')); |
442 this.danger_.appendChild(this.dangerSave_); | 499 this.danger_.appendChild(this.dangerSave_); |
443 | 500 |
444 this.dangerDiscard_ = createButton( | 501 this.dangerDiscard_ = createButton( |
(...skipping 194 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
639 // Format 'control_by_extension' with a link instead of plain text by | 696 // Format 'control_by_extension' with a link instead of plain text by |
640 // splitting the formatted string into pieces. | 697 // splitting the formatted string into pieces. |
641 var slug = 'XXXXX'; | 698 var slug = 'XXXXX'; |
642 var formatted = loadTimeData.getStringF('control_by_extension', slug); | 699 var formatted = loadTimeData.getStringF('control_by_extension', slug); |
643 var slugIndex = formatted.indexOf(slug); | 700 var slugIndex = formatted.indexOf(slug); |
644 this.controlByExtension_.textContent = formatted.substr(0, slugIndex); | 701 this.controlByExtension_.textContent = formatted.substr(0, slugIndex); |
645 this.controlByExtensionLink_ = document.createElement('a'); | 702 this.controlByExtensionLink_ = document.createElement('a'); |
646 this.controlByExtensionLink_.href = | 703 this.controlByExtensionLink_.href = |
647 'chrome://extensions#' + this.byExtensionId_; | 704 'chrome://extensions#' + this.byExtensionId_; |
648 this.controlByExtensionLink_.textContent = this.byExtensionName_; | 705 this.controlByExtensionLink_.textContent = this.byExtensionName_; |
706 Downloads.makeFocusable(this.controlByExtensionLink_); | |
649 this.controlByExtension_.appendChild(this.controlByExtensionLink_); | 707 this.controlByExtension_.appendChild(this.controlByExtensionLink_); |
650 if (slugIndex < (formatted.length - slug.length)) | 708 if (slugIndex < (formatted.length - slug.length)) |
651 this.controlByExtension_.appendChild(document.createTextNode( | 709 this.controlByExtension_.appendChild(document.createTextNode( |
652 formatted.substr(slugIndex + 1))); | 710 formatted.substr(slugIndex + 1))); |
653 } | 711 } |
654 | 712 |
655 this.nodeSince_.textContent = this.since_; | 713 this.nodeSince_.textContent = this.since_; |
656 this.nodeDate_.textContent = this.date_; | 714 this.nodeDate_.textContent = this.date_; |
657 // Don't unnecessarily update the url, as doing so will remove any | 715 // Don't unnecessarily update the url, as doing so will remove any |
658 // text selection the user has started (http://crbug.com/44982). | 716 // text selection the user has started (http://crbug.com/44982). |
(...skipping 319 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
978 if (Date.now() - start > 50) { | 1036 if (Date.now() - start > 50) { |
979 clearTimeout(resultsTimeout); | 1037 clearTimeout(resultsTimeout); |
980 resultsTimeout = setTimeout(tryDownloadUpdatedPeriodically, 5); | 1038 resultsTimeout = setTimeout(tryDownloadUpdatedPeriodically, 5); |
981 break; | 1039 break; |
982 } | 1040 } |
983 } | 1041 } |
984 } | 1042 } |
985 | 1043 |
986 // Add handlers to HTML elements. | 1044 // Add handlers to HTML elements. |
987 window.addEventListener('DOMContentLoaded', load); | 1045 window.addEventListener('DOMContentLoaded', load); |
OLD | NEW |