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