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.resolveRoots_(); | 66 this.initFileSystem_(); |
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 387 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
464 chrome.fileBrowserPrivate.getStrings(function(strings) { | 464 chrome.fileBrowserPrivate.getStrings(function(strings) { |
465 localStrings = new LocalStrings(strings); | 465 localStrings = new LocalStrings(strings); |
466 if (callback) | 466 if (callback) |
467 callback(); | 467 callback(); |
468 }); | 468 }); |
469 }; | 469 }; |
470 | 470 |
471 // Instance methods. | 471 // Instance methods. |
472 | 472 |
473 /** | 473 /** |
474 * Request file system and get root entries asynchronously. Invokes init_ | 474 * Request local file system, resolve roots and init_ after that. |
475 * when have finished. | 475 * @private |
476 */ | 476 */ |
477 FileManager.prototype.resolveRoots_ = function(callback) { | 477 FileManager.prototype.initFileSystem_ = function() { |
478 var rootPaths = ['Downloads', 'removable', 'archive']; | 478 util.installFileErrorToString(); |
| 479 metrics.startInterval('Load.FileSystem'); |
479 | 480 |
480 metrics.startInterval('Load.FileSystem'); | |
481 var self = this; | 481 var self = this; |
482 | 482 |
483 // The list of active mount points to distinct them from other directories. | 483 // The list of active mount points to distinct them from other directories. |
484 chrome.fileBrowserPrivate.getMountPoints(function(mountPoints) { | 484 chrome.fileBrowserPrivate.getMountPoints(function(mountPoints) { |
485 self.mountPoints_ = mountPoints; | 485 self.mountPoints_ = mountPoints; |
486 onDone(); | 486 onDone(); |
487 }); | 487 }); |
488 | 488 |
489 function onDone() { | 489 function onDone() { |
490 if (self.mountPoints_ && self.rootEntries_) | 490 if (self.mountPoints_ && self.rootEntries_) |
491 self.init_(); | 491 self.init_(); |
492 } | 492 } |
493 | 493 |
494 chrome.fileBrowserPrivate.requestLocalFileSystem(function(filesystem) { | 494 chrome.fileBrowserPrivate.requestLocalFileSystem(function(filesystem) { |
495 self.filesystem_ = filesystem; | |
496 util.installFileErrorToString(); | |
497 | |
498 metrics.recordTime('Load.FileSystem'); | 495 metrics.recordTime('Load.FileSystem'); |
499 | 496 |
500 var rootEntries = []; | 497 self.filesystem_ = filesystem; |
501 | 498 self.resolveRoots_(function(rootEntries) { |
502 function onAllRootsFound() { | |
503 metrics.recordTime('Load.Roots'); | |
504 self.rootEntries_ = rootEntries; | 499 self.rootEntries_ = rootEntries; |
505 onDone(); | 500 onDone(); |
506 } | 501 }); |
507 | |
508 function onPathError(path, err) { | |
509 console.error('Error locating root path: ' + path + ': ' + err); | |
510 } | |
511 | |
512 function onEntryFound(entry) { | |
513 if (entry) { | |
514 rootEntries.push(entry); | |
515 } else { | |
516 onAllRootsFound(); | |
517 } | |
518 } | |
519 | |
520 metrics.startInterval('Load.Roots'); | |
521 if (filesystem.name.match(/^chrome-extension_\S+:external/i)) { | |
522 // We've been handed the local filesystem, whose root directory | |
523 // cannot be enumerated. | |
524 util.getDirectories(filesystem.root, {create: false}, rootPaths, | |
525 onEntryFound, onPathError); | |
526 } else { | |
527 util.forEachDirEntry(filesystem.root, onEntryFound); | |
528 } | |
529 }); | 502 }); |
530 }; | 503 }; |
531 | 504 |
532 /** | 505 /** |
| 506 * Get root entries asynchronously. Invokes callback |
| 507 * when have finished. |
| 508 */ |
| 509 FileManager.prototype.resolveRoots_ = function(callback) { |
| 510 var rootPaths = [DOWNLOADS_DIRECTORY, ARCHIVE_DIRECTORY, |
| 511 REMOVABLE_DIRECTORY].map(function(s) { return s.substring(1); }); |
| 512 var rootEntries = []; |
| 513 |
| 514 // The number of entries left to enumerate to get all roots. |
| 515 // When equals to zero, we are done. |
| 516 var entriesToEnumerate = 0; |
| 517 // Entries may be enumerated faster than next one appears, so we have this |
| 518 // guard to not finish too early. |
| 519 var allEntriesFound = false; |
| 520 |
| 521 function onPathError(path, err) { |
| 522 console.error('Error locating root path: ' + path + ': ' + err); |
| 523 } |
| 524 |
| 525 function onRootFound(root) { |
| 526 if (root) { |
| 527 rootEntries.push(root); |
| 528 } else { |
| 529 entriesToEnumerate--; |
| 530 if (entriesToEnumerate == 0 && allEntriesFound) { |
| 531 metrics.recordTime('Load.Roots'); |
| 532 callback(rootEntries); |
| 533 } |
| 534 } |
| 535 } |
| 536 |
| 537 function onEntryFound(entry) { |
| 538 if (entry) { |
| 539 entriesToEnumerate++; |
| 540 var path = entry.fullPath; |
| 541 if (path == ARCHIVE_DIRECTORY || path == REMOVABLE_DIRECTORY) { |
| 542 // All removable devices and mounted archives are considered |
| 543 // roots, and are shown in the sidebar. |
| 544 util.forEachDirEntry(entry, onRootFound); |
| 545 } else { |
| 546 onRootFound(entry); |
| 547 onRootFound(null); |
| 548 } |
| 549 } else { |
| 550 allEntriesFound = true; |
| 551 } |
| 552 } |
| 553 |
| 554 metrics.startInterval('Load.Roots'); |
| 555 if (this.filesystem_.name.match(/^chrome-extension_\S+:external/i)) { |
| 556 // We've been handed the local filesystem, whose root directory |
| 557 // cannot be enumerated. |
| 558 util.getDirectories(this.filesystem_.root, {create: false}, rootPaths, |
| 559 onEntryFound, onPathError); |
| 560 } else { |
| 561 util.forEachDirEntry(this.filesystem_.root, onEntryFound); |
| 562 } |
| 563 }; |
| 564 |
| 565 /** |
533 * Continue initializing the file manager after resolving roots. | 566 * Continue initializing the file manager after resolving roots. |
534 */ | 567 */ |
535 FileManager.prototype.init_ = function() { | 568 FileManager.prototype.init_ = function() { |
536 metrics.startInterval('Load.DOM'); | 569 metrics.startInterval('Load.DOM'); |
| 570 this.initCommands_(); |
537 | 571 |
538 // TODO(rginda): 6/22/11: Remove this test when createDateTimeFormat is | 572 // TODO(rginda): 6/22/11: Remove this test when createDateTimeFormat is |
539 // available in all chrome trunk builds. | 573 // available in all chrome trunk builds. |
540 if ('createDateTimeFormat' in this.locale_) { | 574 if ('createDateTimeFormat' in this.locale_) { |
541 this.shortDateFormatter_ = | 575 this.shortDateFormatter_ = |
542 this.locale_.createDateTimeFormat({'dateType': 'medium'}); | 576 this.locale_.createDateTimeFormat({'dateType': 'medium'}); |
543 } else { | 577 } else { |
544 this.shortDateFormatter_ = { | 578 this.shortDateFormatter_ = { |
545 format: function(d) { | 579 format: function(d) { |
546 return (d.getMonth() + 1) + '/' + d.getDate() + '/' + d.getFullYear(); | 580 return (d.getMonth() + 1) + '/' + d.getDate() + '/' + d.getFullYear(); |
(...skipping 37 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
584 window.addEventListener('unload', this.onUnload_.bind(this)); | 618 window.addEventListener('unload', this.onUnload_.bind(this)); |
585 | 619 |
586 this.addEventListener('directory-changed', | 620 this.addEventListener('directory-changed', |
587 this.onDirectoryChanged_.bind(this)); | 621 this.onDirectoryChanged_.bind(this)); |
588 this.addEventListener('selection-summarized', | 622 this.addEventListener('selection-summarized', |
589 this.onSelectionSummarized_.bind(this)); | 623 this.onSelectionSummarized_.bind(this)); |
590 | 624 |
591 // The list of archives requested to mount. We will show contents once | 625 // The list of archives requested to mount. We will show contents once |
592 // archive is mounted, but only for mounts from within this filebrowser tab. | 626 // archive is mounted, but only for mounts from within this filebrowser tab. |
593 this.mountRequests_ = []; | 627 this.mountRequests_ = []; |
| 628 this.unmountRequests_ = []; |
594 chrome.fileBrowserPrivate.onMountCompleted.addListener( | 629 chrome.fileBrowserPrivate.onMountCompleted.addListener( |
595 this.onMountCompleted_.bind(this)); | 630 this.onMountCompleted_.bind(this)); |
596 | 631 |
597 chrome.fileBrowserPrivate.onFileChanged.addListener( | 632 chrome.fileBrowserPrivate.onFileChanged.addListener( |
598 this.onFileChanged_.bind(this)); | 633 this.onFileChanged_.bind(this)); |
599 | 634 |
600 var self = this; | 635 var self = this; |
601 | 636 |
602 // The list of callbacks to be invoked during the directory rescan after | 637 // The list of callbacks to be invoked during the directory rescan after |
603 // all paste tasks are complete. | 638 // all paste tasks are complete. |
604 this.pasteSuccessCallbacks_ = []; | 639 this.pasteSuccessCallbacks_ = []; |
605 | 640 |
606 this.initCommands_(); | |
607 | |
608 this.setupCurrentDirectory_(); | 641 this.setupCurrentDirectory_(); |
609 | 642 |
610 this.summarizeSelection_(); | 643 this.summarizeSelection_(); |
611 | 644 |
612 this.dataModel_.sort('cachedMtime_', 'desc'); | 645 this.dataModel_.sort('cachedMtime_', 'desc'); |
613 | 646 |
614 this.refocus(); | 647 this.refocus(); |
615 | 648 |
616 this.createMetadataProvider_(); | 649 this.createMetadataProvider_(); |
617 metrics.recordTime('Load.DOM'); | 650 metrics.recordTime('Load.DOM'); |
(...skipping 38 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
656 // Cache nodes we'll be manipulating. | 689 // Cache nodes we'll be manipulating. |
657 this.previewThumbnails_ = | 690 this.previewThumbnails_ = |
658 this.dialogDom_.querySelector('.preview-thumbnails'); | 691 this.dialogDom_.querySelector('.preview-thumbnails'); |
659 this.previewPanel_ = this.dialogDom_.querySelector('.preview-panel'); | 692 this.previewPanel_ = this.dialogDom_.querySelector('.preview-panel'); |
660 this.previewFilename_ = this.dialogDom_.querySelector('.preview-filename'); | 693 this.previewFilename_ = this.dialogDom_.querySelector('.preview-filename'); |
661 this.previewSummary_ = this.dialogDom_.querySelector('.preview-summary'); | 694 this.previewSummary_ = this.dialogDom_.querySelector('.preview-summary'); |
662 this.filenameInput_ = this.dialogDom_.querySelector('.filename-input'); | 695 this.filenameInput_ = this.dialogDom_.querySelector('.filename-input'); |
663 this.taskButtons_ = this.dialogDom_.querySelector('.task-buttons'); | 696 this.taskButtons_ = this.dialogDom_.querySelector('.task-buttons'); |
664 this.okButton_ = this.dialogDom_.querySelector('.ok'); | 697 this.okButton_ = this.dialogDom_.querySelector('.ok'); |
665 this.cancelButton_ = this.dialogDom_.querySelector('.cancel'); | 698 this.cancelButton_ = this.dialogDom_.querySelector('.cancel'); |
666 this.newFolderButton_ = this.dialogDom_.querySelector('.new-folder'); | |
667 this.deleteButton_ = this.dialogDom_.querySelector('.delete-button'); | 699 this.deleteButton_ = this.dialogDom_.querySelector('.delete-button'); |
668 | 700 |
669 this.downloadsWarning_ = | 701 this.downloadsWarning_ = |
670 this.dialogDom_.querySelector('.downloads-warning'); | 702 this.dialogDom_.querySelector('.downloads-warning'); |
671 var html = util.htmlUnescape(str('DOWNLOADS_DIRECTORY_WARNING')); | 703 var html = util.htmlUnescape(str('DOWNLOADS_DIRECTORY_WARNING')); |
672 this.downloadsWarning_.lastElementChild.innerHTML = html; | 704 this.downloadsWarning_.lastElementChild.innerHTML = html; |
673 var link = this.downloadsWarning_.querySelector('a'); | 705 var link = this.downloadsWarning_.querySelector('a'); |
674 link.addEventListener('click', this.onDownloadsWarningClick_.bind(this)); | 706 link.addEventListener('click', this.onDownloadsWarningClick_.bind(this)); |
675 | 707 |
676 this.document_.addEventListener('keydown', this.onKeyDown_.bind(this)); | 708 this.document_.addEventListener('keydown', this.onKeyDown_.bind(this)); |
(...skipping 10 matching lines...) Expand all Loading... |
687 'keyup', this.onFilenameInputKeyUp_.bind(this)); | 719 'keyup', this.onFilenameInputKeyUp_.bind(this)); |
688 this.filenameInput_.addEventListener( | 720 this.filenameInput_.addEventListener( |
689 'focus', this.onFilenameInputFocus_.bind(this)); | 721 'focus', this.onFilenameInputFocus_.bind(this)); |
690 | 722 |
691 var listContainer = this.dialogDom_.querySelector('.list-container'); | 723 var listContainer = this.dialogDom_.querySelector('.list-container'); |
692 listContainer.addEventListener('keydown', this.onListKeyDown_.bind(this)); | 724 listContainer.addEventListener('keydown', this.onListKeyDown_.bind(this)); |
693 listContainer.addEventListener('keypress', this.onListKeyPress_.bind(this)); | 725 listContainer.addEventListener('keypress', this.onListKeyPress_.bind(this)); |
694 this.okButton_.addEventListener('click', this.onOk_.bind(this)); | 726 this.okButton_.addEventListener('click', this.onOk_.bind(this)); |
695 this.cancelButton_.addEventListener('click', this.onCancel_.bind(this)); | 727 this.cancelButton_.addEventListener('click', this.onCancel_.bind(this)); |
696 | 728 |
697 this.dialogDom_.querySelector('button.new-folder').addEventListener( | 729 this.dialogDom_.querySelector('div.open-sidebar').addEventListener( |
698 'click', this.onNewFolderButtonClick_.bind(this)); | 730 'click', this.onToggleSidebar_.bind(this)); |
| 731 this.dialogDom_.querySelector('div.close-sidebar').addEventListener( |
| 732 'click', this.onToggleSidebar_.bind(this)); |
| 733 this.dialogContainer_ = this.dialogDom_.querySelector('.dialog-container'); |
699 | 734 |
700 this.dialogDom_.querySelector('button.detail-view').addEventListener( | 735 this.dialogDom_.querySelector('button.detail-view').addEventListener( |
701 'click', this.onDetailViewButtonClick_.bind(this)); | 736 'click', this.onDetailViewButtonClick_.bind(this)); |
702 this.dialogDom_.querySelector('button.thumbnail-view').addEventListener( | 737 this.dialogDom_.querySelector('button.thumbnail-view').addEventListener( |
703 'click', this.onThumbnailViewButtonClick_.bind(this)); | 738 'click', this.onThumbnailViewButtonClick_.bind(this)); |
704 | 739 |
705 this.dialogDom_.ownerDocument.defaultView.addEventListener( | 740 this.dialogDom_.ownerDocument.defaultView.addEventListener( |
706 'resize', this.onResize_.bind(this)); | 741 'resize', this.onResize_.bind(this)); |
707 | 742 |
708 var ary = this.dialogDom_.querySelectorAll('[visibleif]'); | 743 var ary = this.dialogDom_.querySelectorAll('[visibleif]'); |
(...skipping 32 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
741 if (this.dialogType_ == FileManager.DialogType.SELECT_OPEN_FILE || | 776 if (this.dialogType_ == FileManager.DialogType.SELECT_OPEN_FILE || |
742 this.dialogType_ == FileManager.DialogType.SELECT_OPEN_FOLDER || | 777 this.dialogType_ == FileManager.DialogType.SELECT_OPEN_FOLDER || |
743 this.dialogType_ == FileManager.DialogType.SELECT_SAVEAS_FILE) { | 778 this.dialogType_ == FileManager.DialogType.SELECT_SAVEAS_FILE) { |
744 this.selectionModelClass_ = cr.ui.ListSingleSelectionModel; | 779 this.selectionModelClass_ = cr.ui.ListSingleSelectionModel; |
745 } else { | 780 } else { |
746 this.selectionModelClass_ = cr.ui.ListSelectionModel; | 781 this.selectionModelClass_ = cr.ui.ListSelectionModel; |
747 } | 782 } |
748 | 783 |
749 this.initTable_(); | 784 this.initTable_(); |
750 this.initGrid_(); | 785 this.initGrid_(); |
| 786 this.initRootsList_(); |
751 | 787 |
752 this.setListType(FileManager.ListType.DETAIL); | 788 this.setListType(FileManager.ListType.DETAIL); |
753 | 789 |
754 this.onResize_(); | 790 this.onResize_(); |
755 | 791 |
756 this.textSearchState_ = {text: '', date: new Date()}; | 792 this.textSearchState_ = {text: '', date: new Date()}; |
757 }; | 793 }; |
758 | 794 |
| 795 FileManager.prototype.initRootsList_ = function() { |
| 796 this.rootsList_ = this.dialogDom_.querySelector('.roots-list'); |
| 797 cr.ui.List.decorate(this.rootsList_); |
| 798 |
| 799 var self = this; |
| 800 this.rootsList_.itemConstructor = function(entry) { |
| 801 return self.renderRoot_(entry); |
| 802 }; |
| 803 |
| 804 this.rootsList_.selectionModel = new cr.ui.ListSingleSelectionModel(); |
| 805 this.rootsList_.selectionModel.addEventListener( |
| 806 'change', this.onRootsSelectionChanged_.bind(this)); |
| 807 |
| 808 // TODO(dgozman): add "Add a drive" item. |
| 809 this.rootsList_.dataModel = new cr.ui.ArrayDataModel(this.rootEntries_); |
| 810 }; |
| 811 |
| 812 FileManager.prototype.updateRoots_ = function(opt_changeDirectoryTo) { |
| 813 var self = this; |
| 814 this.resolveRoots_(function(rootEntries) { |
| 815 self.rootEntries_ = rootEntries; |
| 816 |
| 817 var dataModel = self.rootsList_.dataModel; |
| 818 var args = [0, dataModel.length].concat(rootEntries); |
| 819 dataModel.splice.apply(dataModel, args); |
| 820 |
| 821 self.updateRootsListSelection_(); |
| 822 |
| 823 if (opt_changeDirectoryTo) |
| 824 self.changeDirectory(opt_changeDirectoryTo); |
| 825 }); |
| 826 }; |
| 827 |
759 /** | 828 /** |
760 * Get the icon type for a given Entry. | 829 * Get the icon type for a given Entry. |
761 * | 830 * |
762 * @param {Entry} entry An Entry subclass (FileEntry or DirectoryEntry). | 831 * @param {Entry} entry An Entry subclass (FileEntry or DirectoryEntry). |
763 * @return {string} | 832 * @return {string} |
764 */ | 833 */ |
765 FileManager.prototype.getIconType = function(entry) { | 834 FileManager.prototype.getIconType = function(entry) { |
766 if (!('cachedIconType_' in entry)) | 835 if (!('cachedIconType_' in entry)) |
767 entry.cachedIconType_ = this.computeIconType_(entry); | 836 entry.cachedIconType_ = this.computeIconType_(entry); |
768 return entry.cachedIconType_; | 837 return entry.cachedIconType_; |
769 }; | 838 }; |
770 | 839 |
771 /** | 840 /** |
772 * Extract extension from the file name and cat it to to lower case. | 841 * Extract extension from the file name and cat it to to lower case. |
773 * | 842 * |
774 * @param {string} name. | 843 * @param {string} name. |
775 * @return {strin} | 844 * @return {strin} |
776 */ | 845 */ |
777 function getFileExtension(name) { | 846 function getFileExtension(name) { |
778 var extIndex = name.lastIndexOf('.'); | 847 var extIndex = name.lastIndexOf('.'); |
779 if (extIndex < 0) | 848 if (extIndex < 0) |
780 return ''; | 849 return ''; |
781 | 850 |
782 return name.substr(extIndex + 1).toLowerCase(); | 851 return name.substr(extIndex + 1).toLowerCase(); |
783 } | 852 } |
784 | 853 |
785 FileManager.prototype.computeIconType_ = function(entry) { | 854 FileManager.prototype.computeIconType_ = function(entry) { |
| 855 // TODO(dgozman): refactor this to use proper icons in left panel, |
| 856 // and do not depend on mountPoints. |
786 var deviceNumber = this.getDeviceNumber(entry); | 857 var deviceNumber = this.getDeviceNumber(entry); |
787 if (deviceNumber != undefined) { | 858 if (deviceNumber != undefined) { |
788 if (this.mountPoints_[deviceNumber].mountCondition == '') | 859 if (this.mountPoints_[deviceNumber].mountCondition == '') |
789 return 'device'; | 860 return 'device'; |
790 var mountCondition = this.mountPoints_[deviceNumber].mountCondition; | 861 var mountCondition = this.mountPoints_[deviceNumber].mountCondition; |
791 if (mountCondition == 'unknown_filesystem' || | 862 if (mountCondition == 'unknown_filesystem' || |
792 mountCondition == 'unsupported_filesystem') | 863 mountCondition == 'unsupported_filesystem') |
793 return 'unreadable'; | 864 return 'unreadable'; |
794 } | 865 } |
795 if (entry.isDirectory) | 866 if (entry.isDirectory) |
(...skipping 272 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
1068 !isSystemDirEntry(this.currentDirEntry_)); | 1139 !isSystemDirEntry(this.currentDirEntry_)); |
1069 | 1140 |
1070 case 'delete': | 1141 case 'delete': |
1071 return (// Initialized to the point where we have a current directory | 1142 return (// Initialized to the point where we have a current directory |
1072 this.currentDirEntry_ && | 1143 this.currentDirEntry_ && |
1073 // Rename not in progress. | 1144 // Rename not in progress. |
1074 !this.renameInput_.currentEntry && | 1145 !this.renameInput_.currentEntry && |
1075 !isSystemDirEntry(this.currentDirEntry_)) && | 1146 !isSystemDirEntry(this.currentDirEntry_)) && |
1076 this.selection && | 1147 this.selection && |
1077 this.selection.totalCount > 0; | 1148 this.selection.totalCount > 0; |
| 1149 |
| 1150 case 'newfolder': |
| 1151 return this.currentDirEntry_ && |
| 1152 (this.dialogType_ == 'saveas-file' || |
| 1153 this.dialogType_ == 'full-page'); |
1078 } | 1154 } |
1079 }; | 1155 }; |
1080 | 1156 |
1081 FileManager.prototype.updateCommonActionButtons_ = function() { | 1157 FileManager.prototype.updateCommonActionButtons_ = function() { |
1082 if (this.deleteButton_) | 1158 if (this.deleteButton_) |
1083 this.deleteButton_.disabled = !this.canExecute_('delete'); | 1159 this.deleteButton_.disabled = !this.canExecute_('delete'); |
1084 }; | 1160 }; |
1085 | 1161 |
1086 FileManager.prototype.setListType = function(type) { | 1162 FileManager.prototype.setListType = function(type) { |
1087 if (type && type == this.listType_) | 1163 if (type && type == this.listType_) |
(...skipping 187 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
1275 leadIndex); | 1351 leadIndex); |
1276 return; | 1352 return; |
1277 } | 1353 } |
1278 | 1354 |
1279 this.initiateRename_(label); | 1355 this.initiateRename_(label); |
1280 return; | 1356 return; |
1281 | 1357 |
1282 case 'delete': | 1358 case 'delete': |
1283 this.deleteEntries(this.selection.entries); | 1359 this.deleteEntries(this.selection.entries); |
1284 return; | 1360 return; |
| 1361 |
| 1362 case 'newfolder': |
| 1363 this.onNewFolderCommand_(event); |
| 1364 return; |
1285 } | 1365 } |
1286 }; | 1366 }; |
1287 | 1367 |
1288 /** | 1368 /** |
1289 * Respond to the back and forward buttons. | 1369 * Respond to the back and forward buttons. |
1290 */ | 1370 */ |
1291 FileManager.prototype.onPopState_ = function(event) { | 1371 FileManager.prototype.onPopState_ = function(event) { |
1292 // TODO(serya): We should restore selected items here. | 1372 // TODO(serya): We should restore selected items here. |
1293 if (this.rootEntries_) | 1373 if (this.rootEntries_) |
1294 this.setupCurrentDirectory_(); | 1374 this.setupCurrentDirectory_(); |
1295 }; | 1375 }; |
1296 | 1376 |
1297 FileManager.prototype.requestResize_ = function(timeout) { | 1377 FileManager.prototype.requestResize_ = function(timeout) { |
1298 var self = this; | 1378 var self = this; |
1299 setTimeout(function() { self.onResize_() }, timeout || 0); | 1379 setTimeout(function() { self.onResize_() }, timeout || 0); |
1300 }; | 1380 }; |
1301 | 1381 |
1302 /** | 1382 /** |
1303 * Resize details and thumb views to fit the new window size. | 1383 * Resize details and thumb views to fit the new window size. |
1304 */ | 1384 */ |
1305 FileManager.prototype.onResize_ = function() { | 1385 FileManager.prototype.onResize_ = function() { |
1306 this.table_.style.height = this.grid_.style.height = | 1386 this.table_.style.height = this.grid_.style.height = |
1307 this.grid_.parentNode.clientHeight + 'px'; | 1387 this.grid_.parentNode.clientHeight + 'px'; |
1308 this.table_.style.width = this.grid_.style.width = | |
1309 this.grid_.parentNode.clientWidth + 'px'; | |
1310 | |
1311 this.table_.list_.style.width = this.table_.parentNode.clientWidth + 'px'; | |
1312 this.table_.list_.style.height = (this.table_.clientHeight - 1 - | 1388 this.table_.list_.style.height = (this.table_.clientHeight - 1 - |
1313 this.table_.header_.clientHeight) + 'px'; | 1389 this.table_.header_.clientHeight) + 'px'; |
1314 | 1390 |
1315 if (this.listType_ == FileManager.ListType.THUMBNAIL) { | 1391 if (this.listType_ == FileManager.ListType.THUMBNAIL) { |
1316 var self = this; | 1392 var self = this; |
1317 setTimeout(function() { | 1393 setTimeout(function() { |
1318 self.grid_.columns = 0; | 1394 self.grid_.columns = 0; |
1319 self.grid_.redraw(); | 1395 self.grid_.redraw(); |
1320 }, 0); | 1396 }, 0); |
1321 } else { | 1397 } else { |
1322 this.currentList_.redraw(); | 1398 this.currentList_.redraw(); |
1323 } | 1399 } |
| 1400 |
| 1401 this.rootsList_.style.height = |
| 1402 this.rootsList_.parentNode.clientHeight + 'px'; |
| 1403 this.rootsList_.redraw(); |
1324 }; | 1404 }; |
1325 | 1405 |
1326 FileManager.prototype.resolvePath = function( | 1406 FileManager.prototype.resolvePath = function( |
1327 path, resultCallback, errorCallback) { | 1407 path, resultCallback, errorCallback) { |
1328 return util.resolvePath(this.filesystem_.root, path, resultCallback, | 1408 return util.resolvePath(this.filesystem_.root, path, resultCallback, |
1329 errorCallback); | 1409 errorCallback); |
1330 }; | 1410 }; |
1331 | 1411 |
1332 /** | 1412 /** |
1333 * Restores current directory and may be a selected item after page load (or | 1413 * Restores current directory and may be a selected item after page load (or |
(...skipping 13 matching lines...) Expand all Loading... |
1347 this.setupPath_(this.params_.defaultPath); | 1427 this.setupPath_(this.params_.defaultPath); |
1348 } else { | 1428 } else { |
1349 this.setupDefaultPath_(); | 1429 this.setupDefaultPath_(); |
1350 } | 1430 } |
1351 }; | 1431 }; |
1352 | 1432 |
1353 FileManager.prototype.setupDefaultPath_ = function() { | 1433 FileManager.prototype.setupDefaultPath_ = function() { |
1354 // No preset given, find a good place to start. | 1434 // No preset given, find a good place to start. |
1355 // Check for removable devices, if there are none, go to Downloads. | 1435 // Check for removable devices, if there are none, go to Downloads. |
1356 var removableDirectoryEntry = this.rootEntries_.filter(function(rootEntry) { | 1436 var removableDirectoryEntry = this.rootEntries_.filter(function(rootEntry) { |
1357 return rootEntry.fullPath == REMOVABLE_DIRECTORY; | 1437 return isParentPath(REMOVABLE_DIRECTORY, rootEntry.fullPath); |
1358 })[0]; | 1438 })[0]; |
1359 if (!removableDirectoryEntry) { | 1439 var path = removableDirectoryEntry && removableDirectoryEntry.fullPath || |
1360 this.changeDirectory(DOWNLOADS_DIRECTORY, CD_NO_HISTORY); | 1440 DOWNLOADS_DIRECTORY; |
1361 return; | 1441 this.changeDirectory(path, CD_NO_HISTORY); |
1362 } | |
1363 | |
1364 var foundRemovable = false; | |
1365 util.forEachDirEntry(removableDirectoryEntry, function(result) { | |
1366 if (result) { | |
1367 foundRemovable = true; | |
1368 } else { // Done enumerating, and we know the answer. | |
1369 this.changeDirectory(foundRemovable ? '/' : DOWNLOADS_DIRECTORY, | |
1370 CD_NO_HISTORY); | |
1371 } | |
1372 }.bind(this)); | |
1373 }; | 1442 }; |
1374 | 1443 |
1375 FileManager.prototype.setupPath_ = function(path) { | 1444 FileManager.prototype.setupPath_ = function(path) { |
1376 // Split the dirname from the basename. | 1445 // Split the dirname from the basename. |
1377 var ary = path.match(/^(?:(.*)\/)?([^\/]*)$/); | 1446 var ary = path.match(/^(?:(.*)\/)?([^\/]*)$/); |
1378 if (!ary) { | 1447 if (!ary) { |
1379 console.warn('Unable to split default path: ' + path); | 1448 console.warn('Unable to split default path: ' + path); |
1380 self.changeDirectory('/', CD_NO_HISTORY); | 1449 self.changeDirectory('/', CD_NO_HISTORY); |
1381 return; | 1450 return; |
1382 } | 1451 } |
(...skipping 16 matching lines...) Expand all Loading... |
1399 return; | 1468 return; |
1400 } | 1469 } |
1401 | 1470 |
1402 // Leaf is an existing file, cd to its parent directory and select it. | 1471 // Leaf is an existing file, cd to its parent directory and select it. |
1403 self.changeDirectoryEntry(baseDirEntry, CD_NO_HISTORY, leafEntry.name); | 1472 self.changeDirectoryEntry(baseDirEntry, CD_NO_HISTORY, leafEntry.name); |
1404 } | 1473 } |
1405 | 1474 |
1406 function onLeafError(err) { | 1475 function onLeafError(err) { |
1407 // Set filename first so OK button will update in changeDirectoryEntry. | 1476 // Set filename first so OK button will update in changeDirectoryEntry. |
1408 self.filenameInput_.value = leafName; | 1477 self.filenameInput_.value = leafName; |
| 1478 self.selectDefaultPathInFilenameInput_(); |
1409 if (err = FileError.NOT_FOUND_ERR) { | 1479 if (err = FileError.NOT_FOUND_ERR) { |
1410 // Leaf does not exist, it's just a suggested file name. | 1480 // Leaf does not exist, it's just a suggested file name. |
1411 self.changeDirectoryEntry(baseDirEntry, CD_NO_HISTORY); | 1481 self.changeDirectoryEntry(baseDirEntry, CD_NO_HISTORY); |
1412 } else { | 1482 } else { |
1413 console.log('Unexpected error resolving default leaf: ' + err); | 1483 console.log('Unexpected error resolving default leaf: ' + err); |
1414 self.changeDirectoryEntry('/', CD_NO_HISTORY); | 1484 self.changeDirectoryEntry('/', CD_NO_HISTORY); |
1415 } | 1485 } |
1416 } | 1486 } |
1417 | 1487 |
1418 self.resolvePath(path, onLeafFound, onLeafError); | 1488 self.resolvePath(path, onLeafFound, onLeafError); |
1419 } | 1489 } |
1420 | 1490 |
1421 function onBaseError(err) { | 1491 function onBaseError(err) { |
1422 // Set filename first so OK button will update in changeDirectory. | 1492 // Set filename first so OK button will update in changeDirectory. |
1423 self.filenameInput_.value = leafName; | 1493 self.filenameInput_.value = leafName; |
| 1494 self.selectDefaultPathInFilenameInput_(); |
1424 console.log('Unexpected error resolving default base "' + | 1495 console.log('Unexpected error resolving default base "' + |
1425 baseName + '": ' + err); | 1496 baseName + '": ' + err); |
1426 self.changeDirectory('/', CD_NO_HISTORY); | 1497 self.changeDirectory('/', CD_NO_HISTORY); |
1427 } | 1498 } |
1428 | 1499 |
1429 if (baseName) { | 1500 if (baseName) { |
1430 this.filesystem_.root.getDirectory( | 1501 this.filesystem_.root.getDirectory( |
1431 baseName, {create: false}, onBaseFound, onBaseError); | 1502 baseName, {create: false}, onBaseFound, onBaseError); |
1432 } else { | 1503 } else { |
1433 onBaseFound(this.filesystem_.root); | 1504 onBaseFound(this.filesystem_.root); |
(...skipping 218 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
1652 li.className = 'thumbnail-item'; | 1723 li.className = 'thumbnail-item'; |
1653 | 1724 |
1654 if (this.showCheckboxes_) | 1725 if (this.showCheckboxes_) |
1655 li.appendChild(this.renderCheckbox_(entry)); | 1726 li.appendChild(this.renderCheckbox_(entry)); |
1656 | 1727 |
1657 li.appendChild(this.renderThumbnailBox_(entry, false)); | 1728 li.appendChild(this.renderThumbnailBox_(entry, false)); |
1658 | 1729 |
1659 var div = this.document_.createElement('div'); | 1730 var div = this.document_.createElement('div'); |
1660 div.className = 'filename-label'; | 1731 div.className = 'filename-label'; |
1661 var labelText = entry.name; | 1732 var labelText = entry.name; |
1662 if (this.currentDirEntry_.name == '') | |
1663 labelText = this.getLabelForRootPath_(labelText); | |
1664 | 1733 |
1665 div.textContent = labelText; | 1734 div.textContent = labelText; |
1666 div.entry = entry; | 1735 div.entry = entry; |
1667 | 1736 |
1668 li.appendChild(div); | 1737 li.appendChild(div); |
1669 | 1738 |
1670 cr.defineProperty(li, 'lead', cr.PropertyKind.BOOL_ATTR); | 1739 cr.defineProperty(li, 'lead', cr.PropertyKind.BOOL_ATTR); |
1671 cr.defineProperty(li, 'selected', cr.PropertyKind.BOOL_ATTR); | 1740 cr.defineProperty(li, 'selected', cr.PropertyKind.BOOL_ATTR); |
1672 return li; | 1741 return li; |
1673 }; | 1742 }; |
(...skipping 13 matching lines...) Expand all Loading... |
1687 | 1756 |
1688 var icon = this.document_.createElement('div'); | 1757 var icon = this.document_.createElement('div'); |
1689 icon.className = 'detail-icon'; | 1758 icon.className = 'detail-icon'; |
1690 this.getIconType(entry); | 1759 this.getIconType(entry); |
1691 icon.setAttribute('iconType', entry.cachedIconType_); | 1760 icon.setAttribute('iconType', entry.cachedIconType_); |
1692 div.appendChild(icon); | 1761 div.appendChild(icon); |
1693 | 1762 |
1694 return div; | 1763 return div; |
1695 }; | 1764 }; |
1696 | 1765 |
1697 FileManager.prototype.getLabelForRootPath_ = function(path) { | 1766 /** |
1698 // This hack lets us localize the top level directories. | 1767 * Return the localized name for the root. |
1699 if (path == 'Downloads') | 1768 * @param {string} path The full path of the root (starting with slash). |
1700 return str('DOWNLOADS_DIRECTORY_LABEL'); | 1769 * @return {string} The localized name. |
| 1770 */ |
| 1771 FileManager.prototype.getRootLabel_ = function(path) { |
| 1772 if (path == DOWNLOADS_DIRECTORY) |
| 1773 return str('CHROMEBOOK_DIRECTORY_LABEL'); |
1701 | 1774 |
1702 if (path == 'archive') | 1775 if (path == ARCHIVE_DIRECTORY) |
1703 return str('ARCHIVE_DIRECTORY_LABEL'); | 1776 return str('ARCHIVE_DIRECTORY_LABEL'); |
| 1777 if (isParentPath(ARCHIVE_DIRECTORY, path)) |
| 1778 return path.substring(ARCHIVE_DIRECTORY.length + 1); |
1704 | 1779 |
1705 if (path == 'removable') | 1780 if (path == REMOVABLE_DIRECTORY) |
1706 return str('REMOVABLE_DIRECTORY_LABEL'); | 1781 return str('REMOVABLE_DIRECTORY_LABEL'); |
| 1782 if (isParentPath(REMOVABLE_DIRECTORY, path)) |
| 1783 return path.substring(REMOVABLE_DIRECTORY.length + 1); |
1707 | 1784 |
1708 return path || str('ROOT_DIRECTORY_LABEL'); | 1785 return path; |
| 1786 }; |
| 1787 |
| 1788 FileManager.prototype.getRootIconUrl_ = function(path, opt_small) { |
| 1789 var iconUrl = opt_small ? 'images/chromebook_28x28.png' : |
| 1790 'images/chromebook_24x24.png'; |
| 1791 if (isParentPath(REMOVABLE_DIRECTORY, path)) |
| 1792 iconUrl = 'images/filetype_device.png'; |
| 1793 else if (isParentPath(ARCHIVE_DIRECTORY, path)) |
| 1794 iconUrl = 'images/icon_mount_archive_16x16.png'; |
| 1795 return chrome.extension.getURL(iconUrl); |
| 1796 }; |
| 1797 |
| 1798 FileManager.prototype.renderRoot_ = function(entry) { |
| 1799 var li = this.document_.createElement('li'); |
| 1800 li.className = 'root-item'; |
| 1801 |
| 1802 var icon = this.document_.createElement('img'); |
| 1803 icon.src = this.getRootIconUrl_(entry.fullPath, false); |
| 1804 li.appendChild(icon); |
| 1805 |
| 1806 var div = this.document_.createElement('div'); |
| 1807 div.textContent = this.getRootLabel_(entry.fullPath); |
| 1808 li.appendChild(div); |
| 1809 |
| 1810 if (isParentPath(REMOVABLE_DIRECTORY, entry.fullPath) || |
| 1811 isParentPath(ARCHIVE_DIRECTORY, entry.fullPath)) { |
| 1812 var spacer = this.document_.createElement('div'); |
| 1813 spacer.className = 'spacer'; |
| 1814 li.appendChild(spacer); |
| 1815 |
| 1816 var eject = this.document_.createElement('img'); |
| 1817 eject.className = 'root-eject'; |
| 1818 eject.setAttribute('src', chrome.extension.getURL('images/eject.png')); |
| 1819 eject.addEventListener('click', this.onEjectClick_.bind(this, entry)); |
| 1820 li.appendChild(eject); |
| 1821 } |
| 1822 |
| 1823 cr.defineProperty(li, 'lead', cr.PropertyKind.BOOL_ATTR); |
| 1824 cr.defineProperty(li, 'selected', cr.PropertyKind.BOOL_ATTR); |
| 1825 return li; |
| 1826 }; |
| 1827 |
| 1828 /** |
| 1829 * Handler for eject button clicked. |
| 1830 * @param {Entry} entry Entry to eject. |
| 1831 * @param {Event} event The event. |
| 1832 */ |
| 1833 FileManager.prototype.onEjectClick_ = function(entry, event) { |
| 1834 this.unmountRequests_.push(entry.toURL()); |
| 1835 chrome.fileBrowserPrivate.removeMount(entry.fullPath); |
1709 }; | 1836 }; |
1710 | 1837 |
1711 /** | 1838 /** |
1712 * Render the Name column of the detail table. | 1839 * Render the Name column of the detail table. |
1713 * | 1840 * |
1714 * Invoked by cr.ui.Table when a file needs to be rendered. | 1841 * Invoked by cr.ui.Table when a file needs to be rendered. |
1715 * | 1842 * |
1716 * @param {Entry} entry The Entry object to render. | 1843 * @param {Entry} entry The Entry object to render. |
1717 * @param {string} columnId The id of the column to be rendered. | 1844 * @param {string} columnId The id of the column to be rendered. |
1718 * @param {cr.ui.Table} table The table doing the rendering. | 1845 * @param {cr.ui.Table} table The table doing the rendering. |
1719 */ | 1846 */ |
1720 FileManager.prototype.renderName_ = function(entry, columnId, table) { | 1847 FileManager.prototype.renderName_ = function(entry, columnId, table) { |
1721 var label = this.document_.createElement('div'); | 1848 var label = this.document_.createElement('div'); |
1722 label.appendChild(this.renderIconType_(entry, columnId, table)); | 1849 label.appendChild(this.renderIconType_(entry, columnId, table)); |
1723 label.entry = entry; | 1850 label.entry = entry; |
1724 label.className = 'detail-name filename-label'; | 1851 label.className = 'detail-name filename-label'; |
1725 if (this.currentDirEntry_.name == '') { | 1852 label.appendChild(this.document_.createTextNode(entry.name)); |
1726 label.appendChild(this.document_.createTextNode( | |
1727 this.getLabelForRootPath_(entry.name))); | |
1728 } else { | |
1729 label.appendChild(this.document_.createTextNode(entry.name)); | |
1730 } | |
1731 | |
1732 return label; | 1853 return label; |
1733 }; | 1854 }; |
1734 | 1855 |
1735 /** | 1856 /** |
1736 * Render the Size column of the detail table. | 1857 * Render the Size column of the detail table. |
1737 * | 1858 * |
1738 * @param {Entry} entry The Entry object to render. | 1859 * @param {Entry} entry The Entry object to render. |
1739 * @param {string} columnId The id of the column to be rendered. | 1860 * @param {string} columnId The id of the column to be rendered. |
1740 * @param {cr.ui.Table} table The table doing the rendering. | 1861 * @param {cr.ui.Table} table The table doing the rendering. |
1741 */ | 1862 */ |
(...skipping 365 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
2107 // Skip the button creation. | 2228 // Skip the button creation. |
2108 if (!str('ENABLE_PHOTO_EDITOR')) continue; | 2229 if (!str('ENABLE_PHOTO_EDITOR')) continue; |
2109 this.galleryTask_ = task; | 2230 this.galleryTask_ = task; |
2110 } | 2231 } |
2111 } | 2232 } |
2112 this.renderTaskButton_(task); | 2233 this.renderTaskButton_(task); |
2113 } | 2234 } |
2114 | 2235 |
2115 // These are done in separate functions, as the checks require | 2236 // These are done in separate functions, as the checks require |
2116 // asynchronous function calls. | 2237 // asynchronous function calls. |
2117 this.maybeRenderUnmountTask_(selection); | |
2118 this.maybeRenderFormattingTask_(selection); | 2238 this.maybeRenderFormattingTask_(selection); |
2119 }; | 2239 }; |
2120 | 2240 |
2121 FileManager.prototype.renderTaskButton_ = function(task) { | 2241 FileManager.prototype.renderTaskButton_ = function(task) { |
2122 var button = this.document_.createElement('button'); | 2242 var button = this.document_.createElement('button'); |
2123 button.addEventListener('click', | 2243 button.addEventListener('click', |
2124 this.onTaskButtonClicked_.bind(this, task)); | 2244 this.onTaskButtonClicked_.bind(this, task)); |
2125 button.className = 'task-button'; | 2245 button.className = 'task-button'; |
2126 | 2246 |
2127 var img = this.document_.createElement('img'); | 2247 var img = this.document_.createElement('img'); |
2128 img.src = task.iconUrl; | 2248 img.src = task.iconUrl; |
2129 | 2249 |
2130 button.appendChild(img); | 2250 button.appendChild(img); |
2131 var label = this.document_.createElement('div'); | 2251 var label = this.document_.createElement('div'); |
2132 label.appendChild(this.document_.createTextNode(task.title)) | 2252 label.appendChild(this.document_.createTextNode(task.title)) |
2133 button.appendChild(label); | 2253 button.appendChild(label); |
2134 | 2254 |
2135 this.taskButtons_.appendChild(button); | 2255 this.taskButtons_.appendChild(button); |
2136 }; | 2256 }; |
2137 | 2257 |
2138 /** | 2258 /** |
2139 * Checks whether unmount task should be displayed and if the answer is | |
2140 * affirmative renders it. | |
2141 * @param {Object} selection Selected files object. | |
2142 */ | |
2143 FileManager.prototype.maybeRenderUnmountTask_ = function(selection) { | |
2144 for (var index = 0; index < selection.urls.length; ++index) { | |
2145 // Each url should be a mount point. | |
2146 var path = selection.entries[index].fullPath; | |
2147 var found = false; | |
2148 for (var i = 0; i < this.mountPoints_.length; i++) { | |
2149 var mountPath = this.mountPoints_[i].mountPath; | |
2150 if (mountPath[0] != '/') { | |
2151 mountPath = '/' + mountPath; | |
2152 } | |
2153 if (mountPath == path && this.mountPoints_[i].mountType == 'file') { | |
2154 found = true; | |
2155 break; | |
2156 } | |
2157 } | |
2158 if (!found) | |
2159 return; | |
2160 } | |
2161 this.renderTaskButton_({ | |
2162 taskId: this.getExtensionId_() + '|unmount-archive', | |
2163 iconUrl: | |
2164 chrome.extension.getURL('images/icon_unmount_archive_16x16.png'), | |
2165 title: str('UNMOUNT_ARCHIVE'), | |
2166 internal: true | |
2167 }); | |
2168 }; | |
2169 | |
2170 /** | |
2171 * Checks whether formatting task should be displayed and if the answer is | 2259 * Checks whether formatting task should be displayed and if the answer is |
2172 * affirmative renders it. Includes asynchronous calls, so it's splitted into | 2260 * affirmative renders it. Includes asynchronous calls, so it's splitted into |
2173 * three parts. | 2261 * three parts. |
2174 * @param {Object} selection Selected files object. | 2262 * @param {Object} selection Selected files object. |
2175 */ | 2263 */ |
2176 FileManager.prototype.maybeRenderFormattingTask_ = function(selection) { | 2264 FileManager.prototype.maybeRenderFormattingTask_ = function(selection) { |
2177 // Not to make unnecessary getMountPoints() call we doublecheck if there is | 2265 // Not to make unnecessary getMountPoints() call we doublecheck if there is |
2178 // only one selected entry. | 2266 // only one selected entry. |
2179 if (selection.entries.length != 1) | 2267 if (selection.entries.length != 1) |
2180 return; | 2268 return; |
(...skipping 60 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
2241 chrome.fileBrowserPrivate.executeTask(task.taskId, urls); | 2329 chrome.fileBrowserPrivate.executeTask(task.taskId, urls); |
2242 }; | 2330 }; |
2243 | 2331 |
2244 /** | 2332 /** |
2245 * Event handler called when some volume was mounted or unmouted. | 2333 * Event handler called when some volume was mounted or unmouted. |
2246 */ | 2334 */ |
2247 FileManager.prototype.onMountCompleted_ = function(event) { | 2335 FileManager.prototype.onMountCompleted_ = function(event) { |
2248 var self = this; | 2336 var self = this; |
2249 chrome.fileBrowserPrivate.getMountPoints(function(mountPoints) { | 2337 chrome.fileBrowserPrivate.getMountPoints(function(mountPoints) { |
2250 self.mountPoints_ = mountPoints; | 2338 self.mountPoints_ = mountPoints; |
| 2339 var changeDirectoryTo = null; |
| 2340 |
2251 if (event.eventType == 'mount') { | 2341 if (event.eventType == 'mount') { |
2252 for (var index = 0; index < self.mountRequests_.length; ++index) { | 2342 // Mount request finished - remove it. |
2253 if (self.mountRequests_[index] == event.sourceUrl) { | 2343 var index = self.mountRequests_.indexOf(event.sourceUrl); |
2254 self.mountRequests_.splice(index, 1); | 2344 if (index != -1) { |
2255 if (event.status == 'success') { | 2345 self.mountRequests_.splice(index, 1); |
2256 self.changeDirectory(event.mountPath); | 2346 // Go to mounted directory, if request was initiated from this tab. |
2257 } else { | 2347 if (event.status == 'success') |
2258 // Report mount error. | 2348 changeDirectoryTo = event.mountPath; |
2259 if (event.mountType == 'file') { | |
2260 var fileName = event.sourceUrl.substr( | |
2261 event.sourceUrl.lastIndexOf('/') + 1); | |
2262 self.alert.show(strf('ARCHIVE_MOUNT_FAILED', fileName, | |
2263 event.status)); | |
2264 } | |
2265 } | |
2266 return; | |
2267 } | |
2268 } | 2349 } |
2269 } | 2350 } |
2270 | 2351 |
| 2352 if (event.eventType == 'unmount') { |
| 2353 // Unmount request finished - remove it. |
| 2354 var index = self.unmountRequests_.indexOf(event.sourceUrl); |
| 2355 if (index != -1) |
| 2356 self.unmountRequests_.splice(index, 1); |
| 2357 } |
| 2358 |
| 2359 if (event.eventType == 'mount' && event.status != 'success' && |
| 2360 event.mountType == 'file') { |
| 2361 // Report mount error. |
| 2362 var fileName = event.sourceUrl.substr( |
| 2363 event.sourceUrl.lastIndexOf('/') + 1); |
| 2364 self.alert.show(strf('ARCHIVE_MOUNT_FAILED', fileName, |
| 2365 event.status)); |
| 2366 } |
| 2367 |
| 2368 if (event.eventType == 'unmount' && event.status != 'success') { |
| 2369 // Report unmount error. |
| 2370 // TODO(dgozman): introduce string and show alert here. |
| 2371 } |
| 2372 |
2271 if (event.eventType == 'unmount' && event.status == 'success' && | 2373 if (event.eventType == 'unmount' && event.status == 'success' && |
2272 self.currentDirEntry_ && | 2374 self.currentDirEntry_ && |
2273 isParentPath(event.mountPath, self.currentDirEntry_.fullPath)) { | 2375 isParentPath(event.mountPath, self.currentDirEntry_.fullPath)) { |
2274 self.changeDirectory(getParentPath(event.mountPath)); | 2376 changeDirectoryTo = getParentPath(event.mountPath); |
2275 return; | |
2276 } | 2377 } |
2277 | 2378 |
2278 var rescanDirectoryNeeded = (event.status == 'success'); | 2379 // In the case of success, roots are changed and should be rescanned. |
2279 for (var i = 0; i < mountPoints.length; i++) { | 2380 if (event.status == 'success') |
2280 if (event.sourceUrl == mountPoints[i].sourceUrl && | 2381 self.updateRoots_(changeDirectoryTo); |
2281 mountPoints[i].mountCondition != '') { | |
2282 rescanDirectoryNeeded = true; | |
2283 } | |
2284 } | |
2285 // TODO(dgozman): rescan directory, only if it contains mounted points, | |
2286 // when mounts location will be decided. | |
2287 if (rescanDirectoryNeeded) | |
2288 self.rescanDirectory_(null, 300); | |
2289 }); | 2382 }); |
2290 }; | 2383 }; |
2291 | 2384 |
2292 /** | 2385 /** |
2293 * Event handler called when some internal task should be executed. | 2386 * Event handler called when some internal task should be executed. |
2294 */ | 2387 */ |
2295 FileManager.prototype.onFileTaskExecute_ = function(id, details) { | 2388 FileManager.prototype.onFileTaskExecute_ = function(id, details) { |
2296 var urls = details.urls; | 2389 var urls = details.urls; |
2297 if (id == 'preview') { | 2390 if (id == 'preview') { |
2298 g_slideshow_data = urls; | 2391 g_slideshow_data = urls; |
2299 chrome.tabs.create({url: "slideshow.html"}); | 2392 chrome.tabs.create({url: "slideshow.html"}); |
2300 } else if (id == 'play' || id == 'enqueue') { | 2393 } else if (id == 'play' || id == 'enqueue') { |
2301 chrome.fileBrowserPrivate.viewFiles(urls, id); | 2394 chrome.fileBrowserPrivate.viewFiles(urls, id); |
2302 } else if (id == 'mount-archive') { | 2395 } else if (id == 'mount-archive') { |
2303 for (var index = 0; index < urls.length; ++index) { | 2396 for (var index = 0; index < urls.length; ++index) { |
2304 this.mountRequests_.push(urls[index]); | 2397 this.mountRequests_.push(urls[index]); |
2305 chrome.fileBrowserPrivate.addMount(urls[index], 'file', {}); | 2398 chrome.fileBrowserPrivate.addMount(urls[index], 'file', {}); |
2306 } | 2399 } |
2307 } else if (id == 'unmount-archive') { | |
2308 for (var index = 0; index < urls.length; ++index) { | |
2309 chrome.fileBrowserPrivate.removeMount(urls[index]); | |
2310 } | |
2311 } else if (id == 'format-device') { | 2400 } else if (id == 'format-device') { |
2312 this.confirm.show(str('FORMATTING_WARNING'), function() { | 2401 this.confirm.show(str('FORMATTING_WARNING'), function() { |
2313 chrome.fileBrowserPrivate.formatDevice(urls[0]); | 2402 chrome.fileBrowserPrivate.formatDevice(urls[0]); |
2314 }); | 2403 }); |
2315 } else if (id == 'gallery') { | 2404 } else if (id == 'gallery') { |
2316 // Pass to gallery all possible tasks except the gallery itself. | 2405 // Pass to gallery all possible tasks except the gallery itself. |
2317 var noGallery = []; | 2406 var noGallery = []; |
2318 for (var index = 0; index < details.task.allTasks.length; index++) { | 2407 for (var index = 0; index < details.task.allTasks.length; index++) { |
2319 var task = details.task.allTasks[index]; | 2408 var task = details.task.allTasks[index]; |
2320 if (task.taskId != this.getExtensionId_() + '|gallery') { | 2409 if (task.taskId != this.getExtensionId_() + '|gallery') { |
(...skipping 56 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
2377 self.metadataProvider_, | 2466 self.metadataProvider_, |
2378 shareActions, | 2467 shareActions, |
2379 str); | 2468 str); |
2380 }; | 2469 }; |
2381 | 2470 |
2382 galleryFrame.src = 'js/image_editor/gallery.html'; | 2471 galleryFrame.src = 'js/image_editor/gallery.html'; |
2383 this.dialogDom_.appendChild(galleryFrame); | 2472 this.dialogDom_.appendChild(galleryFrame); |
2384 galleryFrame.focus(); | 2473 galleryFrame.focus(); |
2385 }; | 2474 }; |
2386 | 2475 |
| 2476 FileManager.prototype.getRootForPath_ = function(path) { |
| 2477 for (var index = 0; index < this.rootEntries_.length; index++) { |
| 2478 if (isParentPath(this.rootEntries_[index].fullPath, path)) { |
| 2479 return index; |
| 2480 } |
| 2481 } |
| 2482 return -1; |
| 2483 }; |
| 2484 |
2387 /** | 2485 /** |
2388 * Update the breadcrumb display to reflect the current directory. | 2486 * Update the breadcrumb display to reflect the current directory. |
2389 */ | 2487 */ |
2390 FileManager.prototype.updateBreadcrumbs_ = function() { | 2488 FileManager.prototype.updateBreadcrumbs_ = function() { |
2391 var bc = this.dialogDom_.querySelector('.breadcrumbs'); | 2489 var bc = this.dialogDom_.querySelector('.breadcrumbs'); |
2392 bc.innerHTML = ''; | 2490 bc.innerHTML = ''; |
2393 | 2491 |
2394 var fullPath = this.currentDirEntry_.fullPath.replace(/\/$/, ''); | 2492 var fullPath = this.currentDirEntry_.fullPath; |
2395 var pathNames = fullPath.split('/'); | 2493 var rootIndex = this.getRootForPath_(fullPath); |
2396 var path = ''; | 2494 if (rootIndex == -1) { |
| 2495 console.error('Not root for: ' + fullPath); |
| 2496 return; |
| 2497 } |
| 2498 var root = this.rootEntries_[rootIndex]; |
| 2499 |
| 2500 var icon = this.document_.createElement('img'); |
| 2501 icon.className = 'breadcrumb-icon'; |
| 2502 icon.setAttribute('src', this.getRootIconUrl_(root.fullPath, true)); |
| 2503 bc.appendChild(icon); |
| 2504 |
| 2505 var rootPath = root.fullPath; |
| 2506 var relativePath = fullPath.substring(rootPath.length); |
| 2507 var pathNames = relativePath.replace(/\/$/, '').split('/'); |
| 2508 if (pathNames[0] == '') |
| 2509 pathNames.splice(0, 1); |
| 2510 |
| 2511 // We need a first breadcrumb for root, so placing last name from |
| 2512 // rootPath as first name of relativePath. |
| 2513 var rootPathNames = rootPath.replace(/\/$/, '').split('/'); |
| 2514 pathNames.splice(0, 0, rootPathNames[rootPathNames.length - 1]); |
| 2515 rootPathNames.splice(rootPathNames.length - 1, 1); |
| 2516 var path = rootPathNames.join('/') + '/'; |
2397 | 2517 |
2398 for (var i = 0; i < pathNames.length; i++) { | 2518 for (var i = 0; i < pathNames.length; i++) { |
2399 var pathName = pathNames[i]; | 2519 var pathName = pathNames[i]; |
2400 path += pathName + '/'; | 2520 path += pathName; |
2401 | 2521 |
2402 var div = this.document_.createElement('div'); | 2522 var div = this.document_.createElement('div'); |
2403 div.className = 'breadcrumb-path'; | 2523 div.className = 'breadcrumb-path'; |
2404 if (i <= 1) { | 2524 div.textContent = i == 0 ? this.getRootLabel_(path) : pathName; |
2405 // i == 0: root directory itself, i == 1: the files it contains. | |
2406 div.textContent = this.getLabelForRootPath_(pathName); | |
2407 } else { | |
2408 div.textContent = pathName; | |
2409 } | |
2410 | 2525 |
| 2526 path = path + '/'; |
2411 div.path = path; | 2527 div.path = path; |
2412 div.addEventListener('click', this.onBreadcrumbClick_.bind(this)); | 2528 div.addEventListener('click', this.onBreadcrumbClick_.bind(this)); |
2413 | 2529 |
2414 bc.appendChild(div); | 2530 bc.appendChild(div); |
2415 | 2531 |
2416 if (i == pathNames.length - 1) { | 2532 if (i == pathNames.length - 1) { |
2417 div.classList.add('breadcrumb-last'); | 2533 div.classList.add('breadcrumb-last'); |
2418 } else { | 2534 } else { |
2419 var spacer = this.document_.createElement('div'); | 2535 var spacer = this.document_.createElement('div'); |
2420 spacer.className = 'breadcrumb-spacer'; | 2536 spacer.className = 'breadcrumb-spacer'; |
(...skipping 79 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
2500 for (var i = 0; i < this.dataModel_.length; i++) { | 2616 for (var i = 0; i < this.dataModel_.length; i++) { |
2501 if (this.dataModel_.item(i).name == name) { | 2617 if (this.dataModel_.item(i).name == name) { |
2502 this.currentList_.selectionModel.selectedIndex = i; | 2618 this.currentList_.selectionModel.selectedIndex = i; |
2503 this.currentList_.scrollIndexIntoView(i); | 2619 this.currentList_.scrollIndexIntoView(i); |
2504 this.currentList_.focus(); | 2620 this.currentList_.focus(); |
2505 return; | 2621 return; |
2506 } | 2622 } |
2507 } | 2623 } |
2508 }; | 2624 }; |
2509 | 2625 |
| 2626 FileManager.prototype.updateRootsListSelection_ = function() { |
| 2627 if (!this.currentDirEntry_) return; |
| 2628 var index = this.getRootForPath_(this.currentDirEntry_.fullPath); |
| 2629 if (index == -1) { |
| 2630 this.rootsList_.selectionModel.selectedIndex = 0; |
| 2631 } else { |
| 2632 if (this.rootsList_.selectionModel.selectedIndex != index) |
| 2633 this.rootsList_.selectionModel.selectedIndex = index; |
| 2634 } |
| 2635 }; |
| 2636 |
2510 /** | 2637 /** |
2511 * Add the file/directory with given name to the current selection. | 2638 * Add the file/directory with given name to the current selection. |
2512 * | 2639 * |
2513 * @param {string} name The name of the entry to select. | 2640 * @param {string} name The name of the entry to select. |
2514 * @return {boolean} Whether entry exists. | 2641 * @return {boolean} Whether entry exists. |
2515 */ | 2642 */ |
2516 FileManager.prototype.addItemToSelection = function(name) { | 2643 FileManager.prototype.addItemToSelection = function(name) { |
2517 var entryExists = false; | 2644 var entryExists = false; |
2518 for (var i = 0; i < this.dataModel_.length; i++) { | 2645 for (var i = 0; i < this.dataModel_.length; i++) { |
2519 if (this.dataModel_.item(i).name == name) { | 2646 if (this.dataModel_.item(i).name == name) { |
(...skipping 95 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
2615 FileManager.prototype.changeDirectoryEntry = function(dirEntry, | 2742 FileManager.prototype.changeDirectoryEntry = function(dirEntry, |
2616 opt_saveHistory, | 2743 opt_saveHistory, |
2617 opt_selectedEntry, | 2744 opt_selectedEntry, |
2618 opt_callback) { | 2745 opt_callback) { |
2619 if (typeof opt_saveHistory == 'undefined') { | 2746 if (typeof opt_saveHistory == 'undefined') { |
2620 opt_saveHistory = true; | 2747 opt_saveHistory = true; |
2621 } else { | 2748 } else { |
2622 opt_saveHistory = !!opt_saveHistory; | 2749 opt_saveHistory = !!opt_saveHistory; |
2623 } | 2750 } |
2624 | 2751 |
| 2752 // Some directories are above roots, so we instead show the first root. |
| 2753 // There may be request to change directory above the roots. For example, |
| 2754 // when usb-dirve is removed, we try to change to the parent directory, |
| 2755 // which is REMOVABLE_DIRECTORY. |
| 2756 if (!dirEntry || dirEntry.fullPath == '/' || |
| 2757 dirEntry.fullPath == REMOVABLE_DIRECTORY || |
| 2758 dirEntry.fullPath == ARCHIVE_DIRECTORY) { |
| 2759 dirEntry = this.rootEntries_[0] || dirEntry; |
| 2760 } |
| 2761 |
2625 var location = document.location.origin + document.location.pathname + '#' + | 2762 var location = document.location.origin + document.location.pathname + '#' + |
2626 encodeURI(dirEntry.fullPath); | 2763 encodeURI(dirEntry.fullPath); |
2627 if (opt_saveHistory) { | 2764 if (opt_saveHistory) { |
2628 history.pushState(undefined, dirEntry.fullPath, location); | 2765 history.pushState(undefined, dirEntry.fullPath, location); |
2629 } else if (window.location.hash != location) { | 2766 } else if (window.location.hash != location) { |
2630 // If the user typed URL manually that is not canonical it would be fixed | 2767 // If the user typed URL manually that is not canonical it would be fixed |
2631 // here. However it seems history.replaceState doesn't work properly | 2768 // here. However it seems history.replaceState doesn't work properly |
2632 // with rewritable URLs (while does with history.pushState). It changes | 2769 // with rewritable URLs (while does with history.pushState). It changes |
2633 // window.location but doesn't change content of the ombibox. | 2770 // window.location but doesn't change content of the ombibox. |
2634 history.replaceState(undefined, dirEntry.fullPath, location); | 2771 history.replaceState(undefined, dirEntry.fullPath, location); |
(...skipping 217 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
2852 // then the default action of this click event fires and toggles the | 2989 // then the default action of this click event fires and toggles the |
2853 // checkbox back off. | 2990 // checkbox back off. |
2854 // | 2991 // |
2855 // Since we're going to force checkboxes into the correct state for any | 2992 // Since we're going to force checkboxes into the correct state for any |
2856 // multi-selection, we can prevent this shift click from toggling the | 2993 // multi-selection, we can prevent this shift click from toggling the |
2857 // checkbox and avoid the trouble. | 2994 // checkbox and avoid the trouble. |
2858 event.preventDefault(); | 2995 event.preventDefault(); |
2859 } | 2996 } |
2860 }; | 2997 }; |
2861 | 2998 |
| 2999 FileManager.prototype.onRootsSelectionChanged_ = function(event) { |
| 3000 var root = this.rootEntries_[this.rootsList_.selectionModel.selectedIndex]; |
| 3001 this.changeDirectoryEntry(root); |
| 3002 }; |
| 3003 |
| 3004 FileManager.prototype.selectDefaultPathInFilenameInput_ = function() { |
| 3005 var input = this.filenameInput_; |
| 3006 input.focus(); |
| 3007 var selectionEnd = input.value.lastIndexOf('.'); |
| 3008 if (selectionEnd == -1) { |
| 3009 input.select(); |
| 3010 } else { |
| 3011 input.selectionStart = 0; |
| 3012 input.selectionEnd = selectionEnd; |
| 3013 } |
| 3014 // Clear, so we never do this again. |
| 3015 this.params_.defaultPath = ''; |
| 3016 }; |
| 3017 |
2862 /** | 3018 /** |
2863 * Update the UI when the selection model changes. | 3019 * Update the UI when the selection model changes. |
2864 * | 3020 * |
2865 * @param {cr.Event} event The change event. | 3021 * @param {cr.Event} event The change event. |
2866 */ | 3022 */ |
2867 FileManager.prototype.onSelectionChanged_ = function(event) { | 3023 FileManager.prototype.onSelectionChanged_ = function(event) { |
2868 this.summarizeSelection_(); | 3024 this.summarizeSelection_(); |
2869 | 3025 |
2870 if (this.dialogType_ == FileManager.DialogType.SELECT_SAVEAS_FILE) { | 3026 if (this.dialogType_ == FileManager.DialogType.SELECT_SAVEAS_FILE) { |
2871 // If this is a save-as dialog, copy the selected file into the filename | 3027 // If this is a save-as dialog, copy the selected file into the filename |
2872 // input text box. | 3028 // input text box. |
2873 | 3029 |
2874 if (this.selection && | 3030 if (this.selection && |
2875 this.selection.totalCount == 1 && | 3031 this.selection.totalCount == 1 && |
2876 this.selection.entries[0].isFile) | 3032 this.selection.entries[0].isFile && |
| 3033 this.filenameInput_.value != this.selection.entries[0].name) { |
2877 this.filenameInput_.value = this.selection.entries[0].name; | 3034 this.filenameInput_.value = this.selection.entries[0].name; |
| 3035 if (this.params_.defaultPath == this.selection.entries[0].fullPath) |
| 3036 this.selectDefaultPathInFilenameInput_(); |
| 3037 } |
2878 } | 3038 } |
2879 | 3039 |
2880 this.updateOkButton_(); | 3040 this.updateOkButton_(); |
2881 | 3041 |
2882 var self = this; | 3042 var self = this; |
2883 setTimeout(function() { self.onSelectionChangeComplete_(event) }, 0); | 3043 setTimeout(function() { self.onSelectionChangeComplete_(event) }, 0); |
2884 }; | 3044 }; |
2885 | 3045 |
2886 /** | 3046 /** |
2887 * Handle selection change related tasks that won't run properly during | 3047 * Handle selection change related tasks that won't run properly during |
(...skipping 132 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
3020 * Update the UI when the current directory changes. | 3180 * Update the UI when the current directory changes. |
3021 * | 3181 * |
3022 * @param {cr.Event} event The directory-changed event. | 3182 * @param {cr.Event} event The directory-changed event. |
3023 */ | 3183 */ |
3024 FileManager.prototype.onDirectoryChanged_ = function(event) { | 3184 FileManager.prototype.onDirectoryChanged_ = function(event) { |
3025 this.updateCommands_(); | 3185 this.updateCommands_(); |
3026 this.updateOkButton_(); | 3186 this.updateOkButton_(); |
3027 | 3187 |
3028 this.checkFreeSpace_(this.currentDirEntry_.fullPath); | 3188 this.checkFreeSpace_(this.currentDirEntry_.fullPath); |
3029 | 3189 |
3030 // New folder should never be enabled in the root or media/ directories. | 3190 // TODO(dgozman): title may be better than this. |
3031 this.newFolderButton_.disabled = isSystemDirEntry(this.currentDirEntry_); | |
3032 | |
3033 this.document_.title = this.currentDirEntry_.fullPath; | 3191 this.document_.title = this.currentDirEntry_.fullPath; |
3034 | 3192 |
3035 var self = this; | 3193 var self = this; |
3036 | 3194 |
3037 if (this.subscribedOnDirectoryChanges_) { | 3195 if (this.subscribedOnDirectoryChanges_) { |
3038 chrome.fileBrowserPrivate.removeFileWatch(event.previousDirEntry.toURL(), | 3196 chrome.fileBrowserPrivate.removeFileWatch(event.previousDirEntry.toURL(), |
3039 function(result) { | 3197 function(result) { |
3040 if (!result) { | 3198 if (!result) { |
3041 console.log('Failed to remove file watch'); | 3199 console.log('Failed to remove file watch'); |
3042 } | 3200 } |
(...skipping 75 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
3118 // Updated when a user clicks on the label of a file, used to detect | 3276 // Updated when a user clicks on the label of a file, used to detect |
3119 // when a click is eligible to trigger a rename. Can be null, or | 3277 // when a click is eligible to trigger a rename. Can be null, or |
3120 // an object with 'path' and 'date' properties. | 3278 // an object with 'path' and 'date' properties. |
3121 this.lastLabelClick_ = null; | 3279 this.lastLabelClick_ = null; |
3122 | 3280 |
3123 // Clear the table first. | 3281 // Clear the table first. |
3124 this.dataModel_.splice(0, this.dataModel_.length); | 3282 this.dataModel_.splice(0, this.dataModel_.length); |
3125 this.currentList_.selectionModel.clear(); | 3283 this.currentList_.selectionModel.clear(); |
3126 | 3284 |
3127 this.updateBreadcrumbs_(); | 3285 this.updateBreadcrumbs_(); |
| 3286 this.updateRootsListSelection_(); |
3128 | 3287 |
3129 if (this.currentDirEntry_.fullPath != '/') { | 3288 // Add current request to pending result list |
3130 // Add current request to pending result list | 3289 this.pendingRescanQueue_.push({ |
3131 this.pendingRescanQueue_.push({ | 3290 onSuccess:opt_callback, |
3132 onSuccess:opt_callback, | 3291 onError:opt_onError |
3133 onError:opt_onError | 3292 }); |
3134 }); | |
3135 | 3293 |
3136 if (this.rescanRunning_) | 3294 if (this.rescanRunning_) |
3137 return; | 3295 return; |
3138 | 3296 |
3139 this.rescanRunning_ = true; | 3297 this.rescanRunning_ = true; |
3140 | 3298 |
3141 // The current list of callbacks is saved and reset. Subsequent | 3299 // The current list of callbacks is saved and reset. Subsequent |
3142 // calls to rescanDirectory_ while we're still pending will be | 3300 // calls to rescanDirectory_ while we're still pending will be |
3143 // saved and will cause an additional rescan to happen after a delay. | 3301 // saved and will cause an additional rescan to happen after a delay. |
3144 var callbacks = this.pendingRescanQueue_; | 3302 var callbacks = this.pendingRescanQueue_; |
3145 | 3303 |
3146 this.pendingRescanQueue_ = []; | 3304 this.pendingRescanQueue_ = []; |
3147 | 3305 |
3148 var self = this; | 3306 var self = this; |
3149 var reader; | 3307 var reader; |
3150 | 3308 |
3151 function onError() { | 3309 function onError() { |
| 3310 if (self.pendingRescanQueue_.length > 0) { |
| 3311 setTimeout(self.rescanDirectory_.bind(self), |
| 3312 SIMULTANEOUS_RESCAN_INTERVAL); |
| 3313 } |
| 3314 |
| 3315 self.rescanRunning_ = false; |
| 3316 |
| 3317 for (var i= 0; i < callbacks.length; i++) { |
| 3318 if (callbacks[i].onError) |
| 3319 try { |
| 3320 callbacks[i].onError(); |
| 3321 } catch (ex) { |
| 3322 console.error('Caught exception while notifying about error: ' + |
| 3323 name, ex); |
| 3324 } |
| 3325 } |
| 3326 } |
| 3327 |
| 3328 function onReadSome(entries) { |
| 3329 if (entries.length == 0) { |
| 3330 metrics.recordTime('DirectoryScan'); |
| 3331 if (self.currentDirEntry_.fullPath == DOWNLOADS_DIRECTORY) { |
| 3332 metrics.reportCount("DownloadsCount", self.dataModel_.length); |
| 3333 } |
| 3334 |
3152 if (self.pendingRescanQueue_.length > 0) { | 3335 if (self.pendingRescanQueue_.length > 0) { |
3153 setTimeout(self.rescanDirectory_.bind(self), | 3336 setTimeout(self.rescanDirectory_.bind(self), |
3154 SIMULTANEOUS_RESCAN_INTERVAL); | 3337 SIMULTANEOUS_RESCAN_INTERVAL); |
3155 } | 3338 } |
3156 | 3339 |
3157 self.rescanRunning_ = false; | 3340 self.rescanRunning_ = false; |
3158 | |
3159 for (var i= 0; i < callbacks.length; i++) { | 3341 for (var i= 0; i < callbacks.length; i++) { |
3160 if (callbacks[i].onError) | 3342 if (callbacks[i].onSuccess) |
3161 try { | 3343 try { |
3162 callbacks[i].onError(); | 3344 callbacks[i].onSuccess(); |
3163 } catch (ex) { | 3345 } catch (ex) { |
3164 console.error('Caught exception while notifying about error: ' + | 3346 console.error('Caught exception while notifying about error: ' + |
3165 name, ex); | 3347 name, ex); |
3166 } | 3348 } |
3167 } | 3349 } |
| 3350 |
| 3351 return; |
3168 } | 3352 } |
3169 | 3353 |
3170 function onReadSome(entries) { | 3354 // Splice takes the to-be-spliced-in array as individual parameters, |
3171 if (entries.length == 0) { | 3355 // rather than as an array, so we need to perform some acrobatics... |
3172 metrics.recordTime('DirectoryScan'); | 3356 var spliceArgs = [].slice.call(entries); |
3173 if (self.currentDirEntry_.fullPath == DOWNLOADS_DIRECTORY) { | |
3174 metrics.reportCount("DownloadsCount", self.dataModel_.length); | |
3175 } | |
3176 | 3357 |
3177 if (self.pendingRescanQueue_.length > 0) { | 3358 // Hide files that start with a dot ('.'). |
3178 setTimeout(self.rescanDirectory_.bind(self), | 3359 // TODO(rginda): User should be able to override this. Support for other |
3179 SIMULTANEOUS_RESCAN_INTERVAL); | 3360 // commonly hidden patterns might be nice too. |
3180 } | 3361 if (self.filterFiles_) { |
| 3362 spliceArgs = spliceArgs.filter(function(e) { |
| 3363 return e.name.substr(0, 1) != '.'; |
| 3364 }); |
| 3365 } |
3181 | 3366 |
3182 self.rescanRunning_ = false; | 3367 spliceArgs.unshift(0, 0); // index, deleteCount |
3183 for (var i= 0; i < callbacks.length; i++) { | 3368 self.dataModel_.splice.apply(self.dataModel_, spliceArgs); |
3184 if (callbacks[i].onSuccess) | |
3185 try { | |
3186 callbacks[i].onSuccess(); | |
3187 } catch (ex) { | |
3188 console.error('Caught exception while notifying about error: ' + | |
3189 name, ex); | |
3190 } | |
3191 } | |
3192 | 3369 |
3193 return; | 3370 // Keep reading until entries.length is 0. |
3194 } | 3371 reader.readEntries(onReadSome, onError); |
| 3372 }; |
3195 | 3373 |
3196 // Splice takes the to-be-spliced-in array as individual parameters, | 3374 metrics.startInterval('DirectoryScan'); |
3197 // rather than as an array, so we need to perform some acrobatics... | |
3198 var spliceArgs = [].slice.call(entries); | |
3199 | 3375 |
3200 // Hide files that start with a dot ('.'). | 3376 // If not the root directory, just read the contents. |
3201 // TODO(rginda): User should be able to override this. Support for other | 3377 reader = this.currentDirEntry_.createReader(); |
3202 // commonly hidden patterns might be nice too. | 3378 reader.readEntries(onReadSome, onError); |
3203 if (self.filterFiles_) { | |
3204 spliceArgs = spliceArgs.filter(function(e) { | |
3205 return e.name.substr(0, 1) != '.'; | |
3206 }); | |
3207 } | |
3208 | |
3209 spliceArgs.unshift(0, 0); // index, deleteCount | |
3210 self.dataModel_.splice.apply(self.dataModel_, spliceArgs); | |
3211 | |
3212 // Keep reading until entries.length is 0. | |
3213 reader.readEntries(onReadSome, onError); | |
3214 }; | |
3215 | |
3216 metrics.startInterval('DirectoryScan'); | |
3217 | |
3218 // If not the root directory, just read the contents. | |
3219 reader = this.currentDirEntry_.createReader(); | |
3220 reader.readEntries(onReadSome, onError); | |
3221 return; | |
3222 } | |
3223 | |
3224 // Otherwise, use the provided list of root subdirectories, since the | |
3225 // real local filesystem root directory (the one we use outside the | |
3226 // harness) can't be enumerated yet. | |
3227 var spliceArgs = [].slice.call(this.rootEntries_); | |
3228 spliceArgs.unshift(0, 0); // index, deleteCount | |
3229 this.dataModel_.splice.apply(this.dataModel_, spliceArgs); | |
3230 | |
3231 if (opt_callback) | |
3232 opt_callback(); | |
3233 }; | 3379 }; |
3234 | 3380 |
3235 FileManager.prototype.findListItem_ = function(event) { | 3381 FileManager.prototype.findListItem_ = function(event) { |
3236 var node = event.srcElement; | 3382 var node = event.srcElement; |
3237 while (node) { | 3383 while (node) { |
3238 if (node.tagName == 'LI') | 3384 if (node.tagName == 'LI') |
3239 break; | 3385 break; |
3240 node = node.parentNode; | 3386 node = node.parentNode; |
3241 } | 3387 } |
3242 | 3388 |
(...skipping 223 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
3466 var selectionEnd = input.value.lastIndexOf('.'); | 3612 var selectionEnd = input.value.lastIndexOf('.'); |
3467 if (selectionEnd == -1) { | 3613 if (selectionEnd == -1) { |
3468 input.select(); | 3614 input.select(); |
3469 } else { | 3615 } else { |
3470 input.selectionStart = 0; | 3616 input.selectionStart = 0; |
3471 input.selectionEnd = selectionEnd; | 3617 input.selectionEnd = selectionEnd; |
3472 } | 3618 } |
3473 }, 0); | 3619 }, 0); |
3474 }; | 3620 }; |
3475 | 3621 |
3476 FileManager.prototype.onNewFolderButtonClick_ = function(event) { | 3622 FileManager.prototype.onToggleSidebar_ = function(event) { |
| 3623 if (this.dialogContainer_.hasAttribute('sidebar')) { |
| 3624 this.dialogContainer_.removeAttribute('sidebar'); |
| 3625 } else { |
| 3626 this.dialogContainer_.setAttribute('sidebar', 'sidebar'); |
| 3627 } |
| 3628 // TODO(dgozman): make table header css-resizable. |
| 3629 setTimeout(this.onResize_.bind(this), 300); |
| 3630 }; |
| 3631 |
| 3632 FileManager.prototype.onNewFolderCommand_ = function(event) { |
3477 var self = this; | 3633 var self = this; |
3478 | 3634 |
3479 function onNameSelected(name) { | 3635 function onNameSelected(name) { |
3480 var valid = self.validateFileName_(name, function() { | 3636 var valid = self.validateFileName_(name, function() { |
3481 promptForName(name); | 3637 promptForName(name); |
3482 }); | 3638 }); |
3483 | 3639 |
3484 if (!valid) { | 3640 if (!valid) { |
3485 // Validation failed. User will be prompted for a new name after they | 3641 // Validation failed. User will be prompted for a new name after they |
3486 // dismiss the validation error dialog. | 3642 // dismiss the validation error dialog. |
(...skipping 108 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
3595 this.dialogType_ != FileManager.SELECT_FOLDER) { | 3751 this.dialogType_ != FileManager.SELECT_FOLDER) { |
3596 event.preventDefault(); | 3752 event.preventDefault(); |
3597 this.onDirectoryAction(this.selection.entries[0]); | 3753 this.onDirectoryAction(this.selection.entries[0]); |
3598 } else if (!this.okButton_.disabled) { | 3754 } else if (!this.okButton_.disabled) { |
3599 event.preventDefault(); | 3755 event.preventDefault(); |
3600 this.onOk_(); | 3756 this.onOk_(); |
3601 } | 3757 } |
3602 break; | 3758 break; |
3603 | 3759 |
3604 case 32: // Ctrl-Space => New Folder. | 3760 case 32: // Ctrl-Space => New Folder. |
3605 if (this.newFolderButton_.style.display != 'none' && event.ctrlKey) { | 3761 if ((this.dialogType_ == 'saveas-file' || |
| 3762 this.dialogType_ == 'full-page') && event.ctrlKey) { |
3606 event.preventDefault(); | 3763 event.preventDefault(); |
3607 this.onNewFolderButtonClick_(); | 3764 this.onNewFolderCommand_(); |
3608 } | 3765 } |
3609 break; | 3766 break; |
3610 | 3767 |
3611 case 88: // Ctrl-X => Cut. | 3768 case 88: // Ctrl-X => Cut. |
3612 this.updateCommands_(); | 3769 this.updateCommands_(); |
3613 if (!this.commands_['cut'].disabled) { | 3770 if (!this.commands_['cut'].disabled) { |
3614 event.preventDefault(); | 3771 event.preventDefault(); |
3615 this.commands_['cut'].execute(); | 3772 this.commands_['cut'].execute(); |
3616 } | 3773 } |
3617 break; | 3774 break; |
(...skipping 332 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
3950 }); | 4107 }); |
3951 }, onError); | 4108 }, onError); |
3952 | 4109 |
3953 function onError(err) { | 4110 function onError(err) { |
3954 console.log('Error while checking free space: ' + err); | 4111 console.log('Error while checking free space: ' + err); |
3955 setTimeout(doCheck, 1000 * 60); | 4112 setTimeout(doCheck, 1000 * 60); |
3956 } | 4113 } |
3957 } | 4114 } |
3958 } | 4115 } |
3959 })(); | 4116 })(); |
OLD | NEW |