OLD | NEW |
| (Empty) |
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 | |
3 // found in the LICENSE file. | |
4 | |
5 /////////////////////////////////////////////////////////////////////////////// | |
6 // Helper functions | |
7 function $(o) {return document.getElementById(o);} | |
8 | |
9 /** | |
10 * Sets the display style of a node. | |
11 */ | |
12 function showInline(node, isShow) { | |
13 node.style.display = isShow ? 'inline' : 'none'; | |
14 } | |
15 | |
16 function showInlineBlock(node, isShow) { | |
17 node.style.display = isShow ? 'inline-block' : 'none'; | |
18 } | |
19 | |
20 /** | |
21 * Creates an element of a specified type with a specified class name. | |
22 * @param {string} type The node type. | |
23 * @param {string} className The class name to use. | |
24 */ | |
25 function createElementWithClassName(type, className) { | |
26 var elm = document.createElement(type); | |
27 elm.className = className; | |
28 return elm; | |
29 } | |
30 | |
31 /** | |
32 * Creates a link with a specified onclick handler and content | |
33 * @param {function()} onclick The onclick handler | |
34 * @param {string} value The link text | |
35 */ | |
36 function createLink(onclick, value) { | |
37 var link = document.createElement('a'); | |
38 link.onclick = onclick; | |
39 link.href = '#'; | |
40 link.textContent = value; | |
41 link.oncontextmenu = function() { return false; }; | |
42 return link; | |
43 } | |
44 | |
45 /** | |
46 * Creates a button with a specified onclick handler and content | |
47 * @param {function()} onclick The onclick handler | |
48 * @param {string} value The button text | |
49 */ | |
50 function createButton(onclick, value) { | |
51 var button = document.createElement('input'); | |
52 button.type = 'button'; | |
53 button.value = value; | |
54 button.onclick = onclick; | |
55 return button; | |
56 } | |
57 | |
58 /////////////////////////////////////////////////////////////////////////////// | |
59 // Downloads | |
60 /** | |
61 * Class to hold all the information about the visible downloads. | |
62 */ | |
63 function Downloads() { | |
64 this.downloads_ = {}; | |
65 this.node_ = $('downloads-display'); | |
66 this.summary_ = $('downloads-summary-text'); | |
67 this.searchText_ = ''; | |
68 | |
69 // Keep track of the dates of the newest and oldest downloads so that we | |
70 // know where to insert them. | |
71 this.newestTime_ = -1; | |
72 | |
73 // Icon load request queue. | |
74 this.iconLoadQueue_ = []; | |
75 this.isIconLoading_ = false; | |
76 } | |
77 | |
78 /** | |
79 * Called when a download has been updated or added. | |
80 * @param {Object} download A backend download object (see downloads_ui.cc) | |
81 */ | |
82 Downloads.prototype.updated = function(download) { | |
83 var id = download.id; | |
84 if (!!this.downloads_[id]) { | |
85 this.downloads_[id].update(download); | |
86 } else { | |
87 this.downloads_[id] = new Download(download); | |
88 // We get downloads in display order, so we don't have to worry about | |
89 // maintaining correct order - we can assume that any downloads not in | |
90 // display order are new ones and so we can add them to the top of the | |
91 // list. | |
92 if (download.started > this.newestTime_) { | |
93 this.node_.insertBefore(this.downloads_[id].node, this.node_.firstChild); | |
94 this.newestTime_ = download.started; | |
95 } else { | |
96 this.node_.appendChild(this.downloads_[id].node); | |
97 } | |
98 this.updateDateDisplay_(); | |
99 } | |
100 } | |
101 | |
102 /** | |
103 * Set our display search text. | |
104 * @param {string} searchText The string we're searching for. | |
105 */ | |
106 Downloads.prototype.setSearchText = function(searchText) { | |
107 this.searchText_ = searchText; | |
108 } | |
109 | |
110 /** | |
111 * Update the summary block above the results | |
112 */ | |
113 Downloads.prototype.updateSummary = function() { | |
114 if (this.searchText_) { | |
115 this.summary_.textContent = localStrings.getStringF('searchresultsfor', | |
116 this.searchText_); | |
117 } else { | |
118 this.summary_.textContent = localStrings.getString('downloads'); | |
119 } | |
120 | |
121 var hasDownloads = false; | |
122 for (var i in this.downloads_) { | |
123 hasDownloads = true; | |
124 break; | |
125 } | |
126 | |
127 if (!hasDownloads) { | |
128 this.node_.textContent = localStrings.getString('noresults'); | |
129 } | |
130 } | |
131 | |
132 /** | |
133 * Update the date visibility in our nodes so that no date is | |
134 * repeated. | |
135 */ | |
136 Downloads.prototype.updateDateDisplay_ = function() { | |
137 var dateContainers = document.getElementsByClassName('date-container'); | |
138 var displayed = {}; | |
139 for (var i = 0, container; container = dateContainers[i]; i++) { | |
140 var dateString = container.getElementsByClassName('date')[0].innerHTML; | |
141 if (!!displayed[dateString]) { | |
142 container.style.display = 'none'; | |
143 } else { | |
144 displayed[dateString] = true; | |
145 container.style.display = 'block'; | |
146 } | |
147 } | |
148 } | |
149 | |
150 /** | |
151 * Remove a download. | |
152 * @param {number} id The id of the download to remove. | |
153 */ | |
154 Downloads.prototype.remove = function(id) { | |
155 this.node_.removeChild(this.downloads_[id].node); | |
156 delete this.downloads_[id]; | |
157 this.updateDateDisplay_(); | |
158 } | |
159 | |
160 /** | |
161 * Clear all downloads and reset us back to a null state. | |
162 */ | |
163 Downloads.prototype.clear = function() { | |
164 for (var id in this.downloads_) { | |
165 this.downloads_[id].clear(); | |
166 this.remove(id); | |
167 } | |
168 } | |
169 | |
170 /** | |
171 * Schedule icon load. | |
172 * @param {HTMLImageElement} elem Image element that should contain the icon. | |
173 * @param {string} iconURL URL to the icon. | |
174 */ | |
175 Downloads.prototype.scheduleIconLoad = function(elem, iconURL) { | |
176 var self = this; | |
177 | |
178 // Sends request to the next icon in the queue and schedules | |
179 // call to itself when the icon is loaded. | |
180 function loadNext() { | |
181 self.isIconLoading_ = true; | |
182 while (self.iconLoadQueue_.length > 0) { | |
183 var request = self.iconLoadQueue_.shift(); | |
184 var oldSrc = request.element.src; | |
185 request.element.onabort = request.element.onerror = | |
186 request.element.onload = loadNext; | |
187 request.element.src = request.url; | |
188 if (oldSrc != request.element.src) | |
189 return; | |
190 } | |
191 self.isIconLoading_ = false; | |
192 } | |
193 | |
194 // Create new request | |
195 var loadRequest = {element: elem, url: iconURL}; | |
196 this.iconLoadQueue_.push(loadRequest); | |
197 | |
198 // Start loading if none scheduled yet | |
199 if (!this.isIconLoading_) | |
200 loadNext(); | |
201 } | |
202 | |
203 /////////////////////////////////////////////////////////////////////////////// | |
204 // Download | |
205 /** | |
206 * A download and the DOM representation for that download. | |
207 * @param {Object} download A backend download object (see downloads_ui.cc) | |
208 */ | |
209 function Download(download) { | |
210 // Create DOM | |
211 this.node = createElementWithClassName('div','download' + | |
212 (download.otr ? ' otr' : '')); | |
213 | |
214 // Dates | |
215 this.dateContainer_ = createElementWithClassName('div', 'date-container'); | |
216 this.node.appendChild(this.dateContainer_); | |
217 | |
218 this.nodeSince_ = createElementWithClassName('div', 'since'); | |
219 this.nodeDate_ = createElementWithClassName('div', 'date'); | |
220 this.dateContainer_.appendChild(this.nodeSince_); | |
221 this.dateContainer_.appendChild(this.nodeDate_); | |
222 | |
223 // Container for all 'safe download' UI. | |
224 this.safe_ = createElementWithClassName('div', 'safe'); | |
225 this.safe_.ondragstart = this.drag_.bind(this); | |
226 this.node.appendChild(this.safe_); | |
227 | |
228 if (download.state != Download.States.COMPLETE) { | |
229 this.nodeProgressBackground_ = | |
230 createElementWithClassName('div', 'progress background'); | |
231 this.safe_.appendChild(this.nodeProgressBackground_); | |
232 | |
233 this.canvasProgress_ = | |
234 document.getCSSCanvasContext('2d', 'canvas_' + download.id, | |
235 Download.Progress.width, | |
236 Download.Progress.height); | |
237 | |
238 this.nodeProgressForeground_ = | |
239 createElementWithClassName('div', 'progress foreground'); | |
240 this.nodeProgressForeground_.style.webkitMask = | |
241 '-webkit-canvas(canvas_'+download.id+')'; | |
242 this.safe_.appendChild(this.nodeProgressForeground_); | |
243 } | |
244 | |
245 this.nodeImg_ = createElementWithClassName('img', 'icon'); | |
246 this.safe_.appendChild(this.nodeImg_); | |
247 | |
248 // FileLink is used for completed downloads, otherwise we show FileName. | |
249 this.nodeTitleArea_ = createElementWithClassName('div', 'title-area'); | |
250 this.safe_.appendChild(this.nodeTitleArea_); | |
251 | |
252 this.nodeFileLink_ = createLink(this.openFile_.bind(this), ''); | |
253 this.nodeFileLink_.className = 'name'; | |
254 this.nodeFileLink_.style.display = 'none'; | |
255 this.nodeTitleArea_.appendChild(this.nodeFileLink_); | |
256 | |
257 this.nodeFileName_ = createElementWithClassName('span', 'name'); | |
258 this.nodeFileName_.style.display = 'none'; | |
259 this.nodeTitleArea_.appendChild(this.nodeFileName_); | |
260 | |
261 this.nodeStatus_ = createElementWithClassName('span', 'status'); | |
262 this.nodeTitleArea_.appendChild(this.nodeStatus_); | |
263 | |
264 var nodeURLDiv = createElementWithClassName('div', 'url-container'); | |
265 this.safe_.appendChild(nodeURLDiv); | |
266 | |
267 this.nodeURL_ = createElementWithClassName('a', 'src-url'); | |
268 this.nodeURL_.target = "_blank"; | |
269 nodeURLDiv.appendChild(this.nodeURL_); | |
270 | |
271 // Controls. | |
272 this.nodeControls_ = createElementWithClassName('div', 'controls'); | |
273 this.safe_.appendChild(this.nodeControls_); | |
274 | |
275 // We don't need "show in folder" in chromium os. See download_ui.cc and | |
276 // http://code.google.com/p/chromium-os/issues/detail?id=916. | |
277 var showinfolder = localStrings.getString('control_showinfolder'); | |
278 if (showinfolder) { | |
279 this.controlShow_ = createLink(this.show_.bind(this), showinfolder); | |
280 this.nodeControls_.appendChild(this.controlShow_); | |
281 } else { | |
282 this.controlShow_ = null; | |
283 } | |
284 | |
285 this.controlRetry_ = document.createElement('a'); | |
286 this.controlRetry_.textContent = localStrings.getString('control_retry'); | |
287 this.nodeControls_.appendChild(this.controlRetry_); | |
288 | |
289 // Pause/Resume are a toggle. | |
290 this.controlPause_ = createLink(this.togglePause_.bind(this), | |
291 localStrings.getString('control_pause')); | |
292 this.nodeControls_.appendChild(this.controlPause_); | |
293 | |
294 this.controlResume_ = createLink(this.togglePause_.bind(this), | |
295 localStrings.getString('control_resume')); | |
296 this.nodeControls_.appendChild(this.controlResume_); | |
297 | |
298 this.controlRemove_ = createLink(this.remove_.bind(this), | |
299 localStrings.getString('control_removefromlist')); | |
300 this.nodeControls_.appendChild(this.controlRemove_); | |
301 | |
302 this.controlCancel_ = createLink(this.cancel_.bind(this), | |
303 localStrings.getString('control_cancel')); | |
304 this.nodeControls_.appendChild(this.controlCancel_); | |
305 | |
306 // Container for 'unsafe download' UI. | |
307 this.danger_ = createElementWithClassName('div', 'show-dangerous'); | |
308 this.node.appendChild(this.danger_); | |
309 | |
310 this.dangerDesc_ = document.createElement('div'); | |
311 this.danger_.appendChild(this.dangerDesc_); | |
312 | |
313 this.dangerSave_ = createButton(this.saveDangerous_.bind(this), | |
314 localStrings.getString('danger_save')); | |
315 this.danger_.appendChild(this.dangerSave_); | |
316 | |
317 this.dangerDiscard_ = createButton(this.discardDangerous_.bind(this), | |
318 localStrings.getString('danger_discard')); | |
319 this.danger_.appendChild(this.dangerDiscard_); | |
320 | |
321 // Update member vars. | |
322 this.update(download); | |
323 } | |
324 | |
325 /** | |
326 * The states a download can be in. These correspond to states defined in | |
327 * DownloadsDOMHandler::CreateDownloadItemValue | |
328 */ | |
329 Download.States = { | |
330 IN_PROGRESS : "IN_PROGRESS", | |
331 CANCELLED : "CANCELLED", | |
332 COMPLETE : "COMPLETE", | |
333 PAUSED : "PAUSED", | |
334 DANGEROUS : "DANGEROUS", | |
335 INTERRUPTED : "INTERRUPTED", | |
336 } | |
337 | |
338 /** | |
339 * Explains why a download is in DANGEROUS state. | |
340 */ | |
341 Download.DangerType = { | |
342 NOT_DANGEROUS: "NOT_DANGEROUS", | |
343 DANGEROUS_FILE: "DANGEROUS_FILE", | |
344 DANGEROUS_URL: "DANGEROUS_URL", | |
345 DANGEROUS_CONTENT: "DANGEROUS_CONTENT" | |
346 } | |
347 | |
348 /** | |
349 * Constants for the progress meter. | |
350 */ | |
351 Download.Progress = { | |
352 width : 48, | |
353 height : 48, | |
354 radius : 24, | |
355 centerX : 24, | |
356 centerY : 24, | |
357 base : -0.5 * Math.PI, | |
358 dir : false, | |
359 } | |
360 | |
361 /** | |
362 * Updates the download to reflect new data. | |
363 * @param {Object} download A backend download object (see downloads_ui.cc) | |
364 */ | |
365 Download.prototype.update = function(download) { | |
366 this.id_ = download.id; | |
367 this.filePath_ = download.file_path; | |
368 this.fileUrl_ = download.file_url; | |
369 this.fileName_ = download.file_name; | |
370 this.url_ = download.url; | |
371 this.state_ = download.state; | |
372 this.fileExternallyRemoved_ = download.file_externally_removed; | |
373 this.dangerType_ = download.danger_type; | |
374 | |
375 this.since_ = download.since_string; | |
376 this.date_ = download.date_string; | |
377 | |
378 // See DownloadItem::PercentComplete | |
379 this.percent_ = Math.max(download.percent, 0); | |
380 this.progressStatusText_ = download.progress_status_text; | |
381 this.received_ = download.received; | |
382 | |
383 if (this.state_ == Download.States.DANGEROUS) { | |
384 if (this.dangerType_ == Download.DangerType.DANGEROUS_FILE) { | |
385 this.dangerDesc_.textContent = localStrings.getStringF('danger_file_desc', | |
386 this.fileName_); | |
387 } else if (this.dangerType_ == Download.DangerType.DANGEROUS_URL) { | |
388 this.dangerDesc_.textContent = localStrings.getString('danger_url_desc'); | |
389 } else if (this.dangerType_ == Download.DangerType.DANGEROUS_CONTENT) { | |
390 this.dangerDesc_.textContent = localStrings.getStringF( | |
391 'danger_content_desc', this.fileName_); | |
392 } | |
393 this.danger_.style.display = 'block'; | |
394 this.safe_.style.display = 'none'; | |
395 } else { | |
396 downloads.scheduleIconLoad(this.nodeImg_, | |
397 'chrome://fileicon/' + | |
398 encodeURIComponent(this.filePath_)); | |
399 | |
400 if (this.state_ == Download.States.COMPLETE && | |
401 !this.fileExternallyRemoved_) { | |
402 this.nodeFileLink_.textContent = this.fileName_; | |
403 this.nodeFileLink_.href = this.fileUrl_; | |
404 this.nodeFileLink_.oncontextmenu = null; | |
405 } else if (this.nodeFileName_.textContent != this.fileName_) { | |
406 this.nodeFileName_.textContent = this.fileName_; | |
407 } | |
408 | |
409 showInline(this.nodeFileLink_, | |
410 this.state_ == Download.States.COMPLETE && | |
411 !this.fileExternallyRemoved_); | |
412 // nodeFileName_ has to be inline-block to avoid the 'interaction' with | |
413 // nodeStatus_. If both are inline, it appears that their text contents | |
414 // are merged before the bidi algorithm is applied leading to an | |
415 // undesirable reordering. http://crbug.com/13216 | |
416 showInlineBlock(this.nodeFileName_, | |
417 this.state_ != Download.States.COMPLETE || | |
418 this.fileExternallyRemoved_); | |
419 | |
420 if (this.state_ == Download.States.IN_PROGRESS) { | |
421 this.nodeProgressForeground_.style.display = 'block'; | |
422 this.nodeProgressBackground_.style.display = 'block'; | |
423 | |
424 // Draw a pie-slice for the progress. | |
425 this.canvasProgress_.clearRect(0, 0, | |
426 Download.Progress.width, | |
427 Download.Progress.height); | |
428 this.canvasProgress_.beginPath(); | |
429 this.canvasProgress_.moveTo(Download.Progress.centerX, | |
430 Download.Progress.centerY); | |
431 | |
432 // Draw an arc CW for both RTL and LTR. http://crbug.com/13215 | |
433 this.canvasProgress_.arc(Download.Progress.centerX, | |
434 Download.Progress.centerY, | |
435 Download.Progress.radius, | |
436 Download.Progress.base, | |
437 Download.Progress.base + Math.PI * 0.02 * | |
438 Number(this.percent_), | |
439 false); | |
440 | |
441 this.canvasProgress_.lineTo(Download.Progress.centerX, | |
442 Download.Progress.centerY); | |
443 this.canvasProgress_.fill(); | |
444 this.canvasProgress_.closePath(); | |
445 } else if (this.nodeProgressBackground_) { | |
446 this.nodeProgressForeground_.style.display = 'none'; | |
447 this.nodeProgressBackground_.style.display = 'none'; | |
448 } | |
449 | |
450 if (this.controlShow_) { | |
451 showInline(this.controlShow_, | |
452 this.state_ == Download.States.COMPLETE && | |
453 !this.fileExternallyRemoved_); | |
454 } | |
455 showInline(this.controlRetry_, this.state_ == Download.States.CANCELLED); | |
456 this.controlRetry_.href = this.url_; | |
457 showInline(this.controlPause_, this.state_ == Download.States.IN_PROGRESS); | |
458 showInline(this.controlResume_, this.state_ == Download.States.PAUSED); | |
459 var showCancel = this.state_ == Download.States.IN_PROGRESS || | |
460 this.state_ == Download.States.PAUSED; | |
461 showInline(this.controlCancel_, showCancel); | |
462 showInline(this.controlRemove_, !showCancel); | |
463 | |
464 this.nodeSince_.textContent = this.since_; | |
465 this.nodeDate_.textContent = this.date_; | |
466 // Don't unnecessarily update the url, as doing so will remove any | |
467 // text selection the user has started (http://crbug.com/44982). | |
468 if (this.nodeURL_.textContent != this.url_) { | |
469 this.nodeURL_.textContent = this.url_; | |
470 this.nodeURL_.href = this.url_; | |
471 } | |
472 this.nodeStatus_.textContent = this.getStatusText_(); | |
473 | |
474 this.danger_.style.display = 'none'; | |
475 this.safe_.style.display = 'block'; | |
476 } | |
477 } | |
478 | |
479 /** | |
480 * Removes applicable bits from the DOM in preparation for deletion. | |
481 */ | |
482 Download.prototype.clear = function() { | |
483 this.safe_.ondragstart = null; | |
484 this.nodeFileLink_.onclick = null; | |
485 if (this.controlShow_) { | |
486 this.controlShow_.onclick = null; | |
487 } | |
488 this.controlCancel_.onclick = null; | |
489 this.controlPause_.onclick = null; | |
490 this.controlResume_.onclick = null; | |
491 this.dangerDiscard_.onclick = null; | |
492 | |
493 this.node.innerHTML = ''; | |
494 } | |
495 | |
496 /** | |
497 * @return {string} User-visible status update text. | |
498 */ | |
499 Download.prototype.getStatusText_ = function() { | |
500 switch (this.state_) { | |
501 case Download.States.IN_PROGRESS: | |
502 return this.progressStatusText_; | |
503 case Download.States.CANCELLED: | |
504 return localStrings.getString('status_cancelled'); | |
505 case Download.States.PAUSED: | |
506 return localStrings.getString('status_paused'); | |
507 case Download.States.DANGEROUS: | |
508 // danger_url_desc is also used by DANGEROUS_CONTENT. | |
509 var desc = this.dangerType_ == Download.DangerType.DANGEROUS_FILE ? | |
510 'danger_file_desc' : 'danger_url_desc'; | |
511 return localStrings.getString(desc); | |
512 case Download.States.INTERRUPTED: | |
513 return localStrings.getString('status_interrupted'); | |
514 case Download.States.COMPLETE: | |
515 return this.fileExternallyRemoved_ ? | |
516 localStrings.getString('status_removed') : ''; | |
517 } | |
518 } | |
519 | |
520 /** | |
521 * Tells the backend to initiate a drag, allowing users to drag | |
522 * files from the download page and have them appear as native file | |
523 * drags. | |
524 */ | |
525 Download.prototype.drag_ = function() { | |
526 chrome.send('drag', [this.id_.toString()]); | |
527 return false; | |
528 } | |
529 | |
530 /** | |
531 * Tells the backend to open this file. | |
532 */ | |
533 Download.prototype.openFile_ = function() { | |
534 chrome.send('openFile', [this.id_.toString()]); | |
535 return false; | |
536 } | |
537 | |
538 /** | |
539 * Tells the backend that the user chose to save a dangerous file. | |
540 */ | |
541 Download.prototype.saveDangerous_ = function() { | |
542 chrome.send('saveDangerous', [this.id_.toString()]); | |
543 return false; | |
544 } | |
545 | |
546 /** | |
547 * Tells the backend that the user chose to discard a dangerous file. | |
548 */ | |
549 Download.prototype.discardDangerous_ = function() { | |
550 chrome.send('discardDangerous', [this.id_.toString()]); | |
551 downloads.remove(this.id_); | |
552 return false; | |
553 } | |
554 | |
555 /** | |
556 * Tells the backend to show the file in explorer. | |
557 */ | |
558 Download.prototype.show_ = function() { | |
559 chrome.send('show', [this.id_.toString()]); | |
560 return false; | |
561 } | |
562 | |
563 /** | |
564 * Tells the backend to pause this download. | |
565 */ | |
566 Download.prototype.togglePause_ = function() { | |
567 chrome.send('togglepause', [this.id_.toString()]); | |
568 return false; | |
569 } | |
570 | |
571 /** | |
572 * Tells the backend to remove this download from history and download shelf. | |
573 */ | |
574 Download.prototype.remove_ = function() { | |
575 chrome.send('remove', [this.id_.toString()]); | |
576 return false; | |
577 } | |
578 | |
579 /** | |
580 * Tells the backend to cancel this download. | |
581 */ | |
582 Download.prototype.cancel_ = function() { | |
583 chrome.send('cancel', [this.id_.toString()]); | |
584 return false; | |
585 } | |
586 | |
587 /////////////////////////////////////////////////////////////////////////////// | |
588 // Page: | |
589 var downloads, localStrings, resultsTimeout; | |
590 | |
591 // TODO(benjhayden): Rename Downloads to DownloadManager, downloads to | |
592 // downloadManager or theDownloadManager or DownloadManager.get() to prevent | |
593 // confusing Downloads with Download. | |
594 | |
595 /** | |
596 * The FIFO array that stores updates of download files to be appeared | |
597 * on the download page. It is guaranteed that the updates in this array | |
598 * are reflected to the download page in a FIFO order. | |
599 */ | |
600 var fifo_results; | |
601 | |
602 function load() { | |
603 chrome.send('onPageLoaded'); | |
604 fifo_results = new Array(); | |
605 localStrings = new LocalStrings(); | |
606 downloads = new Downloads(); | |
607 $('term').focus(); | |
608 $('term').setAttribute('aria-labelledby', 'search-submit'); | |
609 setSearch(''); | |
610 } | |
611 | |
612 function setSearch(searchText) { | |
613 fifo_results.length = 0; | |
614 downloads.clear(); | |
615 downloads.setSearchText(searchText); | |
616 chrome.send('getDownloads', [searchText.toString()]); | |
617 } | |
618 | |
619 function clearAll() { | |
620 fifo_results.length = 0; | |
621 downloads.clear(); | |
622 downloads.setSearchText(''); | |
623 chrome.send('clearAll', []); | |
624 return false; | |
625 } | |
626 | |
627 function openDownloadsFolder() { | |
628 chrome.send('openDownloadsFolder'); | |
629 return false; | |
630 } | |
631 | |
632 /////////////////////////////////////////////////////////////////////////////// | |
633 // Chrome callbacks: | |
634 /** | |
635 * Our history system calls this function with results from searches or when | |
636 * downloads are added or removed. | |
637 */ | |
638 function downloadsList(results) { | |
639 if (resultsTimeout) | |
640 clearTimeout(resultsTimeout); | |
641 fifo_results.length = 0; | |
642 downloads.clear(); | |
643 downloadUpdated(results); | |
644 downloads.updateSummary(); | |
645 } | |
646 | |
647 /** | |
648 * When a download is updated (progress, state change), this is called. | |
649 */ | |
650 function downloadUpdated(results) { | |
651 // Sometimes this can get called too early. | |
652 if (!downloads) | |
653 return; | |
654 | |
655 fifo_results = fifo_results.concat(results); | |
656 tryDownloadUpdatedPeriodically(); | |
657 } | |
658 | |
659 /** | |
660 * Try to reflect as much updates as possible within 50ms. | |
661 * This function is scheduled again and again until all updates are reflected. | |
662 */ | |
663 function tryDownloadUpdatedPeriodically() { | |
664 var start = Date.now(); | |
665 while (fifo_results.length) { | |
666 var result = fifo_results.shift(); | |
667 downloads.updated(result); | |
668 // Do as much as we can in 50ms. | |
669 if (Date.now() - start > 50) { | |
670 clearTimeout(resultsTimeout); | |
671 resultsTimeout = setTimeout(tryDownloadUpdatedPeriodically, 5); | |
672 break; | |
673 } | |
674 } | |
675 } | |
676 | |
677 // Add handlers to HTML elements. | |
678 window.addEventListener('DOMContentLoaded', load); | |
679 | |
680 var clearAllLink = $('clear-all'); | |
681 clearAllLink.onclick = function () { clearAll(''); }; | |
682 clearAllLink.oncontextmenu = function() { return false; }; | |
683 | |
684 var openDownloadsFolderLink = $('open-downloads-folder'); | |
685 openDownloadsFolderLink.onclick = openDownloadsFolder; | |
686 openDownloadsFolderLink.oncontextmenu = function() { return false; }; | |
687 | |
688 $('search-link').onclick = function () { | |
689 setSearch(''); | |
690 return false; | |
691 }; | |
692 $('search-form').onsubmit = function () { | |
693 setSearch(this.term.value); | |
694 return false; | |
695 }; | |
OLD | NEW |