| OLD | NEW |
| (Empty) |
| 1 // Copyright (c) 2011 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 * Wrapper for chrome.send. | |
| 7 */ | |
| 8 function chromeSend(func, arg) { | |
| 9 if (arg == undefined) | |
| 10 arg = ''; | |
| 11 | |
| 12 // Convert to string. | |
| 13 if (typeof arg == 'number') | |
| 14 arg = '' + arg; | |
| 15 | |
| 16 chrome.send(func, [arg]); | |
| 17 }; | |
| 18 | |
| 19 /** | |
| 20 * Create a child element. | |
| 21 * | |
| 22 * @param {string} type The type - div, span, etc. | |
| 23 * @param {string} className The class name | |
| 24 * @param {HTMLElement} parent Parent to append this child to. | |
| 25 * @param {string} textContent optional text content of child. | |
| 26 * @param {function(*)} onclick onclick function of child. | |
| 27 */ | |
| 28 function createChild(type, className, parent, textContent, onclick) { | |
| 29 var elem = document.createElement(type); | |
| 30 elem.className = className; | |
| 31 if (textContent !== undefined) | |
| 32 elem.textContent = textContent; | |
| 33 elem.onclick = onclick; | |
| 34 parent.appendChild(elem); | |
| 35 return elem; | |
| 36 }; | |
| 37 | |
| 38 var localStrings; | |
| 39 var downloadRowList; | |
| 40 | |
| 41 function init() { | |
| 42 localStrings = new LocalStrings(); | |
| 43 initTestHarness(); | |
| 44 | |
| 45 window.onkeydown = function(e) { | |
| 46 if (e.keyCode == 27) // Escape. | |
| 47 menu.clear(); | |
| 48 e.preventDefault(); // Suppress browser shortcuts. | |
| 49 }; | |
| 50 | |
| 51 document.body.addEventListener("blur", menu.clear); | |
| 52 document.body.addEventListener("click", menu.clear); | |
| 53 document.body.addEventListener("contextmenu", function (e) { | |
| 54 e.preventDefault(); }); | |
| 55 document.body.addEventListener("selectstart", function (e) { | |
| 56 e.preventDefault(); }); | |
| 57 | |
| 58 var sadt = $('showallfilestext'); | |
| 59 sadt.textContent = localStrings.getString('showallfiles'); | |
| 60 sadt.addEventListener("click", showAllFiles); | |
| 61 | |
| 62 downloadRowList = new DownloadRowList(); | |
| 63 chromeSend('getDownloads'); | |
| 64 } | |
| 65 | |
| 66 /** | |
| 67 * Testing. Allow this page to be loaded in a browser. | |
| 68 * Create stubs for localStrings and chrome.send. | |
| 69 */ | |
| 70 function initTestHarness() { | |
| 71 if (location.protocol != 'file:') | |
| 72 return; | |
| 73 | |
| 74 // Enable right click for dom inspector. | |
| 75 document.body.oncontextmenu = ''; | |
| 76 | |
| 77 // Fix localStrings. | |
| 78 localStrings = { | |
| 79 getString: function(name) { | |
| 80 if (name == 'showallfiles') | |
| 81 return 'Show all files'; | |
| 82 if (name == 'dangerousextension') | |
| 83 return 'Extensions, apps, and themes can harm your computer.' + | |
| 84 ' Are you sure you want to continue?' | |
| 85 if (name == 'continue') | |
| 86 return 'Continue'; | |
| 87 if (name == 'discard') | |
| 88 return 'Discard'; | |
| 89 return name; | |
| 90 }, | |
| 91 getStringF: function(name, path) { | |
| 92 return path + ' - Unknown file type.'; | |
| 93 }, | |
| 94 }; | |
| 95 | |
| 96 // Log chrome.send calls. | |
| 97 chrome.send = function(name, ary) { | |
| 98 console.log('chrome.send ' + name + ' ' + ary); | |
| 99 if (name == 'getDownloads' || | |
| 100 (name == 'openNewFullWindow' && | |
| 101 ary[0] == 'chrome://downloads')) | |
| 102 sendTestResults(); | |
| 103 }; | |
| 104 | |
| 105 // Fix resource images. | |
| 106 var cssRules = document.styleSheets[0].cssRules; | |
| 107 for (var i = 0; i < cssRules.length; i++) { | |
| 108 var cssRule = cssRules[i]; | |
| 109 if (cssRule.selectorText.match(/^div\.icon|^\.menuicon/)) { | |
| 110 cssRule.style.backgroundImage = | |
| 111 cssRule.style.backgroundImage.replace('chrome://resources', 'shared'); | |
| 112 } | |
| 113 } | |
| 114 } | |
| 115 | |
| 116 /** | |
| 117 * Create a results array with test data and call downloadsList. | |
| 118 */ | |
| 119 var testElement; | |
| 120 var testId = 0; | |
| 121 var testResults = []; | |
| 122 function sendTestResults() { | |
| 123 var testState = (testId % 3 == 0 ? 'IN_PROGRESS' : | |
| 124 (testId % 3 == 1 ? 'DANGEROUS' : 'COMPLETE')); | |
| 125 state1 = (testId % 3 == 0); | |
| 126 testResults.push({ | |
| 127 state: testState, | |
| 128 percent: (testId % 3 == 0 ? 90 : 100), | |
| 129 id: testId, | |
| 130 file_name: ' Test' + testId + '.pdf', | |
| 131 file_path: '/home/achuith/Downloads/Test' + testId + '.pdf', | |
| 132 progress_status_text : '107 MB/s - 108 MB of 672 MB, 5 secs left', | |
| 133 }); | |
| 134 testId++; | |
| 135 downloadsList(testResults); | |
| 136 } | |
| 137 | |
| 138 /** | |
| 139 * Current Menu. | |
| 140 */ | |
| 141 var menu = { | |
| 142 current_: null, | |
| 143 | |
| 144 /** | |
| 145 * Close the current menu. | |
| 146 */ | |
| 147 clear: function() { | |
| 148 var current = this.current_; | |
| 149 if (current) { | |
| 150 current.firstChild.style.display = 'none'; | |
| 151 current.style.opacity = ''; | |
| 152 this.current_ = null; | |
| 153 } | |
| 154 }, | |
| 155 | |
| 156 /** | |
| 157 * If it's a second click on an open menu, close the menu. | |
| 158 * Otherwise, close any other open menu and open the clicked menu. | |
| 159 */ | |
| 160 clicked: function(row) { | |
| 161 var menuicon = row.menuicon; | |
| 162 if (this.current_ === menuicon) { | |
| 163 this.clear(); | |
| 164 return; | |
| 165 } | |
| 166 this.clear(); | |
| 167 if (menuicon.firstChild.style.display != 'block') { | |
| 168 menuicon.firstChild.style.display = 'block'; | |
| 169 menuicon.style.opacity = '1'; | |
| 170 menuicon.scrollIntoView(); | |
| 171 this.current_ = menuicon; | |
| 172 } | |
| 173 window.event.stopPropagation(); | |
| 174 }, | |
| 175 }; | |
| 176 | |
| 177 function DiscardResult(result) { | |
| 178 return (result.state == 'CANCELLED' || | |
| 179 result.state == 'INTERRUPTED' || | |
| 180 result.state == 'REMOVING'); | |
| 181 }; | |
| 182 | |
| 183 /** | |
| 184 * C++ api calls. | |
| 185 */ | |
| 186 function downloadsList(results) { | |
| 187 downloadRowList.list(results); | |
| 188 } | |
| 189 | |
| 190 function downloadUpdated(result) { | |
| 191 downloadRowList.update(result); | |
| 192 } | |
| 193 | |
| 194 function showAllFiles() { | |
| 195 chromeSend('showAllFiles'); | |
| 196 } | |
| 197 | |
| 198 /** | |
| 199 * DownloadRow contains all the elements that go into a row of the downloads | |
| 200 * list. It represents a single DownloadItem. | |
| 201 * | |
| 202 * @param {DownloadRowList} list Global DownloadRowList. | |
| 203 * @param {Object} result JSON representation of DownloadItem. | |
| 204 * @constructor | |
| 205 */ | |
| 206 function DownloadRow(list, result) { | |
| 207 this.path = result.file_path; | |
| 208 this.name = result.file_name; | |
| 209 this.fileUrl = result.file_url; | |
| 210 this.list = list; | |
| 211 this.id = result.id; | |
| 212 | |
| 213 this.createRow_(list); | |
| 214 this.createMenu_(); | |
| 215 this.createRowButton_(); | |
| 216 this.setMenuHidden_(true); | |
| 217 } | |
| 218 | |
| 219 DownloadRow.prototype = { | |
| 220 /** | |
| 221 * Create the row html element and book-keeping for the row. | |
| 222 * @param {DownloadRowList} list global DownloadRowList instance. | |
| 223 * @private | |
| 224 */ | |
| 225 createRow_: function(list) { | |
| 226 var elem = document.createElement('li'); | |
| 227 elem.className = 'downloadrow'; | |
| 228 elem.id = this.path; | |
| 229 elem.row = this; | |
| 230 this.element = elem; | |
| 231 | |
| 232 list.append(this); | |
| 233 }, | |
| 234 | |
| 235 setErrorText_: function(text) { | |
| 236 this.filename.textContent = text; | |
| 237 }, | |
| 238 | |
| 239 supportsPdf_: function() { | |
| 240 return 'application/pdf' in navigator.mimeTypes; | |
| 241 }, | |
| 242 | |
| 243 openFilePath_: function() { | |
| 244 chromeSend('openNewFullWindow', this.fileUrl); | |
| 245 }, | |
| 246 | |
| 247 /** | |
| 248 * Determine onclick behavior based on filename. | |
| 249 * @private | |
| 250 */ | |
| 251 getFunctionForItem_: function() { | |
| 252 var path = this.path; | |
| 253 var self = this; | |
| 254 | |
| 255 if (pathIsAudioFile(path)) { | |
| 256 return function() { | |
| 257 chromeSend('playMediaFile', path); | |
| 258 }; | |
| 259 } | |
| 260 if (pathIsVideoFile(path)) { | |
| 261 return function() { | |
| 262 chromeSend('playMediaFile', path); | |
| 263 }; | |
| 264 } | |
| 265 if (pathIsImageFile(path)) { | |
| 266 return function() { | |
| 267 self.openFilePath_(); | |
| 268 } | |
| 269 } | |
| 270 if (pathIsHtmlFile(path)) { | |
| 271 return function() { | |
| 272 self.openFilePath_(); | |
| 273 } | |
| 274 } | |
| 275 if (pathIsPdfFile(path) && this.supportsPdf_()) { | |
| 276 return function() { | |
| 277 self.openFilePath_(); | |
| 278 } | |
| 279 } | |
| 280 | |
| 281 return function() { | |
| 282 self.setErrorText_(localStrings.getStringF('error_unknown_file_type', | |
| 283 self.name)); | |
| 284 }; | |
| 285 }, | |
| 286 | |
| 287 setDangerousIcon_: function(warning) { | |
| 288 this.icon.className = warning ? 'iconwarning' : 'icon'; | |
| 289 this.icon.style.background = warning ? '' : | |
| 290 'url(chrome://fileicon' + escape(this.path) + | |
| 291 '?iconsize=small) no-repeat'; | |
| 292 }, | |
| 293 | |
| 294 /** | |
| 295 * Create the row button for the left of the row. | |
| 296 * This contains the icon, filename and error elements. | |
| 297 * @private | |
| 298 */ | |
| 299 createRowButton_: function () { | |
| 300 this.rowbutton = createChild('div', 'rowbutton rowbg', this.element); | |
| 301 | |
| 302 // Icon. | |
| 303 this.icon = createChild('div', 'icon', this.rowbutton); | |
| 304 this.setDangerousIcon_(false); | |
| 305 | |
| 306 // Filename. | |
| 307 this.filename = createChild('span', 'title', this.rowbutton, this.name); | |
| 308 }, | |
| 309 | |
| 310 setMenuHidden_: function(hidden) { | |
| 311 this.menubutton.hidden = hidden; | |
| 312 if (hidden) { | |
| 313 this.rowbutton.style.width = '238px'; | |
| 314 } else { | |
| 315 this.rowbutton.style.width = ''; | |
| 316 } | |
| 317 }, | |
| 318 | |
| 319 /** | |
| 320 * Create the menu button on the right of the row. | |
| 321 * This contains the menuicon. The menuicon contains the menu, which | |
| 322 * contains items for Pause/Resume and Cancel. | |
| 323 * @private | |
| 324 */ | |
| 325 createMenu_: function() { | |
| 326 var self = this; | |
| 327 this.menubutton = createChild('div', 'menubutton rowbg', this.element, '', | |
| 328 function() { | |
| 329 menu.clicked(self); | |
| 330 }); | |
| 331 | |
| 332 this.menuicon = createChild('div', 'menuicon', this.menubutton); | |
| 333 | |
| 334 var menudiv = createChild('div', 'menu', this.menuicon); | |
| 335 | |
| 336 this.pause = createChild('div', 'menuitem', menudiv, | |
| 337 localStrings.getString('pause'), function() { | |
| 338 self.pauseToggleDownload_(); | |
| 339 }); | |
| 340 | |
| 341 this.cancel = createChild('div', 'menuitem', menudiv, | |
| 342 localStrings.getString('cancel'), function() { | |
| 343 self.cancelDownload_(); | |
| 344 }); | |
| 345 }, | |
| 346 | |
| 347 allowDownload_: function() { | |
| 348 chromeSend('allowDownload', this.id); | |
| 349 }, | |
| 350 | |
| 351 cancelDownload_: function() { | |
| 352 chromeSend('cancelDownload', this.id); | |
| 353 }, | |
| 354 | |
| 355 pauseToggleDownload_: function() { | |
| 356 this.pause.textContent = | |
| 357 (this.pause.textContent == localStrings.getString('pause')) ? | |
| 358 localStrings.getString('resume') : | |
| 359 localStrings.getString('pause'); | |
| 360 | |
| 361 chromeSend('pauseToggleDownload', this.id); | |
| 362 }, | |
| 363 | |
| 364 changeElemHeight_: function(elem, x) { | |
| 365 elem.style.height = elem.clientHeight + x + 'px'; | |
| 366 }, | |
| 367 | |
| 368 changeRowHeight_: function(x) { | |
| 369 this.list.rowsHeight += x; | |
| 370 this.changeElemHeight_(this.element, x); | |
| 371 // rowbutton has 5px padding. | |
| 372 this.changeElemHeight_(this.rowbutton, x - 5); | |
| 373 this.list.resize(); | |
| 374 }, | |
| 375 | |
| 376 DANGEROUS_HEIGHT: 60, | |
| 377 createDangerousPrompt_: function(dangerType) { | |
| 378 if (this.dangerous) | |
| 379 return; | |
| 380 | |
| 381 this.dangerous = createChild('div', 'dangerousprompt', this.rowbutton); | |
| 382 | |
| 383 // Handle dangerous files, extensions and dangerous urls. | |
| 384 var dangerText; | |
| 385 if (dangerType == 'DANGEROUS_URL') { | |
| 386 dangerText = localStrings.getString('dangerousurl'); | |
| 387 } else if (dangerType == 'DANGEROUS_CONTENT') { | |
| 388 dangerText = localStrings.getStringF('dangerouscontent', this.name); | |
| 389 } else if (dangerType == 'DANGEROUS_FILE' && this.path.match(/\.crx$/)) { | |
| 390 dangerText = localStrings.getString('dangerousextension'); | |
| 391 } else { | |
| 392 dangerText = localStrings.getStringF('dangerousfile', this.name); | |
| 393 } | |
| 394 createChild('span', 'dangerousprompttext', this.dangerous, dangerText); | |
| 395 | |
| 396 var self = this; | |
| 397 createChild('span', 'confirm', this.dangerous, | |
| 398 localStrings.getString('discard'), | |
| 399 function() { | |
| 400 self.cancelDownload_(); | |
| 401 }); | |
| 402 createChild('span', 'confirm', this.dangerous, | |
| 403 localStrings.getString('continue'), | |
| 404 function() { | |
| 405 self.allowDownload_(); | |
| 406 }); | |
| 407 | |
| 408 this.changeRowHeight_(this.DANGEROUS_HEIGHT); | |
| 409 this.setDangerousIcon_(true); | |
| 410 }, | |
| 411 | |
| 412 removeDangerousPrompt_: function() { | |
| 413 if (!this.dangerous) | |
| 414 return; | |
| 415 | |
| 416 this.rowbutton.removeChild(this.dangerous); | |
| 417 this.dangerous = null; | |
| 418 | |
| 419 this.changeRowHeight_(-this.DANGEROUS_HEIGHT); | |
| 420 this.setDangerousIcon_(false); | |
| 421 }, | |
| 422 | |
| 423 PROGRESS_HEIGHT: 8, | |
| 424 createProgress_: function() { | |
| 425 if (this.progress) | |
| 426 return; | |
| 427 | |
| 428 this.progress = createChild('div', 'progress', this.rowbutton); | |
| 429 | |
| 430 this.setMenuHidden_(false); | |
| 431 this.changeRowHeight_(this.PROGRESS_HEIGHT); | |
| 432 }, | |
| 433 | |
| 434 removeProgress_: function() { | |
| 435 if (!this.progress) | |
| 436 return; | |
| 437 | |
| 438 this.rowbutton.removeChild(this.progress); | |
| 439 this.progress = null; | |
| 440 | |
| 441 this.changeRowHeight_(-this.PROGRESS_HEIGHT); | |
| 442 this.setMenuHidden_(true); | |
| 443 }, | |
| 444 | |
| 445 updatePause_: function(result) { | |
| 446 var pause = this.pause; | |
| 447 var pauseStr = localStrings.getString('pause'); | |
| 448 var resumeStr = localStrings.getString('resume'); | |
| 449 | |
| 450 if (pause && | |
| 451 result.state == 'PAUSED' && | |
| 452 pause.textContent != resumeStr) { | |
| 453 pause.textContent = resumeStr; | |
| 454 } else if (pause && | |
| 455 result.state == 'IN_PROGRESS' && | |
| 456 pause.textContent != pauseStr) { | |
| 457 pause.textContent = pauseStr; | |
| 458 } | |
| 459 }, | |
| 460 | |
| 461 progressStatusText_: function(progress) { | |
| 462 if (!progress) | |
| 463 return progress; | |
| 464 | |
| 465 /* m looks like this: | |
| 466 ["107 MB/s - 108 MB of 672 MB, 5 secs left", | |
| 467 "107 MB/s", "108", "MB", "672", "MB", "5 secs left"] | |
| 468 We want to return progress text like this: | |
| 469 "108 / 672 MB, 5 secs left" | |
| 470 or | |
| 471 "108 kB / 672 MB, 5 secs left" | |
| 472 */ | |
| 473 var m = progress.match( | |
| 474 /([^-]*) - ([0-9\.]*) ([a-zA-Z]*) of ([0-9\.]*) ([a-zA-Z]*), (.*)/); | |
| 475 if (!m || m.length != 7) | |
| 476 return progress; | |
| 477 | |
| 478 return m[2] + (m[3] == m[5] ? '' : ' ' + m[3]) + | |
| 479 ' / ' + m[4] + ' ' + m[5] + ', ' + m[6]; | |
| 480 }, | |
| 481 | |
| 482 updateProgress_: function(result) { | |
| 483 this.removeDangerousPrompt_(); | |
| 484 this.createProgress_(); | |
| 485 this.progress.textContent = | |
| 486 this.progressStatusText_(result.progress_status_text); | |
| 487 this.updatePause_(result); | |
| 488 }, | |
| 489 | |
| 490 /** | |
| 491 * Called when the item has finished downloading. Switch the menu | |
| 492 * and remove the progress bar. | |
| 493 * @private | |
| 494 */ | |
| 495 finishedDownloading_: function() { | |
| 496 // Make rowbutton clickable. | |
| 497 this.rowbutton.onclick = this.getFunctionForItem_(); | |
| 498 this.rowbutton.style.cursor = 'pointer'; | |
| 499 | |
| 500 // Make rowbutton draggable. | |
| 501 this.rowbutton.setAttribute('draggable', 'true'); | |
| 502 var self = this; | |
| 503 this.rowbutton.addEventListener('dragstart', function(e) { | |
| 504 e.dataTransfer.effectAllowed = 'copy'; | |
| 505 e.dataTransfer.setData('Text', self.path); | |
| 506 e.dataTransfer.setData('URL', self.fileUrl); | |
| 507 }, false); | |
| 508 | |
| 509 this.removeDangerousPrompt_(); | |
| 510 this.removeProgress_(); | |
| 511 }, | |
| 512 | |
| 513 /** | |
| 514 * One of the DownloadItem we are observing has updated. | |
| 515 * @param {Object} result JSON representation of DownloadItem. | |
| 516 */ | |
| 517 update: function(result) { | |
| 518 this.filename.textContent = result.file_name; | |
| 519 this.id = result.id; | |
| 520 | |
| 521 if (result.state != 'COMPLETE') { | |
| 522 this.rowbutton.onclick = ''; | |
| 523 this.rowbutton.style.cursor = ''; | |
| 524 } | |
| 525 | |
| 526 if (DiscardResult(result)) { | |
| 527 this.list.remove(this); | |
| 528 } else if (result.state == 'DANGEROUS') { | |
| 529 this.createDangerousPrompt_(result.danger_type); | |
| 530 } else if (result.percent < 100) { | |
| 531 this.updateProgress_(result); | |
| 532 } else if (result.state == 'COMPLETE') { | |
| 533 this.finishedDownloading_(); | |
| 534 } | |
| 535 }, | |
| 536 }; | |
| 537 | |
| 538 /** | |
| 539 * DownloadRowList is a container for DownloadRows. | |
| 540 */ | |
| 541 function DownloadRowList() { | |
| 542 this.element = createChild('ul', 'downloadlist', $('main')); | |
| 543 | |
| 544 document.title = localStrings.getString('downloadpath'). | |
| 545 split('/').pop(); | |
| 546 } | |
| 547 | |
| 548 DownloadRowList.prototype = { | |
| 549 | |
| 550 /** | |
| 551 * numRows is the current number of rows. | |
| 552 * rowsHeight is the sum of the heights of all rows. | |
| 553 * rowListHeight is the height of the container containing the rows. | |
| 554 * rows is the list of DownloadRows. | |
| 555 */ | |
| 556 numRows: 0, | |
| 557 rowsHeight: 0, | |
| 558 rowListHeight: 72, | |
| 559 rows: [], | |
| 560 | |
| 561 /** | |
| 562 * Resize the panel to accomodate all rows. | |
| 563 */ | |
| 564 resize: function() { | |
| 565 var diff = this.rowsHeight - this.rowListHeight; | |
| 566 if (diff != 0 && (this.rowListHeight + diff > 72)) { | |
| 567 window.resizeBy(0, diff); | |
| 568 this.rowListHeight += diff; | |
| 569 } | |
| 570 }, | |
| 571 | |
| 572 /** | |
| 573 * Remove a row from the list, as when a download is canceled, or | |
| 574 * the the number of rows has exceeded the max allowed. | |
| 575 * | |
| 576 * @param {DownloadRow} row Row to be removed. | |
| 577 * @private | |
| 578 */ | |
| 579 remove: function(row) { | |
| 580 this.rows.splice(this.rows.indexOf(row), 1); | |
| 581 | |
| 582 this.numRows--; | |
| 583 this.rowsHeight -= row.element.offsetHeight; | |
| 584 this.resize(); | |
| 585 | |
| 586 this.element.removeChild(row.element); | |
| 587 row.element.row = null; | |
| 588 }, | |
| 589 | |
| 590 removeList: function(rows) { | |
| 591 for (i = 0; i < rows.length; i++) { | |
| 592 this.remove(rows[i]); | |
| 593 } | |
| 594 }, | |
| 595 | |
| 596 updateList: function(results) { | |
| 597 for (var i = 0; i < results.length; i++) { | |
| 598 this.update(results[i]); | |
| 599 } | |
| 600 }, | |
| 601 | |
| 602 /** | |
| 603 * Append a new row to the list, removing the last row if we exceed the | |
| 604 * maximum allowed. | |
| 605 * @param {DownloadRow} row Row to be removed. | |
| 606 */ | |
| 607 append: function(row) { | |
| 608 this.rows.push(row); | |
| 609 | |
| 610 var elem = row.element; | |
| 611 var list = this.element; | |
| 612 if (list.firstChild) { | |
| 613 list.insertBefore(elem, list.firstChild); | |
| 614 } else { | |
| 615 list.appendChild(elem); | |
| 616 } | |
| 617 | |
| 618 this.rowsHeight += elem.offsetHeight; | |
| 619 | |
| 620 this.numRows++; | |
| 621 // We display no more than 5 elements. | |
| 622 if (this.numRows > 5) | |
| 623 this.remove(list.lastChild.row); | |
| 624 | |
| 625 this.resize(); | |
| 626 }, | |
| 627 | |
| 628 getRow: function(path) { | |
| 629 for (var i = 0; i < this.rows.length; i++) { | |
| 630 if (this.rows[i].path == path) | |
| 631 return this.rows[i]; | |
| 632 } | |
| 633 }, | |
| 634 | |
| 635 /** | |
| 636 * Returns the list of rows that are not in the results array. | |
| 637 * @param {Array} results Array of JSONified DownloadItems. | |
| 638 */ | |
| 639 findMissing: function(results) { | |
| 640 var removeList = []; | |
| 641 | |
| 642 for (var i = 0; i < this.rows.length; i++) { | |
| 643 var row = this.rows[i]; | |
| 644 var found = false; | |
| 645 for (var j = 0; j < results.length; j++) { | |
| 646 if (row.path == results[j].file_path) { | |
| 647 found = true; | |
| 648 break; | |
| 649 } | |
| 650 } | |
| 651 if (!found) | |
| 652 removeList.push(row); | |
| 653 } | |
| 654 return removeList; | |
| 655 }, | |
| 656 | |
| 657 /** | |
| 658 * Handle list callback with list of DownloadItems. | |
| 659 * @param {Array} results Array of JSONified DownloadItems. | |
| 660 */ | |
| 661 list: function(results) { | |
| 662 var rows = this.findMissing(results); | |
| 663 this.updateList(results); | |
| 664 this.removeList(rows); | |
| 665 }, | |
| 666 | |
| 667 /** | |
| 668 * Handle update of a DownloadItem we're observing. | |
| 669 * @param {Object} result JSON representation of DownloadItem. | |
| 670 */ | |
| 671 update: function(result) { | |
| 672 var row = this.getRow(result.file_path); | |
| 673 if (!row && !DiscardResult(result)) | |
| 674 row = new DownloadRow(this, result); | |
| 675 | |
| 676 row && row.update(result); | |
| 677 }, | |
| 678 }; | |
| 679 | |
| 680 document.addEventListener('DOMContentLoaded', init); | |
| OLD | NEW |