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