| 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 /** | 5 /** |
| 6 * The current selection object. | 6 * The current selection object. |
| 7 * |
| 7 * @param {FileManager} fileManager FileManager instance. | 8 * @param {FileManager} fileManager FileManager instance. |
| 8 * @param {Array.<number>} indexes Selected indexes. | 9 * @param {Array.<number>} indexes Selected indexes. |
| 10 * @constructor |
| 9 */ | 11 */ |
| 10 function FileSelection(fileManager, indexes) { | 12 function FileSelection(fileManager, indexes) { |
| 11 this.fileManager_ = fileManager; | 13 this.fileManager_ = fileManager; |
| 12 this.indexes = indexes; | 14 this.indexes = indexes; |
| 13 this.entries = []; | 15 this.entries = []; |
| 14 this.urls = []; | 16 this.urls = []; |
| 15 this.totalCount = 0; | 17 this.totalCount = 0; |
| 16 this.fileCount = 0; | 18 this.fileCount = 0; |
| 17 this.directoryCount = 0; | 19 this.directoryCount = 0; |
| 18 this.bytes = 0; | 20 this.bytes = 0; |
| (...skipping 26 matching lines...) Expand all Loading... |
| 45 this.directoryCount += 1; | 47 this.directoryCount += 1; |
| 46 } | 48 } |
| 47 this.totalCount++; | 49 this.totalCount++; |
| 48 } | 50 } |
| 49 | 51 |
| 50 this.tasks = new FileTasks(this.fileManager_); | 52 this.tasks = new FileTasks(this.fileManager_); |
| 51 } | 53 } |
| 52 | 54 |
| 53 /** | 55 /** |
| 54 * Computes data required to get file tasks and requests the tasks. | 56 * Computes data required to get file tasks and requests the tasks. |
| 57 * |
| 55 * @param {function} callback The callback. | 58 * @param {function} callback The callback. |
| 56 */ | 59 */ |
| 57 FileSelection.prototype.createTasks = function(callback) { | 60 FileSelection.prototype.createTasks = function(callback) { |
| 58 if (!this.fileManager_.isOnDrive()) { | 61 if (!this.fileManager_.isOnDrive()) { |
| 59 this.tasks.init(this.urls); | 62 this.tasks.init(this.urls); |
| 60 callback(); | 63 callback(); |
| 61 return; | 64 return; |
| 62 } | 65 } |
| 63 | 66 |
| 64 this.fileManager_.metadataCache_.get(this.urls, 'drive', function(props) { | 67 this.fileManager_.metadataCache_.get(this.urls, 'drive', function(props) { |
| 65 var present = props.filter(function(p) { return p && p.availableOffline }); | 68 var present = props.filter(function(p) { return p && p.availableOffline }); |
| 66 this.allDriveFilesPresent = present.length == props.length; | 69 this.allDriveFilesPresent = present.length == props.length; |
| 67 | 70 |
| 68 // Collect all of the mime types and push that info into the selection. | 71 // Collect all of the mime types and push that info into the selection. |
| 69 this.mimeTypes = props.map(function(value) { | 72 this.mimeTypes = props.map(function(value) { |
| 70 return (value && value.contentMimeType) || ''; | 73 return (value && value.contentMimeType) || ''; |
| 71 }); | 74 }); |
| 72 | 75 |
| 73 this.tasks.init(this.urls, this.mimeTypes); | 76 this.tasks.init(this.urls, this.mimeTypes); |
| 74 callback(); | 77 callback(); |
| 75 }.bind(this)); | 78 }.bind(this)); |
| 76 }; | 79 }; |
| 77 | 80 |
| 78 /** | 81 /** |
| 79 * Computes the total size of selected files. | 82 * Computes the total size of selected files. |
| 83 * |
| 80 * @param {function} callback The callback. | 84 * @param {function} callback The callback. |
| 81 */ | 85 */ |
| 82 FileSelection.prototype.computeBytes = function(callback) { | 86 FileSelection.prototype.computeBytes = function(callback) { |
| 83 if (this.entries.length == 0) { | 87 if (this.entries.length == 0) { |
| 84 this.bytesKnown = true; | 88 this.bytesKnown = true; |
| 85 this.bytes = 0; | 89 this.bytes = 0; |
| 86 return; | 90 return; |
| 87 } | 91 } |
| 88 | 92 |
| 89 var countdown = this.entries.length; | 93 var countdown = this.entries.length; |
| (...skipping 26 matching lines...) Expand all Loading... |
| 116 return !this.cancelled_; | 120 return !this.cancelled_; |
| 117 }.bind(this); | 121 }.bind(this); |
| 118 | 122 |
| 119 for (var index = 0; index < this.entries.length; index++) { | 123 for (var index = 0; index < this.entries.length; index++) { |
| 120 util.forEachEntryInTree(this.entries[index], onEntry); | 124 util.forEachEntryInTree(this.entries[index], onEntry); |
| 121 } | 125 } |
| 122 }; | 126 }; |
| 123 | 127 |
| 124 /** | 128 /** |
| 125 * Cancels any async computation. | 129 * Cancels any async computation. |
| 130 * |
| 126 * @private | 131 * @private |
| 127 */ | 132 */ |
| 128 FileSelection.prototype.cancelComputing_ = function() { | 133 FileSelection.prototype.cancelComputing_ = function() { |
| 129 this.cancelled_ = true; | 134 this.cancelled_ = true; |
| 130 }; | 135 }; |
| 131 | 136 |
| 132 /** | 137 /** |
| 133 * This object encapsulates everything related to current selection. | 138 * This object encapsulates everything related to current selection. |
| 139 * |
| 134 * @param {FileManager} fileManager File manager instance. | 140 * @param {FileManager} fileManager File manager instance. |
| 141 * @constructor |
| 135 */ | 142 */ |
| 136 function FileSelectionHandler(fileManager) { | 143 function FileSelectionHandler(fileManager) { |
| 137 this.fileManager_ = fileManager; | 144 this.fileManager_ = fileManager; |
| 138 // TODO(dgozman): create a shared object with most of UI elements. | 145 // TODO(dgozman): create a shared object with most of UI elements. |
| 139 this.okButton_ = fileManager.okButton_; | 146 this.okButton_ = fileManager.okButton_; |
| 140 this.filenameInput_ = fileManager.filenameInput_; | 147 this.filenameInput_ = fileManager.filenameInput_; |
| 141 | 148 |
| 142 this.previewPanel_ = fileManager.dialogDom_.querySelector('.preview-panel'); | 149 this.previewPanel_ = fileManager.dialogDom_.querySelector('.preview-panel'); |
| 143 this.previewThumbnails_ = this.previewPanel_. | 150 this.previewThumbnails_ = this.previewPanel_. |
| 144 querySelector('.preview-thumbnails'); | 151 querySelector('.preview-thumbnails'); |
| 145 this.previewSummary_ = this.previewPanel_.querySelector('.preview-summary'); | 152 this.previewSummary_ = this.previewPanel_.querySelector('.preview-summary'); |
| 146 this.previewText_ = this.previewSummary_.querySelector('.preview-text'); | 153 this.previewText_ = this.previewSummary_.querySelector('.preview-text'); |
| 147 this.calculatingSize_ = this.previewSummary_. | 154 this.calculatingSize_ = this.previewSummary_. |
| 148 querySelector('.calculating-size'); | 155 querySelector('.calculating-size'); |
| 149 this.calculatingSize_.textContent = str('CALCULATING_SIZE'); | 156 this.calculatingSize_.textContent = str('CALCULATING_SIZE'); |
| 150 | 157 |
| 151 this.searchBreadcrumbs_ = fileManager.searchBreadcrumbs_; | 158 this.searchBreadcrumbs_ = fileManager.searchBreadcrumbs_; |
| 152 this.taskItems_ = fileManager.taskItems_; | 159 this.taskItems_ = fileManager.taskItems_; |
| 153 | 160 |
| 154 this.animationTimeout_ = null; | 161 this.animationTimeout_ = null; |
| 155 } | 162 } |
| 156 | 163 |
| 157 /** | 164 /** |
| 158 * Maximum amount of thumbnails in the preview pane. | 165 * Maximum amount of thumbnails in the preview pane. |
| 166 * |
| 167 * @const |
| 168 * @type {number} |
| 159 */ | 169 */ |
| 160 FileSelectionHandler.MAX_PREVIEW_THUMBNAIL_COUNT = 4; | 170 FileSelectionHandler.MAX_PREVIEW_THUMBNAIL_COUNT = 4; |
| 161 | 171 |
| 162 /** | 172 /** |
| 163 * Maximum width or height of an image what pops up when the mouse hovers | 173 * Maximum width or height of an image what pops up when the mouse hovers |
| 164 * thumbnail in the bottom panel (in pixels). | 174 * thumbnail in the bottom panel (in pixels). |
| 175 * |
| 176 * @const |
| 177 * @type {number} |
| 165 */ | 178 */ |
| 166 FileSelectionHandler.IMAGE_HOVER_PREVIEW_SIZE = 200; | 179 FileSelectionHandler.IMAGE_HOVER_PREVIEW_SIZE = 200; |
| 167 | 180 |
| 168 /** | 181 /** |
| 169 * Update the UI when the selection model changes. | 182 * Update the UI when the selection model changes. |
| 170 * | 183 * |
| 171 * @param {cr.Event} event The change event. | 184 * @param {cr.Event} event The change event. |
| 172 */ | 185 */ |
| 173 FileSelectionHandler.prototype.onFileSelectionChanged = function(event) { | 186 FileSelectionHandler.prototype.onFileSelectionChanged = function(event) { |
| 174 var indexes = | 187 var indexes = |
| (...skipping 53 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 228 FileSelectionHandler.prototype.clearUI = function() { | 241 FileSelectionHandler.prototype.clearUI = function() { |
| 229 this.previewThumbnails_.textContent = ''; | 242 this.previewThumbnails_.textContent = ''; |
| 230 this.previewText_.textContent = ''; | 243 this.previewText_.textContent = ''; |
| 231 this.hideCalculating_(); | 244 this.hideCalculating_(); |
| 232 this.taskItems_.hidden = true; | 245 this.taskItems_.hidden = true; |
| 233 this.okButton_.disabled = true; | 246 this.okButton_.disabled = true; |
| 234 }; | 247 }; |
| 235 | 248 |
| 236 /** | 249 /** |
| 237 * Updates the Ok button enabled state. | 250 * Updates the Ok button enabled state. |
| 251 * |
| 238 * @return {boolean} Whether button is enabled. | 252 * @return {boolean} Whether button is enabled. |
| 239 */ | 253 */ |
| 240 FileSelectionHandler.prototype.updateOkButton = function() { | 254 FileSelectionHandler.prototype.updateOkButton = function() { |
| 241 var selectable; | 255 var selectable; |
| 242 var dialogType = this.fileManager_.dialogType; | 256 var dialogType = this.fileManager_.dialogType; |
| 243 | 257 |
| 244 if (dialogType == DialogType.SELECT_FOLDER) { | 258 if (dialogType == DialogType.SELECT_FOLDER) { |
| 245 // In SELECT_FOLDER mode, we allow to select current directory | 259 // In SELECT_FOLDER mode, we allow to select current directory |
| 246 // when nothing is selected. | 260 // when nothing is selected. |
| 247 selectable = this.selection.directoryCount <= 1 && | 261 selectable = this.selection.directoryCount <= 1 && |
| (...skipping 20 matching lines...) Expand all Loading... |
| 268 } | 282 } |
| 269 | 283 |
| 270 this.okButton_.disabled = !selectable; | 284 this.okButton_.disabled = !selectable; |
| 271 return selectable; | 285 return selectable; |
| 272 }; | 286 }; |
| 273 | 287 |
| 274 /** | 288 /** |
| 275 * Check if all the files in the current selection are available. The only | 289 * Check if all the files in the current selection are available. The only |
| 276 * case when files might be not available is when the selection contains | 290 * case when files might be not available is when the selection contains |
| 277 * uncached Drive files and the browser is offline. | 291 * uncached Drive files and the browser is offline. |
| 292 * |
| 278 * @return {boolean} True if all files in the current selection are | 293 * @return {boolean} True if all files in the current selection are |
| 279 * available. | 294 * available. |
| 280 */ | 295 */ |
| 281 FileSelectionHandler.prototype.isFileSelectionAvailable = function() { | 296 FileSelectionHandler.prototype.isFileSelectionAvailable = function() { |
| 282 return !this.fileManager_.isOnDrive() || | 297 return !this.fileManager_.isOnDrive() || |
| 283 !this.fileManager_.isDriveOffline() || | 298 !this.fileManager_.isDriveOffline() || |
| 284 this.selection.allDriveFilesPresent; | 299 this.selection.allDriveFilesPresent; |
| 285 }; | 300 }; |
| 286 | 301 |
| 287 /** | 302 /** |
| 288 * Animates preview panel show/hide transitions. | 303 * Animates preview panel show/hide transitions. |
| 304 * |
| 289 * @private | 305 * @private |
| 290 */ | 306 */ |
| 291 FileSelectionHandler.prototype.updatePreviewPanelVisibility_ = function() { | 307 FileSelectionHandler.prototype.updatePreviewPanelVisibility_ = function() { |
| 292 var panel = this.previewPanel_; | 308 var panel = this.previewPanel_; |
| 293 var state = panel.getAttribute('visibility'); | 309 var state = panel.getAttribute('visibility'); |
| 294 var mustBeVisible = (this.selection.totalCount > 0); | 310 var mustBeVisible = (this.selection.totalCount > 0); |
| 295 var self = this; | 311 var self = this; |
| 296 var fm = this.fileManager_; | 312 var fm = this.fileManager_; |
| 297 | 313 |
| 298 var stopHidingAndShow = function() { | 314 var stopHidingAndShow = function() { |
| (...skipping 41 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 340 /** | 356 /** |
| 341 * @return {boolean} True if space reserverd for the preview panel. | 357 * @return {boolean} True if space reserverd for the preview panel. |
| 342 * @private | 358 * @private |
| 343 */ | 359 */ |
| 344 FileSelectionHandler.prototype.isPreviewPanelVisibile_ = function() { | 360 FileSelectionHandler.prototype.isPreviewPanelVisibile_ = function() { |
| 345 return this.previewPanel_.getAttribute('visibility') != 'hidden'; | 361 return this.previewPanel_.getAttribute('visibility') != 'hidden'; |
| 346 }; | 362 }; |
| 347 | 363 |
| 348 /** | 364 /** |
| 349 * Update the selection summary in preview panel. | 365 * Update the selection summary in preview panel. |
| 366 * |
| 350 * @private | 367 * @private |
| 351 */ | 368 */ |
| 352 FileSelectionHandler.prototype.updatePreviewPanelText_ = function() { | 369 FileSelectionHandler.prototype.updatePreviewPanelText_ = function() { |
| 353 var selection = this.selection; | 370 var selection = this.selection; |
| 354 if (selection.totalCount == 0) { | 371 if (selection.totalCount == 0) { |
| 355 // We dont want to change the string during preview panel animating away. | 372 // We dont want to change the string during preview panel animating away. |
| 356 return; | 373 return; |
| 357 } | 374 } |
| 358 | 375 |
| 359 var text = ''; | 376 var text = ''; |
| (...skipping 15 matching lines...) Expand all Loading... |
| 375 } | 392 } |
| 376 } else { | 393 } else { |
| 377 this.showCalculating_(); | 394 this.showCalculating_(); |
| 378 } | 395 } |
| 379 | 396 |
| 380 this.previewText_.textContent = text; | 397 this.previewText_.textContent = text; |
| 381 }; | 398 }; |
| 382 | 399 |
| 383 /** | 400 /** |
| 384 * Displays the 'calculating size' label. | 401 * Displays the 'calculating size' label. |
| 402 * |
| 385 * @private | 403 * @private |
| 386 */ | 404 */ |
| 387 FileSelectionHandler.prototype.showCalculating_ = function() { | 405 FileSelectionHandler.prototype.showCalculating_ = function() { |
| 388 if (this.animationTimeout_) { | 406 if (this.animationTimeout_) { |
| 389 clearTimeout(this.animationTimeout_); | 407 clearTimeout(this.animationTimeout_); |
| 390 this.animationTimeout_ = null; | 408 this.animationTimeout_ = null; |
| 391 } | 409 } |
| 392 | 410 |
| 393 var dotCount = 0; | 411 var dotCount = 0; |
| 394 | 412 |
| (...skipping 13 matching lines...) Expand all Loading... |
| 408 var start = function() { | 426 var start = function() { |
| 409 this.calculatingSize_.hidden = false; | 427 this.calculatingSize_.hidden = false; |
| 410 advance(); | 428 advance(); |
| 411 }.bind(this); | 429 }.bind(this); |
| 412 | 430 |
| 413 this.animationTimeout_ = setTimeout(start, 500); | 431 this.animationTimeout_ = setTimeout(start, 500); |
| 414 }; | 432 }; |
| 415 | 433 |
| 416 /** | 434 /** |
| 417 * Hides the 'calculating size' label. | 435 * Hides the 'calculating size' label. |
| 436 * |
| 418 * @private | 437 * @private |
| 419 */ | 438 */ |
| 420 FileSelectionHandler.prototype.hideCalculating_ = function() { | 439 FileSelectionHandler.prototype.hideCalculating_ = function() { |
| 421 if (this.animationTimeout_) { | 440 if (this.animationTimeout_) { |
| 422 clearTimeout(this.animationTimeout_); | 441 clearTimeout(this.animationTimeout_); |
| 423 this.animationTimeout_ = null; | 442 this.animationTimeout_ = null; |
| 424 } | 443 } |
| 425 this.calculatingSize_.hidden = true; | 444 this.calculatingSize_.hidden = true; |
| 426 }; | 445 }; |
| 427 | 446 |
| 428 /** | 447 /** |
| 429 * Calculates async selection stats and updates secondary UI elements. | 448 * Calculates async selection stats and updates secondary UI elements. |
| 449 * |
| 430 * @param {FileSelection} selection The selection object. | 450 * @param {FileSelection} selection The selection object. |
| 431 */ | 451 */ |
| 432 FileSelectionHandler.prototype.updateFileSelectionAsync = function(selection) { | 452 FileSelectionHandler.prototype.updateFileSelectionAsync = function(selection) { |
| 433 if (this.selection != selection) return; | 453 if (this.selection != selection) return; |
| 434 | 454 |
| 435 // Update the file tasks. | 455 // Update the file tasks. |
| 436 var onTasks = function() { | 456 var onTasks = function() { |
| 437 if (this.selection != selection) return; | 457 if (this.selection != selection) return; |
| 438 selection.tasks.display(this.taskItems_); | 458 selection.tasks.display(this.taskItems_); |
| 439 selection.tasks.updateMenuItem(); | 459 selection.tasks.updateMenuItem(); |
| (...skipping 31 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 471 | 491 |
| 472 // Inform tests it's OK to click buttons now. | 492 // Inform tests it's OK to click buttons now. |
| 473 chrome.test.sendMessage('selection-change-complete'); | 493 chrome.test.sendMessage('selection-change-complete'); |
| 474 | 494 |
| 475 // Show thumbnails. | 495 // Show thumbnails. |
| 476 this.showPreviewThumbnails_(selection); | 496 this.showPreviewThumbnails_(selection); |
| 477 }; | 497 }; |
| 478 | 498 |
| 479 /** | 499 /** |
| 480 * Renders preview thumbnails in preview panel. | 500 * Renders preview thumbnails in preview panel. |
| 501 * |
| 481 * @param {FileSelection} selection The selection object. | 502 * @param {FileSelection} selection The selection object. |
| 482 * @private | 503 * @private |
| 483 */ | 504 */ |
| 484 FileSelectionHandler.prototype.showPreviewThumbnails_ = function(selection) { | 505 FileSelectionHandler.prototype.showPreviewThumbnails_ = function(selection) { |
| 485 var thumbnails = []; | 506 var thumbnails = []; |
| 486 var thumbnailCount = 0; | 507 var thumbnailCount = 0; |
| 487 var thumbnailLoaded = -1; | 508 var thumbnailLoaded = -1; |
| 488 var forcedShowTimeout = null; | 509 var forcedShowTimeout = null; |
| 489 var thumbnailsHaveZoom = false; | 510 var thumbnailsHaveZoom = false; |
| 490 var self = this; | 511 var self = this; |
| (...skipping 66 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 557 } | 578 } |
| 558 } | 579 } |
| 559 | 580 |
| 560 forcedShowTimeout = setTimeout(showThumbnails, | 581 forcedShowTimeout = setTimeout(showThumbnails, |
| 561 FileManager.THUMBNAIL_SHOW_DELAY); | 582 FileManager.THUMBNAIL_SHOW_DELAY); |
| 562 onThumbnailLoaded(); | 583 onThumbnailLoaded(); |
| 563 }; | 584 }; |
| 564 | 585 |
| 565 /** | 586 /** |
| 566 * Renders a thumbnail for the buttom panel. | 587 * Renders a thumbnail for the buttom panel. |
| 588 * |
| 567 * @param {Entry} entry Entry to render for. | 589 * @param {Entry} entry Entry to render for. |
| 568 * @param {Function} callback Callend when image loaded. | 590 * @param {Function} callback Callend when image loaded. |
| 569 * @return {HTMLDivElement} Created element. | 591 * @return {HTMLDivElement} Created element. |
| 570 * @private | 592 * @private |
| 571 */ | 593 */ |
| 572 FileSelectionHandler.prototype.renderThumbnail_ = function(entry, callback) { | 594 FileSelectionHandler.prototype.renderThumbnail_ = function(entry, callback) { |
| 573 var thumbnail = this.fileManager_.document_.createElement('div'); | 595 var thumbnail = this.fileManager_.document_.createElement('div'); |
| 574 FileGrid.decorateThumbnailBox(thumbnail, | 596 FileGrid.decorateThumbnailBox(thumbnail, |
| 575 entry, | 597 entry, |
| 576 this.fileManager_.metadataCache_, | 598 this.fileManager_.metadataCache_, |
| 577 ThumbnailLoader.FillMode.FILL, | 599 ThumbnailLoader.FillMode.FILL, |
| 578 callback); | 600 callback); |
| 579 return thumbnail; | 601 return thumbnail; |
| 580 }; | 602 }; |
| 581 | 603 |
| 582 /** | 604 /** |
| 583 * Updates the search breadcrumbs. | 605 * Updates the search breadcrumbs. |
| 606 * |
| 584 * @private | 607 * @private |
| 585 */ | 608 */ |
| 586 FileSelectionHandler.prototype.updateSearchBreadcrumbs_ = function() { | 609 FileSelectionHandler.prototype.updateSearchBreadcrumbs_ = function() { |
| 587 var selectedIndexes = | 610 var selectedIndexes = |
| 588 this.fileManager_.getCurrentList().selectionModel.selectedIndexes; | 611 this.fileManager_.getCurrentList().selectionModel.selectedIndexes; |
| 589 if (selectedIndexes.length !== 1 || | 612 if (selectedIndexes.length !== 1 || |
| 590 !this.fileManager_.directoryModel_.isSearching()) { | 613 !this.fileManager_.directoryModel_.isSearching()) { |
| 591 this.searchBreadcrumbs_.hide(); | 614 this.searchBreadcrumbs_.hide(); |
| 592 return; | 615 return; |
| 593 } | 616 } |
| (...skipping 70 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 664 style.left = (boxWidth - imageWidth) / 2 + 'px'; | 687 style.left = (boxWidth - imageWidth) / 2 + 'px'; |
| 665 style.top = (boxHeight - imageHeight) / 2 + 'px'; | 688 style.top = (boxHeight - imageHeight) / 2 + 'px'; |
| 666 style.position = 'relative'; | 689 style.position = 'relative'; |
| 667 | 690 |
| 668 util.applyTransform(largeImage, transform); | 691 util.applyTransform(largeImage, transform); |
| 669 | 692 |
| 670 largeImageBox.appendChild(largeImage); | 693 largeImageBox.appendChild(largeImage); |
| 671 largeImageBox.style.zIndex = 1000; | 694 largeImageBox.style.zIndex = 1000; |
| 672 return true; | 695 return true; |
| 673 }; | 696 }; |
| OLD | NEW |