| 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.resolveRoots_(); | 64 this.initFileSystem_(); |
| 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 418 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 493 chrome.fileBrowserPrivate.getStrings(function(strings) { | 493 chrome.fileBrowserPrivate.getStrings(function(strings) { |
| 494 localStrings = new LocalStrings(strings); | 494 localStrings = new LocalStrings(strings); |
| 495 if (callback) | 495 if (callback) |
| 496 callback(); | 496 callback(); |
| 497 }); | 497 }); |
| 498 }; | 498 }; |
| 499 | 499 |
| 500 // Instance methods. | 500 // Instance methods. |
| 501 | 501 |
| 502 /** | 502 /** |
| 503 * Request file system and get root entries asynchronously. Invokes init_ | 503 * Request local file system, resolve roots and init_ after that. |
| 504 * when have finished. | 504 * @private |
| 505 */ | 505 */ |
| 506 FileManager.prototype.resolveRoots_ = function(callback) { | 506 FileManager.prototype.initFileSystem_ = function() { |
| 507 var rootPaths = ['Downloads', 'removable', 'archive']; | 507 util.installFileErrorToString(); |
| 508 metrics.startInterval('Load.FileSystem'); |
| 508 | 509 |
| 509 metrics.startInterval('Load.FileSystem'); | |
| 510 var self = this; | 510 var self = this; |
| 511 | 511 |
| 512 // The list of active mount points to distinct them from other directories. | 512 // The list of active mount points to distinct them from other directories. |
| 513 chrome.fileBrowserPrivate.getMountPoints(function(mountPoints) { | 513 chrome.fileBrowserPrivate.getMountPoints(function(mountPoints) { |
| 514 self.mountPoints_ = mountPoints; | 514 self.mountPoints_ = mountPoints; |
| 515 onDone(); | 515 onDone(); |
| 516 }); | 516 }); |
| 517 | 517 |
| 518 function onDone() { | 518 function onDone() { |
| 519 if (self.mountPoints_ && self.rootEntries_) | 519 if (self.mountPoints_ && self.rootEntries_) |
| 520 self.init_(); | 520 self.init_(); |
| 521 } | 521 } |
| 522 | 522 |
| 523 chrome.fileBrowserPrivate.requestLocalFileSystem(function(filesystem) { | 523 chrome.fileBrowserPrivate.requestLocalFileSystem(function(filesystem) { |
| 524 self.filesystem_ = filesystem; | |
| 525 util.installFileErrorToString(); | |
| 526 | |
| 527 metrics.recordInterval('Load.FileSystem'); | 524 metrics.recordInterval('Load.FileSystem'); |
| 528 | 525 |
| 529 var rootEntries = []; | 526 self.filesystem_ = filesystem; |
| 530 | 527 self.resolveRoots_(function(rootEntries) { |
| 531 function onAllRootsFound() { | |
| 532 metrics.recordInterval('Load.Roots'); | |
| 533 self.rootEntries_ = rootEntries; | 528 self.rootEntries_ = rootEntries; |
| 534 onDone(); | 529 onDone(); |
| 535 } | 530 }); |
| 536 | |
| 537 function onPathError(path, err) { | |
| 538 console.error('Error locating root path: ' + path + ': ' + err); | |
| 539 } | |
| 540 | |
| 541 function onEntryFound(entry) { | |
| 542 if (entry) { | |
| 543 rootEntries.push(entry); | |
| 544 } else { | |
| 545 onAllRootsFound(); | |
| 546 } | |
| 547 } | |
| 548 | |
| 549 metrics.startInterval('Load.Roots'); | |
| 550 if (filesystem.name.match(/^chrome-extension_\S+:external/i)) { | |
| 551 // We've been handed the local filesystem, whose root directory | |
| 552 // cannot be enumerated. | |
| 553 util.getDirectories(filesystem.root, {create: false}, rootPaths, | |
| 554 onEntryFound, onPathError); | |
| 555 } else { | |
| 556 util.forEachDirEntry(filesystem.root, onEntryFound); | |
| 557 } | |
| 558 }); | 531 }); |
| 559 }; | 532 }; |
| 560 | 533 |
| 561 /** | 534 /** |
| 535 * Get root entries asynchronously. Invokes callback |
| 536 * when have finished. |
| 537 */ |
| 538 FileManager.prototype.resolveRoots_ = function(callback) { |
| 539 var rootPaths = [DOWNLOADS_DIRECTORY, ARCHIVE_DIRECTORY, |
| 540 REMOVABLE_DIRECTORY].map(function(s) { return s.substring(1); }); |
| 541 var rootEntries = []; |
| 542 |
| 543 // The number of entries left to enumerate to get all roots. |
| 544 // When equals to zero, we are done. |
| 545 var entriesToEnumerate = 0; |
| 546 // Entries may be enumerated faster than next one appears, so we have this |
| 547 // guard to not finish too early. |
| 548 var allEntriesFound = false; |
| 549 var done = false; |
| 550 |
| 551 function onDone() { |
| 552 if (done) return; |
| 553 done = true; |
| 554 metrics.recordInterval('Load.Roots'); |
| 555 callback(rootEntries); |
| 556 } |
| 557 |
| 558 function onPathError(path, err) { |
| 559 console.error('Error locating root path: ' + path + ': ' + err); |
| 560 } |
| 561 |
| 562 function onRootFound(root) { |
| 563 if (root) { |
| 564 rootEntries.push(root); |
| 565 } else { |
| 566 entriesToEnumerate--; |
| 567 if (entriesToEnumerate == 0 && allEntriesFound) |
| 568 onDone(); |
| 569 } |
| 570 } |
| 571 |
| 572 function onEntryFound(entry) { |
| 573 if (entry) { |
| 574 entriesToEnumerate++; |
| 575 var path = entry.fullPath; |
| 576 if (path == ARCHIVE_DIRECTORY || path == REMOVABLE_DIRECTORY) { |
| 577 // All removable devices and mounted archives are considered |
| 578 // roots, and are shown in the sidebar. |
| 579 util.forEachDirEntry(entry, onRootFound); |
| 580 } else { |
| 581 onRootFound(entry); |
| 582 onRootFound(null); |
| 583 } |
| 584 } else { |
| 585 allEntriesFound = true; |
| 586 if (entriesToEnumerate == 0) |
| 587 onDone(); |
| 588 } |
| 589 } |
| 590 |
| 591 metrics.startInterval('Load.Roots'); |
| 592 if (this.filesystem_.name.match(/^chrome-extension_\S+:external/i)) { |
| 593 // We've been handed the local filesystem, whose root directory |
| 594 // cannot be enumerated. |
| 595 util.getDirectories(this.filesystem_.root, {create: false}, rootPaths, |
| 596 onEntryFound, onPathError); |
| 597 } else { |
| 598 util.forEachDirEntry(this.filesystem_.root, onEntryFound); |
| 599 } |
| 600 }; |
| 601 |
| 602 /** |
| 562 * Continue initializing the file manager after resolving roots. | 603 * Continue initializing the file manager after resolving roots. |
| 563 */ | 604 */ |
| 564 FileManager.prototype.init_ = function() { | 605 FileManager.prototype.init_ = function() { |
| 565 metrics.startInterval('Load.DOM'); | 606 metrics.startInterval('Load.DOM'); |
| 607 this.initCommands_(); |
| 566 | 608 |
| 567 // TODO(rginda): 6/22/11: Remove this test when createDateTimeFormat is | 609 // TODO(rginda): 6/22/11: Remove this test when createDateTimeFormat is |
| 568 // available in all chrome trunk builds. | 610 // available in all chrome trunk builds. |
| 569 if ('createDateTimeFormat' in this.locale_) { | 611 if ('createDateTimeFormat' in this.locale_) { |
| 570 this.shortDateFormatter_ = | 612 this.shortDateFormatter_ = |
| 571 this.locale_.createDateTimeFormat({'dateType': 'medium'}); | 613 this.locale_.createDateTimeFormat({'dateType': 'medium'}); |
| 572 } else { | 614 } else { |
| 573 this.shortDateFormatter_ = { | 615 this.shortDateFormatter_ = { |
| 574 format: function(d) { | 616 format: function(d) { |
| 575 return (d.getMonth() + 1) + '/' + d.getDate() + '/' + d.getFullYear(); | 617 return (d.getMonth() + 1) + '/' + d.getDate() + '/' + d.getFullYear(); |
| (...skipping 37 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 613 window.addEventListener('unload', this.onUnload_.bind(this)); | 655 window.addEventListener('unload', this.onUnload_.bind(this)); |
| 614 | 656 |
| 615 this.addEventListener('directory-changed', | 657 this.addEventListener('directory-changed', |
| 616 this.onDirectoryChanged_.bind(this)); | 658 this.onDirectoryChanged_.bind(this)); |
| 617 this.addEventListener('selection-summarized', | 659 this.addEventListener('selection-summarized', |
| 618 this.onSelectionSummarized_.bind(this)); | 660 this.onSelectionSummarized_.bind(this)); |
| 619 | 661 |
| 620 // The list of archives requested to mount. We will show contents once | 662 // The list of archives requested to mount. We will show contents once |
| 621 // archive is mounted, but only for mounts from within this filebrowser tab. | 663 // archive is mounted, but only for mounts from within this filebrowser tab. |
| 622 this.mountRequests_ = []; | 664 this.mountRequests_ = []; |
| 665 this.unmountRequests_ = []; |
| 623 chrome.fileBrowserPrivate.onMountCompleted.addListener( | 666 chrome.fileBrowserPrivate.onMountCompleted.addListener( |
| 624 this.onMountCompleted_.bind(this)); | 667 this.onMountCompleted_.bind(this)); |
| 625 | 668 |
| 626 chrome.fileBrowserPrivate.onFileChanged.addListener( | 669 chrome.fileBrowserPrivate.onFileChanged.addListener( |
| 627 this.onFileChanged_.bind(this)); | 670 this.onFileChanged_.bind(this)); |
| 628 | 671 |
| 629 var self = this; | 672 var self = this; |
| 630 | 673 |
| 631 // The list of callbacks to be invoked during the directory rescan after | 674 // The list of callbacks to be invoked during the directory rescan after |
| 632 // all paste tasks are complete. | 675 // all paste tasks are complete. |
| 633 this.pasteSuccessCallbacks_ = []; | 676 this.pasteSuccessCallbacks_ = []; |
| 634 | 677 |
| 635 this.initCommands_(); | |
| 636 | |
| 637 this.setupCurrentDirectory_(); | 678 this.setupCurrentDirectory_(); |
| 638 | 679 |
| 639 this.summarizeSelection_(); | 680 this.summarizeSelection_(); |
| 640 | 681 |
| 641 this.dataModel_.sort('cachedMtime_', 'desc'); | 682 this.dataModel_.sort('cachedMtime_', 'desc'); |
| 642 | 683 |
| 643 this.refocus(); | 684 this.refocus(); |
| 644 | 685 |
| 645 this.createMetadataProvider_(); | 686 this.createMetadataProvider_(); |
| 646 metrics.recordInterval('Load.DOM'); | 687 metrics.recordInterval('Load.DOM'); |
| (...skipping 38 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 685 // Cache nodes we'll be manipulating. | 726 // Cache nodes we'll be manipulating. |
| 686 this.previewThumbnails_ = | 727 this.previewThumbnails_ = |
| 687 this.dialogDom_.querySelector('.preview-thumbnails'); | 728 this.dialogDom_.querySelector('.preview-thumbnails'); |
| 688 this.previewPanel_ = this.dialogDom_.querySelector('.preview-panel'); | 729 this.previewPanel_ = this.dialogDom_.querySelector('.preview-panel'); |
| 689 this.previewFilename_ = this.dialogDom_.querySelector('.preview-filename'); | 730 this.previewFilename_ = this.dialogDom_.querySelector('.preview-filename'); |
| 690 this.previewSummary_ = this.dialogDom_.querySelector('.preview-summary'); | 731 this.previewSummary_ = this.dialogDom_.querySelector('.preview-summary'); |
| 691 this.filenameInput_ = this.dialogDom_.querySelector('.filename-input'); | 732 this.filenameInput_ = this.dialogDom_.querySelector('.filename-input'); |
| 692 this.taskButtons_ = this.dialogDom_.querySelector('.task-buttons'); | 733 this.taskButtons_ = this.dialogDom_.querySelector('.task-buttons'); |
| 693 this.okButton_ = this.dialogDom_.querySelector('.ok'); | 734 this.okButton_ = this.dialogDom_.querySelector('.ok'); |
| 694 this.cancelButton_ = this.dialogDom_.querySelector('.cancel'); | 735 this.cancelButton_ = this.dialogDom_.querySelector('.cancel'); |
| 695 this.newFolderButton_ = this.dialogDom_.querySelector('.new-folder'); | |
| 696 this.deleteButton_ = this.dialogDom_.querySelector('.delete-button'); | 736 this.deleteButton_ = this.dialogDom_.querySelector('.delete-button'); |
| 697 | 737 |
| 698 this.downloadsWarning_ = | 738 this.downloadsWarning_ = |
| 699 this.dialogDom_.querySelector('.downloads-warning'); | 739 this.dialogDom_.querySelector('.downloads-warning'); |
| 700 var html = util.htmlUnescape(str('DOWNLOADS_DIRECTORY_WARNING')); | 740 var html = util.htmlUnescape(str('DOWNLOADS_DIRECTORY_WARNING')); |
| 701 this.downloadsWarning_.lastElementChild.innerHTML = html; | 741 this.downloadsWarning_.lastElementChild.innerHTML = html; |
| 702 var link = this.downloadsWarning_.querySelector('a'); | 742 var link = this.downloadsWarning_.querySelector('a'); |
| 703 link.addEventListener('click', this.onDownloadsWarningClick_.bind(this)); | 743 link.addEventListener('click', this.onDownloadsWarningClick_.bind(this)); |
| 704 | 744 |
| 705 this.document_.addEventListener('keydown', this.onKeyDown_.bind(this)); | 745 this.document_.addEventListener('keydown', this.onKeyDown_.bind(this)); |
| (...skipping 10 matching lines...) Expand all Loading... |
| 716 'keyup', this.onFilenameInputKeyUp_.bind(this)); | 756 'keyup', this.onFilenameInputKeyUp_.bind(this)); |
| 717 this.filenameInput_.addEventListener( | 757 this.filenameInput_.addEventListener( |
| 718 'focus', this.onFilenameInputFocus_.bind(this)); | 758 'focus', this.onFilenameInputFocus_.bind(this)); |
| 719 | 759 |
| 720 var listContainer = this.dialogDom_.querySelector('.list-container'); | 760 var listContainer = this.dialogDom_.querySelector('.list-container'); |
| 721 listContainer.addEventListener('keydown', this.onListKeyDown_.bind(this)); | 761 listContainer.addEventListener('keydown', this.onListKeyDown_.bind(this)); |
| 722 listContainer.addEventListener('keypress', this.onListKeyPress_.bind(this)); | 762 listContainer.addEventListener('keypress', this.onListKeyPress_.bind(this)); |
| 723 this.okButton_.addEventListener('click', this.onOk_.bind(this)); | 763 this.okButton_.addEventListener('click', this.onOk_.bind(this)); |
| 724 this.cancelButton_.addEventListener('click', this.onCancel_.bind(this)); | 764 this.cancelButton_.addEventListener('click', this.onCancel_.bind(this)); |
| 725 | 765 |
| 726 this.dialogDom_.querySelector('button.new-folder').addEventListener( | 766 this.dialogDom_.querySelector('div.open-sidebar').addEventListener( |
| 727 'click', this.onNewFolderButtonClick_.bind(this)); | 767 'click', this.onToggleSidebar_.bind(this)); |
| 768 this.dialogDom_.querySelector('div.close-sidebar').addEventListener( |
| 769 'click', this.onToggleSidebar_.bind(this)); |
| 770 this.dialogContainer_ = this.dialogDom_.querySelector('.dialog-container'); |
| 728 | 771 |
| 729 this.dialogDom_.querySelector('button.detail-view').addEventListener( | 772 this.dialogDom_.querySelector('button.detail-view').addEventListener( |
| 730 'click', this.onDetailViewButtonClick_.bind(this)); | 773 'click', this.onDetailViewButtonClick_.bind(this)); |
| 731 this.dialogDom_.querySelector('button.thumbnail-view').addEventListener( | 774 this.dialogDom_.querySelector('button.thumbnail-view').addEventListener( |
| 732 'click', this.onThumbnailViewButtonClick_.bind(this)); | 775 'click', this.onThumbnailViewButtonClick_.bind(this)); |
| 733 | 776 |
| 734 this.dialogDom_.ownerDocument.defaultView.addEventListener( | 777 this.dialogDom_.ownerDocument.defaultView.addEventListener( |
| 735 'resize', this.onResize_.bind(this)); | 778 'resize', this.onResize_.bind(this)); |
| 736 | 779 |
| 737 var ary = this.dialogDom_.querySelectorAll('[visibleif]'); | 780 var ary = this.dialogDom_.querySelectorAll('[visibleif]'); |
| (...skipping 35 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 773 this.selectionModelClass_ = cr.ui.ListSingleSelectionModel; | 816 this.selectionModelClass_ = cr.ui.ListSingleSelectionModel; |
| 774 } else { | 817 } else { |
| 775 this.selectionModelClass_ = cr.ui.ListSelectionModel; | 818 this.selectionModelClass_ = cr.ui.ListSelectionModel; |
| 776 } | 819 } |
| 777 | 820 |
| 778 this.dataModel_.addEventListener('splice', | 821 this.dataModel_.addEventListener('splice', |
| 779 this.onDataModelSplice_.bind(this)); | 822 this.onDataModelSplice_.bind(this)); |
| 780 | 823 |
| 781 this.initTable_(); | 824 this.initTable_(); |
| 782 this.initGrid_(); | 825 this.initGrid_(); |
| 826 this.initRootsList_(); |
| 783 | 827 |
| 784 this.setListType(FileManager.ListType.DETAIL); | 828 this.setListType(FileManager.ListType.DETAIL); |
| 785 | 829 |
| 786 this.onResize_(); | 830 this.onResize_(); |
| 787 | 831 |
| 788 this.textSearchState_ = {text: '', date: new Date()}; | 832 this.textSearchState_ = {text: '', date: new Date()}; |
| 789 }; | 833 }; |
| 790 | 834 |
| 835 FileManager.prototype.initRootsList_ = function() { |
| 836 this.rootsList_ = this.dialogDom_.querySelector('.roots-list'); |
| 837 cr.ui.List.decorate(this.rootsList_); |
| 838 |
| 839 var self = this; |
| 840 this.rootsList_.itemConstructor = function(entry) { |
| 841 return self.renderRoot_(entry); |
| 842 }; |
| 843 |
| 844 this.rootsList_.selectionModel = new cr.ui.ListSingleSelectionModel(); |
| 845 this.rootsList_.selectionModel.addEventListener( |
| 846 'change', this.onRootsSelectionChanged_.bind(this)); |
| 847 |
| 848 // TODO(dgozman): add "Add a drive" item. |
| 849 this.rootsList_.dataModel = new cr.ui.ArrayDataModel(this.rootEntries_); |
| 850 }; |
| 851 |
| 852 FileManager.prototype.updateRoots_ = function(opt_changeDirectoryTo) { |
| 853 var self = this; |
| 854 this.resolveRoots_(function(rootEntries) { |
| 855 self.rootEntries_ = rootEntries; |
| 856 |
| 857 var dataModel = self.rootsList_.dataModel; |
| 858 var args = [0, dataModel.length].concat(rootEntries); |
| 859 dataModel.splice.apply(dataModel, args); |
| 860 |
| 861 self.updateRootsListSelection_(); |
| 862 |
| 863 if (opt_changeDirectoryTo) |
| 864 self.changeDirectory(opt_changeDirectoryTo); |
| 865 }); |
| 866 }; |
| 867 |
| 791 /** | 868 /** |
| 792 * Get the icon type for a given Entry. | 869 * Get the icon type for a given Entry. |
| 793 * | 870 * |
| 794 * @param {Entry} entry An Entry subclass (FileEntry or DirectoryEntry). | 871 * @param {Entry} entry An Entry subclass (FileEntry or DirectoryEntry). |
| 795 * @return {string} | 872 * @return {string} |
| 796 */ | 873 */ |
| 797 FileManager.prototype.getIconType = function(entry) { | 874 FileManager.prototype.getIconType = function(entry) { |
| 798 if (!('cachedIconType_' in entry)) | 875 if (!('cachedIconType_' in entry)) |
| 799 entry.cachedIconType_ = this.computeIconType_(entry); | 876 entry.cachedIconType_ = this.computeIconType_(entry); |
| 800 return entry.cachedIconType_; | 877 return entry.cachedIconType_; |
| 801 }; | 878 }; |
| 802 | 879 |
| 803 /** | 880 /** |
| 804 * Extract extension from the file name and cat it to to lower case. | 881 * Extract extension from the file name and cat it to to lower case. |
| 805 * | 882 * |
| 806 * @param {string} name. | 883 * @param {string} name. |
| 807 * @return {strin} | 884 * @return {strin} |
| 808 */ | 885 */ |
| 809 function getFileExtension(name) { | 886 function getFileExtension(name) { |
| 810 var extIndex = name.lastIndexOf('.'); | 887 var extIndex = name.lastIndexOf('.'); |
| 811 if (extIndex < 0) | 888 if (extIndex < 0) |
| 812 return ''; | 889 return ''; |
| 813 | 890 |
| 814 return name.substr(extIndex + 1).toLowerCase(); | 891 return name.substr(extIndex + 1).toLowerCase(); |
| 815 } | 892 } |
| 816 | 893 |
| 817 FileManager.prototype.computeIconType_ = function(entry) { | 894 FileManager.prototype.computeIconType_ = function(entry) { |
| 895 // TODO(dgozman): refactor this to use proper icons in left panel, |
| 896 // and do not depend on mountPoints. |
| 818 var deviceNumber = this.getDeviceNumber(entry); | 897 var deviceNumber = this.getDeviceNumber(entry); |
| 819 if (deviceNumber != undefined) { | 898 if (deviceNumber != undefined) { |
| 820 if (this.mountPoints_[deviceNumber].mountCondition == '') | 899 if (this.mountPoints_[deviceNumber].mountCondition == '') |
| 821 return 'device'; | 900 return 'device'; |
| 822 var mountCondition = this.mountPoints_[deviceNumber].mountCondition; | 901 var mountCondition = this.mountPoints_[deviceNumber].mountCondition; |
| 823 if (mountCondition == 'unknown_filesystem' || | 902 if (mountCondition == 'unknown_filesystem' || |
| 824 mountCondition == 'unsupported_filesystem') | 903 mountCondition == 'unsupported_filesystem') |
| 825 return 'unreadable'; | 904 return 'unreadable'; |
| 826 } | 905 } |
| 827 if (entry.isDirectory) | 906 if (entry.isDirectory) |
| (...skipping 281 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 1109 !isSystemDirEntry(this.currentDirEntry_)); | 1188 !isSystemDirEntry(this.currentDirEntry_)); |
| 1110 | 1189 |
| 1111 case 'delete': | 1190 case 'delete': |
| 1112 return (// Initialized to the point where we have a current directory | 1191 return (// Initialized to the point where we have a current directory |
| 1113 this.currentDirEntry_ && | 1192 this.currentDirEntry_ && |
| 1114 // Rename not in progress. | 1193 // Rename not in progress. |
| 1115 !this.renameInput_.currentEntry && | 1194 !this.renameInput_.currentEntry && |
| 1116 !isSystemDirEntry(this.currentDirEntry_)) && | 1195 !isSystemDirEntry(this.currentDirEntry_)) && |
| 1117 this.selection && | 1196 this.selection && |
| 1118 this.selection.totalCount > 0; | 1197 this.selection.totalCount > 0; |
| 1198 |
| 1199 case 'newfolder': |
| 1200 return this.currentDirEntry_ && |
| 1201 (this.dialogType_ == 'saveas-file' || |
| 1202 this.dialogType_ == 'full-page'); |
| 1119 } | 1203 } |
| 1120 }; | 1204 }; |
| 1121 | 1205 |
| 1122 FileManager.prototype.updateCommonActionButtons_ = function() { | 1206 FileManager.prototype.updateCommonActionButtons_ = function() { |
| 1123 if (this.deleteButton_) | 1207 if (this.deleteButton_) |
| 1124 this.deleteButton_.disabled = !this.canExecute_('delete'); | 1208 this.deleteButton_.disabled = !this.canExecute_('delete'); |
| 1125 }; | 1209 }; |
| 1126 | 1210 |
| 1127 FileManager.prototype.setListType = function(type) { | 1211 FileManager.prototype.setListType = function(type) { |
| 1128 if (type && type == this.listType_) | 1212 if (type && type == this.listType_) |
| (...skipping 183 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 1312 case 'rename': | 1396 case 'rename': |
| 1313 var index = this.currentList_.selectionModel.selectedIndex; | 1397 var index = this.currentList_.selectionModel.selectedIndex; |
| 1314 var item = this.currentList_.getListItemByIndex(index); | 1398 var item = this.currentList_.getListItemByIndex(index); |
| 1315 if (item) | 1399 if (item) |
| 1316 this.initiateRename_(item); | 1400 this.initiateRename_(item); |
| 1317 return; | 1401 return; |
| 1318 | 1402 |
| 1319 case 'delete': | 1403 case 'delete': |
| 1320 this.deleteEntries(this.selection.entries); | 1404 this.deleteEntries(this.selection.entries); |
| 1321 return; | 1405 return; |
| 1406 |
| 1407 case 'newfolder': |
| 1408 this.onNewFolderCommand_(event); |
| 1409 return; |
| 1322 } | 1410 } |
| 1323 }; | 1411 }; |
| 1324 | 1412 |
| 1325 /** | 1413 /** |
| 1326 * Respond to the back and forward buttons. | 1414 * Respond to the back and forward buttons. |
| 1327 */ | 1415 */ |
| 1328 FileManager.prototype.onPopState_ = function(event) { | 1416 FileManager.prototype.onPopState_ = function(event) { |
| 1329 // TODO(serya): We should restore selected items here. | 1417 // TODO(serya): We should restore selected items here. |
| 1330 if (this.rootEntries_) | 1418 if (this.rootEntries_) |
| 1331 this.setupCurrentDirectory_(); | 1419 this.setupCurrentDirectory_(); |
| 1332 }; | 1420 }; |
| 1333 | 1421 |
| 1334 FileManager.prototype.requestResize_ = function(timeout) { | 1422 FileManager.prototype.requestResize_ = function(timeout) { |
| 1335 var self = this; | 1423 var self = this; |
| 1336 setTimeout(function() { self.onResize_() }, timeout || 0); | 1424 setTimeout(function() { self.onResize_() }, timeout || 0); |
| 1337 }; | 1425 }; |
| 1338 | 1426 |
| 1339 /** | 1427 /** |
| 1340 * Resize details and thumb views to fit the new window size. | 1428 * Resize details and thumb views to fit the new window size. |
| 1341 */ | 1429 */ |
| 1342 FileManager.prototype.onResize_ = function() { | 1430 FileManager.prototype.onResize_ = function() { |
| 1343 this.table_.style.height = this.grid_.style.height = | 1431 this.table_.style.height = this.grid_.style.height = |
| 1344 this.grid_.parentNode.clientHeight + 'px'; | 1432 this.grid_.parentNode.clientHeight + 'px'; |
| 1345 this.table_.style.width = this.grid_.style.width = | |
| 1346 this.grid_.parentNode.clientWidth + 'px'; | |
| 1347 | |
| 1348 this.table_.list_.style.width = this.table_.parentNode.clientWidth + 'px'; | |
| 1349 this.table_.list_.style.height = (this.table_.clientHeight - 1 - | 1433 this.table_.list_.style.height = (this.table_.clientHeight - 1 - |
| 1350 this.table_.header_.clientHeight) + 'px'; | 1434 this.table_.header_.clientHeight) + 'px'; |
| 1351 | 1435 |
| 1352 if (this.listType_ == FileManager.ListType.THUMBNAIL) { | 1436 if (this.listType_ == FileManager.ListType.THUMBNAIL) { |
| 1353 var self = this; | 1437 var self = this; |
| 1354 setTimeout(function() { | 1438 setTimeout(function() { |
| 1355 self.grid_.columns = 0; | 1439 self.grid_.columns = 0; |
| 1356 self.grid_.redraw(); | 1440 self.grid_.redraw(); |
| 1357 }, 0); | 1441 }, 0); |
| 1358 } else { | 1442 } else { |
| 1359 this.currentList_.redraw(); | 1443 this.currentList_.redraw(); |
| 1360 } | 1444 } |
| 1445 |
| 1446 this.rootsList_.style.height = |
| 1447 this.rootsList_.parentNode.clientHeight + 'px'; |
| 1448 this.rootsList_.redraw(); |
| 1361 }; | 1449 }; |
| 1362 | 1450 |
| 1363 FileManager.prototype.resolvePath = function( | 1451 FileManager.prototype.resolvePath = function( |
| 1364 path, resultCallback, errorCallback) { | 1452 path, resultCallback, errorCallback) { |
| 1365 return util.resolvePath(this.filesystem_.root, path, resultCallback, | 1453 return util.resolvePath(this.filesystem_.root, path, resultCallback, |
| 1366 errorCallback); | 1454 errorCallback); |
| 1367 }; | 1455 }; |
| 1368 | 1456 |
| 1369 /** | 1457 /** |
| 1370 * Restores current directory and may be a selected item after page load (or | 1458 * Restores current directory and may be a selected item after page load (or |
| (...skipping 13 matching lines...) Expand all Loading... |
| 1384 this.setupPath_(this.params_.defaultPath); | 1472 this.setupPath_(this.params_.defaultPath); |
| 1385 } else { | 1473 } else { |
| 1386 this.setupDefaultPath_(); | 1474 this.setupDefaultPath_(); |
| 1387 } | 1475 } |
| 1388 }; | 1476 }; |
| 1389 | 1477 |
| 1390 FileManager.prototype.setupDefaultPath_ = function() { | 1478 FileManager.prototype.setupDefaultPath_ = function() { |
| 1391 // No preset given, find a good place to start. | 1479 // No preset given, find a good place to start. |
| 1392 // Check for removable devices, if there are none, go to Downloads. | 1480 // Check for removable devices, if there are none, go to Downloads. |
| 1393 var removableDirectoryEntry = this.rootEntries_.filter(function(rootEntry) { | 1481 var removableDirectoryEntry = this.rootEntries_.filter(function(rootEntry) { |
| 1394 return rootEntry.fullPath == REMOVABLE_DIRECTORY; | 1482 return isParentPath(REMOVABLE_DIRECTORY, rootEntry.fullPath); |
| 1395 })[0]; | 1483 })[0]; |
| 1396 if (!removableDirectoryEntry) { | 1484 var path = removableDirectoryEntry && removableDirectoryEntry.fullPath || |
| 1397 this.changeDirectory(DOWNLOADS_DIRECTORY, CD_NO_HISTORY); | 1485 DOWNLOADS_DIRECTORY; |
| 1398 return; | 1486 this.changeDirectory(path, CD_NO_HISTORY); |
| 1399 } | |
| 1400 | |
| 1401 var foundRemovable = false; | |
| 1402 util.forEachDirEntry(removableDirectoryEntry, function(result) { | |
| 1403 if (result) { | |
| 1404 foundRemovable = true; | |
| 1405 } else { // Done enumerating, and we know the answer. | |
| 1406 this.changeDirectory(foundRemovable ? '/' : DOWNLOADS_DIRECTORY, | |
| 1407 CD_NO_HISTORY); | |
| 1408 } | |
| 1409 }.bind(this)); | |
| 1410 }; | 1487 }; |
| 1411 | 1488 |
| 1412 FileManager.prototype.setupPath_ = function(path) { | 1489 FileManager.prototype.setupPath_ = function(path) { |
| 1413 // Split the dirname from the basename. | 1490 // Split the dirname from the basename. |
| 1414 var ary = path.match(/^(?:(.*)\/)?([^\/]*)$/); | 1491 var ary = path.match(/^(?:(.*)\/)?([^\/]*)$/); |
| 1415 if (!ary) { | 1492 if (!ary) { |
| 1416 console.warn('Unable to split default path: ' + path); | 1493 console.warn('Unable to split default path: ' + path); |
| 1417 self.changeDirectory('/', CD_NO_HISTORY); | 1494 self.changeDirectory('/', CD_NO_HISTORY); |
| 1418 return; | 1495 return; |
| 1419 } | 1496 } |
| (...skipping 18 matching lines...) Expand all Loading... |
| 1438 | 1515 |
| 1439 // Leaf is an existing file, cd to its parent directory and select it. | 1516 // Leaf is an existing file, cd to its parent directory and select it. |
| 1440 self.changeDirectoryEntry(baseDirEntry, CD_NO_HISTORY, function() { | 1517 self.changeDirectoryEntry(baseDirEntry, CD_NO_HISTORY, function() { |
| 1441 self.selectEntry(leafEntry.name); | 1518 self.selectEntry(leafEntry.name); |
| 1442 }); | 1519 }); |
| 1443 } | 1520 } |
| 1444 | 1521 |
| 1445 function onLeafError(err) { | 1522 function onLeafError(err) { |
| 1446 // Set filename first so OK button will update in changeDirectoryEntry. | 1523 // Set filename first so OK button will update in changeDirectoryEntry. |
| 1447 self.filenameInput_.value = leafName; | 1524 self.filenameInput_.value = leafName; |
| 1525 self.selectDefaultPathInFilenameInput_(); |
| 1448 if (err = FileError.NOT_FOUND_ERR) { | 1526 if (err = FileError.NOT_FOUND_ERR) { |
| 1449 // Leaf does not exist, it's just a suggested file name. | 1527 // Leaf does not exist, it's just a suggested file name. |
| 1450 self.changeDirectoryEntry(baseDirEntry, CD_NO_HISTORY); | 1528 self.changeDirectoryEntry(baseDirEntry, CD_NO_HISTORY); |
| 1451 } else { | 1529 } else { |
| 1452 console.log('Unexpected error resolving default leaf: ' + err); | 1530 console.log('Unexpected error resolving default leaf: ' + err); |
| 1453 self.changeDirectoryEntry('/', CD_NO_HISTORY); | 1531 self.changeDirectoryEntry('/', CD_NO_HISTORY); |
| 1454 } | 1532 } |
| 1455 } | 1533 } |
| 1456 | 1534 |
| 1457 self.resolvePath(path, onLeafFound, onLeafError); | 1535 self.resolvePath(path, onLeafFound, onLeafError); |
| 1458 } | 1536 } |
| 1459 | 1537 |
| 1460 function onBaseError(err) { | 1538 function onBaseError(err) { |
| 1461 // Set filename first so OK button will update in changeDirectory. | 1539 // Set filename first so OK button will update in changeDirectory. |
| 1462 self.filenameInput_.value = leafName; | 1540 self.filenameInput_.value = leafName; |
| 1541 self.selectDefaultPathInFilenameInput_(); |
| 1463 console.log('Unexpected error resolving default base "' + | 1542 console.log('Unexpected error resolving default base "' + |
| 1464 baseName + '": ' + err); | 1543 baseName + '": ' + err); |
| 1465 self.changeDirectory('/', CD_NO_HISTORY); | 1544 self.changeDirectory('/', CD_NO_HISTORY); |
| 1466 } | 1545 } |
| 1467 | 1546 |
| 1468 if (baseName) { | 1547 if (baseName) { |
| 1469 this.filesystem_.root.getDirectory( | 1548 this.filesystem_.root.getDirectory( |
| 1470 baseName, {create: false}, onBaseFound, onBaseError); | 1549 baseName, {create: false}, onBaseFound, onBaseError); |
| 1471 } else { | 1550 } else { |
| 1472 onBaseFound(this.filesystem_.root); | 1551 onBaseFound(this.filesystem_.root); |
| (...skipping 235 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 1708 * @param {cr.ui.Table} table The table doing the rendering. | 1787 * @param {cr.ui.Table} table The table doing the rendering. |
| 1709 */ | 1788 */ |
| 1710 FileManager.prototype.renderIconType_ = function(entry, columnId, table) { | 1789 FileManager.prototype.renderIconType_ = function(entry, columnId, table) { |
| 1711 var icon = this.document_.createElement('div'); | 1790 var icon = this.document_.createElement('div'); |
| 1712 icon.className = 'detail-icon'; | 1791 icon.className = 'detail-icon'; |
| 1713 this.getIconType(entry); | 1792 this.getIconType(entry); |
| 1714 icon.setAttribute('iconType', entry.cachedIconType_); | 1793 icon.setAttribute('iconType', entry.cachedIconType_); |
| 1715 return icon; | 1794 return icon; |
| 1716 }; | 1795 }; |
| 1717 | 1796 |
| 1718 FileManager.prototype.getLabelForRootPath_ = function(path) { | 1797 /** |
| 1719 // This hack lets us localize the top level directories. | 1798 * Return the localized name for the root. |
| 1720 if (path == 'Downloads') | 1799 * @param {string} path The full path of the root (starting with slash). |
| 1721 return str('DOWNLOADS_DIRECTORY_LABEL'); | 1800 * @return {string} The localized name. |
| 1801 */ |
| 1802 FileManager.prototype.getRootLabel_ = function(path) { |
| 1803 if (path == DOWNLOADS_DIRECTORY) |
| 1804 return str('CHROMEBOOK_DIRECTORY_LABEL'); |
| 1722 | 1805 |
| 1723 if (path == 'archive') | 1806 if (path == ARCHIVE_DIRECTORY) |
| 1724 return str('ARCHIVE_DIRECTORY_LABEL'); | 1807 return str('ARCHIVE_DIRECTORY_LABEL'); |
| 1808 if (isParentPath(ARCHIVE_DIRECTORY, path)) |
| 1809 return path.substring(ARCHIVE_DIRECTORY.length + 1); |
| 1725 | 1810 |
| 1726 if (path == 'removable') | 1811 if (path == REMOVABLE_DIRECTORY) |
| 1727 return str('REMOVABLE_DIRECTORY_LABEL'); | 1812 return str('REMOVABLE_DIRECTORY_LABEL'); |
| 1813 if (isParentPath(REMOVABLE_DIRECTORY, path)) |
| 1814 return path.substring(REMOVABLE_DIRECTORY.length + 1); |
| 1728 | 1815 |
| 1729 return path || str('ROOT_DIRECTORY_LABEL'); | 1816 return path; |
| 1817 }; |
| 1818 |
| 1819 FileManager.prototype.getRootIconUrl_ = function(path, opt_small) { |
| 1820 var iconUrl = opt_small ? 'images/chromebook_28x28.png' : |
| 1821 'images/chromebook_24x24.png'; |
| 1822 if (isParentPath(REMOVABLE_DIRECTORY, path)) |
| 1823 iconUrl = 'images/filetype_device.png'; |
| 1824 else if (isParentPath(ARCHIVE_DIRECTORY, path)) |
| 1825 iconUrl = 'images/icon_mount_archive_16x16.png'; |
| 1826 return chrome.extension.getURL(iconUrl); |
| 1827 }; |
| 1828 |
| 1829 FileManager.prototype.renderRoot_ = function(entry) { |
| 1830 var li = this.document_.createElement('li'); |
| 1831 li.className = 'root-item'; |
| 1832 |
| 1833 var icon = this.document_.createElement('img'); |
| 1834 icon.src = this.getRootIconUrl_(entry.fullPath, false); |
| 1835 li.appendChild(icon); |
| 1836 |
| 1837 var div = this.document_.createElement('div'); |
| 1838 div.className = 'text'; |
| 1839 div.textContent = this.getRootLabel_(entry.fullPath); |
| 1840 li.appendChild(div); |
| 1841 |
| 1842 if (isParentPath(REMOVABLE_DIRECTORY, entry.fullPath) || |
| 1843 isParentPath(ARCHIVE_DIRECTORY, entry.fullPath)) { |
| 1844 var spacer = this.document_.createElement('div'); |
| 1845 spacer.className = 'spacer'; |
| 1846 li.appendChild(spacer); |
| 1847 |
| 1848 var eject = this.document_.createElement('img'); |
| 1849 eject.className = 'root-eject'; |
| 1850 eject.setAttribute('src', chrome.extension.getURL('images/eject.png')); |
| 1851 eject.addEventListener('click', this.onEjectClick_.bind(this, entry)); |
| 1852 li.appendChild(eject); |
| 1853 } |
| 1854 |
| 1855 cr.defineProperty(li, 'lead', cr.PropertyKind.BOOL_ATTR); |
| 1856 cr.defineProperty(li, 'selected', cr.PropertyKind.BOOL_ATTR); |
| 1857 return li; |
| 1858 }; |
| 1859 |
| 1860 /** |
| 1861 * Handler for eject button clicked. |
| 1862 * @param {Entry} entry Entry to eject. |
| 1863 * @param {Event} event The event. |
| 1864 */ |
| 1865 FileManager.prototype.onEjectClick_ = function(entry, event) { |
| 1866 this.unmountRequests_.push(entry.toURL()); |
| 1867 chrome.fileBrowserPrivate.removeMount(entry.fullPath); |
| 1730 }; | 1868 }; |
| 1731 | 1869 |
| 1732 /** | 1870 /** |
| 1733 * Render the Name column of the detail table. | 1871 * Render the Name column of the detail table. |
| 1734 * | 1872 * |
| 1735 * Invoked by cr.ui.Table when a file needs to be rendered. | 1873 * Invoked by cr.ui.Table when a file needs to be rendered. |
| 1736 * | 1874 * |
| 1737 * @param {Entry} entry The Entry object to render. | 1875 * @param {Entry} entry The Entry object to render. |
| 1738 * @param {string} columnId The id of the column to be rendered. | 1876 * @param {string} columnId The id of the column to be rendered. |
| 1739 * @param {cr.ui.Table} table The table doing the rendering. | 1877 * @param {cr.ui.Table} table The table doing the rendering. |
| (...skipping 387 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 2127 task.title = str('GALLERY'); | 2265 task.title = str('GALLERY'); |
| 2128 task.allTasks = tasksList; | 2266 task.allTasks = tasksList; |
| 2129 this.galleryTask_ = task; | 2267 this.galleryTask_ = task; |
| 2130 } | 2268 } |
| 2131 } | 2269 } |
| 2132 this.renderTaskButton_(task); | 2270 this.renderTaskButton_(task); |
| 2133 } | 2271 } |
| 2134 | 2272 |
| 2135 // These are done in separate functions, as the checks require | 2273 // These are done in separate functions, as the checks require |
| 2136 // asynchronous function calls. | 2274 // asynchronous function calls. |
| 2137 this.maybeRenderUnmountTask_(selection); | |
| 2138 this.maybeRenderFormattingTask_(selection); | 2275 this.maybeRenderFormattingTask_(selection); |
| 2139 }; | 2276 }; |
| 2140 | 2277 |
| 2141 FileManager.prototype.renderTaskButton_ = function(task) { | 2278 FileManager.prototype.renderTaskButton_ = function(task) { |
| 2142 var button = this.document_.createElement('button'); | 2279 var button = this.document_.createElement('button'); |
| 2143 button.addEventListener('click', | 2280 button.addEventListener('click', |
| 2144 this.onTaskButtonClicked_.bind(this, task)); | 2281 this.onTaskButtonClicked_.bind(this, task)); |
| 2145 button.className = 'task-button'; | 2282 button.className = 'task-button'; |
| 2146 | 2283 |
| 2147 var img = this.document_.createElement('img'); | 2284 var img = this.document_.createElement('img'); |
| 2148 img.src = task.iconUrl; | 2285 img.src = task.iconUrl; |
| 2149 | 2286 |
| 2150 button.appendChild(img); | 2287 button.appendChild(img); |
| 2151 var label = this.document_.createElement('div'); | 2288 var label = this.document_.createElement('div'); |
| 2152 label.appendChild(this.document_.createTextNode(task.title)) | 2289 label.appendChild(this.document_.createTextNode(task.title)) |
| 2153 button.appendChild(label); | 2290 button.appendChild(label); |
| 2154 | 2291 |
| 2155 this.taskButtons_.appendChild(button); | 2292 this.taskButtons_.appendChild(button); |
| 2156 }; | 2293 }; |
| 2157 | 2294 |
| 2158 /** | 2295 /** |
| 2159 * Checks whether unmount task should be displayed and if the answer is | |
| 2160 * affirmative renders it. | |
| 2161 * @param {Object} selection Selected files object. | |
| 2162 */ | |
| 2163 FileManager.prototype.maybeRenderUnmountTask_ = function(selection) { | |
| 2164 for (var index = 0; index < selection.urls.length; ++index) { | |
| 2165 // Each url should be a mount point. | |
| 2166 var path = selection.entries[index].fullPath; | |
| 2167 var found = false; | |
| 2168 for (var i = 0; i < this.mountPoints_.length; i++) { | |
| 2169 var mountPath = this.mountPoints_[i].mountPath; | |
| 2170 if (mountPath[0] != '/') { | |
| 2171 mountPath = '/' + mountPath; | |
| 2172 } | |
| 2173 if (mountPath == path && this.mountPoints_[i].mountType == 'file') { | |
| 2174 found = true; | |
| 2175 break; | |
| 2176 } | |
| 2177 } | |
| 2178 if (!found) | |
| 2179 return; | |
| 2180 } | |
| 2181 this.renderTaskButton_({ | |
| 2182 taskId: this.getExtensionId_() + '|unmount-archive', | |
| 2183 iconUrl: | |
| 2184 chrome.extension.getURL('images/icon_unmount_archive_16x16.png'), | |
| 2185 title: str('UNMOUNT_ARCHIVE'), | |
| 2186 internal: true | |
| 2187 }); | |
| 2188 }; | |
| 2189 | |
| 2190 /** | |
| 2191 * Checks whether formatting task should be displayed and if the answer is | 2296 * Checks whether formatting task should be displayed and if the answer is |
| 2192 * affirmative renders it. Includes asynchronous calls, so it's splitted into | 2297 * affirmative renders it. Includes asynchronous calls, so it's splitted into |
| 2193 * three parts. | 2298 * three parts. |
| 2194 * @param {Object} selection Selected files object. | 2299 * @param {Object} selection Selected files object. |
| 2195 */ | 2300 */ |
| 2196 FileManager.prototype.maybeRenderFormattingTask_ = function(selection) { | 2301 FileManager.prototype.maybeRenderFormattingTask_ = function(selection) { |
| 2197 // Not to make unnecessary getMountPoints() call we doublecheck if there is | 2302 // Not to make unnecessary getMountPoints() call we doublecheck if there is |
| 2198 // only one selected entry. | 2303 // only one selected entry. |
| 2199 if (selection.entries.length != 1) | 2304 if (selection.entries.length != 1) |
| 2200 return; | 2305 return; |
| (...skipping 60 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 2261 chrome.fileBrowserPrivate.executeTask(task.taskId, urls); | 2366 chrome.fileBrowserPrivate.executeTask(task.taskId, urls); |
| 2262 }; | 2367 }; |
| 2263 | 2368 |
| 2264 /** | 2369 /** |
| 2265 * Event handler called when some volume was mounted or unmouted. | 2370 * Event handler called when some volume was mounted or unmouted. |
| 2266 */ | 2371 */ |
| 2267 FileManager.prototype.onMountCompleted_ = function(event) { | 2372 FileManager.prototype.onMountCompleted_ = function(event) { |
| 2268 var self = this; | 2373 var self = this; |
| 2269 chrome.fileBrowserPrivate.getMountPoints(function(mountPoints) { | 2374 chrome.fileBrowserPrivate.getMountPoints(function(mountPoints) { |
| 2270 self.mountPoints_ = mountPoints; | 2375 self.mountPoints_ = mountPoints; |
| 2376 var changeDirectoryTo = null; |
| 2377 |
| 2271 if (event.eventType == 'mount') { | 2378 if (event.eventType == 'mount') { |
| 2272 for (var index = 0; index < self.mountRequests_.length; ++index) { | 2379 // Mount request finished - remove it. |
| 2273 if (self.mountRequests_[index] == event.sourceUrl) { | 2380 var index = self.mountRequests_.indexOf(event.sourceUrl); |
| 2274 self.mountRequests_.splice(index, 1); | 2381 if (index != -1) { |
| 2275 if (event.status == 'success') { | 2382 self.mountRequests_.splice(index, 1); |
| 2276 self.changeDirectory(event.mountPath); | 2383 // Go to mounted directory, if request was initiated from this tab. |
| 2277 } else { | 2384 if (event.status == 'success') |
| 2278 // Report mount error. | 2385 changeDirectoryTo = event.mountPath; |
| 2279 if (event.mountType == 'file') { | |
| 2280 var fileName = event.sourceUrl.substr( | |
| 2281 event.sourceUrl.lastIndexOf('/') + 1); | |
| 2282 self.alert.show(strf('ARCHIVE_MOUNT_FAILED', fileName, | |
| 2283 event.status)); | |
| 2284 } | |
| 2285 } | |
| 2286 return; | |
| 2287 } | |
| 2288 } | 2386 } |
| 2289 } | 2387 } |
| 2290 | 2388 |
| 2389 if (event.eventType == 'unmount') { |
| 2390 // Unmount request finished - remove it. |
| 2391 var index = self.unmountRequests_.indexOf(event.sourceUrl); |
| 2392 if (index != -1) |
| 2393 self.unmountRequests_.splice(index, 1); |
| 2394 } |
| 2395 |
| 2396 if (event.eventType == 'mount' && event.status != 'success' && |
| 2397 event.mountType == 'file') { |
| 2398 // Report mount error. |
| 2399 var fileName = event.sourceUrl.substr( |
| 2400 event.sourceUrl.lastIndexOf('/') + 1); |
| 2401 self.alert.show(strf('ARCHIVE_MOUNT_FAILED', fileName, |
| 2402 event.status)); |
| 2403 } |
| 2404 |
| 2405 if (event.eventType == 'unmount' && event.status != 'success') { |
| 2406 // Report unmount error. |
| 2407 // TODO(dgozman): introduce string and show alert here. |
| 2408 } |
| 2409 |
| 2291 if (event.eventType == 'unmount' && event.status == 'success' && | 2410 if (event.eventType == 'unmount' && event.status == 'success' && |
| 2292 self.currentDirEntry_ && | 2411 self.currentDirEntry_ && |
| 2293 isParentPath(event.mountPath, self.currentDirEntry_.fullPath)) { | 2412 isParentPath(event.mountPath, self.currentDirEntry_.fullPath)) { |
| 2294 self.changeDirectory(getParentPath(event.mountPath)); | 2413 changeDirectoryTo = getParentPath(event.mountPath); |
| 2295 return; | |
| 2296 } | 2414 } |
| 2297 | 2415 |
| 2298 var rescanDirectoryNeeded = (event.status == 'success'); | 2416 // In the case of success, roots are changed and should be rescanned. |
| 2299 for (var i = 0; i < mountPoints.length; i++) { | 2417 if (event.status == 'success') |
| 2300 if (event.sourceUrl == mountPoints[i].sourceUrl && | 2418 self.updateRoots_(changeDirectoryTo); |
| 2301 mountPoints[i].mountCondition != '') { | |
| 2302 rescanDirectoryNeeded = true; | |
| 2303 } | |
| 2304 } | |
| 2305 // TODO(dgozman): rescan directory, only if it contains mounted points, | |
| 2306 // when mounts location will be decided. | |
| 2307 if (rescanDirectoryNeeded) | |
| 2308 self.rescanDirectory_(null, 300); | |
| 2309 }); | 2419 }); |
| 2310 }; | 2420 }; |
| 2311 | 2421 |
| 2312 /** | 2422 /** |
| 2313 * Event handler called when some internal task should be executed. | 2423 * Event handler called when some internal task should be executed. |
| 2314 */ | 2424 */ |
| 2315 FileManager.prototype.onFileTaskExecute_ = function(id, details) { | 2425 FileManager.prototype.onFileTaskExecute_ = function(id, details) { |
| 2316 var urls = details.urls; | 2426 var urls = details.urls; |
| 2317 if (id == 'play' || id == 'enqueue') { | 2427 if (id == 'play' || id == 'enqueue') { |
| 2318 chrome.fileBrowserPrivate.viewFiles(urls, id); | 2428 chrome.fileBrowserPrivate.viewFiles(urls, id); |
| 2319 } else if (id == 'mount-archive') { | 2429 } else if (id == 'mount-archive') { |
| 2320 for (var index = 0; index < urls.length; ++index) { | 2430 for (var index = 0; index < urls.length; ++index) { |
| 2321 this.mountRequests_.push(urls[index]); | 2431 this.mountRequests_.push(urls[index]); |
| 2322 chrome.fileBrowserPrivate.addMount(urls[index], 'file', {}); | 2432 chrome.fileBrowserPrivate.addMount(urls[index], 'file', {}); |
| 2323 } | 2433 } |
| 2324 } else if (id == 'unmount-archive') { | |
| 2325 for (var index = 0; index < urls.length; ++index) { | |
| 2326 chrome.fileBrowserPrivate.removeMount(urls[index]); | |
| 2327 } | |
| 2328 } else if (id == 'format-device') { | 2434 } else if (id == 'format-device') { |
| 2329 this.confirm.show(str('FORMATTING_WARNING'), function() { | 2435 this.confirm.show(str('FORMATTING_WARNING'), function() { |
| 2330 chrome.fileBrowserPrivate.formatDevice(urls[0]); | 2436 chrome.fileBrowserPrivate.formatDevice(urls[0]); |
| 2331 }); | 2437 }); |
| 2332 } else if (id == 'gallery') { | 2438 } else if (id == 'gallery') { |
| 2333 // Pass to gallery all possible tasks except the gallery itself. | 2439 // Pass to gallery all possible tasks except the gallery itself. |
| 2334 var noGallery = []; | 2440 var noGallery = []; |
| 2335 for (var index = 0; index < details.task.allTasks.length; index++) { | 2441 for (var index = 0; index < details.task.allTasks.length; index++) { |
| 2336 var task = details.task.allTasks[index]; | 2442 var task = details.task.allTasks[index]; |
| 2337 if (task.taskId != this.getExtensionId_() + '|gallery') { | 2443 if (task.taskId != this.getExtensionId_() + '|gallery') { |
| (...skipping 57 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 2395 self.metadataProvider_, | 2501 self.metadataProvider_, |
| 2396 shareActions, | 2502 shareActions, |
| 2397 str); | 2503 str); |
| 2398 }; | 2504 }; |
| 2399 | 2505 |
| 2400 galleryFrame.src = 'js/image_editor/gallery.html'; | 2506 galleryFrame.src = 'js/image_editor/gallery.html'; |
| 2401 this.dialogDom_.appendChild(galleryFrame); | 2507 this.dialogDom_.appendChild(galleryFrame); |
| 2402 galleryFrame.focus(); | 2508 galleryFrame.focus(); |
| 2403 }; | 2509 }; |
| 2404 | 2510 |
| 2511 FileManager.prototype.getRootForPath_ = function(path) { |
| 2512 for (var index = 0; index < this.rootEntries_.length; index++) { |
| 2513 if (isParentPath(this.rootEntries_[index].fullPath, path)) { |
| 2514 return index; |
| 2515 } |
| 2516 } |
| 2517 return -1; |
| 2518 }; |
| 2519 |
| 2405 /** | 2520 /** |
| 2406 * Update the breadcrumb display to reflect the current directory. | 2521 * Update the breadcrumb display to reflect the current directory. |
| 2407 */ | 2522 */ |
| 2408 FileManager.prototype.updateBreadcrumbs_ = function() { | 2523 FileManager.prototype.updateBreadcrumbs_ = function() { |
| 2409 var bc = this.dialogDom_.querySelector('.breadcrumbs'); | 2524 var bc = this.dialogDom_.querySelector('.breadcrumbs'); |
| 2410 removeChildren(bc); | 2525 removeChildren(bc); |
| 2411 | 2526 |
| 2412 var fullPath = this.currentDirEntry_.fullPath.replace(/\/$/, ''); | 2527 var fullPath = this.currentDirEntry_.fullPath; |
| 2413 var pathNames = fullPath.split('/'); | 2528 var rootIndex = this.getRootForPath_(fullPath); |
| 2414 var path = ''; | 2529 if (rootIndex == -1) { |
| 2530 console.error('Not root for: ' + fullPath); |
| 2531 return; |
| 2532 } |
| 2533 var root = this.rootEntries_[rootIndex]; |
| 2534 |
| 2535 var icon = this.document_.createElement('img'); |
| 2536 icon.className = 'breadcrumb-icon'; |
| 2537 icon.setAttribute('src', this.getRootIconUrl_(root.fullPath, true)); |
| 2538 bc.appendChild(icon); |
| 2539 |
| 2540 var rootPath = root.fullPath; |
| 2541 var relativePath = fullPath.substring(rootPath.length); |
| 2542 var pathNames = relativePath.replace(/\/$/, '').split('/'); |
| 2543 if (pathNames[0] == '') |
| 2544 pathNames.splice(0, 1); |
| 2545 |
| 2546 // We need a first breadcrumb for root, so placing last name from |
| 2547 // rootPath as first name of relativePath. |
| 2548 var rootPathNames = rootPath.replace(/\/$/, '').split('/'); |
| 2549 pathNames.splice(0, 0, rootPathNames[rootPathNames.length - 1]); |
| 2550 rootPathNames.splice(rootPathNames.length - 1, 1); |
| 2551 var path = rootPathNames.join('/') + '/'; |
| 2415 | 2552 |
| 2416 for (var i = 0; i < pathNames.length; i++) { | 2553 for (var i = 0; i < pathNames.length; i++) { |
| 2417 var pathName = pathNames[i]; | 2554 var pathName = pathNames[i]; |
| 2418 path += pathName + '/'; | 2555 path += pathName; |
| 2419 | 2556 |
| 2420 var div = this.document_.createElement('div'); | 2557 var div = this.document_.createElement('div'); |
| 2421 div.className = 'breadcrumb-path'; | 2558 div.className = 'breadcrumb-path'; |
| 2422 if (i <= 1) { | 2559 div.textContent = i == 0 ? this.getRootLabel_(path) : pathName; |
| 2423 // i == 0: root directory itself, i == 1: the files it contains. | |
| 2424 div.textContent = this.getLabelForRootPath_(pathName); | |
| 2425 } else { | |
| 2426 div.textContent = pathName; | |
| 2427 } | |
| 2428 | 2560 |
| 2561 path = path + '/'; |
| 2429 div.path = path; | 2562 div.path = path; |
| 2430 div.addEventListener('click', this.onBreadcrumbClick_.bind(this)); | 2563 div.addEventListener('click', this.onBreadcrumbClick_.bind(this)); |
| 2431 | 2564 |
| 2432 bc.appendChild(div); | 2565 bc.appendChild(div); |
| 2433 | 2566 |
| 2434 if (i == pathNames.length - 1) { | 2567 if (i == pathNames.length - 1) { |
| 2435 div.classList.add('breadcrumb-last'); | 2568 div.classList.add('breadcrumb-last'); |
| 2436 } else { | 2569 } else { |
| 2437 var spacer = this.document_.createElement('div'); | 2570 var spacer = this.document_.createElement('div'); |
| 2438 spacer.className = 'breadcrumb-spacer'; | 2571 spacer.className = 'breadcrumb-spacer'; |
| (...skipping 83 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 2522 | 2655 |
| 2523 FileManager.prototype.selectEntry = function(name) { | 2656 FileManager.prototype.selectEntry = function(name) { |
| 2524 for (var i = 0; i < this.dataModel_.length; i++) { | 2657 for (var i = 0; i < this.dataModel_.length; i++) { |
| 2525 if (this.dataModel_.item(i).name == name) { | 2658 if (this.dataModel_.item(i).name == name) { |
| 2526 this.selectIndex(i); | 2659 this.selectIndex(i); |
| 2527 return; | 2660 return; |
| 2528 } | 2661 } |
| 2529 } | 2662 } |
| 2530 }; | 2663 }; |
| 2531 | 2664 |
| 2665 FileManager.prototype.updateRootsListSelection_ = function() { |
| 2666 if (!this.currentDirEntry_) return; |
| 2667 var index = this.getRootForPath_(this.currentDirEntry_.fullPath); |
| 2668 if (index == -1) { |
| 2669 this.rootsList_.selectionModel.selectedIndex = 0; |
| 2670 } else { |
| 2671 if (this.rootsList_.selectionModel.selectedIndex != index) |
| 2672 this.rootsList_.selectionModel.selectedIndex = index; |
| 2673 } |
| 2674 }; |
| 2675 |
| 2532 FileManager.prototype.selectIndex = function(index) { | 2676 FileManager.prototype.selectIndex = function(index) { |
| 2533 this.focusCurrentList_(); | 2677 this.focusCurrentList_(); |
| 2534 if (index >= this.dataModel_.length) | 2678 if (index >= this.dataModel_.length) |
| 2535 return; | 2679 return; |
| 2536 this.currentList_.selectionModel.selectedIndex = index; | 2680 this.currentList_.selectionModel.selectedIndex = index; |
| 2537 this.currentList_.scrollIndexIntoView(index); | 2681 this.currentList_.scrollIndexIntoView(index); |
| 2538 }; | 2682 }; |
| 2539 | 2683 |
| 2540 /** | 2684 /** |
| 2541 * Add the file/directory with given name to the current selection. | 2685 * Add the file/directory with given name to the current selection. |
| (...skipping 110 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 2652 */ | 2796 */ |
| 2653 FileManager.prototype.changeDirectoryEntry = function(dirEntry, | 2797 FileManager.prototype.changeDirectoryEntry = function(dirEntry, |
| 2654 opt_saveHistory, | 2798 opt_saveHistory, |
| 2655 opt_action) { | 2799 opt_action) { |
| 2656 if (typeof opt_saveHistory == 'undefined') { | 2800 if (typeof opt_saveHistory == 'undefined') { |
| 2657 opt_saveHistory = true; | 2801 opt_saveHistory = true; |
| 2658 } else { | 2802 } else { |
| 2659 opt_saveHistory = !!opt_saveHistory; | 2803 opt_saveHistory = !!opt_saveHistory; |
| 2660 } | 2804 } |
| 2661 | 2805 |
| 2806 // Some directories are above roots, so we instead show the first root. |
| 2807 // There may be request to change directory above the roots. For example, |
| 2808 // when usb-dirve is removed, we try to change to the parent directory, |
| 2809 // which is REMOVABLE_DIRECTORY. |
| 2810 if (!dirEntry || dirEntry.fullPath == '/' || |
| 2811 dirEntry.fullPath == REMOVABLE_DIRECTORY || |
| 2812 dirEntry.fullPath == ARCHIVE_DIRECTORY) { |
| 2813 dirEntry = this.rootEntries_[0] || dirEntry; |
| 2814 } |
| 2815 |
| 2662 var action = opt_action || | 2816 var action = opt_action || |
| 2663 (this.dialogType_ == FileManager.DialogType.SELECT_SAVEAS_FILE ? | 2817 (this.dialogType_ == FileManager.DialogType.SELECT_SAVEAS_FILE ? |
| 2664 undefined : this.selectIndex.bind(this, 0)); | 2818 undefined : this.selectIndex.bind(this, 0)); |
| 2665 | 2819 |
| 2666 var location = document.location.origin + document.location.pathname + '#' + | 2820 var location = document.location.origin + document.location.pathname + '#' + |
| 2667 encodeURI(dirEntry.fullPath); | 2821 encodeURI(dirEntry.fullPath); |
| 2668 if (opt_saveHistory) { | 2822 if (opt_saveHistory) { |
| 2669 history.pushState(undefined, dirEntry.fullPath, location); | 2823 history.pushState(undefined, dirEntry.fullPath, location); |
| 2670 } else if (window.location.hash != location) { | 2824 } else if (window.location.hash != location) { |
| 2671 // If the user typed URL manually that is not canonical it would be fixed | 2825 // 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... |
| 2886 // then the default action of this click event fires and toggles the | 3040 // then the default action of this click event fires and toggles the |
| 2887 // checkbox back off. | 3041 // checkbox back off. |
| 2888 // | 3042 // |
| 2889 // Since we're going to force checkboxes into the correct state for any | 3043 // Since we're going to force checkboxes into the correct state for any |
| 2890 // multi-selection, we can prevent this shift click from toggling the | 3044 // multi-selection, we can prevent this shift click from toggling the |
| 2891 // checkbox and avoid the trouble. | 3045 // checkbox and avoid the trouble. |
| 2892 event.preventDefault(); | 3046 event.preventDefault(); |
| 2893 } | 3047 } |
| 2894 }; | 3048 }; |
| 2895 | 3049 |
| 3050 FileManager.prototype.onRootsSelectionChanged_ = function(event) { |
| 3051 var root = this.rootEntries_[this.rootsList_.selectionModel.selectedIndex]; |
| 3052 if (!this.currentDirEntry_ || |
| 3053 !isParentPath(root.fullPath, this.currentDirEntry_.fullPath)) |
| 3054 this.changeDirectoryEntry(root); |
| 3055 }; |
| 3056 |
| 3057 FileManager.prototype.selectDefaultPathInFilenameInput_ = function() { |
| 3058 var input = this.filenameInput_; |
| 3059 input.focus(); |
| 3060 var selectionEnd = input.value.lastIndexOf('.'); |
| 3061 if (selectionEnd == -1) { |
| 3062 input.select(); |
| 3063 } else { |
| 3064 input.selectionStart = 0; |
| 3065 input.selectionEnd = selectionEnd; |
| 3066 } |
| 3067 // Clear, so we never do this again. |
| 3068 this.params_.defaultPath = ''; |
| 3069 }; |
| 3070 |
| 2896 /** | 3071 /** |
| 2897 * Update the UI when the selection model changes. | 3072 * Update the UI when the selection model changes. |
| 2898 * | 3073 * |
| 2899 * @param {cr.Event} event The change event. | 3074 * @param {cr.Event} event The change event. |
| 2900 */ | 3075 */ |
| 2901 FileManager.prototype.onSelectionChanged_ = function(event) { | 3076 FileManager.prototype.onSelectionChanged_ = function(event) { |
| 2902 this.summarizeSelection_(); | 3077 this.summarizeSelection_(); |
| 2903 | 3078 |
| 2904 if (this.dialogType_ == FileManager.DialogType.SELECT_SAVEAS_FILE) { | 3079 if (this.dialogType_ == FileManager.DialogType.SELECT_SAVEAS_FILE) { |
| 2905 // If this is a save-as dialog, copy the selected file into the filename | 3080 // If this is a save-as dialog, copy the selected file into the filename |
| 2906 // input text box. | 3081 // input text box. |
| 2907 | 3082 |
| 2908 if (this.selection && | 3083 if (this.selection && |
| 2909 this.selection.totalCount == 1 && | 3084 this.selection.totalCount == 1 && |
| 2910 this.selection.entries[0].isFile) | 3085 this.selection.entries[0].isFile && |
| 3086 this.filenameInput_.value != this.selection.entries[0].name) { |
| 2911 this.filenameInput_.value = this.selection.entries[0].name; | 3087 this.filenameInput_.value = this.selection.entries[0].name; |
| 3088 if (this.params_.defaultPath == this.selection.entries[0].fullPath) |
| 3089 this.selectDefaultPathInFilenameInput_(); |
| 3090 } |
| 2912 } | 3091 } |
| 2913 | 3092 |
| 2914 this.updateOkButton_(); | 3093 this.updateOkButton_(); |
| 2915 | 3094 |
| 2916 var self = this; | 3095 var self = this; |
| 2917 setTimeout(function() { self.onSelectionChangeComplete_(event) }, 0); | 3096 setTimeout(function() { self.onSelectionChangeComplete_(event) }, 0); |
| 2918 }; | 3097 }; |
| 2919 | 3098 |
| 2920 /** | 3099 /** |
| 2921 * Handle selection change related tasks that won't run properly during | 3100 * Handle selection change related tasks that won't run properly during |
| (...skipping 132 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 3054 * Update the UI when the current directory changes. | 3233 * Update the UI when the current directory changes. |
| 3055 * | 3234 * |
| 3056 * @param {cr.Event} event The directory-changed event. | 3235 * @param {cr.Event} event The directory-changed event. |
| 3057 */ | 3236 */ |
| 3058 FileManager.prototype.onDirectoryChanged_ = function(event) { | 3237 FileManager.prototype.onDirectoryChanged_ = function(event) { |
| 3059 this.updateCommands_(); | 3238 this.updateCommands_(); |
| 3060 this.updateOkButton_(); | 3239 this.updateOkButton_(); |
| 3061 | 3240 |
| 3062 this.checkFreeSpace_(this.currentDirEntry_.fullPath); | 3241 this.checkFreeSpace_(this.currentDirEntry_.fullPath); |
| 3063 | 3242 |
| 3064 // New folder should never be enabled in the root or media/ directories. | 3243 // TODO(dgozman): title may be better than this. |
| 3065 this.newFolderButton_.disabled = isSystemDirEntry(this.currentDirEntry_); | |
| 3066 | |
| 3067 this.document_.title = this.currentDirEntry_.fullPath; | 3244 this.document_.title = this.currentDirEntry_.fullPath; |
| 3068 | 3245 |
| 3069 var self = this; | 3246 var self = this; |
| 3070 | 3247 |
| 3071 if (this.subscribedOnDirectoryChanges_) { | 3248 if (this.subscribedOnDirectoryChanges_) { |
| 3072 chrome.fileBrowserPrivate.removeFileWatch(event.previousDirEntry.toURL(), | 3249 chrome.fileBrowserPrivate.removeFileWatch(event.previousDirEntry.toURL(), |
| 3073 function(result) { | 3250 function(result) { |
| 3074 if (!result) { | 3251 if (!result) { |
| 3075 console.log('Failed to remove file watch'); | 3252 console.log('Failed to remove file watch'); |
| 3076 } | 3253 } |
| (...skipping 73 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 3150 // Updated when a user clicks on the label of a file, used to detect | 3327 // Updated when a user clicks on the label of a file, used to detect |
| 3151 // when a click is eligible to trigger a rename. Can be null, or | 3328 // when a click is eligible to trigger a rename. Can be null, or |
| 3152 // an object with 'path' and 'date' properties. | 3329 // an object with 'path' and 'date' properties. |
| 3153 this.lastLabelClick_ = null; | 3330 this.lastLabelClick_ = null; |
| 3154 | 3331 |
| 3155 // Clear the table first. | 3332 // Clear the table first. |
| 3156 this.dataModel_.splice(0, this.dataModel_.length); | 3333 this.dataModel_.splice(0, this.dataModel_.length); |
| 3157 this.currentList_.selectionModel.clear(); | 3334 this.currentList_.selectionModel.clear(); |
| 3158 | 3335 |
| 3159 this.updateBreadcrumbs_(); | 3336 this.updateBreadcrumbs_(); |
| 3337 this.updateRootsListSelection_(); |
| 3160 | 3338 |
| 3161 if (this.currentDirEntry_.fullPath != '/') { | 3339 // Add current request to pending result list |
| 3162 // Add current request to pending result list | 3340 this.pendingRescanQueue_.push({ |
| 3163 this.pendingRescanQueue_.push({ | 3341 onSuccess:opt_callback, |
| 3164 onSuccess:opt_callback, | 3342 onError:opt_onError |
| 3165 onError:opt_onError | 3343 }); |
| 3166 }); | |
| 3167 | 3344 |
| 3168 if (this.rescanRunning_) | 3345 if (this.rescanRunning_) |
| 3169 return; | 3346 return; |
| 3170 | 3347 |
| 3171 this.rescanRunning_ = true; | 3348 this.rescanRunning_ = true; |
| 3172 | 3349 |
| 3173 // The current list of callbacks is saved and reset. Subsequent | 3350 // The current list of callbacks is saved and reset. Subsequent |
| 3174 // calls to rescanDirectory_ while we're still pending will be | 3351 // calls to rescanDirectory_ while we're still pending will be |
| 3175 // saved and will cause an additional rescan to happen after a delay. | 3352 // saved and will cause an additional rescan to happen after a delay. |
| 3176 var callbacks = this.pendingRescanQueue_; | 3353 var callbacks = this.pendingRescanQueue_; |
| 3177 | 3354 |
| 3178 this.pendingRescanQueue_ = []; | 3355 this.pendingRescanQueue_ = []; |
| 3179 | 3356 |
| 3180 var self = this; | 3357 var self = this; |
| 3181 var reader; | 3358 var reader; |
| 3182 | 3359 |
| 3183 function onError() { | 3360 function onError() { |
| 3361 if (self.pendingRescanQueue_.length > 0) { |
| 3362 setTimeout(self.rescanDirectory_.bind(self), |
| 3363 SIMULTANEOUS_RESCAN_INTERVAL); |
| 3364 } |
| 3365 |
| 3366 self.rescanRunning_ = false; |
| 3367 |
| 3368 for (var i= 0; i < callbacks.length; i++) { |
| 3369 if (callbacks[i].onError) |
| 3370 try { |
| 3371 callbacks[i].onError(); |
| 3372 } catch (ex) { |
| 3373 console.error('Caught exception while notifying about error: ' + |
| 3374 name, ex); |
| 3375 } |
| 3376 } |
| 3377 } |
| 3378 |
| 3379 function onReadSome(entries) { |
| 3380 if (entries.length == 0) { |
| 3381 metrics.recordInterval('DirectoryScan'); |
| 3382 if (self.currentDirEntry_.fullPath == DOWNLOADS_DIRECTORY) { |
| 3383 metrics.recordMediumCount("DownloadsCount", self.dataModel_.length); |
| 3384 } |
| 3385 |
| 3184 if (self.pendingRescanQueue_.length > 0) { | 3386 if (self.pendingRescanQueue_.length > 0) { |
| 3185 setTimeout(self.rescanDirectory_.bind(self), | 3387 setTimeout(self.rescanDirectory_.bind(self), |
| 3186 SIMULTANEOUS_RESCAN_INTERVAL); | 3388 SIMULTANEOUS_RESCAN_INTERVAL); |
| 3187 } | 3389 } |
| 3188 | 3390 |
| 3189 self.rescanRunning_ = false; | 3391 self.rescanRunning_ = false; |
| 3190 | |
| 3191 for (var i= 0; i < callbacks.length; i++) { | 3392 for (var i= 0; i < callbacks.length; i++) { |
| 3192 if (callbacks[i].onError) | 3393 if (callbacks[i].onSuccess) |
| 3193 try { | 3394 try { |
| 3194 callbacks[i].onError(); | 3395 callbacks[i].onSuccess(); |
| 3195 } catch (ex) { | 3396 } catch (ex) { |
| 3196 console.error('Caught exception while notifying about error: ' + | 3397 console.error('Caught exception while notifying about error: ' + |
| 3197 name, ex); | 3398 name, ex); |
| 3198 } | 3399 } |
| 3199 } | 3400 } |
| 3401 |
| 3402 return; |
| 3200 } | 3403 } |
| 3201 | 3404 |
| 3202 function onReadSome(entries) { | 3405 // Splice takes the to-be-spliced-in array as individual parameters, |
| 3203 if (entries.length == 0) { | 3406 // rather than as an array, so we need to perform some acrobatics... |
| 3204 metrics.recordInterval('DirectoryScan'); | 3407 var spliceArgs = [].slice.call(entries); |
| 3205 if (self.currentDirEntry_.fullPath == DOWNLOADS_DIRECTORY) { | |
| 3206 metrics.recordMediumCount("DownloadsCount", self.dataModel_.length); | |
| 3207 } | |
| 3208 | 3408 |
| 3209 if (self.pendingRescanQueue_.length > 0) { | 3409 // Hide files that start with a dot ('.'). |
| 3210 setTimeout(self.rescanDirectory_.bind(self), | 3410 // TODO(rginda): User should be able to override this. Support for other |
| 3211 SIMULTANEOUS_RESCAN_INTERVAL); | 3411 // commonly hidden patterns might be nice too. |
| 3212 } | 3412 if (self.filterFiles_) { |
| 3413 spliceArgs = spliceArgs.filter(function(e) { |
| 3414 return e.name.substr(0, 1) != '.'; |
| 3415 }); |
| 3416 } |
| 3213 | 3417 |
| 3214 self.rescanRunning_ = false; | 3418 self.prefetchCacheForSorting_(spliceArgs, function() { |
| 3215 for (var i= 0; i < callbacks.length; i++) { | 3419 spliceArgs.unshift(0, 0); // index, deleteCount |
| 3216 if (callbacks[i].onSuccess) | 3420 self.dataModel_.splice.apply(self.dataModel_, spliceArgs); |
| 3217 try { | |
| 3218 callbacks[i].onSuccess(); | |
| 3219 } catch (ex) { | |
| 3220 console.error('Caught exception while notifying about error: ' + | |
| 3221 name, ex); | |
| 3222 } | |
| 3223 } | |
| 3224 | 3421 |
| 3225 return; | 3422 // Keep reading until entries.length is 0. |
| 3226 } | 3423 reader.readEntries(onReadSome, onError); |
| 3424 }); |
| 3425 }; |
| 3227 | 3426 |
| 3228 // Splice takes the to-be-spliced-in array as individual parameters, | 3427 metrics.startInterval('DirectoryScan'); |
| 3229 // rather than as an array, so we need to perform some acrobatics... | |
| 3230 var spliceArgs = [].slice.call(entries); | |
| 3231 | 3428 |
| 3232 // Hide files that start with a dot ('.'). | 3429 // If not the root directory, just read the contents. |
| 3233 // TODO(rginda): User should be able to override this. Support for other | 3430 reader = this.currentDirEntry_.createReader(); |
| 3234 // commonly hidden patterns might be nice too. | 3431 reader.readEntries(onReadSome, onError); |
| 3235 if (self.filterFiles_) { | |
| 3236 spliceArgs = spliceArgs.filter(function(e) { | |
| 3237 return e.name.substr(0, 1) != '.'; | |
| 3238 }); | |
| 3239 } | |
| 3240 | |
| 3241 self.prefetchCacheForSorting_(spliceArgs, function() { | |
| 3242 spliceArgs.unshift(0, 0); // index, deleteCount | |
| 3243 self.dataModel_.splice.apply(self.dataModel_, spliceArgs); | |
| 3244 | |
| 3245 // Keep reading until entries.length is 0. | |
| 3246 reader.readEntries(onReadSome, onError); | |
| 3247 }); | |
| 3248 }; | |
| 3249 | |
| 3250 metrics.startInterval('DirectoryScan'); | |
| 3251 | |
| 3252 // If not the root directory, just read the contents. | |
| 3253 reader = this.currentDirEntry_.createReader(); | |
| 3254 reader.readEntries(onReadSome, onError); | |
| 3255 return; | |
| 3256 } | |
| 3257 | |
| 3258 // Otherwise, use the provided list of root subdirectories, since the | |
| 3259 // real local filesystem root directory (the one we use outside the | |
| 3260 // harness) can't be enumerated yet. | |
| 3261 var spliceArgs = [].slice.call(this.rootEntries_); | |
| 3262 spliceArgs.unshift(0, 0); // index, deleteCount | |
| 3263 this.dataModel_.splice.apply(this.dataModel_, spliceArgs); | |
| 3264 | |
| 3265 if (opt_callback) | |
| 3266 opt_callback(); | |
| 3267 }; | 3432 }; |
| 3268 | 3433 |
| 3269 FileManager.prototype.prefetchCacheForSorting_ = function(entries, callback) { | 3434 FileManager.prototype.prefetchCacheForSorting_ = function(entries, callback) { |
| 3270 var field = this.dataModel_.sortStatus.field; | 3435 var field = this.dataModel_.sortStatus.field; |
| 3271 if (field) { | 3436 if (field) { |
| 3272 this.prepareSortEntries_(entries, field, callback); | 3437 this.prepareSortEntries_(entries, field, callback); |
| 3273 } else { | 3438 } else { |
| 3274 callback(); | 3439 callback(); |
| 3275 return; | 3440 return; |
| 3276 } | 3441 } |
| (...skipping 216 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 3493 var selectionEnd = input.value.lastIndexOf('.'); | 3658 var selectionEnd = input.value.lastIndexOf('.'); |
| 3494 if (selectionEnd == -1) { | 3659 if (selectionEnd == -1) { |
| 3495 input.select(); | 3660 input.select(); |
| 3496 } else { | 3661 } else { |
| 3497 input.selectionStart = 0; | 3662 input.selectionStart = 0; |
| 3498 input.selectionEnd = selectionEnd; | 3663 input.selectionEnd = selectionEnd; |
| 3499 } | 3664 } |
| 3500 }, 0); | 3665 }, 0); |
| 3501 }; | 3666 }; |
| 3502 | 3667 |
| 3503 FileManager.prototype.onNewFolderButtonClick_ = function(event) { | 3668 FileManager.prototype.onToggleSidebar_ = function(event) { |
| 3669 if (this.dialogContainer_.hasAttribute('sidebar')) { |
| 3670 this.dialogContainer_.removeAttribute('sidebar'); |
| 3671 } else { |
| 3672 this.dialogContainer_.setAttribute('sidebar', 'sidebar'); |
| 3673 } |
| 3674 // TODO(dgozman): make table header css-resizable. |
| 3675 setTimeout(this.onResize_.bind(this), 300); |
| 3676 }; |
| 3677 |
| 3678 FileManager.prototype.onNewFolderCommand_ = function(event) { |
| 3504 var self = this; | 3679 var self = this; |
| 3505 | 3680 |
| 3506 function onNameSelected(name) { | 3681 function onNameSelected(name) { |
| 3507 var valid = self.validateFileName_(name, function() { | 3682 var valid = self.validateFileName_(name, function() { |
| 3508 promptForName(name); | 3683 promptForName(name); |
| 3509 }); | 3684 }); |
| 3510 | 3685 |
| 3511 if (!valid) { | 3686 if (!valid) { |
| 3512 // Validation failed. User will be prompted for a new name after they | 3687 // Validation failed. User will be prompted for a new name after they |
| 3513 // dismiss the validation error dialog. | 3688 // dismiss the validation error dialog. |
| 3514 return; | 3689 return; |
| 3515 } | 3690 } |
| 3516 | 3691 |
| 3517 self.createNewFolder(name); | 3692 self.createNewFolder(name); |
| 3518 } | 3693 } |
| 3519 | 3694 |
| 3520 function promptForName(suggestedName) { | 3695 function promptForName(suggestedName) { |
| 3521 self.prompt.show(str('NEW_FOLDER_PROMPT'), suggestedName, onNameSelected); | 3696 self.prompt.show(str('NEW_FOLDER_PROMPT'), suggestedName, onNameSelected); |
| 3522 } | 3697 } |
| 3523 | 3698 |
| 3524 promptForName(str('DEFAULT_NEW_FOLDER_NAME')); | 3699 promptForName(str('DEFAULT_NEW_FOLDER_NAME')); |
| 3525 }; | 3700 }; |
| 3526 | 3701 |
| 3527 FileManager.prototype.createNewFolder = function(name, opt_callback) { | 3702 FileManager.prototype.createNewFolder = function(name, opt_callback) { |
| 3528 metrics.recordAction('CreateNewFolder'); | 3703 metrics.recordUserAction('CreateNewFolder'); |
| 3529 | 3704 |
| 3530 var self = this; | 3705 var self = this; |
| 3531 | 3706 |
| 3532 function onSuccess(dirEntry) { | 3707 function onSuccess(dirEntry) { |
| 3533 self.rescanDirectory_(function() { | 3708 self.rescanDirectory_(function() { |
| 3534 self.selectEntry(name); | 3709 self.selectEntry(name); |
| 3535 if (opt_callback) | 3710 if (opt_callback) |
| 3536 opt_callback(); | 3711 opt_callback(); |
| 3537 }); | 3712 }); |
| 3538 } | 3713 } |
| (...skipping 83 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 3622 this.dialogType_ != FileManager.SELECT_FOLDER) { | 3797 this.dialogType_ != FileManager.SELECT_FOLDER) { |
| 3623 event.preventDefault(); | 3798 event.preventDefault(); |
| 3624 this.onDirectoryAction(this.selection.entries[0]); | 3799 this.onDirectoryAction(this.selection.entries[0]); |
| 3625 } else if (!this.okButton_.disabled) { | 3800 } else if (!this.okButton_.disabled) { |
| 3626 event.preventDefault(); | 3801 event.preventDefault(); |
| 3627 this.onOk_(); | 3802 this.onOk_(); |
| 3628 } | 3803 } |
| 3629 break; | 3804 break; |
| 3630 | 3805 |
| 3631 case 32: // Ctrl-Space => New Folder. | 3806 case 32: // Ctrl-Space => New Folder. |
| 3632 if (this.newFolderButton_.style.display != 'none' && event.ctrlKey) { | 3807 if ((this.dialogType_ == 'saveas-file' || |
| 3808 this.dialogType_ == 'full-page') && event.ctrlKey) { |
| 3633 event.preventDefault(); | 3809 event.preventDefault(); |
| 3634 this.onNewFolderButtonClick_(); | 3810 this.onNewFolderCommand_(); |
| 3635 } | 3811 } |
| 3636 break; | 3812 break; |
| 3637 | 3813 |
| 3638 case 88: // Ctrl-X => Cut. | 3814 case 88: // Ctrl-X => Cut. |
| 3639 this.updateCommands_(); | 3815 this.updateCommands_(); |
| 3640 if (!this.commands_['cut'].disabled) { | 3816 if (!this.commands_['cut'].disabled) { |
| 3641 event.preventDefault(); | 3817 event.preventDefault(); |
| 3642 this.commands_['cut'].execute(); | 3818 this.commands_['cut'].execute(); |
| 3643 } | 3819 } |
| 3644 break; | 3820 break; |
| (...skipping 332 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 3977 }); | 4153 }); |
| 3978 }, onError); | 4154 }, onError); |
| 3979 | 4155 |
| 3980 function onError(err) { | 4156 function onError(err) { |
| 3981 console.log('Error while checking free space: ' + err); | 4157 console.log('Error while checking free space: ' + err); |
| 3982 setTimeout(doCheck, 1000 * 60); | 4158 setTimeout(doCheck, 1000 * 60); |
| 3983 } | 4159 } |
| 3984 } | 4160 } |
| 3985 } | 4161 } |
| 3986 })(); | 4162 })(); |
| OLD | NEW |