| OLD | NEW |
| 1 // Copyright (c) 2011 The Chromium Authors. All rights reserved. | 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 | 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 // Setting the src of an img to an empty string can crash the browser, so we | 5 // Setting the src of an img to an empty string can crash the browser, so we |
| 6 // use an empty 1x1 gif instead. | 6 // use an empty 1x1 gif instead. |
| 7 const EMPTY_IMAGE_URI = 'data:image/gif;base64,' | 7 const EMPTY_IMAGE_URI = 'data:image/gif;base64,' |
| 8 + 'R0lGODlhAQABAPABAP///wAAACH5BAEKAAAALAAAAAABAAEAAAICRAEAOw%3D%3D'; | 8 + 'R0lGODlhAQABAPABAP///wAAACH5BAEKAAAALAAAAAABAAEAAAICRAEAOw%3D%3D'; |
| 9 | 9 |
| 10 // If directory files changes too often, don't rescan directory more than once | 10 // If directory files changes too often, don't rescan directory more than once |
| (...skipping 43 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 54 metrics.recordEnum('Create', this.dialogType_, | 54 metrics.recordEnum('Create', this.dialogType_, |
| 55 [FileManager.DialogType.SELECT_FOLDER, | 55 [FileManager.DialogType.SELECT_FOLDER, |
| 56 FileManager.DialogType.SELECT_SAVEAS_FILE, | 56 FileManager.DialogType.SELECT_SAVEAS_FILE, |
| 57 FileManager.DialogType.SELECT_OPEN_FILE, | 57 FileManager.DialogType.SELECT_OPEN_FILE, |
| 58 FileManager.DialogType.SELECT_OPEN_MULTI_FILE, | 58 FileManager.DialogType.SELECT_OPEN_MULTI_FILE, |
| 59 FileManager.DialogType.FULL_PAGE]); | 59 FileManager.DialogType.FULL_PAGE]); |
| 60 | 60 |
| 61 // TODO(dgozman): This will be changed to LocaleInfo. | 61 // TODO(dgozman): This will be changed to LocaleInfo. |
| 62 this.locale_ = new v8Locale(navigator.language); | 62 this.locale_ = new v8Locale(navigator.language); |
| 63 | 63 |
| 64 this.initFileSystem_(); | 64 this.resolveRoots_(); |
| 65 this.initDom_(); | 65 this.initDom_(); |
| 66 this.initDialogType_(); | 66 this.initDialogType_(); |
| 67 this.dialogDom_.style.opacity = '1'; | 67 this.dialogDom_.style.opacity = '1'; |
| 68 } | 68 } |
| 69 | 69 |
| 70 FileManager.prototype = { | 70 FileManager.prototype = { |
| 71 __proto__: cr.EventTarget.prototype | 71 __proto__: cr.EventTarget.prototype |
| 72 }; | 72 }; |
| 73 | 73 |
| 74 // Anonymous "namespace". | 74 // Anonymous "namespace". |
| (...skipping 391 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 466 chrome.fileBrowserPrivate.getStrings(function(strings) { | 466 chrome.fileBrowserPrivate.getStrings(function(strings) { |
| 467 localStrings = new LocalStrings(strings); | 467 localStrings = new LocalStrings(strings); |
| 468 if (callback) | 468 if (callback) |
| 469 callback(); | 469 callback(); |
| 470 }); | 470 }); |
| 471 }; | 471 }; |
| 472 | 472 |
| 473 // Instance methods. | 473 // Instance methods. |
| 474 | 474 |
| 475 /** | 475 /** |
| 476 * Request local file system, resolve roots and init_ after that. | 476 * Request file system and get root entries asynchronously. Invokes init_ |
| 477 * @private | 477 * when have finished. |
| 478 */ | 478 */ |
| 479 FileManager.prototype.initFileSystem_ = function() { | 479 FileManager.prototype.resolveRoots_ = function(callback) { |
| 480 util.installFileErrorToString(); | 480 var rootPaths = ['Downloads', 'removable', 'archive']; |
| 481 |
| 481 metrics.startInterval('Load.FileSystem'); | 482 metrics.startInterval('Load.FileSystem'); |
| 482 | |
| 483 var self = this; | 483 var self = this; |
| 484 | 484 |
| 485 // The list of active mount points to distinct them from other directories. | 485 // The list of active mount points to distinct them from other directories. |
| 486 chrome.fileBrowserPrivate.getMountPoints(function(mountPoints) { | 486 chrome.fileBrowserPrivate.getMountPoints(function(mountPoints) { |
| 487 self.mountPoints_ = mountPoints; | 487 self.mountPoints_ = mountPoints; |
| 488 onDone(); | 488 onDone(); |
| 489 }); | 489 }); |
| 490 | 490 |
| 491 function onDone() { | 491 function onDone() { |
| 492 if (self.mountPoints_ && self.rootEntries_) | 492 if (self.mountPoints_ && self.rootEntries_) |
| 493 self.init_(); | 493 self.init_(); |
| 494 } | 494 } |
| 495 | 495 |
| 496 chrome.fileBrowserPrivate.requestLocalFileSystem(function(filesystem) { | 496 chrome.fileBrowserPrivate.requestLocalFileSystem(function(filesystem) { |
| 497 self.filesystem_ = filesystem; |
| 498 util.installFileErrorToString(); |
| 499 |
| 497 metrics.recordInterval('Load.FileSystem'); | 500 metrics.recordInterval('Load.FileSystem'); |
| 498 | 501 |
| 499 self.filesystem_ = filesystem; | 502 var rootEntries = []; |
| 500 self.resolveRoots_(function(rootEntries) { | 503 |
| 504 function onAllRootsFound() { |
| 505 metrics.recordInterval('Load.Roots'); |
| 501 self.rootEntries_ = rootEntries; | 506 self.rootEntries_ = rootEntries; |
| 502 onDone(); | 507 onDone(); |
| 503 }); | 508 } |
| 509 |
| 510 function onPathError(path, err) { |
| 511 console.error('Error locating root path: ' + path + ': ' + err); |
| 512 } |
| 513 |
| 514 function onEntryFound(entry) { |
| 515 if (entry) { |
| 516 rootEntries.push(entry); |
| 517 } else { |
| 518 onAllRootsFound(); |
| 519 } |
| 520 } |
| 521 |
| 522 metrics.startInterval('Load.Roots'); |
| 523 if (filesystem.name.match(/^chrome-extension_\S+:external/i)) { |
| 524 // We've been handed the local filesystem, whose root directory |
| 525 // cannot be enumerated. |
| 526 util.getDirectories(filesystem.root, {create: false}, rootPaths, |
| 527 onEntryFound, onPathError); |
| 528 } else { |
| 529 util.forEachDirEntry(filesystem.root, onEntryFound); |
| 530 } |
| 504 }); | 531 }); |
| 505 }; | 532 }; |
| 506 | 533 |
| 507 /** | 534 /** |
| 508 * Get root entries asynchronously. Invokes callback | |
| 509 * when have finished. | |
| 510 */ | |
| 511 FileManager.prototype.resolveRoots_ = function(callback) { | |
| 512 var rootPaths = [DOWNLOADS_DIRECTORY, ARCHIVE_DIRECTORY, | |
| 513 REMOVABLE_DIRECTORY].map(function(s) { return s.substring(1); }); | |
| 514 var rootEntries = []; | |
| 515 | |
| 516 // The number of entries left to enumerate to get all roots. | |
| 517 // When equals to zero, we are done. | |
| 518 var entriesToEnumerate = 0; | |
| 519 // Entries may be enumerated faster than next one appears, so we have this | |
| 520 // guard to not finish too early. | |
| 521 var allEntriesFound = false; | |
| 522 | |
| 523 function onPathError(path, err) { | |
| 524 console.error('Error locating root path: ' + path + ': ' + err); | |
| 525 } | |
| 526 | |
| 527 function onRootFound(root) { | |
| 528 if (root) { | |
| 529 rootEntries.push(root); | |
| 530 } else { | |
| 531 entriesToEnumerate--; | |
| 532 if (entriesToEnumerate == 0 && allEntriesFound) { | |
| 533 metrics.recordInterval('Load.Roots'); | |
| 534 callback(rootEntries); | |
| 535 } | |
| 536 } | |
| 537 } | |
| 538 | |
| 539 function onEntryFound(entry) { | |
| 540 if (entry) { | |
| 541 entriesToEnumerate++; | |
| 542 var path = entry.fullPath; | |
| 543 if (path == ARCHIVE_DIRECTORY || path == REMOVABLE_DIRECTORY) { | |
| 544 // All removable devices and mounted archives are considered | |
| 545 // roots, and are shown in the sidebar. | |
| 546 util.forEachDirEntry(entry, onRootFound); | |
| 547 } else { | |
| 548 onRootFound(entry); | |
| 549 onRootFound(null); | |
| 550 } | |
| 551 } else { | |
| 552 allEntriesFound = true; | |
| 553 } | |
| 554 } | |
| 555 | |
| 556 metrics.startInterval('Load.Roots'); | |
| 557 if (this.filesystem_.name.match(/^chrome-extension_\S+:external/i)) { | |
| 558 // We've been handed the local filesystem, whose root directory | |
| 559 // cannot be enumerated. | |
| 560 util.getDirectories(this.filesystem_.root, {create: false}, rootPaths, | |
| 561 onEntryFound, onPathError); | |
| 562 } else { | |
| 563 util.forEachDirEntry(this.filesystem_.root, onEntryFound); | |
| 564 } | |
| 565 }; | |
| 566 | |
| 567 /** | |
| 568 * Continue initializing the file manager after resolving roots. | 535 * Continue initializing the file manager after resolving roots. |
| 569 */ | 536 */ |
| 570 FileManager.prototype.init_ = function() { | 537 FileManager.prototype.init_ = function() { |
| 571 metrics.startInterval('Load.DOM'); | 538 metrics.startInterval('Load.DOM'); |
| 572 this.initCommands_(); | |
| 573 | 539 |
| 574 // TODO(rginda): 6/22/11: Remove this test when createDateTimeFormat is | 540 // TODO(rginda): 6/22/11: Remove this test when createDateTimeFormat is |
| 575 // available in all chrome trunk builds. | 541 // available in all chrome trunk builds. |
| 576 if ('createDateTimeFormat' in this.locale_) { | 542 if ('createDateTimeFormat' in this.locale_) { |
| 577 this.shortDateFormatter_ = | 543 this.shortDateFormatter_ = |
| 578 this.locale_.createDateTimeFormat({'dateType': 'medium'}); | 544 this.locale_.createDateTimeFormat({'dateType': 'medium'}); |
| 579 } else { | 545 } else { |
| 580 this.shortDateFormatter_ = { | 546 this.shortDateFormatter_ = { |
| 581 format: function(d) { | 547 format: function(d) { |
| 582 return (d.getMonth() + 1) + '/' + d.getDate() + '/' + d.getFullYear(); | 548 return (d.getMonth() + 1) + '/' + d.getDate() + '/' + d.getFullYear(); |
| (...skipping 37 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 620 window.addEventListener('unload', this.onUnload_.bind(this)); | 586 window.addEventListener('unload', this.onUnload_.bind(this)); |
| 621 | 587 |
| 622 this.addEventListener('directory-changed', | 588 this.addEventListener('directory-changed', |
| 623 this.onDirectoryChanged_.bind(this)); | 589 this.onDirectoryChanged_.bind(this)); |
| 624 this.addEventListener('selection-summarized', | 590 this.addEventListener('selection-summarized', |
| 625 this.onSelectionSummarized_.bind(this)); | 591 this.onSelectionSummarized_.bind(this)); |
| 626 | 592 |
| 627 // The list of archives requested to mount. We will show contents once | 593 // The list of archives requested to mount. We will show contents once |
| 628 // archive is mounted, but only for mounts from within this filebrowser tab. | 594 // archive is mounted, but only for mounts from within this filebrowser tab. |
| 629 this.mountRequests_ = []; | 595 this.mountRequests_ = []; |
| 630 this.unmountRequests_ = []; | |
| 631 chrome.fileBrowserPrivate.onMountCompleted.addListener( | 596 chrome.fileBrowserPrivate.onMountCompleted.addListener( |
| 632 this.onMountCompleted_.bind(this)); | 597 this.onMountCompleted_.bind(this)); |
| 633 | 598 |
| 634 chrome.fileBrowserPrivate.onFileChanged.addListener( | 599 chrome.fileBrowserPrivate.onFileChanged.addListener( |
| 635 this.onFileChanged_.bind(this)); | 600 this.onFileChanged_.bind(this)); |
| 636 | 601 |
| 637 var self = this; | 602 var self = this; |
| 638 | 603 |
| 639 // The list of callbacks to be invoked during the directory rescan after | 604 // The list of callbacks to be invoked during the directory rescan after |
| 640 // all paste tasks are complete. | 605 // all paste tasks are complete. |
| 641 this.pasteSuccessCallbacks_ = []; | 606 this.pasteSuccessCallbacks_ = []; |
| 642 | 607 |
| 608 this.initCommands_(); |
| 609 |
| 643 this.setupCurrentDirectory_(); | 610 this.setupCurrentDirectory_(); |
| 644 | 611 |
| 645 this.summarizeSelection_(); | 612 this.summarizeSelection_(); |
| 646 | 613 |
| 647 this.dataModel_.sort('cachedMtime_', 'desc'); | 614 this.dataModel_.sort('cachedMtime_', 'desc'); |
| 648 | 615 |
| 649 this.refocus(); | 616 this.refocus(); |
| 650 | 617 |
| 651 this.createMetadataProvider_(); | 618 this.createMetadataProvider_(); |
| 652 metrics.recordInterval('Load.DOM'); | 619 metrics.recordInterval('Load.DOM'); |
| (...skipping 38 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 691 // Cache nodes we'll be manipulating. | 658 // Cache nodes we'll be manipulating. |
| 692 this.previewThumbnails_ = | 659 this.previewThumbnails_ = |
| 693 this.dialogDom_.querySelector('.preview-thumbnails'); | 660 this.dialogDom_.querySelector('.preview-thumbnails'); |
| 694 this.previewPanel_ = this.dialogDom_.querySelector('.preview-panel'); | 661 this.previewPanel_ = this.dialogDom_.querySelector('.preview-panel'); |
| 695 this.previewFilename_ = this.dialogDom_.querySelector('.preview-filename'); | 662 this.previewFilename_ = this.dialogDom_.querySelector('.preview-filename'); |
| 696 this.previewSummary_ = this.dialogDom_.querySelector('.preview-summary'); | 663 this.previewSummary_ = this.dialogDom_.querySelector('.preview-summary'); |
| 697 this.filenameInput_ = this.dialogDom_.querySelector('.filename-input'); | 664 this.filenameInput_ = this.dialogDom_.querySelector('.filename-input'); |
| 698 this.taskButtons_ = this.dialogDom_.querySelector('.task-buttons'); | 665 this.taskButtons_ = this.dialogDom_.querySelector('.task-buttons'); |
| 699 this.okButton_ = this.dialogDom_.querySelector('.ok'); | 666 this.okButton_ = this.dialogDom_.querySelector('.ok'); |
| 700 this.cancelButton_ = this.dialogDom_.querySelector('.cancel'); | 667 this.cancelButton_ = this.dialogDom_.querySelector('.cancel'); |
| 668 this.newFolderButton_ = this.dialogDom_.querySelector('.new-folder'); |
| 701 this.deleteButton_ = this.dialogDom_.querySelector('.delete-button'); | 669 this.deleteButton_ = this.dialogDom_.querySelector('.delete-button'); |
| 702 | 670 |
| 703 this.downloadsWarning_ = | 671 this.downloadsWarning_ = |
| 704 this.dialogDom_.querySelector('.downloads-warning'); | 672 this.dialogDom_.querySelector('.downloads-warning'); |
| 705 var html = util.htmlUnescape(str('DOWNLOADS_DIRECTORY_WARNING')); | 673 var html = util.htmlUnescape(str('DOWNLOADS_DIRECTORY_WARNING')); |
| 706 this.downloadsWarning_.lastElementChild.innerHTML = html; | 674 this.downloadsWarning_.lastElementChild.innerHTML = html; |
| 707 var link = this.downloadsWarning_.querySelector('a'); | 675 var link = this.downloadsWarning_.querySelector('a'); |
| 708 link.addEventListener('click', this.onDownloadsWarningClick_.bind(this)); | 676 link.addEventListener('click', this.onDownloadsWarningClick_.bind(this)); |
| 709 | 677 |
| 710 this.document_.addEventListener('keydown', this.onKeyDown_.bind(this)); | 678 this.document_.addEventListener('keydown', this.onKeyDown_.bind(this)); |
| (...skipping 10 matching lines...) Expand all Loading... |
| 721 'keyup', this.onFilenameInputKeyUp_.bind(this)); | 689 'keyup', this.onFilenameInputKeyUp_.bind(this)); |
| 722 this.filenameInput_.addEventListener( | 690 this.filenameInput_.addEventListener( |
| 723 'focus', this.onFilenameInputFocus_.bind(this)); | 691 'focus', this.onFilenameInputFocus_.bind(this)); |
| 724 | 692 |
| 725 var listContainer = this.dialogDom_.querySelector('.list-container'); | 693 var listContainer = this.dialogDom_.querySelector('.list-container'); |
| 726 listContainer.addEventListener('keydown', this.onListKeyDown_.bind(this)); | 694 listContainer.addEventListener('keydown', this.onListKeyDown_.bind(this)); |
| 727 listContainer.addEventListener('keypress', this.onListKeyPress_.bind(this)); | 695 listContainer.addEventListener('keypress', this.onListKeyPress_.bind(this)); |
| 728 this.okButton_.addEventListener('click', this.onOk_.bind(this)); | 696 this.okButton_.addEventListener('click', this.onOk_.bind(this)); |
| 729 this.cancelButton_.addEventListener('click', this.onCancel_.bind(this)); | 697 this.cancelButton_.addEventListener('click', this.onCancel_.bind(this)); |
| 730 | 698 |
| 731 this.dialogDom_.querySelector('div.open-sidebar').addEventListener( | 699 this.dialogDom_.querySelector('button.new-folder').addEventListener( |
| 732 'click', this.onToggleSidebar_.bind(this)); | 700 'click', this.onNewFolderButtonClick_.bind(this)); |
| 733 this.dialogDom_.querySelector('div.close-sidebar').addEventListener( | |
| 734 'click', this.onToggleSidebar_.bind(this)); | |
| 735 this.dialogContainer_ = this.dialogDom_.querySelector('.dialog-container'); | |
| 736 | 701 |
| 737 this.dialogDom_.querySelector('button.detail-view').addEventListener( | 702 this.dialogDom_.querySelector('button.detail-view').addEventListener( |
| 738 'click', this.onDetailViewButtonClick_.bind(this)); | 703 'click', this.onDetailViewButtonClick_.bind(this)); |
| 739 this.dialogDom_.querySelector('button.thumbnail-view').addEventListener( | 704 this.dialogDom_.querySelector('button.thumbnail-view').addEventListener( |
| 740 'click', this.onThumbnailViewButtonClick_.bind(this)); | 705 'click', this.onThumbnailViewButtonClick_.bind(this)); |
| 741 | 706 |
| 742 this.dialogDom_.ownerDocument.defaultView.addEventListener( | 707 this.dialogDom_.ownerDocument.defaultView.addEventListener( |
| 743 'resize', this.onResize_.bind(this)); | 708 'resize', this.onResize_.bind(this)); |
| 744 | 709 |
| 745 var ary = this.dialogDom_.querySelectorAll('[visibleif]'); | 710 var ary = this.dialogDom_.querySelectorAll('[visibleif]'); |
| (...skipping 35 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 781 this.selectionModelClass_ = cr.ui.ListSingleSelectionModel; | 746 this.selectionModelClass_ = cr.ui.ListSingleSelectionModel; |
| 782 } else { | 747 } else { |
| 783 this.selectionModelClass_ = cr.ui.ListSelectionModel; | 748 this.selectionModelClass_ = cr.ui.ListSelectionModel; |
| 784 } | 749 } |
| 785 | 750 |
| 786 this.dataModel_.addEventListener('splice', | 751 this.dataModel_.addEventListener('splice', |
| 787 this.onDataModelSplice_.bind(this)); | 752 this.onDataModelSplice_.bind(this)); |
| 788 | 753 |
| 789 this.initTable_(); | 754 this.initTable_(); |
| 790 this.initGrid_(); | 755 this.initGrid_(); |
| 791 this.initRootsList_(); | |
| 792 | 756 |
| 793 this.setListType(FileManager.ListType.DETAIL); | 757 this.setListType(FileManager.ListType.DETAIL); |
| 794 | 758 |
| 795 this.onResize_(); | 759 this.onResize_(); |
| 796 | 760 |
| 797 this.textSearchState_ = {text: '', date: new Date()}; | 761 this.textSearchState_ = {text: '', date: new Date()}; |
| 798 }; | 762 }; |
| 799 | 763 |
| 800 FileManager.prototype.initRootsList_ = function() { | |
| 801 this.rootsList_ = this.dialogDom_.querySelector('.roots-list'); | |
| 802 cr.ui.List.decorate(this.rootsList_); | |
| 803 | |
| 804 var self = this; | |
| 805 this.rootsList_.itemConstructor = function(entry) { | |
| 806 return self.renderRoot_(entry); | |
| 807 }; | |
| 808 | |
| 809 this.rootsList_.selectionModel = new cr.ui.ListSingleSelectionModel(); | |
| 810 this.rootsList_.selectionModel.addEventListener( | |
| 811 'change', this.onRootsSelectionChanged_.bind(this)); | |
| 812 | |
| 813 // TODO(dgozman): add "Add a drive" item. | |
| 814 this.rootsList_.dataModel = new cr.ui.ArrayDataModel(this.rootEntries_); | |
| 815 }; | |
| 816 | |
| 817 FileManager.prototype.updateRoots_ = function(opt_changeDirectoryTo) { | |
| 818 var self = this; | |
| 819 this.resolveRoots_(function(rootEntries) { | |
| 820 self.rootEntries_ = rootEntries; | |
| 821 | |
| 822 var dataModel = self.rootsList_.dataModel; | |
| 823 var args = [0, dataModel.length].concat(rootEntries); | |
| 824 dataModel.splice.apply(dataModel, args); | |
| 825 | |
| 826 self.updateRootsListSelection_(); | |
| 827 | |
| 828 if (opt_changeDirectoryTo) | |
| 829 self.changeDirectory(opt_changeDirectoryTo); | |
| 830 }); | |
| 831 }; | |
| 832 | |
| 833 /** | 764 /** |
| 834 * Get the icon type for a given Entry. | 765 * Get the icon type for a given Entry. |
| 835 * | 766 * |
| 836 * @param {Entry} entry An Entry subclass (FileEntry or DirectoryEntry). | 767 * @param {Entry} entry An Entry subclass (FileEntry or DirectoryEntry). |
| 837 * @return {string} | 768 * @return {string} |
| 838 */ | 769 */ |
| 839 FileManager.prototype.getIconType = function(entry) { | 770 FileManager.prototype.getIconType = function(entry) { |
| 840 if (!('cachedIconType_' in entry)) | 771 if (!('cachedIconType_' in entry)) |
| 841 entry.cachedIconType_ = this.computeIconType_(entry); | 772 entry.cachedIconType_ = this.computeIconType_(entry); |
| 842 return entry.cachedIconType_; | 773 return entry.cachedIconType_; |
| 843 }; | 774 }; |
| 844 | 775 |
| 845 /** | 776 /** |
| 846 * Extract extension from the file name and cat it to to lower case. | 777 * Extract extension from the file name and cat it to to lower case. |
| 847 * | 778 * |
| 848 * @param {string} name. | 779 * @param {string} name. |
| 849 * @return {strin} | 780 * @return {strin} |
| 850 */ | 781 */ |
| 851 function getFileExtension(name) { | 782 function getFileExtension(name) { |
| 852 var extIndex = name.lastIndexOf('.'); | 783 var extIndex = name.lastIndexOf('.'); |
| 853 if (extIndex < 0) | 784 if (extIndex < 0) |
| 854 return ''; | 785 return ''; |
| 855 | 786 |
| 856 return name.substr(extIndex + 1).toLowerCase(); | 787 return name.substr(extIndex + 1).toLowerCase(); |
| 857 } | 788 } |
| 858 | 789 |
| 859 FileManager.prototype.computeIconType_ = function(entry) { | 790 FileManager.prototype.computeIconType_ = function(entry) { |
| 860 // TODO(dgozman): refactor this to use proper icons in left panel, | |
| 861 // and do not depend on mountPoints. | |
| 862 var deviceNumber = this.getDeviceNumber(entry); | 791 var deviceNumber = this.getDeviceNumber(entry); |
| 863 if (deviceNumber != undefined) { | 792 if (deviceNumber != undefined) { |
| 864 if (this.mountPoints_[deviceNumber].mountCondition == '') | 793 if (this.mountPoints_[deviceNumber].mountCondition == '') |
| 865 return 'device'; | 794 return 'device'; |
| 866 var mountCondition = this.mountPoints_[deviceNumber].mountCondition; | 795 var mountCondition = this.mountPoints_[deviceNumber].mountCondition; |
| 867 if (mountCondition == 'unknown_filesystem' || | 796 if (mountCondition == 'unknown_filesystem' || |
| 868 mountCondition == 'unsupported_filesystem') | 797 mountCondition == 'unsupported_filesystem') |
| 869 return 'unreadable'; | 798 return 'unreadable'; |
| 870 } | 799 } |
| 871 if (entry.isDirectory) | 800 if (entry.isDirectory) |
| (...skipping 281 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 1153 !isSystemDirEntry(this.currentDirEntry_)); | 1082 !isSystemDirEntry(this.currentDirEntry_)); |
| 1154 | 1083 |
| 1155 case 'delete': | 1084 case 'delete': |
| 1156 return (// Initialized to the point where we have a current directory | 1085 return (// Initialized to the point where we have a current directory |
| 1157 this.currentDirEntry_ && | 1086 this.currentDirEntry_ && |
| 1158 // Rename not in progress. | 1087 // Rename not in progress. |
| 1159 !this.renameInput_.currentEntry && | 1088 !this.renameInput_.currentEntry && |
| 1160 !isSystemDirEntry(this.currentDirEntry_)) && | 1089 !isSystemDirEntry(this.currentDirEntry_)) && |
| 1161 this.selection && | 1090 this.selection && |
| 1162 this.selection.totalCount > 0; | 1091 this.selection.totalCount > 0; |
| 1163 | |
| 1164 case 'newfolder': | |
| 1165 return this.currentDirEntry_ && | |
| 1166 (this.dialogType_ == 'saveas-file' || | |
| 1167 this.dialogType_ == 'full-page'); | |
| 1168 } | 1092 } |
| 1169 }; | 1093 }; |
| 1170 | 1094 |
| 1171 FileManager.prototype.updateCommonActionButtons_ = function() { | 1095 FileManager.prototype.updateCommonActionButtons_ = function() { |
| 1172 if (this.deleteButton_) | 1096 if (this.deleteButton_) |
| 1173 this.deleteButton_.disabled = !this.canExecute_('delete'); | 1097 this.deleteButton_.disabled = !this.canExecute_('delete'); |
| 1174 }; | 1098 }; |
| 1175 | 1099 |
| 1176 FileManager.prototype.setListType = function(type) { | 1100 FileManager.prototype.setListType = function(type) { |
| 1177 if (type && type == this.listType_) | 1101 if (type && type == this.listType_) |
| (...skipping 185 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 1363 leadIndex); | 1287 leadIndex); |
| 1364 return; | 1288 return; |
| 1365 } | 1289 } |
| 1366 | 1290 |
| 1367 this.initiateRename_(label); | 1291 this.initiateRename_(label); |
| 1368 return; | 1292 return; |
| 1369 | 1293 |
| 1370 case 'delete': | 1294 case 'delete': |
| 1371 this.deleteEntries(this.selection.entries); | 1295 this.deleteEntries(this.selection.entries); |
| 1372 return; | 1296 return; |
| 1373 | |
| 1374 case 'newfolder': | |
| 1375 this.onNewFolderCommand_(event); | |
| 1376 return; | |
| 1377 } | 1297 } |
| 1378 }; | 1298 }; |
| 1379 | 1299 |
| 1380 /** | 1300 /** |
| 1381 * Respond to the back and forward buttons. | 1301 * Respond to the back and forward buttons. |
| 1382 */ | 1302 */ |
| 1383 FileManager.prototype.onPopState_ = function(event) { | 1303 FileManager.prototype.onPopState_ = function(event) { |
| 1384 // TODO(serya): We should restore selected items here. | 1304 // TODO(serya): We should restore selected items here. |
| 1385 if (this.rootEntries_) | 1305 if (this.rootEntries_) |
| 1386 this.setupCurrentDirectory_(); | 1306 this.setupCurrentDirectory_(); |
| 1387 }; | 1307 }; |
| 1388 | 1308 |
| 1389 FileManager.prototype.requestResize_ = function(timeout) { | 1309 FileManager.prototype.requestResize_ = function(timeout) { |
| 1390 var self = this; | 1310 var self = this; |
| 1391 setTimeout(function() { self.onResize_() }, timeout || 0); | 1311 setTimeout(function() { self.onResize_() }, timeout || 0); |
| 1392 }; | 1312 }; |
| 1393 | 1313 |
| 1394 /** | 1314 /** |
| 1395 * Resize details and thumb views to fit the new window size. | 1315 * Resize details and thumb views to fit the new window size. |
| 1396 */ | 1316 */ |
| 1397 FileManager.prototype.onResize_ = function() { | 1317 FileManager.prototype.onResize_ = function() { |
| 1398 this.table_.style.height = this.grid_.style.height = | 1318 this.table_.style.height = this.grid_.style.height = |
| 1399 this.grid_.parentNode.clientHeight + 'px'; | 1319 this.grid_.parentNode.clientHeight + 'px'; |
| 1320 this.table_.style.width = this.grid_.style.width = |
| 1321 this.grid_.parentNode.clientWidth + 'px'; |
| 1322 |
| 1323 this.table_.list_.style.width = this.table_.parentNode.clientWidth + 'px'; |
| 1400 this.table_.list_.style.height = (this.table_.clientHeight - 1 - | 1324 this.table_.list_.style.height = (this.table_.clientHeight - 1 - |
| 1401 this.table_.header_.clientHeight) + 'px'; | 1325 this.table_.header_.clientHeight) + 'px'; |
| 1402 | 1326 |
| 1403 if (this.listType_ == FileManager.ListType.THUMBNAIL) { | 1327 if (this.listType_ == FileManager.ListType.THUMBNAIL) { |
| 1404 var self = this; | 1328 var self = this; |
| 1405 setTimeout(function() { | 1329 setTimeout(function() { |
| 1406 self.grid_.columns = 0; | 1330 self.grid_.columns = 0; |
| 1407 self.grid_.redraw(); | 1331 self.grid_.redraw(); |
| 1408 }, 0); | 1332 }, 0); |
| 1409 } else { | 1333 } else { |
| 1410 this.currentList_.redraw(); | 1334 this.currentList_.redraw(); |
| 1411 } | 1335 } |
| 1412 | |
| 1413 this.rootsList_.style.height = | |
| 1414 this.rootsList_.parentNode.clientHeight + 'px'; | |
| 1415 this.rootsList_.redraw(); | |
| 1416 }; | 1336 }; |
| 1417 | 1337 |
| 1418 FileManager.prototype.resolvePath = function( | 1338 FileManager.prototype.resolvePath = function( |
| 1419 path, resultCallback, errorCallback) { | 1339 path, resultCallback, errorCallback) { |
| 1420 return util.resolvePath(this.filesystem_.root, path, resultCallback, | 1340 return util.resolvePath(this.filesystem_.root, path, resultCallback, |
| 1421 errorCallback); | 1341 errorCallback); |
| 1422 }; | 1342 }; |
| 1423 | 1343 |
| 1424 /** | 1344 /** |
| 1425 * Restores current directory and may be a selected item after page load (or | 1345 * Restores current directory and may be a selected item after page load (or |
| (...skipping 13 matching lines...) Expand all Loading... |
| 1439 this.setupPath_(this.params_.defaultPath); | 1359 this.setupPath_(this.params_.defaultPath); |
| 1440 } else { | 1360 } else { |
| 1441 this.setupDefaultPath_(); | 1361 this.setupDefaultPath_(); |
| 1442 } | 1362 } |
| 1443 }; | 1363 }; |
| 1444 | 1364 |
| 1445 FileManager.prototype.setupDefaultPath_ = function() { | 1365 FileManager.prototype.setupDefaultPath_ = function() { |
| 1446 // No preset given, find a good place to start. | 1366 // No preset given, find a good place to start. |
| 1447 // Check for removable devices, if there are none, go to Downloads. | 1367 // Check for removable devices, if there are none, go to Downloads. |
| 1448 var removableDirectoryEntry = this.rootEntries_.filter(function(rootEntry) { | 1368 var removableDirectoryEntry = this.rootEntries_.filter(function(rootEntry) { |
| 1449 return isParentPath(REMOVABLE_DIRECTORY, rootEntry.fullPath); | 1369 return rootEntry.fullPath == REMOVABLE_DIRECTORY; |
| 1450 })[0]; | 1370 })[0]; |
| 1451 var path = removableDirectoryEntry && removableDirectoryEntry.fullPath || | 1371 if (!removableDirectoryEntry) { |
| 1452 DOWNLOADS_DIRECTORY; | 1372 this.changeDirectory(DOWNLOADS_DIRECTORY, CD_NO_HISTORY); |
| 1453 this.changeDirectory(path, CD_NO_HISTORY); | 1373 return; |
| 1374 } |
| 1375 |
| 1376 var foundRemovable = false; |
| 1377 util.forEachDirEntry(removableDirectoryEntry, function(result) { |
| 1378 if (result) { |
| 1379 foundRemovable = true; |
| 1380 } else { // Done enumerating, and we know the answer. |
| 1381 this.changeDirectory(foundRemovable ? '/' : DOWNLOADS_DIRECTORY, |
| 1382 CD_NO_HISTORY); |
| 1383 } |
| 1384 }.bind(this)); |
| 1454 }; | 1385 }; |
| 1455 | 1386 |
| 1456 FileManager.prototype.setupPath_ = function(path) { | 1387 FileManager.prototype.setupPath_ = function(path) { |
| 1457 // Split the dirname from the basename. | 1388 // Split the dirname from the basename. |
| 1458 var ary = path.match(/^(?:(.*)\/)?([^\/]*)$/); | 1389 var ary = path.match(/^(?:(.*)\/)?([^\/]*)$/); |
| 1459 if (!ary) { | 1390 if (!ary) { |
| 1460 console.warn('Unable to split default path: ' + path); | 1391 console.warn('Unable to split default path: ' + path); |
| 1461 self.changeDirectory('/', CD_NO_HISTORY); | 1392 self.changeDirectory('/', CD_NO_HISTORY); |
| 1462 return; | 1393 return; |
| 1463 } | 1394 } |
| (...skipping 18 matching lines...) Expand all Loading... |
| 1482 | 1413 |
| 1483 // Leaf is an existing file, cd to its parent directory and select it. | 1414 // Leaf is an existing file, cd to its parent directory and select it. |
| 1484 self.changeDirectoryEntry(baseDirEntry, CD_NO_HISTORY, function() { | 1415 self.changeDirectoryEntry(baseDirEntry, CD_NO_HISTORY, function() { |
| 1485 self.selectEntry(leafEntry.name); | 1416 self.selectEntry(leafEntry.name); |
| 1486 }); | 1417 }); |
| 1487 } | 1418 } |
| 1488 | 1419 |
| 1489 function onLeafError(err) { | 1420 function onLeafError(err) { |
| 1490 // Set filename first so OK button will update in changeDirectoryEntry. | 1421 // Set filename first so OK button will update in changeDirectoryEntry. |
| 1491 self.filenameInput_.value = leafName; | 1422 self.filenameInput_.value = leafName; |
| 1492 self.selectDefaultPathInFilenameInput_(); | |
| 1493 if (err = FileError.NOT_FOUND_ERR) { | 1423 if (err = FileError.NOT_FOUND_ERR) { |
| 1494 // Leaf does not exist, it's just a suggested file name. | 1424 // Leaf does not exist, it's just a suggested file name. |
| 1495 self.changeDirectoryEntry(baseDirEntry, CD_NO_HISTORY); | 1425 self.changeDirectoryEntry(baseDirEntry, CD_NO_HISTORY); |
| 1496 } else { | 1426 } else { |
| 1497 console.log('Unexpected error resolving default leaf: ' + err); | 1427 console.log('Unexpected error resolving default leaf: ' + err); |
| 1498 self.changeDirectoryEntry('/', CD_NO_HISTORY); | 1428 self.changeDirectoryEntry('/', CD_NO_HISTORY); |
| 1499 } | 1429 } |
| 1500 } | 1430 } |
| 1501 | 1431 |
| 1502 self.resolvePath(path, onLeafFound, onLeafError); | 1432 self.resolvePath(path, onLeafFound, onLeafError); |
| 1503 } | 1433 } |
| 1504 | 1434 |
| 1505 function onBaseError(err) { | 1435 function onBaseError(err) { |
| 1506 // Set filename first so OK button will update in changeDirectory. | 1436 // Set filename first so OK button will update in changeDirectory. |
| 1507 self.filenameInput_.value = leafName; | 1437 self.filenameInput_.value = leafName; |
| 1508 self.selectDefaultPathInFilenameInput_(); | |
| 1509 console.log('Unexpected error resolving default base "' + | 1438 console.log('Unexpected error resolving default base "' + |
| 1510 baseName + '": ' + err); | 1439 baseName + '": ' + err); |
| 1511 self.changeDirectory('/', CD_NO_HISTORY); | 1440 self.changeDirectory('/', CD_NO_HISTORY); |
| 1512 } | 1441 } |
| 1513 | 1442 |
| 1514 if (baseName) { | 1443 if (baseName) { |
| 1515 this.filesystem_.root.getDirectory( | 1444 this.filesystem_.root.getDirectory( |
| 1516 baseName, {create: false}, onBaseFound, onBaseError); | 1445 baseName, {create: false}, onBaseFound, onBaseError); |
| 1517 } else { | 1446 } else { |
| 1518 onBaseFound(this.filesystem_.root); | 1447 onBaseFound(this.filesystem_.root); |
| (...skipping 220 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 1739 li.className = 'thumbnail-item'; | 1668 li.className = 'thumbnail-item'; |
| 1740 | 1669 |
| 1741 if (this.showCheckboxes_) | 1670 if (this.showCheckboxes_) |
| 1742 li.appendChild(this.renderCheckbox_(entry)); | 1671 li.appendChild(this.renderCheckbox_(entry)); |
| 1743 | 1672 |
| 1744 li.appendChild(this.renderThumbnailBox_(entry, false)); | 1673 li.appendChild(this.renderThumbnailBox_(entry, false)); |
| 1745 | 1674 |
| 1746 var div = this.document_.createElement('div'); | 1675 var div = this.document_.createElement('div'); |
| 1747 div.className = 'filename-label'; | 1676 div.className = 'filename-label'; |
| 1748 var labelText = entry.name; | 1677 var labelText = entry.name; |
| 1678 if (this.currentDirEntry_.name == '') |
| 1679 labelText = this.getLabelForRootPath_(labelText); |
| 1749 | 1680 |
| 1750 div.textContent = labelText; | 1681 div.textContent = labelText; |
| 1751 div.entry = entry; | 1682 div.entry = entry; |
| 1752 | 1683 |
| 1753 li.appendChild(div); | 1684 li.appendChild(div); |
| 1754 | 1685 |
| 1755 cr.defineProperty(li, 'lead', cr.PropertyKind.BOOL_ATTR); | 1686 cr.defineProperty(li, 'lead', cr.PropertyKind.BOOL_ATTR); |
| 1756 cr.defineProperty(li, 'selected', cr.PropertyKind.BOOL_ATTR); | 1687 cr.defineProperty(li, 'selected', cr.PropertyKind.BOOL_ATTR); |
| 1757 return li; | 1688 return li; |
| 1758 }; | 1689 }; |
| 1759 | 1690 |
| 1760 /** | 1691 /** |
| 1761 * Render the type column of the detail table. | 1692 * Render the type column of the detail table. |
| 1762 * | 1693 * |
| 1763 * Invoked by cr.ui.Table when a file needs to be rendered. | 1694 * Invoked by cr.ui.Table when a file needs to be rendered. |
| 1764 * | 1695 * |
| 1765 * @param {Entry} entry The Entry object to render. | 1696 * @param {Entry} entry The Entry object to render. |
| 1766 * @param {string} columnId The id of the column to be rendered. | 1697 * @param {string} columnId The id of the column to be rendered. |
| 1767 * @param {cr.ui.Table} table The table doing the rendering. | 1698 * @param {cr.ui.Table} table The table doing the rendering. |
| 1768 */ | 1699 */ |
| 1769 FileManager.prototype.renderIconType_ = function(entry, columnId, table) { | 1700 FileManager.prototype.renderIconType_ = function(entry, columnId, table) { |
| 1770 var icon = this.document_.createElement('div'); | 1701 var icon = this.document_.createElement('div'); |
| 1771 icon.className = 'detail-icon'; | 1702 icon.className = 'detail-icon'; |
| 1772 this.getIconType(entry); | 1703 this.getIconType(entry); |
| 1773 icon.setAttribute('iconType', entry.cachedIconType_); | 1704 icon.setAttribute('iconType', entry.cachedIconType_); |
| 1774 return icon; | 1705 return icon; |
| 1775 }; | 1706 }; |
| 1776 | 1707 |
| 1777 /** | 1708 FileManager.prototype.getLabelForRootPath_ = function(path) { |
| 1778 * Return the localized name for the root. | 1709 // This hack lets us localize the top level directories. |
| 1779 * @param {string} path The full path of the root (starting with slash). | 1710 if (path == 'Downloads') |
| 1780 * @return {string} The localized name. | 1711 return str('DOWNLOADS_DIRECTORY_LABEL'); |
| 1781 */ | |
| 1782 FileManager.prototype.getRootLabel_ = function(path) { | |
| 1783 if (path == DOWNLOADS_DIRECTORY) | |
| 1784 return str('CHROMEBOOK_DIRECTORY_LABEL'); | |
| 1785 | 1712 |
| 1786 if (path == ARCHIVE_DIRECTORY) | 1713 if (path == 'archive') |
| 1787 return str('ARCHIVE_DIRECTORY_LABEL'); | 1714 return str('ARCHIVE_DIRECTORY_LABEL'); |
| 1788 if (isParentPath(ARCHIVE_DIRECTORY, path)) | |
| 1789 return path.substring(ARCHIVE_DIRECTORY.length + 1); | |
| 1790 | 1715 |
| 1791 if (path == REMOVABLE_DIRECTORY) | 1716 if (path == 'removable') |
| 1792 return str('REMOVABLE_DIRECTORY_LABEL'); | 1717 return str('REMOVABLE_DIRECTORY_LABEL'); |
| 1793 if (isParentPath(REMOVABLE_DIRECTORY, path)) | |
| 1794 return path.substring(REMOVABLE_DIRECTORY.length + 1); | |
| 1795 | 1718 |
| 1796 return path; | 1719 return path || str('ROOT_DIRECTORY_LABEL'); |
| 1797 }; | |
| 1798 | |
| 1799 FileManager.prototype.getRootIconUrl_ = function(path, opt_small) { | |
| 1800 var iconUrl = opt_small ? 'images/chromebook_28x28.png' : | |
| 1801 'images/chromebook_24x24.png'; | |
| 1802 if (isParentPath(REMOVABLE_DIRECTORY, path)) | |
| 1803 iconUrl = 'images/filetype_device.png'; | |
| 1804 else if (isParentPath(ARCHIVE_DIRECTORY, path)) | |
| 1805 iconUrl = 'images/icon_mount_archive_16x16.png'; | |
| 1806 return chrome.extension.getURL(iconUrl); | |
| 1807 }; | |
| 1808 | |
| 1809 FileManager.prototype.renderRoot_ = function(entry) { | |
| 1810 var li = this.document_.createElement('li'); | |
| 1811 li.className = 'root-item'; | |
| 1812 | |
| 1813 var icon = this.document_.createElement('img'); | |
| 1814 icon.src = this.getRootIconUrl_(entry.fullPath, false); | |
| 1815 li.appendChild(icon); | |
| 1816 | |
| 1817 var div = this.document_.createElement('div'); | |
| 1818 div.className = 'text'; | |
| 1819 div.textContent = this.getRootLabel_(entry.fullPath); | |
| 1820 li.appendChild(div); | |
| 1821 | |
| 1822 if (isParentPath(REMOVABLE_DIRECTORY, entry.fullPath) || | |
| 1823 isParentPath(ARCHIVE_DIRECTORY, entry.fullPath)) { | |
| 1824 var spacer = this.document_.createElement('div'); | |
| 1825 spacer.className = 'spacer'; | |
| 1826 li.appendChild(spacer); | |
| 1827 | |
| 1828 var eject = this.document_.createElement('img'); | |
| 1829 eject.className = 'root-eject'; | |
| 1830 eject.setAttribute('src', chrome.extension.getURL('images/eject.png')); | |
| 1831 eject.addEventListener('click', this.onEjectClick_.bind(this, entry)); | |
| 1832 li.appendChild(eject); | |
| 1833 } | |
| 1834 | |
| 1835 cr.defineProperty(li, 'lead', cr.PropertyKind.BOOL_ATTR); | |
| 1836 cr.defineProperty(li, 'selected', cr.PropertyKind.BOOL_ATTR); | |
| 1837 return li; | |
| 1838 }; | |
| 1839 | |
| 1840 /** | |
| 1841 * Handler for eject button clicked. | |
| 1842 * @param {Entry} entry Entry to eject. | |
| 1843 * @param {Event} event The event. | |
| 1844 */ | |
| 1845 FileManager.prototype.onEjectClick_ = function(entry, event) { | |
| 1846 this.unmountRequests_.push(entry.toURL()); | |
| 1847 chrome.fileBrowserPrivate.removeMount(entry.fullPath); | |
| 1848 }; | 1720 }; |
| 1849 | 1721 |
| 1850 /** | 1722 /** |
| 1851 * Render the Name column of the detail table. | 1723 * Render the Name column of the detail table. |
| 1852 * | 1724 * |
| 1853 * Invoked by cr.ui.Table when a file needs to be rendered. | 1725 * Invoked by cr.ui.Table when a file needs to be rendered. |
| 1854 * | 1726 * |
| 1855 * @param {Entry} entry The Entry object to render. | 1727 * @param {Entry} entry The Entry object to render. |
| 1856 * @param {string} columnId The id of the column to be rendered. | 1728 * @param {string} columnId The id of the column to be rendered. |
| 1857 * @param {cr.ui.Table} table The table doing the rendering. | 1729 * @param {cr.ui.Table} table The table doing the rendering. |
| 1858 */ | 1730 */ |
| 1859 FileManager.prototype.renderName_ = function(entry, columnId, table) { | 1731 FileManager.prototype.renderName_ = function(entry, columnId, table) { |
| 1860 var label = this.document_.createElement('div'); | 1732 var label = this.document_.createElement('div'); |
| 1861 if (this.showCheckboxes_) | 1733 if (this.showCheckboxes_) |
| 1862 label.appendChild(this.renderCheckbox_(entry)); | 1734 label.appendChild(this.renderCheckbox_(entry)); |
| 1863 label.appendChild(this.renderIconType_(entry, columnId, table)); | 1735 label.appendChild(this.renderIconType_(entry, columnId, table)); |
| 1864 label.entry = entry; | 1736 label.entry = entry; |
| 1865 label.className = 'detail-name'; | 1737 label.className = 'detail-name'; |
| 1866 label.appendChild(this.document_.createTextNode(entry.name)); | 1738 if (this.currentDirEntry_.name == '') { |
| 1739 label.appendChild(this.document_.createTextNode( |
| 1740 this.getLabelForRootPath_(entry.name))); |
| 1741 } else { |
| 1742 label.appendChild(this.document_.createTextNode(entry.name)); |
| 1743 } |
| 1744 |
| 1867 return label; | 1745 return label; |
| 1868 }; | 1746 }; |
| 1869 | 1747 |
| 1870 /** | 1748 /** |
| 1871 * Render the Size column of the detail table. | 1749 * Render the Size column of the detail table. |
| 1872 * | 1750 * |
| 1873 * @param {Entry} entry The Entry object to render. | 1751 * @param {Entry} entry The Entry object to render. |
| 1874 * @param {string} columnId The id of the column to be rendered. | 1752 * @param {string} columnId The id of the column to be rendered. |
| 1875 * @param {cr.ui.Table} table The table doing the rendering. | 1753 * @param {cr.ui.Table} table The table doing the rendering. |
| 1876 */ | 1754 */ |
| (...skipping 353 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 2230 task.title = str('GALLERY'); | 2108 task.title = str('GALLERY'); |
| 2231 task.allTasks = tasksList; | 2109 task.allTasks = tasksList; |
| 2232 this.galleryTask_ = task; | 2110 this.galleryTask_ = task; |
| 2233 } | 2111 } |
| 2234 } | 2112 } |
| 2235 this.renderTaskButton_(task); | 2113 this.renderTaskButton_(task); |
| 2236 } | 2114 } |
| 2237 | 2115 |
| 2238 // These are done in separate functions, as the checks require | 2116 // These are done in separate functions, as the checks require |
| 2239 // asynchronous function calls. | 2117 // asynchronous function calls. |
| 2118 this.maybeRenderUnmountTask_(selection); |
| 2240 this.maybeRenderFormattingTask_(selection); | 2119 this.maybeRenderFormattingTask_(selection); |
| 2241 }; | 2120 }; |
| 2242 | 2121 |
| 2243 FileManager.prototype.renderTaskButton_ = function(task) { | 2122 FileManager.prototype.renderTaskButton_ = function(task) { |
| 2244 var button = this.document_.createElement('button'); | 2123 var button = this.document_.createElement('button'); |
| 2245 button.addEventListener('click', | 2124 button.addEventListener('click', |
| 2246 this.onTaskButtonClicked_.bind(this, task)); | 2125 this.onTaskButtonClicked_.bind(this, task)); |
| 2247 button.className = 'task-button'; | 2126 button.className = 'task-button'; |
| 2248 | 2127 |
| 2249 var img = this.document_.createElement('img'); | 2128 var img = this.document_.createElement('img'); |
| 2250 img.src = task.iconUrl; | 2129 img.src = task.iconUrl; |
| 2251 | 2130 |
| 2252 button.appendChild(img); | 2131 button.appendChild(img); |
| 2253 var label = this.document_.createElement('div'); | 2132 var label = this.document_.createElement('div'); |
| 2254 label.appendChild(this.document_.createTextNode(task.title)) | 2133 label.appendChild(this.document_.createTextNode(task.title)) |
| 2255 button.appendChild(label); | 2134 button.appendChild(label); |
| 2256 | 2135 |
| 2257 this.taskButtons_.appendChild(button); | 2136 this.taskButtons_.appendChild(button); |
| 2258 }; | 2137 }; |
| 2259 | 2138 |
| 2260 /** | 2139 /** |
| 2140 * Checks whether unmount task should be displayed and if the answer is |
| 2141 * affirmative renders it. |
| 2142 * @param {Object} selection Selected files object. |
| 2143 */ |
| 2144 FileManager.prototype.maybeRenderUnmountTask_ = function(selection) { |
| 2145 for (var index = 0; index < selection.urls.length; ++index) { |
| 2146 // Each url should be a mount point. |
| 2147 var path = selection.entries[index].fullPath; |
| 2148 var found = false; |
| 2149 for (var i = 0; i < this.mountPoints_.length; i++) { |
| 2150 var mountPath = this.mountPoints_[i].mountPath; |
| 2151 if (mountPath[0] != '/') { |
| 2152 mountPath = '/' + mountPath; |
| 2153 } |
| 2154 if (mountPath == path && this.mountPoints_[i].mountType == 'file') { |
| 2155 found = true; |
| 2156 break; |
| 2157 } |
| 2158 } |
| 2159 if (!found) |
| 2160 return; |
| 2161 } |
| 2162 this.renderTaskButton_({ |
| 2163 taskId: this.getExtensionId_() + '|unmount-archive', |
| 2164 iconUrl: |
| 2165 chrome.extension.getURL('images/icon_unmount_archive_16x16.png'), |
| 2166 title: str('UNMOUNT_ARCHIVE'), |
| 2167 internal: true |
| 2168 }); |
| 2169 }; |
| 2170 |
| 2171 /** |
| 2261 * Checks whether formatting task should be displayed and if the answer is | 2172 * Checks whether formatting task should be displayed and if the answer is |
| 2262 * affirmative renders it. Includes asynchronous calls, so it's splitted into | 2173 * affirmative renders it. Includes asynchronous calls, so it's splitted into |
| 2263 * three parts. | 2174 * three parts. |
| 2264 * @param {Object} selection Selected files object. | 2175 * @param {Object} selection Selected files object. |
| 2265 */ | 2176 */ |
| 2266 FileManager.prototype.maybeRenderFormattingTask_ = function(selection) { | 2177 FileManager.prototype.maybeRenderFormattingTask_ = function(selection) { |
| 2267 // Not to make unnecessary getMountPoints() call we doublecheck if there is | 2178 // Not to make unnecessary getMountPoints() call we doublecheck if there is |
| 2268 // only one selected entry. | 2179 // only one selected entry. |
| 2269 if (selection.entries.length != 1) | 2180 if (selection.entries.length != 1) |
| 2270 return; | 2181 return; |
| (...skipping 60 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 2331 chrome.fileBrowserPrivate.executeTask(task.taskId, urls); | 2242 chrome.fileBrowserPrivate.executeTask(task.taskId, urls); |
| 2332 }; | 2243 }; |
| 2333 | 2244 |
| 2334 /** | 2245 /** |
| 2335 * Event handler called when some volume was mounted or unmouted. | 2246 * Event handler called when some volume was mounted or unmouted. |
| 2336 */ | 2247 */ |
| 2337 FileManager.prototype.onMountCompleted_ = function(event) { | 2248 FileManager.prototype.onMountCompleted_ = function(event) { |
| 2338 var self = this; | 2249 var self = this; |
| 2339 chrome.fileBrowserPrivate.getMountPoints(function(mountPoints) { | 2250 chrome.fileBrowserPrivate.getMountPoints(function(mountPoints) { |
| 2340 self.mountPoints_ = mountPoints; | 2251 self.mountPoints_ = mountPoints; |
| 2341 var changeDirectoryTo = null; | |
| 2342 | |
| 2343 if (event.eventType == 'mount') { | 2252 if (event.eventType == 'mount') { |
| 2344 // Mount request finished - remove it. | 2253 for (var index = 0; index < self.mountRequests_.length; ++index) { |
| 2345 var index = self.mountRequests_.indexOf(event.sourceUrl); | 2254 if (self.mountRequests_[index] == event.sourceUrl) { |
| 2346 if (index != -1) { | 2255 self.mountRequests_.splice(index, 1); |
| 2347 self.mountRequests_.splice(index, 1); | 2256 if (event.status == 'success') { |
| 2348 // Go to mounted directory, if request was initiated from this tab. | 2257 self.changeDirectory(event.mountPath); |
| 2349 if (event.status == 'success') | 2258 } else { |
| 2350 changeDirectoryTo = event.mountPath; | 2259 // Report mount error. |
| 2260 if (event.mountType == 'file') { |
| 2261 var fileName = event.sourceUrl.substr( |
| 2262 event.sourceUrl.lastIndexOf('/') + 1); |
| 2263 self.alert.show(strf('ARCHIVE_MOUNT_FAILED', fileName, |
| 2264 event.status)); |
| 2265 } |
| 2266 } |
| 2267 return; |
| 2268 } |
| 2351 } | 2269 } |
| 2352 } | 2270 } |
| 2353 | 2271 |
| 2354 if (event.eventType == 'unmount') { | |
| 2355 // Unmount request finished - remove it. | |
| 2356 var index = self.unmountRequests_.indexOf(event.sourceUrl); | |
| 2357 if (index != -1) | |
| 2358 self.unmountRequests_.splice(index, 1); | |
| 2359 } | |
| 2360 | |
| 2361 if (event.eventType == 'mount' && event.status != 'success' && | |
| 2362 event.mountType == 'file') { | |
| 2363 // Report mount error. | |
| 2364 var fileName = event.sourceUrl.substr( | |
| 2365 event.sourceUrl.lastIndexOf('/') + 1); | |
| 2366 self.alert.show(strf('ARCHIVE_MOUNT_FAILED', fileName, | |
| 2367 event.status)); | |
| 2368 } | |
| 2369 | |
| 2370 if (event.eventType == 'unmount' && event.status != 'success') { | |
| 2371 // Report unmount error. | |
| 2372 // TODO(dgozman): introduce string and show alert here. | |
| 2373 } | |
| 2374 | |
| 2375 if (event.eventType == 'unmount' && event.status == 'success' && | 2272 if (event.eventType == 'unmount' && event.status == 'success' && |
| 2376 self.currentDirEntry_ && | 2273 self.currentDirEntry_ && |
| 2377 isParentPath(event.mountPath, self.currentDirEntry_.fullPath)) { | 2274 isParentPath(event.mountPath, self.currentDirEntry_.fullPath)) { |
| 2378 changeDirectoryTo = getParentPath(event.mountPath); | 2275 self.changeDirectory(getParentPath(event.mountPath)); |
| 2276 return; |
| 2379 } | 2277 } |
| 2380 | 2278 |
| 2381 // In the case of success, roots are changed and should be rescanned. | 2279 var rescanDirectoryNeeded = (event.status == 'success'); |
| 2382 if (event.status == 'success') | 2280 for (var i = 0; i < mountPoints.length; i++) { |
| 2383 self.updateRoots_(changeDirectoryTo); | 2281 if (event.sourceUrl == mountPoints[i].sourceUrl && |
| 2282 mountPoints[i].mountCondition != '') { |
| 2283 rescanDirectoryNeeded = true; |
| 2284 } |
| 2285 } |
| 2286 // TODO(dgozman): rescan directory, only if it contains mounted points, |
| 2287 // when mounts location will be decided. |
| 2288 if (rescanDirectoryNeeded) |
| 2289 self.rescanDirectory_(null, 300); |
| 2384 }); | 2290 }); |
| 2385 }; | 2291 }; |
| 2386 | 2292 |
| 2387 /** | 2293 /** |
| 2388 * Event handler called when some internal task should be executed. | 2294 * Event handler called when some internal task should be executed. |
| 2389 */ | 2295 */ |
| 2390 FileManager.prototype.onFileTaskExecute_ = function(id, details) { | 2296 FileManager.prototype.onFileTaskExecute_ = function(id, details) { |
| 2391 var urls = details.urls; | 2297 var urls = details.urls; |
| 2392 if (id == 'play' || id == 'enqueue') { | 2298 if (id == 'play' || id == 'enqueue') { |
| 2393 chrome.fileBrowserPrivate.viewFiles(urls, id); | 2299 chrome.fileBrowserPrivate.viewFiles(urls, id); |
| 2394 } else if (id == 'mount-archive') { | 2300 } else if (id == 'mount-archive') { |
| 2395 for (var index = 0; index < urls.length; ++index) { | 2301 for (var index = 0; index < urls.length; ++index) { |
| 2396 this.mountRequests_.push(urls[index]); | 2302 this.mountRequests_.push(urls[index]); |
| 2397 chrome.fileBrowserPrivate.addMount(urls[index], 'file', {}); | 2303 chrome.fileBrowserPrivate.addMount(urls[index], 'file', {}); |
| 2398 } | 2304 } |
| 2305 } else if (id == 'unmount-archive') { |
| 2306 for (var index = 0; index < urls.length; ++index) { |
| 2307 chrome.fileBrowserPrivate.removeMount(urls[index]); |
| 2308 } |
| 2399 } else if (id == 'format-device') { | 2309 } else if (id == 'format-device') { |
| 2400 this.confirm.show(str('FORMATTING_WARNING'), function() { | 2310 this.confirm.show(str('FORMATTING_WARNING'), function() { |
| 2401 chrome.fileBrowserPrivate.formatDevice(urls[0]); | 2311 chrome.fileBrowserPrivate.formatDevice(urls[0]); |
| 2402 }); | 2312 }); |
| 2403 } else if (id == 'gallery') { | 2313 } else if (id == 'gallery') { |
| 2404 // Pass to gallery all possible tasks except the gallery itself. | 2314 // Pass to gallery all possible tasks except the gallery itself. |
| 2405 var noGallery = []; | 2315 var noGallery = []; |
| 2406 for (var index = 0; index < details.task.allTasks.length; index++) { | 2316 for (var index = 0; index < details.task.allTasks.length; index++) { |
| 2407 var task = details.task.allTasks[index]; | 2317 var task = details.task.allTasks[index]; |
| 2408 if (task.taskId != this.getExtensionId_() + '|gallery') { | 2318 if (task.taskId != this.getExtensionId_() + '|gallery') { |
| (...skipping 57 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 2466 self.metadataProvider_, | 2376 self.metadataProvider_, |
| 2467 shareActions, | 2377 shareActions, |
| 2468 str); | 2378 str); |
| 2469 }; | 2379 }; |
| 2470 | 2380 |
| 2471 galleryFrame.src = 'js/image_editor/gallery.html'; | 2381 galleryFrame.src = 'js/image_editor/gallery.html'; |
| 2472 this.dialogDom_.appendChild(galleryFrame); | 2382 this.dialogDom_.appendChild(galleryFrame); |
| 2473 galleryFrame.focus(); | 2383 galleryFrame.focus(); |
| 2474 }; | 2384 }; |
| 2475 | 2385 |
| 2476 FileManager.prototype.getRootForPath_ = function(path) { | |
| 2477 for (var index = 0; index < this.rootEntries_.length; index++) { | |
| 2478 if (isParentPath(this.rootEntries_[index].fullPath, path)) { | |
| 2479 return index; | |
| 2480 } | |
| 2481 } | |
| 2482 return -1; | |
| 2483 }; | |
| 2484 | |
| 2485 /** | 2386 /** |
| 2486 * Update the breadcrumb display to reflect the current directory. | 2387 * Update the breadcrumb display to reflect the current directory. |
| 2487 */ | 2388 */ |
| 2488 FileManager.prototype.updateBreadcrumbs_ = function() { | 2389 FileManager.prototype.updateBreadcrumbs_ = function() { |
| 2489 var bc = this.dialogDom_.querySelector('.breadcrumbs'); | 2390 var bc = this.dialogDom_.querySelector('.breadcrumbs'); |
| 2490 removeChildren(bc); | 2391 removeChildren(bc); |
| 2491 | 2392 |
| 2492 var fullPath = this.currentDirEntry_.fullPath; | 2393 var fullPath = this.currentDirEntry_.fullPath.replace(/\/$/, ''); |
| 2493 var rootIndex = this.getRootForPath_(fullPath); | 2394 var pathNames = fullPath.split('/'); |
| 2494 if (rootIndex == -1) { | 2395 var path = ''; |
| 2495 console.error('Not root for: ' + fullPath); | |
| 2496 return; | |
| 2497 } | |
| 2498 var root = this.rootEntries_[rootIndex]; | |
| 2499 | |
| 2500 var icon = this.document_.createElement('img'); | |
| 2501 icon.className = 'breadcrumb-icon'; | |
| 2502 icon.setAttribute('src', this.getRootIconUrl_(root.fullPath, true)); | |
| 2503 bc.appendChild(icon); | |
| 2504 | |
| 2505 var rootPath = root.fullPath; | |
| 2506 var relativePath = fullPath.substring(rootPath.length); | |
| 2507 var pathNames = relativePath.replace(/\/$/, '').split('/'); | |
| 2508 if (pathNames[0] == '') | |
| 2509 pathNames.splice(0, 1); | |
| 2510 | |
| 2511 // We need a first breadcrumb for root, so placing last name from | |
| 2512 // rootPath as first name of relativePath. | |
| 2513 var rootPathNames = rootPath.replace(/\/$/, '').split('/'); | |
| 2514 pathNames.splice(0, 0, rootPathNames[rootPathNames.length - 1]); | |
| 2515 rootPathNames.splice(rootPathNames.length - 1, 1); | |
| 2516 var path = rootPathNames.join('/') + '/'; | |
| 2517 | 2396 |
| 2518 for (var i = 0; i < pathNames.length; i++) { | 2397 for (var i = 0; i < pathNames.length; i++) { |
| 2519 var pathName = pathNames[i]; | 2398 var pathName = pathNames[i]; |
| 2520 path += pathName; | 2399 path += pathName + '/'; |
| 2521 | 2400 |
| 2522 var div = this.document_.createElement('div'); | 2401 var div = this.document_.createElement('div'); |
| 2523 div.className = 'breadcrumb-path'; | 2402 div.className = 'breadcrumb-path'; |
| 2524 div.textContent = i == 0 ? this.getRootLabel_(path) : pathName; | 2403 if (i <= 1) { |
| 2404 // i == 0: root directory itself, i == 1: the files it contains. |
| 2405 div.textContent = this.getLabelForRootPath_(pathName); |
| 2406 } else { |
| 2407 div.textContent = pathName; |
| 2408 } |
| 2525 | 2409 |
| 2526 path = path + '/'; | |
| 2527 div.path = path; | 2410 div.path = path; |
| 2528 div.addEventListener('click', this.onBreadcrumbClick_.bind(this)); | 2411 div.addEventListener('click', this.onBreadcrumbClick_.bind(this)); |
| 2529 | 2412 |
| 2530 bc.appendChild(div); | 2413 bc.appendChild(div); |
| 2531 | 2414 |
| 2532 if (i == pathNames.length - 1) { | 2415 if (i == pathNames.length - 1) { |
| 2533 div.classList.add('breadcrumb-last'); | 2416 div.classList.add('breadcrumb-last'); |
| 2534 } else { | 2417 } else { |
| 2535 var spacer = this.document_.createElement('div'); | 2418 var spacer = this.document_.createElement('div'); |
| 2536 spacer.className = 'breadcrumb-spacer'; | 2419 spacer.className = 'breadcrumb-spacer'; |
| (...skipping 83 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 2620 | 2503 |
| 2621 FileManager.prototype.selectEntry = function(name) { | 2504 FileManager.prototype.selectEntry = function(name) { |
| 2622 for (var i = 0; i < this.dataModel_.length; i++) { | 2505 for (var i = 0; i < this.dataModel_.length; i++) { |
| 2623 if (this.dataModel_.item(i).name == name) { | 2506 if (this.dataModel_.item(i).name == name) { |
| 2624 this.selectIndex(i); | 2507 this.selectIndex(i); |
| 2625 return; | 2508 return; |
| 2626 } | 2509 } |
| 2627 } | 2510 } |
| 2628 }; | 2511 }; |
| 2629 | 2512 |
| 2630 FileManager.prototype.updateRootsListSelection_ = function() { | |
| 2631 if (!this.currentDirEntry_) return; | |
| 2632 var index = this.getRootForPath_(this.currentDirEntry_.fullPath); | |
| 2633 if (index == -1) { | |
| 2634 this.rootsList_.selectionModel.selectedIndex = 0; | |
| 2635 } else { | |
| 2636 if (this.rootsList_.selectionModel.selectedIndex != index) | |
| 2637 this.rootsList_.selectionModel.selectedIndex = index; | |
| 2638 } | |
| 2639 }; | |
| 2640 | |
| 2641 FileManager.prototype.selectIndex = function(index) { | 2513 FileManager.prototype.selectIndex = function(index) { |
| 2642 this.currentList_.focus(); | 2514 this.currentList_.focus(); |
| 2643 if (index >= this.dataModel_.length) | 2515 if (index >= this.dataModel_.length) |
| 2644 return; | 2516 return; |
| 2645 this.currentList_.selectionModel.selectedIndex = index; | 2517 this.currentList_.selectionModel.selectedIndex = index; |
| 2646 this.currentList_.scrollIndexIntoView(index); | 2518 this.currentList_.scrollIndexIntoView(index); |
| 2647 }; | 2519 }; |
| 2648 | 2520 |
| 2649 /** | 2521 /** |
| 2650 * Add the file/directory with given name to the current selection. | 2522 * Add the file/directory with given name to the current selection. |
| (...skipping 103 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 2754 */ | 2626 */ |
| 2755 FileManager.prototype.changeDirectoryEntry = function(dirEntry, | 2627 FileManager.prototype.changeDirectoryEntry = function(dirEntry, |
| 2756 opt_saveHistory, | 2628 opt_saveHistory, |
| 2757 opt_action) { | 2629 opt_action) { |
| 2758 if (typeof opt_saveHistory == 'undefined') { | 2630 if (typeof opt_saveHistory == 'undefined') { |
| 2759 opt_saveHistory = true; | 2631 opt_saveHistory = true; |
| 2760 } else { | 2632 } else { |
| 2761 opt_saveHistory = !!opt_saveHistory; | 2633 opt_saveHistory = !!opt_saveHistory; |
| 2762 } | 2634 } |
| 2763 | 2635 |
| 2764 // Some directories are above roots, so we instead show the first root. | |
| 2765 // There may be request to change directory above the roots. For example, | |
| 2766 // when usb-dirve is removed, we try to change to the parent directory, | |
| 2767 // which is REMOVABLE_DIRECTORY. | |
| 2768 if (!dirEntry || dirEntry.fullPath == '/' || | |
| 2769 dirEntry.fullPath == REMOVABLE_DIRECTORY || | |
| 2770 dirEntry.fullPath == ARCHIVE_DIRECTORY) { | |
| 2771 dirEntry = this.rootEntries_[0] || dirEntry; | |
| 2772 } | |
| 2773 | |
| 2774 var action = opt_action || | 2636 var action = opt_action || |
| 2775 (this.dialogType_ == FileManager.DialogType.SELECT_SAVEAS_FILE ? | 2637 (this.dialogType_ == FileManager.DialogType.SELECT_SAVEAS_FILE ? |
| 2776 undefined : this.selectIndex.bind(this, 0)); | 2638 undefined : this.selectIndex.bind(this, 0)); |
| 2777 | 2639 |
| 2778 var location = document.location.origin + document.location.pathname + '#' + | 2640 var location = document.location.origin + document.location.pathname + '#' + |
| 2779 encodeURI(dirEntry.fullPath); | 2641 encodeURI(dirEntry.fullPath); |
| 2780 if (opt_saveHistory) { | 2642 if (opt_saveHistory) { |
| 2781 history.pushState(undefined, dirEntry.fullPath, location); | 2643 history.pushState(undefined, dirEntry.fullPath, location); |
| 2782 } else if (window.location.hash != location) { | 2644 } else if (window.location.hash != location) { |
| 2783 // If the user typed URL manually that is not canonical it would be fixed | 2645 // If the user typed URL manually that is not canonical it would be fixed |
| (...skipping 214 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 2998 // then the default action of this click event fires and toggles the | 2860 // then the default action of this click event fires and toggles the |
| 2999 // checkbox back off. | 2861 // checkbox back off. |
| 3000 // | 2862 // |
| 3001 // Since we're going to force checkboxes into the correct state for any | 2863 // Since we're going to force checkboxes into the correct state for any |
| 3002 // multi-selection, we can prevent this shift click from toggling the | 2864 // multi-selection, we can prevent this shift click from toggling the |
| 3003 // checkbox and avoid the trouble. | 2865 // checkbox and avoid the trouble. |
| 3004 event.preventDefault(); | 2866 event.preventDefault(); |
| 3005 } | 2867 } |
| 3006 }; | 2868 }; |
| 3007 | 2869 |
| 3008 FileManager.prototype.onRootsSelectionChanged_ = function(event) { | |
| 3009 var root = this.rootEntries_[this.rootsList_.selectionModel.selectedIndex]; | |
| 3010 if (!this.currentDirEntry_ || | |
| 3011 !isParentPath(root.fullPath, this.currentDirEntry_.fullPath)) | |
| 3012 this.changeDirectoryEntry(root); | |
| 3013 }; | |
| 3014 | |
| 3015 FileManager.prototype.selectDefaultPathInFilenameInput_ = function() { | |
| 3016 var input = this.filenameInput_; | |
| 3017 input.focus(); | |
| 3018 var selectionEnd = input.value.lastIndexOf('.'); | |
| 3019 if (selectionEnd == -1) { | |
| 3020 input.select(); | |
| 3021 } else { | |
| 3022 input.selectionStart = 0; | |
| 3023 input.selectionEnd = selectionEnd; | |
| 3024 } | |
| 3025 // Clear, so we never do this again. | |
| 3026 this.params_.defaultPath = ''; | |
| 3027 }; | |
| 3028 | |
| 3029 /** | 2870 /** |
| 3030 * Update the UI when the selection model changes. | 2871 * Update the UI when the selection model changes. |
| 3031 * | 2872 * |
| 3032 * @param {cr.Event} event The change event. | 2873 * @param {cr.Event} event The change event. |
| 3033 */ | 2874 */ |
| 3034 FileManager.prototype.onSelectionChanged_ = function(event) { | 2875 FileManager.prototype.onSelectionChanged_ = function(event) { |
| 3035 this.summarizeSelection_(); | 2876 this.summarizeSelection_(); |
| 3036 | 2877 |
| 3037 if (this.dialogType_ == FileManager.DialogType.SELECT_SAVEAS_FILE) { | 2878 if (this.dialogType_ == FileManager.DialogType.SELECT_SAVEAS_FILE) { |
| 3038 // If this is a save-as dialog, copy the selected file into the filename | 2879 // If this is a save-as dialog, copy the selected file into the filename |
| 3039 // input text box. | 2880 // input text box. |
| 3040 | 2881 |
| 3041 if (this.selection && | 2882 if (this.selection && |
| 3042 this.selection.totalCount == 1 && | 2883 this.selection.totalCount == 1 && |
| 3043 this.selection.entries[0].isFile && | 2884 this.selection.entries[0].isFile) |
| 3044 this.filenameInput_.value != this.selection.entries[0].name) { | |
| 3045 this.filenameInput_.value = this.selection.entries[0].name; | 2885 this.filenameInput_.value = this.selection.entries[0].name; |
| 3046 if (this.params_.defaultPath == this.selection.entries[0].fullPath) | |
| 3047 this.selectDefaultPathInFilenameInput_(); | |
| 3048 } | |
| 3049 } | 2886 } |
| 3050 | 2887 |
| 3051 this.updateOkButton_(); | 2888 this.updateOkButton_(); |
| 3052 | 2889 |
| 3053 var self = this; | 2890 var self = this; |
| 3054 setTimeout(function() { self.onSelectionChangeComplete_(event) }, 0); | 2891 setTimeout(function() { self.onSelectionChangeComplete_(event) }, 0); |
| 3055 }; | 2892 }; |
| 3056 | 2893 |
| 3057 /** | 2894 /** |
| 3058 * Handle selection change related tasks that won't run properly during | 2895 * Handle selection change related tasks that won't run properly during |
| (...skipping 132 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 3191 * Update the UI when the current directory changes. | 3028 * Update the UI when the current directory changes. |
| 3192 * | 3029 * |
| 3193 * @param {cr.Event} event The directory-changed event. | 3030 * @param {cr.Event} event The directory-changed event. |
| 3194 */ | 3031 */ |
| 3195 FileManager.prototype.onDirectoryChanged_ = function(event) { | 3032 FileManager.prototype.onDirectoryChanged_ = function(event) { |
| 3196 this.updateCommands_(); | 3033 this.updateCommands_(); |
| 3197 this.updateOkButton_(); | 3034 this.updateOkButton_(); |
| 3198 | 3035 |
| 3199 this.checkFreeSpace_(this.currentDirEntry_.fullPath); | 3036 this.checkFreeSpace_(this.currentDirEntry_.fullPath); |
| 3200 | 3037 |
| 3201 // TODO(dgozman): title may be better than this. | 3038 // New folder should never be enabled in the root or media/ directories. |
| 3039 this.newFolderButton_.disabled = isSystemDirEntry(this.currentDirEntry_); |
| 3040 |
| 3202 this.document_.title = this.currentDirEntry_.fullPath; | 3041 this.document_.title = this.currentDirEntry_.fullPath; |
| 3203 | 3042 |
| 3204 var self = this; | 3043 var self = this; |
| 3205 | 3044 |
| 3206 if (this.subscribedOnDirectoryChanges_) { | 3045 if (this.subscribedOnDirectoryChanges_) { |
| 3207 chrome.fileBrowserPrivate.removeFileWatch(event.previousDirEntry.toURL(), | 3046 chrome.fileBrowserPrivate.removeFileWatch(event.previousDirEntry.toURL(), |
| 3208 function(result) { | 3047 function(result) { |
| 3209 if (!result) { | 3048 if (!result) { |
| 3210 console.log('Failed to remove file watch'); | 3049 console.log('Failed to remove file watch'); |
| 3211 } | 3050 } |
| (...skipping 73 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 3285 // Updated when a user clicks on the label of a file, used to detect | 3124 // Updated when a user clicks on the label of a file, used to detect |
| 3286 // when a click is eligible to trigger a rename. Can be null, or | 3125 // when a click is eligible to trigger a rename. Can be null, or |
| 3287 // an object with 'path' and 'date' properties. | 3126 // an object with 'path' and 'date' properties. |
| 3288 this.lastLabelClick_ = null; | 3127 this.lastLabelClick_ = null; |
| 3289 | 3128 |
| 3290 // Clear the table first. | 3129 // Clear the table first. |
| 3291 this.dataModel_.splice(0, this.dataModel_.length); | 3130 this.dataModel_.splice(0, this.dataModel_.length); |
| 3292 this.currentList_.selectionModel.clear(); | 3131 this.currentList_.selectionModel.clear(); |
| 3293 | 3132 |
| 3294 this.updateBreadcrumbs_(); | 3133 this.updateBreadcrumbs_(); |
| 3295 this.updateRootsListSelection_(); | |
| 3296 | 3134 |
| 3297 // Add current request to pending result list | 3135 if (this.currentDirEntry_.fullPath != '/') { |
| 3298 this.pendingRescanQueue_.push({ | 3136 // Add current request to pending result list |
| 3299 onSuccess:opt_callback, | 3137 this.pendingRescanQueue_.push({ |
| 3300 onError:opt_onError | 3138 onSuccess:opt_callback, |
| 3301 }); | 3139 onError:opt_onError |
| 3140 }); |
| 3302 | 3141 |
| 3303 if (this.rescanRunning_) | 3142 if (this.rescanRunning_) |
| 3304 return; | 3143 return; |
| 3305 | 3144 |
| 3306 this.rescanRunning_ = true; | 3145 this.rescanRunning_ = true; |
| 3307 | 3146 |
| 3308 // The current list of callbacks is saved and reset. Subsequent | 3147 // The current list of callbacks is saved and reset. Subsequent |
| 3309 // calls to rescanDirectory_ while we're still pending will be | 3148 // calls to rescanDirectory_ while we're still pending will be |
| 3310 // saved and will cause an additional rescan to happen after a delay. | 3149 // saved and will cause an additional rescan to happen after a delay. |
| 3311 var callbacks = this.pendingRescanQueue_; | 3150 var callbacks = this.pendingRescanQueue_; |
| 3312 | 3151 |
| 3313 this.pendingRescanQueue_ = []; | 3152 this.pendingRescanQueue_ = []; |
| 3314 | 3153 |
| 3315 var self = this; | 3154 var self = this; |
| 3316 var reader; | 3155 var reader; |
| 3317 | 3156 |
| 3318 function onError() { | 3157 function onError() { |
| 3319 if (self.pendingRescanQueue_.length > 0) { | |
| 3320 setTimeout(self.rescanDirectory_.bind(self), | |
| 3321 SIMULTANEOUS_RESCAN_INTERVAL); | |
| 3322 } | |
| 3323 | |
| 3324 self.rescanRunning_ = false; | |
| 3325 | |
| 3326 for (var i= 0; i < callbacks.length; i++) { | |
| 3327 if (callbacks[i].onError) | |
| 3328 try { | |
| 3329 callbacks[i].onError(); | |
| 3330 } catch (ex) { | |
| 3331 console.error('Caught exception while notifying about error: ' + | |
| 3332 name, ex); | |
| 3333 } | |
| 3334 } | |
| 3335 } | |
| 3336 | |
| 3337 function onReadSome(entries) { | |
| 3338 if (entries.length == 0) { | |
| 3339 metrics.recordInterval('DirectoryScan'); | |
| 3340 if (self.currentDirEntry_.fullPath == DOWNLOADS_DIRECTORY) { | |
| 3341 metrics.recordMediumCount("DownloadsCount", self.dataModel_.length); | |
| 3342 } | |
| 3343 | |
| 3344 if (self.pendingRescanQueue_.length > 0) { | 3158 if (self.pendingRescanQueue_.length > 0) { |
| 3345 setTimeout(self.rescanDirectory_.bind(self), | 3159 setTimeout(self.rescanDirectory_.bind(self), |
| 3346 SIMULTANEOUS_RESCAN_INTERVAL); | 3160 SIMULTANEOUS_RESCAN_INTERVAL); |
| 3347 } | 3161 } |
| 3348 | 3162 |
| 3349 self.rescanRunning_ = false; | 3163 self.rescanRunning_ = false; |
| 3164 |
| 3350 for (var i= 0; i < callbacks.length; i++) { | 3165 for (var i= 0; i < callbacks.length; i++) { |
| 3351 if (callbacks[i].onSuccess) | 3166 if (callbacks[i].onError) |
| 3352 try { | 3167 try { |
| 3353 callbacks[i].onSuccess(); | 3168 callbacks[i].onError(); |
| 3354 } catch (ex) { | 3169 } catch (ex) { |
| 3355 console.error('Caught exception while notifying about error: ' + | 3170 console.error('Caught exception while notifying about error: ' + |
| 3356 name, ex); | 3171 name, ex); |
| 3357 } | 3172 } |
| 3358 } | 3173 } |
| 3359 | |
| 3360 return; | |
| 3361 } | 3174 } |
| 3362 | 3175 |
| 3363 // Splice takes the to-be-spliced-in array as individual parameters, | 3176 function onReadSome(entries) { |
| 3364 // rather than as an array, so we need to perform some acrobatics... | 3177 if (entries.length == 0) { |
| 3365 var spliceArgs = [].slice.call(entries); | 3178 metrics.recordInterval('DirectoryScan'); |
| 3179 if (self.currentDirEntry_.fullPath == DOWNLOADS_DIRECTORY) { |
| 3180 metrics.recordMediumCount("DownloadsCount", self.dataModel_.length); |
| 3181 } |
| 3366 | 3182 |
| 3367 // Hide files that start with a dot ('.'). | 3183 if (self.pendingRescanQueue_.length > 0) { |
| 3368 // TODO(rginda): User should be able to override this. Support for other | 3184 setTimeout(self.rescanDirectory_.bind(self), |
| 3369 // commonly hidden patterns might be nice too. | 3185 SIMULTANEOUS_RESCAN_INTERVAL); |
| 3370 if (self.filterFiles_) { | 3186 } |
| 3371 spliceArgs = spliceArgs.filter(function(e) { | |
| 3372 return e.name.substr(0, 1) != '.'; | |
| 3373 }); | |
| 3374 } | |
| 3375 | 3187 |
| 3376 self.prefetchCacheForSorting_(spliceArgs, function() { | 3188 self.rescanRunning_ = false; |
| 3377 spliceArgs.unshift(0, 0); // index, deleteCount | 3189 for (var i= 0; i < callbacks.length; i++) { |
| 3378 self.dataModel_.splice.apply(self.dataModel_, spliceArgs); | 3190 if (callbacks[i].onSuccess) |
| 3191 try { |
| 3192 callbacks[i].onSuccess(); |
| 3193 } catch (ex) { |
| 3194 console.error('Caught exception while notifying about error: ' + |
| 3195 name, ex); |
| 3196 } |
| 3197 } |
| 3379 | 3198 |
| 3380 // Keep reading until entries.length is 0. | 3199 return; |
| 3381 reader.readEntries(onReadSome, onError); | 3200 } |
| 3382 }); | |
| 3383 }; | |
| 3384 | 3201 |
| 3385 metrics.startInterval('DirectoryScan'); | 3202 // Splice takes the to-be-spliced-in array as individual parameters, |
| 3203 // rather than as an array, so we need to perform some acrobatics... |
| 3204 var spliceArgs = [].slice.call(entries); |
| 3386 | 3205 |
| 3387 // If not the root directory, just read the contents. | 3206 // Hide files that start with a dot ('.'). |
| 3388 reader = this.currentDirEntry_.createReader(); | 3207 // TODO(rginda): User should be able to override this. Support for other |
| 3389 reader.readEntries(onReadSome, onError); | 3208 // commonly hidden patterns might be nice too. |
| 3209 if (self.filterFiles_) { |
| 3210 spliceArgs = spliceArgs.filter(function(e) { |
| 3211 return e.name.substr(0, 1) != '.'; |
| 3212 }); |
| 3213 } |
| 3214 |
| 3215 self.prefetchCacheForSorting_(spliceArgs, function() { |
| 3216 spliceArgs.unshift(0, 0); // index, deleteCount |
| 3217 self.dataModel_.splice.apply(self.dataModel_, spliceArgs); |
| 3218 |
| 3219 // Keep reading until entries.length is 0. |
| 3220 reader.readEntries(onReadSome, onError); |
| 3221 }); |
| 3222 }; |
| 3223 |
| 3224 metrics.startInterval('DirectoryScan'); |
| 3225 |
| 3226 // If not the root directory, just read the contents. |
| 3227 reader = this.currentDirEntry_.createReader(); |
| 3228 reader.readEntries(onReadSome, onError); |
| 3229 return; |
| 3230 } |
| 3231 |
| 3232 // Otherwise, use the provided list of root subdirectories, since the |
| 3233 // real local filesystem root directory (the one we use outside the |
| 3234 // harness) can't be enumerated yet. |
| 3235 var spliceArgs = [].slice.call(this.rootEntries_); |
| 3236 spliceArgs.unshift(0, 0); // index, deleteCount |
| 3237 this.dataModel_.splice.apply(this.dataModel_, spliceArgs); |
| 3238 |
| 3239 if (opt_callback) |
| 3240 opt_callback(); |
| 3390 }; | 3241 }; |
| 3391 | 3242 |
| 3392 FileManager.prototype.prefetchCacheForSorting_ = function(entries, callback) { | 3243 FileManager.prototype.prefetchCacheForSorting_ = function(entries, callback) { |
| 3393 var field = this.dataModel_.sortStatus.field; | 3244 var field = this.dataModel_.sortStatus.field; |
| 3394 if (field) { | 3245 if (field) { |
| 3395 this.prepareSortEntries_(entries, field, callback); | 3246 this.prepareSortEntries_(entries, field, callback); |
| 3396 } else { | 3247 } else { |
| 3397 callback(); | 3248 callback(); |
| 3398 return; | 3249 return; |
| 3399 } | 3250 } |
| (...skipping 233 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 3633 var selectionEnd = input.value.lastIndexOf('.'); | 3484 var selectionEnd = input.value.lastIndexOf('.'); |
| 3634 if (selectionEnd == -1) { | 3485 if (selectionEnd == -1) { |
| 3635 input.select(); | 3486 input.select(); |
| 3636 } else { | 3487 } else { |
| 3637 input.selectionStart = 0; | 3488 input.selectionStart = 0; |
| 3638 input.selectionEnd = selectionEnd; | 3489 input.selectionEnd = selectionEnd; |
| 3639 } | 3490 } |
| 3640 }, 0); | 3491 }, 0); |
| 3641 }; | 3492 }; |
| 3642 | 3493 |
| 3643 FileManager.prototype.onToggleSidebar_ = function(event) { | 3494 FileManager.prototype.onNewFolderButtonClick_ = function(event) { |
| 3644 if (this.dialogContainer_.hasAttribute('sidebar')) { | |
| 3645 this.dialogContainer_.removeAttribute('sidebar'); | |
| 3646 } else { | |
| 3647 this.dialogContainer_.setAttribute('sidebar', 'sidebar'); | |
| 3648 } | |
| 3649 // TODO(dgozman): make table header css-resizable. | |
| 3650 setTimeout(this.onResize_.bind(this), 300); | |
| 3651 }; | |
| 3652 | |
| 3653 FileManager.prototype.onNewFolderCommand_ = function(event) { | |
| 3654 var self = this; | 3495 var self = this; |
| 3655 | 3496 |
| 3656 function onNameSelected(name) { | 3497 function onNameSelected(name) { |
| 3657 var valid = self.validateFileName_(name, function() { | 3498 var valid = self.validateFileName_(name, function() { |
| 3658 promptForName(name); | 3499 promptForName(name); |
| 3659 }); | 3500 }); |
| 3660 | 3501 |
| 3661 if (!valid) { | 3502 if (!valid) { |
| 3662 // Validation failed. User will be prompted for a new name after they | 3503 // Validation failed. User will be prompted for a new name after they |
| 3663 // dismiss the validation error dialog. | 3504 // dismiss the validation error dialog. |
| (...skipping 108 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 3772 this.dialogType_ != FileManager.SELECT_FOLDER) { | 3613 this.dialogType_ != FileManager.SELECT_FOLDER) { |
| 3773 event.preventDefault(); | 3614 event.preventDefault(); |
| 3774 this.onDirectoryAction(this.selection.entries[0]); | 3615 this.onDirectoryAction(this.selection.entries[0]); |
| 3775 } else if (!this.okButton_.disabled) { | 3616 } else if (!this.okButton_.disabled) { |
| 3776 event.preventDefault(); | 3617 event.preventDefault(); |
| 3777 this.onOk_(); | 3618 this.onOk_(); |
| 3778 } | 3619 } |
| 3779 break; | 3620 break; |
| 3780 | 3621 |
| 3781 case 32: // Ctrl-Space => New Folder. | 3622 case 32: // Ctrl-Space => New Folder. |
| 3782 if ((this.dialogType_ == 'saveas-file' || | 3623 if (this.newFolderButton_.style.display != 'none' && event.ctrlKey) { |
| 3783 this.dialogType_ == 'full-page') && event.ctrlKey) { | |
| 3784 event.preventDefault(); | 3624 event.preventDefault(); |
| 3785 this.onNewFolderCommand_(); | 3625 this.onNewFolderButtonClick_(); |
| 3786 } | 3626 } |
| 3787 break; | 3627 break; |
| 3788 | 3628 |
| 3789 case 88: // Ctrl-X => Cut. | 3629 case 88: // Ctrl-X => Cut. |
| 3790 this.updateCommands_(); | 3630 this.updateCommands_(); |
| 3791 if (!this.commands_['cut'].disabled) { | 3631 if (!this.commands_['cut'].disabled) { |
| 3792 event.preventDefault(); | 3632 event.preventDefault(); |
| 3793 this.commands_['cut'].execute(); | 3633 this.commands_['cut'].execute(); |
| 3794 } | 3634 } |
| 3795 break; | 3635 break; |
| (...skipping 332 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 4128 }); | 3968 }); |
| 4129 }, onError); | 3969 }, onError); |
| 4130 | 3970 |
| 4131 function onError(err) { | 3971 function onError(err) { |
| 4132 console.log('Error while checking free space: ' + err); | 3972 console.log('Error while checking free space: ' + err); |
| 4133 setTimeout(doCheck, 1000 * 60); | 3973 setTimeout(doCheck, 1000 * 60); |
| 4134 } | 3974 } |
| 4135 } | 3975 } |
| 4136 } | 3976 } |
| 4137 })(); | 3977 })(); |
| OLD | NEW |