Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(555)

Side by Side Diff: chrome/browser/resources/file_manager/js/file_manager.js

Issue 8554003: [filebrowser] Add left panel with roots. (Closed) Base URL: svn://svn.chromium.org/chrome/trunk/src/
Patch Set: '' Created 9 years ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch | Annotate | Revision Log
OLDNEW
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
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
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
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
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
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
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
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
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
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
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
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
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
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
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
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
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
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
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
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
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
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
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
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 })();
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698