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

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 47 matching lines...) Expand 10 before | Expand all | Expand 10 after
58 metrics.recordEnum('Create', this.dialogType_, 58 metrics.recordEnum('Create', this.dialogType_,
59 [FileManager.DialogType.SELECT_FOLDER, 59 [FileManager.DialogType.SELECT_FOLDER,
60 FileManager.DialogType.SELECT_SAVEAS_FILE, 60 FileManager.DialogType.SELECT_SAVEAS_FILE,
61 FileManager.DialogType.SELECT_OPEN_FILE, 61 FileManager.DialogType.SELECT_OPEN_FILE,
62 FileManager.DialogType.SELECT_OPEN_MULTI_FILE, 62 FileManager.DialogType.SELECT_OPEN_MULTI_FILE,
63 FileManager.DialogType.FULL_PAGE]); 63 FileManager.DialogType.FULL_PAGE]);
64 64
65 // TODO(dgozman): This will be changed to LocaleInfo. 65 // TODO(dgozman): This will be changed to LocaleInfo.
66 this.locale_ = new v8Locale(navigator.language); 66 this.locale_ = new v8Locale(navigator.language);
67 67
68 this.resolveRoots_(); 68 this.requestFileSystem_();
69 this.initDom_(); 69 this.initDom_();
70 this.initDialogType_(); 70 this.initDialogType_();
71 this.dialogDom_.style.opacity = '1'; 71 this.dialogDom_.style.opacity = '1';
72 } 72 }
73 73
74 FileManager.prototype = { 74 FileManager.prototype = {
75 __proto__: cr.EventTarget.prototype 75 __proto__: cr.EventTarget.prototype
76 }; 76 };
77 77
78 // Anonymous "namespace". 78 // Anonymous "namespace".
(...skipping 381 matching lines...) Expand 10 before | Expand all | Expand 10 after
460 chrome.fileBrowserPrivate.getStrings(function(strings) { 460 chrome.fileBrowserPrivate.getStrings(function(strings) {
461 localStrings = new LocalStrings(strings); 461 localStrings = new LocalStrings(strings);
462 if (callback) 462 if (callback)
463 callback(); 463 callback();
464 }); 464 });
465 }; 465 };
466 466
467 // Instance methods. 467 // Instance methods.
468 468
469 /** 469 /**
470 * Request file system and get root entries asynchronously. Invokes init_ 470 * Request local file system, resolve roots and init_ after that.
471 * when have finished.
472 */ 471 */
473 FileManager.prototype.resolveRoots_ = function(callback) { 472 FileManager.prototype.requestFileSystem_ = function() {
474 var rootPaths = ['Downloads', 'removable', 'archive']; 473 util.installFileErrorToString();
474 metrics.startInterval('RequestLocalFileSystem');
475 475
476 metrics.startInterval('RequestLocalFileSystem');
477 var self = this; 476 var self = this;
478 477
479 // The list of active mount points to distinct them from other directories. 478 // The list of active mount points to distinct them from other directories.
480 chrome.fileBrowserPrivate.getMountPoints(function(mountPoints) { 479 chrome.fileBrowserPrivate.getMountPoints(function(mountPoints) {
481 self.mountPoints_ = mountPoints; 480 self.mountPoints_ = mountPoints;
482 onDone(); 481 onDone();
483 }); 482 });
484 483
485 function onDone() { 484 function onDone() {
486 if (self.mountPoints_ && self.rootEntries_) 485 if (self.mountPoints_ && self.rootEntries_)
487 self.init_(); 486 self.init_();
488 } 487 }
489 488
490 chrome.fileBrowserPrivate.requestLocalFileSystem(function(filesystem) { 489 chrome.fileBrowserPrivate.requestLocalFileSystem(function(filesystem) {
491 self.filesystem_ = filesystem;
492 util.installFileErrorToString();
493
494 metrics.recordTime('RequestLocalFileSystem'); 490 metrics.recordTime('RequestLocalFileSystem');
495 console.log('Found filesystem: ' + filesystem.name, filesystem); 491 console.log('Found filesystem: ' + filesystem.name, filesystem);
496 492
497 var rootEntries = []; 493 self.filesystem_ = filesystem;
498 494 self.resolveRoots_(function(rootEntries) {
499 function onAllRootsFound() {
500 self.rootEntries_ = rootEntries; 495 self.rootEntries_ = rootEntries;
501 onDone(); 496 onDone();
502 } 497 });
503
504 function onPathError(path, err) {
505 console.error('Error locating root path: ' + path + ': ' + err);
506 }
507
508 function onEntryFound(entry) {
509 if (entry) {
510 rootEntries.push(entry);
511 } else {
512 onAllRootsFound();
513 }
514 }
515
516 metrics.startInterval('EnumerateRoots');
517 if (filesystem.name.match(/^chrome-extension_\S+:external/i)) {
518 // We've been handed the local filesystem, whose root directory
519 // cannot be enumerated.
520 util.getDirectories(filesystem.root, {create: false}, rootPaths,
521 onEntryFound, onPathError);
522 } else {
523 util.forEachDirEntry(filesystem.root, onEntryFound);
524 }
525 }); 498 });
526 }; 499 };
527 500
528 /** 501 /**
502 * Get root entries asynchronously. Invokes callback
503 * when have finished.
504 */
505 FileManager.prototype.resolveRoots_ = function(callback) {
506 var rootPaths = [DOWNLOADS_DIRECTORY, ARCHIVE_DIRECTORY,
507 REMOVABLE_DIRECTORY].map(function(s) { return s.substring(1); });
508 var rootEntries = [];
509
510 // The number of entries left to enumerate to get all roots.
511 // When equals to zero, we are done.
512 // Initially is 100 to prevent finishing before these entries themselves
513 // are found.
514 var entriesToEnumerate = 100;
515
516 function onPathError(path, err) {
517 console.error('Error locating root path: ' + path + ': ' + err);
518 }
519
520 function onRootFound(root) {
521 if (root) {
522 rootEntries.push(root);
523 } else {
524 entriesToEnumerate--;
525 if (entriesToEnumerate == 0)
526 callback(rootEntries);
527 }
528 }
529
530 function onEntryFound(entry) {
531 if (entry) {
532 entriesToEnumerate++;
533 var path = entry.fullPath;
534 if (path == ARCHIVE_DIRECTORY || path == REMOVABLE_DIRECTORY) {
535 // All removable devices and mounted archives are considered
536 // roots, and are shown in the sidebar.
537 util.forEachDirEntry(entry, onRootFound);
538 } else {
539 onRootFound(entry);
540 onRootFound(null);
541 }
542 } else {
543 // No more entries to enumerate - remove fake 100.
544 entriesToEnumerate -= 100;
545 }
546 }
547
548 metrics.startInterval('EnumerateRoots');
549 if (this.filesystem_.name.match(/^chrome-extension_\S+:external/i)) {
550 // We've been handed the local filesystem, whose root directory
551 // cannot be enumerated.
552 util.getDirectories(this.filesystem_.root, {create: false}, rootPaths,
553 onEntryFound, onPathError);
554 } else {
555 util.forEachDirEntry(this.filesystem_.root, onEntryFound);
556 }
557 };
558
559 /**
529 * Continue initializing the file manager after resolving roots. 560 * Continue initializing the file manager after resolving roots.
530 */ 561 */
531 FileManager.prototype.init_ = function() { 562 FileManager.prototype.init_ = function() {
532 metrics.startInterval('InitFileManager'); 563 metrics.startInterval('InitFileManager');
564 this.initCommands_();
533 565
534 // TODO(rginda): 6/22/11: Remove this test when createDateTimeFormat is 566 // TODO(rginda): 6/22/11: Remove this test when createDateTimeFormat is
535 // available in all chrome trunk builds. 567 // available in all chrome trunk builds.
536 if ('createDateTimeFormat' in this.locale_) { 568 if ('createDateTimeFormat' in this.locale_) {
537 this.shortDateFormatter_ = 569 this.shortDateFormatter_ =
538 this.locale_.createDateTimeFormat({'dateType': 'medium'}); 570 this.locale_.createDateTimeFormat({'dateType': 'medium'});
539 } else { 571 } else {
540 this.shortDateFormatter_ = { 572 this.shortDateFormatter_ = {
541 format: function(d) { 573 format: function(d) {
542 return (d.getMonth() + 1) + '/' + d.getDate() + '/' + d.getFullYear(); 574 return (d.getMonth() + 1) + '/' + d.getDate() + '/' + d.getFullYear();
(...skipping 37 matching lines...) Expand 10 before | Expand all | Expand 10 after
580 window.addEventListener('unload', this.onUnload_.bind(this)); 612 window.addEventListener('unload', this.onUnload_.bind(this));
581 613
582 this.addEventListener('directory-changed', 614 this.addEventListener('directory-changed',
583 this.onDirectoryChanged_.bind(this)); 615 this.onDirectoryChanged_.bind(this));
584 this.addEventListener('selection-summarized', 616 this.addEventListener('selection-summarized',
585 this.onSelectionSummarized_.bind(this)); 617 this.onSelectionSummarized_.bind(this));
586 618
587 // The list of archives requested to mount. We will show contents once 619 // The list of archives requested to mount. We will show contents once
588 // archive is mounted, but only for mounts from within this filebrowser tab. 620 // archive is mounted, but only for mounts from within this filebrowser tab.
589 this.mountRequests_ = []; 621 this.mountRequests_ = [];
622 this.unmountRequests_ = [];
590 chrome.fileBrowserPrivate.onMountCompleted.addListener( 623 chrome.fileBrowserPrivate.onMountCompleted.addListener(
591 this.onMountCompleted_.bind(this)); 624 this.onMountCompleted_.bind(this));
592 625
593 chrome.fileBrowserPrivate.onFileChanged.addListener( 626 chrome.fileBrowserPrivate.onFileChanged.addListener(
594 this.onFileChanged_.bind(this)); 627 this.onFileChanged_.bind(this));
595 628
596 var self = this; 629 var self = this;
597 630
598 // The list of callbacks to be invoked during the directory rescan after 631 // The list of callbacks to be invoked during the directory rescan after
599 // all paste tasks are complete. 632 // all paste tasks are complete.
600 this.pasteSuccessCallbacks_ = []; 633 this.pasteSuccessCallbacks_ = [];
601 634
602 this.initCommands_();
603
604 this.setupCurrentDirectory_(); 635 this.setupCurrentDirectory_();
605 636
606 this.summarizeSelection_(); 637 this.summarizeSelection_();
607 638
608 this.dataModel_.sort('cachedMtime_', 'desc'); 639 this.dataModel_.sort('cachedMtime_', 'desc');
609 640
610 this.refocus(); 641 this.refocus();
611 642
612 this.createMetadataProvider_(); 643 this.createMetadataProvider_();
613 metrics.recordTime('InitFileManager'); 644 metrics.recordTime('InitFileManager');
614 metrics.recordTime('TotalLoad'); 645 metrics.recordTime('TotalLoad');
615 }; 646 };
616 647
617 /** 648 /**
618 * One-time initialization of commands. 649 * One-time initialization of commands.
619 */ 650 */
620 FileManager.prototype.initCommands_ = function() { 651 FileManager.prototype.initCommands_ = function() {
621 var commands = this.dialogDom_.querySelectorAll('command'); 652 var commands = this.dialogDom_.querySelectorAll('command');
622 for (var i = 0; i < commands.length; i++) { 653 for (var i = 0; i < commands.length; i++) {
623 var command = commands[i]; 654 var command = commands[i];
624 cr.ui.Command.decorate(command); 655 cr.ui.Command.decorate(command);
656 command.label = command.textContent;
657 command.textContent = '';
625 this.commands_[command.id] = command; 658 this.commands_[command.id] = command;
626 } 659 }
627 660
628 this.fileContextMenu_ = this.dialogDom_.querySelector('.file-context-menu'); 661 this.fileContextMenu_ = this.dialogDom_.querySelector('.file-context-menu');
629 cr.ui.Menu.decorate(this.fileContextMenu_); 662 cr.ui.Menu.decorate(this.fileContextMenu_);
630 663
631 this.document_.addEventListener('canExecute', 664 this.document_.addEventListener('canExecute',
632 this.onCanExecute_.bind(this)); 665 this.onCanExecute_.bind(this));
633 this.document_.addEventListener('command', this.onCommand_.bind(this)); 666 this.document_.addEventListener('command', this.onCommand_.bind(this));
634 } 667 }
(...skipping 17 matching lines...) Expand all
652 // Cache nodes we'll be manipulating. 685 // Cache nodes we'll be manipulating.
653 this.previewThumbnails_ = 686 this.previewThumbnails_ =
654 this.dialogDom_.querySelector('.preview-thumbnails'); 687 this.dialogDom_.querySelector('.preview-thumbnails');
655 this.previewPanel_ = this.dialogDom_.querySelector('.preview-panel'); 688 this.previewPanel_ = this.dialogDom_.querySelector('.preview-panel');
656 this.previewFilename_ = this.dialogDom_.querySelector('.preview-filename'); 689 this.previewFilename_ = this.dialogDom_.querySelector('.preview-filename');
657 this.previewSummary_ = this.dialogDom_.querySelector('.preview-summary'); 690 this.previewSummary_ = this.dialogDom_.querySelector('.preview-summary');
658 this.filenameInput_ = this.dialogDom_.querySelector('.filename-input'); 691 this.filenameInput_ = this.dialogDom_.querySelector('.filename-input');
659 this.taskButtons_ = this.dialogDom_.querySelector('.task-buttons'); 692 this.taskButtons_ = this.dialogDom_.querySelector('.task-buttons');
660 this.okButton_ = this.dialogDom_.querySelector('.ok'); 693 this.okButton_ = this.dialogDom_.querySelector('.ok');
661 this.cancelButton_ = this.dialogDom_.querySelector('.cancel'); 694 this.cancelButton_ = this.dialogDom_.querySelector('.cancel');
662 this.newFolderButton_ = this.dialogDom_.querySelector('.new-folder');
663 this.deleteButton_ = this.dialogDom_.querySelector('.delete-button'); 695 this.deleteButton_ = this.dialogDom_.querySelector('.delete-button');
664 696
665 this.downloadsWarning_ = 697 this.downloadsWarning_ =
666 this.dialogDom_.querySelector('.downloads-warning'); 698 this.dialogDom_.querySelector('.downloads-warning');
667 var html = util.htmlUnescape(str('DOWNLOADS_DIRECTORY_WARNING')); 699 var html = util.htmlUnescape(str('DOWNLOADS_DIRECTORY_WARNING'));
668 this.downloadsWarning_.lastElementChild.innerHTML = html; 700 this.downloadsWarning_.lastElementChild.innerHTML = html;
669 var link = this.downloadsWarning_.querySelector('a'); 701 var link = this.downloadsWarning_.querySelector('a');
670 link.addEventListener('click', this.onDownloadsWarningClick_.bind(this)); 702 link.addEventListener('click', this.onDownloadsWarningClick_.bind(this));
671 703
672 this.document_.addEventListener('keydown', this.onKeyDown_.bind(this)); 704 this.document_.addEventListener('keydown', this.onKeyDown_.bind(this));
(...skipping 10 matching lines...) Expand all
683 'keyup', this.onFilenameInputKeyUp_.bind(this)); 715 'keyup', this.onFilenameInputKeyUp_.bind(this));
684 this.filenameInput_.addEventListener( 716 this.filenameInput_.addEventListener(
685 'focus', this.onFilenameInputFocus_.bind(this)); 717 'focus', this.onFilenameInputFocus_.bind(this));
686 718
687 var listContainer = this.dialogDom_.querySelector('.list-container'); 719 var listContainer = this.dialogDom_.querySelector('.list-container');
688 listContainer.addEventListener('keydown', this.onListKeyDown_.bind(this)); 720 listContainer.addEventListener('keydown', this.onListKeyDown_.bind(this));
689 listContainer.addEventListener('keypress', this.onListKeyPress_.bind(this)); 721 listContainer.addEventListener('keypress', this.onListKeyPress_.bind(this));
690 this.okButton_.addEventListener('click', this.onOk_.bind(this)); 722 this.okButton_.addEventListener('click', this.onOk_.bind(this));
691 this.cancelButton_.addEventListener('click', this.onCancel_.bind(this)); 723 this.cancelButton_.addEventListener('click', this.onCancel_.bind(this));
692 724
693 this.dialogDom_.querySelector('button.new-folder').addEventListener( 725 this.dialogDom_.querySelector('div.open-sidebar').addEventListener(
694 'click', this.onNewFolderButtonClick_.bind(this)); 726 'click', this.onToggleSidebar_.bind(this));
727 this.dialogDom_.querySelector('div.close-sidebar').addEventListener(
728 'click', this.onToggleSidebar_.bind(this));
729 this.dialogContainer_ = this.dialogDom_.querySelector('.dialog-container');
695 730
696 this.dialogDom_.querySelector('button.detail-view').addEventListener( 731 this.dialogDom_.querySelector('button.detail-view').addEventListener(
697 'click', this.onDetailViewButtonClick_.bind(this)); 732 'click', this.onDetailViewButtonClick_.bind(this));
698 this.dialogDom_.querySelector('button.thumbnail-view').addEventListener( 733 this.dialogDom_.querySelector('button.thumbnail-view').addEventListener(
699 'click', this.onThumbnailViewButtonClick_.bind(this)); 734 'click', this.onThumbnailViewButtonClick_.bind(this));
700 735
701 this.dialogDom_.ownerDocument.defaultView.addEventListener( 736 this.dialogDom_.ownerDocument.defaultView.addEventListener(
702 'resize', this.onResize_.bind(this)); 737 'resize', this.onResize_.bind(this));
703 738
704 var ary = this.dialogDom_.querySelectorAll('[visibleif]'); 739 var ary = this.dialogDom_.querySelectorAll('[visibleif]');
(...skipping 32 matching lines...) Expand 10 before | Expand all | Expand 10 after
737 if (this.dialogType_ == FileManager.DialogType.SELECT_OPEN_FILE || 772 if (this.dialogType_ == FileManager.DialogType.SELECT_OPEN_FILE ||
738 this.dialogType_ == FileManager.DialogType.SELECT_OPEN_FOLDER || 773 this.dialogType_ == FileManager.DialogType.SELECT_OPEN_FOLDER ||
739 this.dialogType_ == FileManager.DialogType.SELECT_SAVEAS_FILE) { 774 this.dialogType_ == FileManager.DialogType.SELECT_SAVEAS_FILE) {
740 this.selectionModelClass_ = cr.ui.ListSingleSelectionModel; 775 this.selectionModelClass_ = cr.ui.ListSingleSelectionModel;
741 } else { 776 } else {
742 this.selectionModelClass_ = cr.ui.ListSelectionModel; 777 this.selectionModelClass_ = cr.ui.ListSelectionModel;
743 } 778 }
744 779
745 this.initTable_(); 780 this.initTable_();
746 this.initGrid_(); 781 this.initGrid_();
782 this.initRootsList_();
747 783
748 this.setListType(FileManager.ListType.DETAIL); 784 this.setListType(FileManager.ListType.DETAIL);
749 785
750 this.onResize_(); 786 this.onResize_();
751 787
752 this.textSearchState_ = {text: '', date: new Date()}; 788 this.textSearchState_ = {text: '', date: new Date()};
753 }; 789 };
754 790
791 FileManager.prototype.initRootsList_ = function() {
792 this.rootsList_ = this.dialogDom_.querySelector('.roots-list');
793 cr.ui.List.decorate(this.rootsList_);
794
795 var self = this;
796 this.rootsList_.itemConstructor = function(entry) {
797 return self.renderRoot_(entry);
798 };
799
800 this.rootsList_.selectionModel = new cr.ui.ListSingleSelectionModel();
801 this.rootsList_.selectionModel.addEventListener(
802 'change', this.onRootsSelectionChanged_.bind(this));
803
804 // TODO(dgozman): add "Add a drive" item.
805 this.rootsList_.dataModel = new cr.ui.ArrayDataModel(this.rootEntries_);
806 };
807
808 FileManager.prototype.updateRoots_ = function(opt_changeDirectoryTo) {
809 var self = this;
810 this.resolveRoots_(function(rootEntries) {
811 self.rootEntries_ = rootEntries;
812
813 var dataModel = self.rootsList_.dataModel;
814 var args = [0, dataModel.length].concat(rootEntries);
815 dataModel.splice.apply(dataModel, args);
816
817 self.updateRootsListSelection_();
818
819 if (opt_changeDirectoryTo)
820 self.changeDirectory(opt_changeDirectoryTo);
821 });
822 };
823
755 /** 824 /**
756 * Get the icon type for a given Entry. 825 * Get the icon type for a given Entry.
757 * 826 *
758 * @param {Entry} entry An Entry subclass (FileEntry or DirectoryEntry). 827 * @param {Entry} entry An Entry subclass (FileEntry or DirectoryEntry).
759 * @return {string} 828 * @return {string}
760 */ 829 */
761 FileManager.prototype.getIconType = function(entry) { 830 FileManager.prototype.getIconType = function(entry) {
762 if (!('cachedIconType_' in entry)) 831 if (!('cachedIconType_' in entry))
763 entry.cachedIconType_ = this.computeIconType_(entry); 832 entry.cachedIconType_ = this.computeIconType_(entry);
764 return entry.cachedIconType_; 833 return entry.cachedIconType_;
(...skipping 299 matching lines...) Expand 10 before | Expand all | Expand 10 after
1064 !isSystemDirEntry(this.currentDirEntry_)); 1133 !isSystemDirEntry(this.currentDirEntry_));
1065 1134
1066 case 'delete': 1135 case 'delete':
1067 return (// Initialized to the point where we have a current directory 1136 return (// Initialized to the point where we have a current directory
1068 this.currentDirEntry_ && 1137 this.currentDirEntry_ &&
1069 // Rename not in progress. 1138 // Rename not in progress.
1070 !this.renameInput_.currentEntry && 1139 !this.renameInput_.currentEntry &&
1071 !isSystemDirEntry(this.currentDirEntry_)) && 1140 !isSystemDirEntry(this.currentDirEntry_)) &&
1072 this.selection && 1141 this.selection &&
1073 this.selection.totalCount > 0; 1142 this.selection.totalCount > 0;
1143
1144 case 'newfolder':
1145 return this.currentDirEntry_ &&
1146 (this.dialogType_ == 'saveas-file' ||
1147 this.dialogType_ == 'full-page');
1074 } 1148 }
1075 }; 1149 };
1076 1150
1077 FileManager.prototype.updateCommonActionButtons_ = function() { 1151 FileManager.prototype.updateCommonActionButtons_ = function() {
1078 if (this.deleteButton_) 1152 if (this.deleteButton_)
1079 this.deleteButton_.disabled = !this.canExecute_('delete'); 1153 this.deleteButton_.disabled = !this.canExecute_('delete');
1080 }; 1154 };
1081 1155
1082 FileManager.prototype.setListType = function(type) { 1156 FileManager.prototype.setListType = function(type) {
1083 if (type && type == this.listType_) 1157 if (type && type == this.listType_)
(...skipping 187 matching lines...) Expand 10 before | Expand all | Expand 10 after
1271 leadIndex); 1345 leadIndex);
1272 return; 1346 return;
1273 } 1347 }
1274 1348
1275 this.initiateRename_(label); 1349 this.initiateRename_(label);
1276 return; 1350 return;
1277 1351
1278 case 'delete': 1352 case 'delete':
1279 this.deleteEntries(this.selection.entries); 1353 this.deleteEntries(this.selection.entries);
1280 return; 1354 return;
1355
1356 case 'newfolder':
1357 this.onNewFolderCommand_(event);
1358 return;
1281 } 1359 }
1282 }; 1360 };
1283 1361
1284 /** 1362 /**
1285 * Respond to the back and forward buttons. 1363 * Respond to the back and forward buttons.
1286 */ 1364 */
1287 FileManager.prototype.onPopState_ = function(event) { 1365 FileManager.prototype.onPopState_ = function(event) {
1288 // TODO(serya): We should restore selected items here. 1366 // TODO(serya): We should restore selected items here.
1289 if (this.rootEntries_) 1367 if (this.rootEntries_)
1290 this.setupCurrentDirectory_(); 1368 this.setupCurrentDirectory_();
1291 }; 1369 };
1292 1370
1293 FileManager.prototype.requestResize_ = function(timeout) { 1371 FileManager.prototype.requestResize_ = function(timeout) {
1294 var self = this; 1372 var self = this;
1295 setTimeout(function() { self.onResize_() }, timeout || 0); 1373 setTimeout(function() { self.onResize_() }, timeout || 0);
1296 }; 1374 };
1297 1375
1298 /** 1376 /**
1299 * Resize details and thumb views to fit the new window size. 1377 * Resize details and thumb views to fit the new window size.
1300 */ 1378 */
1301 FileManager.prototype.onResize_ = function() { 1379 FileManager.prototype.onResize_ = function() {
1302 this.table_.style.height = this.grid_.style.height = 1380 this.table_.style.height = this.grid_.style.height =
1303 this.grid_.parentNode.clientHeight + 'px'; 1381 this.grid_.parentNode.clientHeight + 'px';
1304 this.table_.style.width = this.grid_.style.width =
1305 this.grid_.parentNode.clientWidth + 'px';
1306
1307 this.table_.list_.style.width = this.table_.parentNode.clientWidth + 'px';
1308 this.table_.list_.style.height = (this.table_.clientHeight - 1 - 1382 this.table_.list_.style.height = (this.table_.clientHeight - 1 -
1309 this.table_.header_.clientHeight) + 'px'; 1383 this.table_.header_.clientHeight) + 'px';
1310 1384
1311 if (this.listType_ == FileManager.ListType.THUMBNAIL) { 1385 if (this.listType_ == FileManager.ListType.THUMBNAIL) {
1312 var self = this; 1386 var self = this;
1313 setTimeout(function() { 1387 setTimeout(function() {
1314 self.grid_.columns = 0; 1388 self.grid_.columns = 0;
1315 self.grid_.redraw(); 1389 self.grid_.redraw();
1316 }, 0); 1390 }, 0);
1317 } else { 1391 } else {
1318 this.currentList_.redraw(); 1392 this.currentList_.redraw();
1319 } 1393 }
1394
1395 this.rootsList_.style.height =
1396 this.rootsList_.parentNode.clientHeight + 'px';
1397 this.rootsList_.redraw();
1320 }; 1398 };
1321 1399
1322 FileManager.prototype.resolvePath = function( 1400 FileManager.prototype.resolvePath = function(
1323 path, resultCallback, errorCallback) { 1401 path, resultCallback, errorCallback) {
1324 return util.resolvePath(this.filesystem_.root, path, resultCallback, 1402 return util.resolvePath(this.filesystem_.root, path, resultCallback,
1325 errorCallback); 1403 errorCallback);
1326 }; 1404 };
1327 1405
1328 /** 1406 /**
1329 * Restores current directory and may be a selected item after page load (or 1407 * Restores current directory and may be a selected item after page load (or
(...skipping 13 matching lines...) Expand all
1343 this.setupPath_(this.params_.defaultPath); 1421 this.setupPath_(this.params_.defaultPath);
1344 } else { 1422 } else {
1345 this.setupDefaultPath_(); 1423 this.setupDefaultPath_();
1346 } 1424 }
1347 }; 1425 };
1348 1426
1349 FileManager.prototype.setupDefaultPath_ = function() { 1427 FileManager.prototype.setupDefaultPath_ = function() {
1350 // No preset given, find a good place to start. 1428 // No preset given, find a good place to start.
1351 // Check for removable devices, if there are none, go to Downloads. 1429 // Check for removable devices, if there are none, go to Downloads.
1352 var removableDirectoryEntry = this.rootEntries_.filter(function(rootEntry) { 1430 var removableDirectoryEntry = this.rootEntries_.filter(function(rootEntry) {
1353 return rootEntry.fullPath == REMOVABLE_DIRECTORY; 1431 return isParentPath(REMOVABLE_DIRECTORY, rootEntry.fullPath);
1354 })[0]; 1432 })[0];
1355 if (!removableDirectoryEntry) { 1433 this.changeDirectory(
1356 this.changeDirectory(DOWNLOADS_DIRECTORY, CD_NO_HISTORY); 1434 removableDirectoryEntry.fullPath || DOWNLOADS_DIRECTORY,
1357 return; 1435 CD_NO_HISTORY);
1358 }
1359
1360 var foundRemovable = false;
1361 util.forEachDirEntry(removableDirectoryEntry, function(result) {
1362 if (result) {
1363 foundRemovable = true;
1364 } else { // Done enumerating, and we know the answer.
1365 this.changeDirectory(foundRemovable ? '/' : DOWNLOADS_DIRECTORY,
1366 CD_NO_HISTORY);
1367 }
1368 }.bind(this));
1369 }; 1436 };
1370 1437
1371 FileManager.prototype.setupPath_ = function(path) { 1438 FileManager.prototype.setupPath_ = function(path) {
1372 // Split the dirname from the basename. 1439 // Split the dirname from the basename.
1373 var ary = path.match(/^(?:(.*)\/)?([^\/]*)$/); 1440 var ary = path.match(/^(?:(.*)\/)?([^\/]*)$/);
1374 if (!ary) { 1441 if (!ary) {
1375 console.warn('Unable to split default path: ' + path); 1442 console.warn('Unable to split default path: ' + path);
1376 self.changeDirectory('/', CD_NO_HISTORY); 1443 self.changeDirectory('/', CD_NO_HISTORY);
1377 return; 1444 return;
1378 } 1445 }
(...skipping 16 matching lines...) Expand all
1395 return; 1462 return;
1396 } 1463 }
1397 1464
1398 // Leaf is an existing file, cd to its parent directory and select it. 1465 // Leaf is an existing file, cd to its parent directory and select it.
1399 self.changeDirectoryEntry(baseDirEntry, CD_NO_HISTORY, leafEntry.name); 1466 self.changeDirectoryEntry(baseDirEntry, CD_NO_HISTORY, leafEntry.name);
1400 } 1467 }
1401 1468
1402 function onLeafError(err) { 1469 function onLeafError(err) {
1403 // Set filename first so OK button will update in changeDirectoryEntry. 1470 // Set filename first so OK button will update in changeDirectoryEntry.
1404 self.filenameInput_.value = leafName; 1471 self.filenameInput_.value = leafName;
1472 self.selectDefaultPathInFilenameInput_();
1405 if (err = FileError.NOT_FOUND_ERR) { 1473 if (err = FileError.NOT_FOUND_ERR) {
1406 // Leaf does not exist, it's just a suggested file name. 1474 // Leaf does not exist, it's just a suggested file name.
1407 self.changeDirectoryEntry(baseDirEntry, CD_NO_HISTORY); 1475 self.changeDirectoryEntry(baseDirEntry, CD_NO_HISTORY);
1408 } else { 1476 } else {
1409 console.log('Unexpected error resolving default leaf: ' + err); 1477 console.log('Unexpected error resolving default leaf: ' + err);
1410 self.changeDirectoryEntry('/', CD_NO_HISTORY); 1478 self.changeDirectoryEntry('/', CD_NO_HISTORY);
1411 } 1479 }
1412 } 1480 }
1413 1481
1414 self.resolvePath(path, onLeafFound, onLeafError); 1482 self.resolvePath(path, onLeafFound, onLeafError);
1415 } 1483 }
1416 1484
1417 function onBaseError(err) { 1485 function onBaseError(err) {
1418 // Set filename first so OK button will update in changeDirectory. 1486 // Set filename first so OK button will update in changeDirectory.
1419 self.filenameInput_.value = leafName; 1487 self.filenameInput_.value = leafName;
1488 self.selectDefaultPathInFilenameInput_();
1420 console.log('Unexpected error resolving default base "' + 1489 console.log('Unexpected error resolving default base "' +
1421 baseName + '": ' + err); 1490 baseName + '": ' + err);
1422 self.changeDirectory('/', CD_NO_HISTORY); 1491 self.changeDirectory('/', CD_NO_HISTORY);
1423 } 1492 }
1424 1493
1425 if (baseName) { 1494 if (baseName) {
1426 this.filesystem_.root.getDirectory( 1495 this.filesystem_.root.getDirectory(
1427 baseName, {create: false}, onBaseFound, onBaseError); 1496 baseName, {create: false}, onBaseFound, onBaseError);
1428 } else { 1497 } else {
1429 onBaseFound(this.filesystem_.root); 1498 onBaseFound(this.filesystem_.root);
(...skipping 245 matching lines...) Expand 10 before | Expand all | Expand 10 after
1675 icon.className = 'detail-icon'; 1744 icon.className = 'detail-icon';
1676 this.getIconType(entry); 1745 this.getIconType(entry);
1677 icon.setAttribute('iconType', entry.cachedIconType_); 1746 icon.setAttribute('iconType', entry.cachedIconType_);
1678 div.appendChild(icon); 1747 div.appendChild(icon);
1679 1748
1680 return div; 1749 return div;
1681 }; 1750 };
1682 1751
1683 FileManager.prototype.getLabelForRootPath_ = function(path) { 1752 FileManager.prototype.getLabelForRootPath_ = function(path) {
1684 // This hack lets us localize the top level directories. 1753 // This hack lets us localize the top level directories.
1685 if (path == 'Downloads') 1754 if (path == DOWNLOADS_DIRECTORY)
1686 return str('DOWNLOADS_DIRECTORY_LABEL'); 1755 return str('CHROMEBOOK_DIRECTORY_LABEL');
1687 1756
1688 if (path == 'archive') 1757 if (path == ARCHIVE_DIRECTORY)
1689 return str('ARCHIVE_DIRECTORY_LABEL'); 1758 return str('ARCHIVE_DIRECTORY_LABEL');
1759 if (isParentPath(ARCHIVE_DIRECTORY, path))
1760 return path.substring(ARCHIVE_DIRECTORY.length + 1);
1690 1761
1691 if (path == 'removable') 1762 if (path == REMOVABLE_DIRECTORY)
1692 return str('REMOVABLE_DIRECTORY_LABEL'); 1763 return str('REMOVABLE_DIRECTORY_LABEL');
1764 if (isParentPath(REMOVABLE_DIRECTORY, path))
1765 return path.substring(REMOVABLE_DIRECTORY.length + 1);
1693 1766
1694 return path || str('ROOT_DIRECTORY_LABEL'); 1767 return path || str('ROOT_DIRECTORY_LABEL');
1695 }; 1768 };
1696 1769
1770 FileManager.prototype.getRootIconUrl_ = function(path, opt_small) {
1771 var iconUrl = opt_small ? 'images/chromebook_28x28.png' :
1772 'images/chromebook_24x24.png';
1773 if (isParentPath(REMOVABLE_DIRECTORY, path))
1774 iconUrl = 'images/filetype_device.png';
1775 if (isParentPath(ARCHIVE_DIRECTORY, path))
1776 iconUrl = 'images/icon_mount_archive_16x16.png';
1777 return chrome.extension.getURL(iconUrl);
1778 };
1779
1780 FileManager.prototype.renderRoot_ = function(entry) {
1781 var li = this.document_.createElement('li');
1782 li.className = 'root-item';
1783
1784 var icon = this.document_.createElement('img');
1785 icon.setAttribute('src', this.getRootIconUrl_(entry.fullPath, false));
1786 li.appendChild(icon);
1787
1788 var div = this.document_.createElement('div');
1789 div.textContent = this.getLabelForRootPath_(entry.fullPath);
1790 li.appendChild(div);
1791
1792 if (isParentPath(REMOVABLE_DIRECTORY, entry.fullPath) ||
1793 isParentPath(ARCHIVE_DIRECTORY, entry.fullPath)) {
1794 var spacer = this.document_.createElement('div');
1795 spacer.className = 'spacer';
1796 li.appendChild(spacer);
1797
1798 var eject = this.document_.createElement('img');
1799 eject.className = 'root-eject';
1800 eject.setAttribute('src', chrome.extension.getURL('images/eject.png'));
1801 eject.addEventListener('click', this.onEjectClick_.bind(this, entry));
1802 li.appendChild(eject);
1803 }
1804
1805 cr.defineProperty(li, 'lead', cr.PropertyKind.BOOL_ATTR);
1806 cr.defineProperty(li, 'selected', cr.PropertyKind.BOOL_ATTR);
1807 return li;
1808 };
1809
1810 /**
1811 * Handler for eject button clicked.
1812 * @param {Entry} entry Entry to eject.
1813 * @param {Event} event The event.
1814 */
1815 FileManager.prototype.onEjectClick_ = function(entry, event) {
1816 this.unmountRequests_.push(entry.toURL());
1817 chrome.fileBrowserPrivate.removeMount(entry.fullPath);
1818 };
1819
1697 /** 1820 /**
1698 * Render the Name column of the detail table. 1821 * Render the Name column of the detail table.
1699 * 1822 *
1700 * Invoked by cr.ui.Table when a file needs to be rendered. 1823 * Invoked by cr.ui.Table when a file needs to be rendered.
1701 * 1824 *
1702 * @param {Entry} entry The Entry object to render. 1825 * @param {Entry} entry The Entry object to render.
1703 * @param {string} columnId The id of the column to be rendered. 1826 * @param {string} columnId The id of the column to be rendered.
1704 * @param {cr.ui.Table} table The table doing the rendering. 1827 * @param {cr.ui.Table} table The table doing the rendering.
1705 */ 1828 */
1706 FileManager.prototype.renderName_ = function(entry, columnId, table) { 1829 FileManager.prototype.renderName_ = function(entry, columnId, table) {
(...skipping 328 matching lines...) Expand 10 before | Expand all | Expand 10 after
2035 // Skip the button creation. 2158 // Skip the button creation.
2036 if (!str('ENABLE_PHOTO_EDITOR')) continue; 2159 if (!str('ENABLE_PHOTO_EDITOR')) continue;
2037 this.galleryTask_ = task; 2160 this.galleryTask_ = task;
2038 } 2161 }
2039 } 2162 }
2040 this.renderTaskButton_(task); 2163 this.renderTaskButton_(task);
2041 } 2164 }
2042 2165
2043 // These are done in separate functions, as the checks require 2166 // These are done in separate functions, as the checks require
2044 // asynchronous function calls. 2167 // asynchronous function calls.
2045 this.maybeRenderUnmountTask_(selection);
2046 this.maybeRenderFormattingTask_(selection); 2168 this.maybeRenderFormattingTask_(selection);
2047 }; 2169 };
2048 2170
2049 FileManager.prototype.renderTaskButton_ = function(task) { 2171 FileManager.prototype.renderTaskButton_ = function(task) {
2050 var button = this.document_.createElement('button'); 2172 var button = this.document_.createElement('button');
2051 button.addEventListener('click', 2173 button.addEventListener('click',
2052 this.onTaskButtonClicked_.bind(this, task)); 2174 this.onTaskButtonClicked_.bind(this, task));
2053 button.className = 'task-button'; 2175 button.className = 'task-button';
2054 2176
2055 var img = this.document_.createElement('img'); 2177 var img = this.document_.createElement('img');
2056 img.src = task.iconUrl; 2178 img.src = task.iconUrl;
2057 2179
2058 button.appendChild(img); 2180 button.appendChild(img);
2059 var label = this.document_.createElement('div'); 2181 var label = this.document_.createElement('div');
2060 label.appendChild(this.document_.createTextNode(task.title)) 2182 label.appendChild(this.document_.createTextNode(task.title))
2061 button.appendChild(label); 2183 button.appendChild(label);
2062 2184
2063 this.taskButtons_.appendChild(button); 2185 this.taskButtons_.appendChild(button);
2064 }; 2186 };
2065 2187
2066 /** 2188 /**
2067 * Checks whether unmount task should be displayed and if the answer is
2068 * affirmative renders it.
2069 * @param {Object} selection Selected files object.
2070 */
2071 FileManager.prototype.maybeRenderUnmountTask_ = function(selection) {
2072 for (var index = 0; index < selection.urls.length; ++index) {
2073 // Each url should be a mount point.
2074 var path = selection.entries[index].fullPath;
2075 var found = false;
2076 for (var i = 0; i < this.mountPoints_.length; i++) {
2077 var mountPath = this.mountPoints_[i].mountPath;
2078 if (mountPath[0] != '/') {
2079 mountPath = '/' + mountPath;
2080 }
2081 if (mountPath == path && this.mountPoints_[i].mountType == 'file') {
2082 found = true;
2083 break;
2084 }
2085 }
2086 if (!found)
2087 return;
2088 }
2089 this.renderTaskButton_({
2090 taskId: this.getExtensionId_() + '|unmount-archive',
2091 iconUrl:
2092 chrome.extension.getURL('images/icon_unmount_archive_16x16.png'),
2093 title: str('UNMOUNT_ARCHIVE'),
2094 internal: true
2095 });
2096 };
2097
2098 /**
2099 * Checks whether formatting task should be displayed and if the answer is 2189 * Checks whether formatting task should be displayed and if the answer is
2100 * affirmative renders it. Includes asynchronous calls, so it's splitted into 2190 * affirmative renders it. Includes asynchronous calls, so it's splitted into
2101 * three parts. 2191 * three parts.
2102 * @param {Object} selection Selected files object. 2192 * @param {Object} selection Selected files object.
2103 */ 2193 */
2104 FileManager.prototype.maybeRenderFormattingTask_ = function(selection) { 2194 FileManager.prototype.maybeRenderFormattingTask_ = function(selection) {
2105 // Not to make unnecessary getMountPoints() call we doublecheck if there is 2195 // Not to make unnecessary getMountPoints() call we doublecheck if there is
2106 // only one selected entry. 2196 // only one selected entry.
2107 if (selection.entries.length != 1) 2197 if (selection.entries.length != 1)
2108 return; 2198 return;
(...skipping 60 matching lines...) Expand 10 before | Expand all | Expand 10 after
2169 chrome.fileBrowserPrivate.executeTask(task.taskId, urls); 2259 chrome.fileBrowserPrivate.executeTask(task.taskId, urls);
2170 }; 2260 };
2171 2261
2172 /** 2262 /**
2173 * Event handler called when some volume was mounted or unmouted. 2263 * Event handler called when some volume was mounted or unmouted.
2174 */ 2264 */
2175 FileManager.prototype.onMountCompleted_ = function(event) { 2265 FileManager.prototype.onMountCompleted_ = function(event) {
2176 var self = this; 2266 var self = this;
2177 chrome.fileBrowserPrivate.getMountPoints(function(mountPoints) { 2267 chrome.fileBrowserPrivate.getMountPoints(function(mountPoints) {
2178 self.mountPoints_ = mountPoints; 2268 self.mountPoints_ = mountPoints;
2269 var changeDirectoryTo = null;
2270
2179 if (event.eventType == 'mount') { 2271 if (event.eventType == 'mount') {
2180 for (var index = 0; index < self.mountRequests_.length; ++index) { 2272 // Mount request finished - remove it.
2181 if (self.mountRequests_[index] == event.sourceUrl) { 2273 var index = self.mountRequests_.indexOf(event.sourceUrl);
2182 self.mountRequests_.splice(index, 1); 2274 if (index != -1) {
2183 if (event.status == 'success') { 2275 self.mountRequests_.splice(index, 1);
2184 self.changeDirectory(event.mountPath); 2276 // Go to mounted directory, if request was initiated from this tab.
2185 } else { 2277 if (event.status == 'success')
2186 // Report mount error. 2278 changeDirectoryTo = event.mountPath;
2187 if (event.mountType == 'file') {
2188 var fileName = event.sourceUrl.substr(
2189 event.sourceUrl.lastIndexOf('/') + 1);
2190 self.alert.show(strf('ARCHIVE_MOUNT_FAILED', fileName,
2191 event.status));
2192 }
2193 }
2194 return;
2195 }
2196 } 2279 }
2197 } 2280 }
2198 2281
2282 if (event.eventType == 'unmount') {
2283 // Unmount request finished - remove it.
2284 var index = self.unmountRequests_.indexOf(event.sourceUrl);
2285 if (index != -1)
2286 self.unmountRequests_.splice(index, 1);
2287 }
2288
2289 if (event.eventType == 'mount' && event.status != 'success' &&
2290 event.mountType == 'file') {
2291 // Report mount error.
2292 var fileName = event.sourceUrl.substr(
2293 event.sourceUrl.lastIndexOf('/') + 1);
2294 self.alert.show(strf('ARCHIVE_MOUNT_FAILED', fileName,
2295 event.status));
2296 }
2297
2298 if (event.eventType == 'unmount' && event.status != 'success') {
2299 // Report unmount error.
2300 // TODO(dgozman): introduce string and show alert here.
2301 }
2302
2199 if (event.eventType == 'unmount' && event.status == 'success' && 2303 if (event.eventType == 'unmount' && event.status == 'success' &&
2200 self.currentDirEntry_ && 2304 self.currentDirEntry_ &&
2201 isParentPath(event.mountPath, self.currentDirEntry_.fullPath)) { 2305 isParentPath(event.mountPath, self.currentDirEntry_.fullPath)) {
2202 self.changeDirectory(getParentPath(event.mountPath)); 2306 changeDirectoryTo = getParentPath(event.mountPath);
2203 return;
2204 } 2307 }
2205 2308
2206 var rescanDirectoryNeeded = (event.status == 'success'); 2309 // In the case of success, roots are changed and should be rescanned.
2310 if (event.status == 'success')
2311 self.updateRoots_(changeDirectoryTo);
2312
2313 var rescanDirectoryNeeded = false;
Dmitry Zvorygin 2011/11/22 14:42:47 rescanDirecoty API has changed a bit. There's auto
dgozman 2011/11/22 14:51:38 Deleted this. Hooray!
2207 for (var i = 0; i < mountPoints.length; i++) { 2314 for (var i = 0; i < mountPoints.length; i++) {
2208 if (event.sourceUrl == mountPoints[i].sourceUrl && 2315 if (event.sourceUrl == mountPoints[i].sourceUrl &&
2209 mountPoints[i].mountCondition != '') { 2316 mountPoints[i].mountCondition != '') {
2210 rescanDirectoryNeeded = true; 2317 rescanDirectoryNeeded = true;
2211 } 2318 }
2212 } 2319 }
2213 // TODO(dgozman): rescan directory, only if it contains mounted points,
2214 // when mounts location will be decided.
2215 if (rescanDirectoryNeeded) 2320 if (rescanDirectoryNeeded)
2216 self.rescanDirectory_(null, 300); 2321 self.rescanDirectory_(null, 300);
Dmitry Zvorygin 2011/11/22 14:42:47 Rescan directory second parameter is error callbac
dgozman 2011/11/22 14:51:38 And this too. Hooray!
2217 }); 2322 });
2218 }; 2323 };
2219 2324
2220 /** 2325 /**
2221 * Event handler called when some internal task should be executed. 2326 * Event handler called when some internal task should be executed.
2222 */ 2327 */
2223 FileManager.prototype.onFileTaskExecute_ = function(id, details) { 2328 FileManager.prototype.onFileTaskExecute_ = function(id, details) {
2224 var urls = details.urls; 2329 var urls = details.urls;
2225 if (id == 'preview') { 2330 if (id == 'preview') {
2226 g_slideshow_data = urls; 2331 g_slideshow_data = urls;
2227 chrome.tabs.create({url: "slideshow.html"}); 2332 chrome.tabs.create({url: "slideshow.html"});
2228 } else if (id == 'play' || id == 'enqueue') { 2333 } else if (id == 'play' || id == 'enqueue') {
2229 chrome.fileBrowserPrivate.viewFiles(urls, id); 2334 chrome.fileBrowserPrivate.viewFiles(urls, id);
2230 } else if (id == 'mount-archive') { 2335 } else if (id == 'mount-archive') {
2231 for (var index = 0; index < urls.length; ++index) { 2336 for (var index = 0; index < urls.length; ++index) {
2232 this.mountRequests_.push(urls[index]); 2337 this.mountRequests_.push(urls[index]);
2233 chrome.fileBrowserPrivate.addMount(urls[index], 'file', {}); 2338 chrome.fileBrowserPrivate.addMount(urls[index], 'file', {});
2234 } 2339 }
2235 } else if (id == 'unmount-archive') {
2236 for (var index = 0; index < urls.length; ++index) {
2237 chrome.fileBrowserPrivate.removeMount(urls[index]);
2238 }
2239 } else if (id == 'format-device') { 2340 } else if (id == 'format-device') {
2240 this.confirm.show(str('FORMATTING_WARNING'), function() { 2341 this.confirm.show(str('FORMATTING_WARNING'), function() {
2241 chrome.fileBrowserPrivate.formatDevice(urls[0]); 2342 chrome.fileBrowserPrivate.formatDevice(urls[0]);
2242 }); 2343 });
2243 } else if (id == 'gallery') { 2344 } else if (id == 'gallery') {
2244 // Pass to gallery all possible tasks except the gallery itself. 2345 // Pass to gallery all possible tasks except the gallery itself.
2245 var noGallery = []; 2346 var noGallery = [];
2246 for (var index = 0; index < details.task.allTasks.length; index++) { 2347 for (var index = 0; index < details.task.allTasks.length; index++) {
2247 var task = details.task.allTasks[index]; 2348 var task = details.task.allTasks[index];
2248 if (task.taskId != this.getExtensionId_() + '|gallery') { 2349 if (task.taskId != this.getExtensionId_() + '|gallery') {
(...skipping 56 matching lines...) Expand 10 before | Expand all | Expand 10 after
2305 self.metadataProvider_, 2406 self.metadataProvider_,
2306 shareActions, 2407 shareActions,
2307 str); 2408 str);
2308 }; 2409 };
2309 2410
2310 galleryFrame.src = 'js/image_editor/gallery.html'; 2411 galleryFrame.src = 'js/image_editor/gallery.html';
2311 this.dialogDom_.appendChild(galleryFrame); 2412 this.dialogDom_.appendChild(galleryFrame);
2312 galleryFrame.focus(); 2413 galleryFrame.focus();
2313 }; 2414 };
2314 2415
2416 FileManager.prototype.getRootForPath_ = function(path) {
2417 for (var index = 0; index < this.rootEntries_.length; index++) {
2418 if (isParentPath(this.rootEntries_[index].fullPath, path)) {
2419 return index;
2420 }
2421 }
2422 return -1;
2423 };
2424
2315 /** 2425 /**
2316 * Update the breadcrumb display to reflect the current directory. 2426 * Update the breadcrumb display to reflect the current directory.
2317 */ 2427 */
2318 FileManager.prototype.updateBreadcrumbs_ = function() { 2428 FileManager.prototype.updateBreadcrumbs_ = function() {
2319 var bc = this.dialogDom_.querySelector('.breadcrumbs'); 2429 var bc = this.dialogDom_.querySelector('.breadcrumbs');
2320 bc.innerHTML = ''; 2430 bc.innerHTML = '';
2321 2431
2322 var fullPath = this.currentDirEntry_.fullPath.replace(/\/$/, ''); 2432 var fullPath = this.currentDirEntry_.fullPath;
2323 var pathNames = fullPath.split('/'); 2433 var rootIndex = this.getRootForPath_(fullPath);
2324 var path = ''; 2434 if (rootIndex == -1) {
2435 console.error('Not root for: ' + fullPath);
2436 return;
2437 }
2438 var root = this.rootEntries_[rootIndex];
2439
2440 var icon = this.document_.createElement('img');
2441 icon.className = 'breadcrumb-icon';
2442 icon.setAttribute('src', this.getRootIconUrl_(root.fullPath, true));
2443 bc.appendChild(icon);
2444
2445 var rootPath = root.fullPath;
2446 var relativePath = fullPath.substring(rootPath.length);
2447 var pathNames = relativePath.replace(/\/$/, '').split('/');
2448 if (pathNames[0] == '')
2449 pathNames.splice(0, 1);
2450
2451 // We need a first breadcrumb for root, so placing last name from
2452 // rootPath as first name of relativePath.
2453 var rootPathNames = rootPath.replace(/\/$/, '').split('/');
2454 pathNames.splice(0, 0, rootPathNames[rootPathNames.length - 1]);
2455 rootPathNames.splice(rootPathNames.length - 1, 1);
2456 var path = rootPathNames.join('/') + '/';
2325 2457
2326 for (var i = 0; i < pathNames.length; i++) { 2458 for (var i = 0; i < pathNames.length; i++) {
2327 var pathName = pathNames[i]; 2459 var pathName = pathNames[i];
2328 path += pathName + '/'; 2460 path += pathName;
2329 2461
2330 var div = this.document_.createElement('div'); 2462 var div = this.document_.createElement('div');
2331 div.className = 'breadcrumb-path'; 2463 div.className = 'breadcrumb-path';
2332 if (i <= 1) { 2464 if (i == 0) {
2333 // i == 0: root directory itself, i == 1: the files it contains. 2465 div.textContent = this.getLabelForRootPath_(path);
2334 div.textContent = this.getLabelForRootPath_(pathName);
2335 } else { 2466 } else {
2336 div.textContent = pathName; 2467 div.textContent = pathName;
2337 } 2468 }
2338 2469
2470 path = path + '/';
2339 div.path = path; 2471 div.path = path;
2340 div.addEventListener('click', this.onBreadcrumbClick_.bind(this)); 2472 div.addEventListener('click', this.onBreadcrumbClick_.bind(this));
2341 2473
2342 bc.appendChild(div); 2474 bc.appendChild(div);
2343 2475
2344 if (i == pathNames.length - 1) { 2476 if (i == pathNames.length - 1) {
2345 div.classList.add('breadcrumb-last'); 2477 div.classList.add('breadcrumb-last');
2346 } else { 2478 } else {
2347 var spacer = this.document_.createElement('div'); 2479 var spacer = this.document_.createElement('div');
2348 spacer.className = 'breadcrumb-spacer'; 2480 spacer.className = 'breadcrumb-spacer';
(...skipping 79 matching lines...) Expand 10 before | Expand all | Expand 10 after
2428 for (var i = 0; i < this.dataModel_.length; i++) { 2560 for (var i = 0; i < this.dataModel_.length; i++) {
2429 if (this.dataModel_.item(i).name == name) { 2561 if (this.dataModel_.item(i).name == name) {
2430 this.currentList_.selectionModel.selectedIndex = i; 2562 this.currentList_.selectionModel.selectedIndex = i;
2431 this.currentList_.scrollIndexIntoView(i); 2563 this.currentList_.scrollIndexIntoView(i);
2432 this.currentList_.focus(); 2564 this.currentList_.focus();
2433 return; 2565 return;
2434 } 2566 }
2435 } 2567 }
2436 }; 2568 };
2437 2569
2570 FileManager.prototype.updateRootsListSelection_ = function() {
2571 if (!this.currentDirEntry_) return;
2572 var index = this.getRootForPath_(this.currentDirEntry_.fullPath);
2573 if (index == -1) {
2574 this.rootsList_.selectionModel.selectedIndex = 0;
2575 } else {
2576 if (this.rootsList_.selectionModel.selectedIndex != index)
2577 this.rootsList_.selectionModel.selectedIndex = index;
2578 }
2579 };
2580
2438 /** 2581 /**
2439 * Add the file/directory with given name to the current selection. 2582 * Add the file/directory with given name to the current selection.
2440 * 2583 *
2441 * @param {string} name The name of the entry to select. 2584 * @param {string} name The name of the entry to select.
2442 * @return {boolean} Whether entry exists. 2585 * @return {boolean} Whether entry exists.
2443 */ 2586 */
2444 FileManager.prototype.addItemToSelection = function(name) { 2587 FileManager.prototype.addItemToSelection = function(name) {
2445 var entryExists = false; 2588 var entryExists = false;
2446 for (var i = 0; i < this.dataModel_.length; i++) { 2589 for (var i = 0; i < this.dataModel_.length; i++) {
2447 if (this.dataModel_.item(i).name == name) { 2590 if (this.dataModel_.item(i).name == name) {
(...skipping 95 matching lines...) Expand 10 before | Expand all | Expand 10 after
2543 FileManager.prototype.changeDirectoryEntry = function(dirEntry, 2686 FileManager.prototype.changeDirectoryEntry = function(dirEntry,
2544 opt_saveHistory, 2687 opt_saveHistory,
2545 opt_selectedEntry, 2688 opt_selectedEntry,
2546 opt_callback) { 2689 opt_callback) {
2547 if (typeof opt_saveHistory == 'undefined') { 2690 if (typeof opt_saveHistory == 'undefined') {
2548 opt_saveHistory = true; 2691 opt_saveHistory = true;
2549 } else { 2692 } else {
2550 opt_saveHistory = !!opt_saveHistory; 2693 opt_saveHistory = !!opt_saveHistory;
2551 } 2694 }
2552 2695
2696 // Some directories are above roots, so we instead show the first root.
2697 // There may be request to change directory above the roots. For example,
2698 // when usb-dirve is removed, we try to change to the parent directory,
2699 // which is REMOVABLE_DIRECTORY.
2700 if (!dirEntry || dirEntry.fullPath == '/' ||
2701 dirEntry.fullPath == REMOVABLE_DIRECTORY ||
2702 dirEntry.fullPath == ARCHIVE_DIRECTORY) {
2703 dirEntry = this.rootEntries_[0] || dirEntry;
2704 }
2705
2553 var location = '#' + encodeURI(dirEntry.fullPath); 2706 var location = '#' + encodeURI(dirEntry.fullPath);
2554 if (opt_saveHistory) { 2707 if (opt_saveHistory) {
2555 history.pushState(undefined, dirEntry.fullPath, location); 2708 history.pushState(undefined, dirEntry.fullPath, location);
2556 } else if (window.location.hash != location) { 2709 } else if (window.location.hash != location) {
2557 // If the user typed URL manually that is not canonical it would be fixed 2710 // If the user typed URL manually that is not canonical it would be fixed
2558 // here. However it seems history.replaceState doesn't work properly 2711 // here. However it seems history.replaceState doesn't work properly
2559 // with rewritable URLs (while does with history.pushState). It changes 2712 // with rewritable URLs (while does with history.pushState). It changes
2560 // window.location but doesn't change content of the ombibox. 2713 // window.location but doesn't change content of the ombibox.
2561 history.replaceState(undefined, dirEntry.fullPath, location); 2714 history.replaceState(undefined, dirEntry.fullPath, location);
2562 } 2715 }
(...skipping 216 matching lines...) Expand 10 before | Expand all | Expand 10 after
2779 // then the default action of this click event fires and toggles the 2932 // then the default action of this click event fires and toggles the
2780 // checkbox back off. 2933 // checkbox back off.
2781 // 2934 //
2782 // Since we're going to force checkboxes into the correct state for any 2935 // Since we're going to force checkboxes into the correct state for any
2783 // multi-selection, we can prevent this shift click from toggling the 2936 // multi-selection, we can prevent this shift click from toggling the
2784 // checkbox and avoid the trouble. 2937 // checkbox and avoid the trouble.
2785 event.preventDefault(); 2938 event.preventDefault();
2786 } 2939 }
2787 }; 2940 };
2788 2941
2942 FileManager.prototype.onRootsSelectionChanged_ = function(event) {
2943 var root = this.rootEntries_[this.rootsList_.selectionModel.selectedIndex];
2944 this.changeDirectoryEntry(root);
2945 };
2946
2947 FileManager.prototype.selectDefaultPathInFilenameInput_ = function() {
2948 var input = this.filenameInput_;
2949 input.focus();
2950 var selectionEnd = input.value.lastIndexOf('.');
2951 if (selectionEnd == -1) {
2952 input.select();
2953 } else {
2954 input.selectionStart = 0;
2955 input.selectionEnd = selectionEnd;
2956 }
2957 // Clear, so we never do this again.
2958 this.params_.defaultPath = '';
2959 };
2960
2789 /** 2961 /**
2790 * Update the UI when the selection model changes. 2962 * Update the UI when the selection model changes.
2791 * 2963 *
2792 * @param {cr.Event} event The change event. 2964 * @param {cr.Event} event The change event.
2793 */ 2965 */
2794 FileManager.prototype.onSelectionChanged_ = function(event) { 2966 FileManager.prototype.onSelectionChanged_ = function(event) {
2795 this.summarizeSelection_(); 2967 this.summarizeSelection_();
2796 2968
2797 if (this.dialogType_ == FileManager.DialogType.SELECT_SAVEAS_FILE) { 2969 if (this.dialogType_ == FileManager.DialogType.SELECT_SAVEAS_FILE) {
2798 // If this is a save-as dialog, copy the selected file into the filename 2970 // If this is a save-as dialog, copy the selected file into the filename
2799 // input text box. 2971 // input text box.
2800 2972
2801 if (this.selection && 2973 if (this.selection &&
2802 this.selection.totalCount == 1 && 2974 this.selection.totalCount == 1 &&
2803 this.selection.entries[0].isFile) 2975 this.selection.entries[0].isFile &&
2976 this.filenameInput_.value != this.selection.entries[0].name) {
2804 this.filenameInput_.value = this.selection.entries[0].name; 2977 this.filenameInput_.value = this.selection.entries[0].name;
2978 if (this.params_.defaultPath == this.selection.entries[0].fullPath)
2979 this.selectDefaultPathInFilenameInput_();
2980 }
2805 } 2981 }
2806 2982
2807 this.updateOkButton_(); 2983 this.updateOkButton_();
2808 2984
2809 var self = this; 2985 var self = this;
2810 setTimeout(function() { self.onSelectionChangeComplete_(event) }, 0); 2986 setTimeout(function() { self.onSelectionChangeComplete_(event) }, 0);
2811 }; 2987 };
2812 2988
2813 /** 2989 /**
2814 * Handle selection change related tasks that won't run properly during 2990 * Handle selection change related tasks that won't run properly during
(...skipping 132 matching lines...) Expand 10 before | Expand all | Expand 10 after
2947 * Update the UI when the current directory changes. 3123 * Update the UI when the current directory changes.
2948 * 3124 *
2949 * @param {cr.Event} event The directory-changed event. 3125 * @param {cr.Event} event The directory-changed event.
2950 */ 3126 */
2951 FileManager.prototype.onDirectoryChanged_ = function(event) { 3127 FileManager.prototype.onDirectoryChanged_ = function(event) {
2952 this.updateCommands_(); 3128 this.updateCommands_();
2953 this.updateOkButton_(); 3129 this.updateOkButton_();
2954 3130
2955 this.checkFreeSpace_(this.currentDirEntry_.fullPath); 3131 this.checkFreeSpace_(this.currentDirEntry_.fullPath);
2956 3132
2957 // New folder should never be enabled in the root or media/ directories. 3133 // TODO(dgozman): title may be better than this.
2958 this.newFolderButton_.disabled = isSystemDirEntry(this.currentDirEntry_);
2959
2960 this.document_.title = this.currentDirEntry_.fullPath; 3134 this.document_.title = this.currentDirEntry_.fullPath;
2961 3135
2962 var self = this; 3136 var self = this;
2963 3137
2964 if (this.subscribedOnDirectoryChanges_) { 3138 if (this.subscribedOnDirectoryChanges_) {
2965 chrome.fileBrowserPrivate.removeFileWatch(event.previousDirEntry.toURL(), 3139 chrome.fileBrowserPrivate.removeFileWatch(event.previousDirEntry.toURL(),
2966 function(result) { 3140 function(result) {
2967 if (!result) { 3141 if (!result) {
2968 console.log('Failed to remove file watch'); 3142 console.log('Failed to remove file watch');
2969 } 3143 }
(...skipping 75 matching lines...) Expand 10 before | Expand all | Expand 10 after
3045 // Updated when a user clicks on the label of a file, used to detect 3219 // Updated when a user clicks on the label of a file, used to detect
3046 // when a click is eligible to trigger a rename. Can be null, or 3220 // when a click is eligible to trigger a rename. Can be null, or
3047 // an object with 'path' and 'date' properties. 3221 // an object with 'path' and 'date' properties.
3048 this.lastLabelClick_ = null; 3222 this.lastLabelClick_ = null;
3049 3223
3050 // Clear the table first. 3224 // Clear the table first.
3051 this.dataModel_.splice(0, this.dataModel_.length); 3225 this.dataModel_.splice(0, this.dataModel_.length);
3052 this.currentList_.selectionModel.clear(); 3226 this.currentList_.selectionModel.clear();
3053 3227
3054 this.updateBreadcrumbs_(); 3228 this.updateBreadcrumbs_();
3229 this.updateRootsListSelection_();
3055 3230
3056 if (this.currentDirEntry_.fullPath != '/') { 3231 if (this.currentDirEntry_.fullPath != '/') {
3057 // Add current request to pending result list 3232 // Add current request to pending result list
3058 this.pendingRescanQueue_.push({ 3233 this.pendingRescanQueue_.push({
3059 onSuccess:opt_callback, 3234 onSuccess:opt_callback,
3060 onError:opt_onError 3235 onError:opt_onError
3061 }); 3236 });
3062 3237
3063 if (this.rescanRunning_) 3238 if (this.rescanRunning_)
3064 return; 3239 return;
(...skipping 328 matching lines...) Expand 10 before | Expand all | Expand 10 after
3393 var selectionEnd = input.value.lastIndexOf('.'); 3568 var selectionEnd = input.value.lastIndexOf('.');
3394 if (selectionEnd == -1) { 3569 if (selectionEnd == -1) {
3395 input.select(); 3570 input.select();
3396 } else { 3571 } else {
3397 input.selectionStart = 0; 3572 input.selectionStart = 0;
3398 input.selectionEnd = selectionEnd; 3573 input.selectionEnd = selectionEnd;
3399 } 3574 }
3400 }, 0); 3575 }, 0);
3401 }; 3576 };
3402 3577
3403 FileManager.prototype.onNewFolderButtonClick_ = function(event) { 3578 FileManager.prototype.onToggleSidebar_ = function(event) {
3579 if (this.dialogContainer_.hasAttribute('sidebar')) {
3580 this.dialogContainer_.removeAttribute('sidebar');
3581 } else {
3582 this.dialogContainer_.setAttribute('sidebar', 'sidebar');
3583 }
3584 setTimeout(this.onResize_.bind(this), 300);
3585 };
3586
3587 FileManager.prototype.onNewFolderCommand_ = function(event) {
3404 var self = this; 3588 var self = this;
3405 3589
3406 function onNameSelected(name) { 3590 function onNameSelected(name) {
3407 var valid = self.validateFileName_(name, function() { 3591 var valid = self.validateFileName_(name, function() {
3408 promptForName(name); 3592 promptForName(name);
3409 }); 3593 });
3410 3594
3411 if (!valid) { 3595 if (!valid) {
3412 // Validation failed. User will be prompted for a new name after they 3596 // Validation failed. User will be prompted for a new name after they
3413 // dismiss the validation error dialog. 3597 // dismiss the validation error dialog.
(...skipping 108 matching lines...) Expand 10 before | Expand all | Expand 10 after
3522 this.dialogType_ != FileManager.SELECT_FOLDER) { 3706 this.dialogType_ != FileManager.SELECT_FOLDER) {
3523 event.preventDefault(); 3707 event.preventDefault();
3524 this.onDirectoryAction(this.selection.entries[0]); 3708 this.onDirectoryAction(this.selection.entries[0]);
3525 } else if (!this.okButton_.disabled) { 3709 } else if (!this.okButton_.disabled) {
3526 event.preventDefault(); 3710 event.preventDefault();
3527 this.onOk_(); 3711 this.onOk_();
3528 } 3712 }
3529 break; 3713 break;
3530 3714
3531 case 32: // Ctrl-Space => New Folder. 3715 case 32: // Ctrl-Space => New Folder.
3532 if (this.newFolderButton_.style.display != 'none' && event.ctrlKey) { 3716 if ((this.dialogType_ == 'saveas-file' ||
3717 this.dialogType_ == 'full-page') && event.ctrlKey) {
3533 event.preventDefault(); 3718 event.preventDefault();
3534 this.onNewFolderButtonClick_(); 3719 this.onNewFolderCommand_();
3535 } 3720 }
3536 break; 3721 break;
3537 3722
3538 case 88: // Ctrl-X => Cut. 3723 case 88: // Ctrl-X => Cut.
3539 this.updateCommands_(); 3724 this.updateCommands_();
3540 if (!this.commands_['cut'].disabled) { 3725 if (!this.commands_['cut'].disabled) {
3541 event.preventDefault(); 3726 event.preventDefault();
3542 this.commands_['cut'].execute(); 3727 this.commands_['cut'].execute();
3543 } 3728 }
3544 break; 3729 break;
(...skipping 308 matching lines...) Expand 10 before | Expand all | Expand 10 after
3853 }); 4038 });
3854 }, onError); 4039 }, onError);
3855 4040
3856 function onError(err) { 4041 function onError(err) {
3857 console.log('Error while checking free space: ' + err); 4042 console.log('Error while checking free space: ' + err);
3858 setTimeout(doCheck, 1000 * 60); 4043 setTimeout(doCheck, 1000 * 60);
3859 } 4044 }
3860 } 4045 }
3861 } 4046 }
3862 })(); 4047 })();
OLDNEW
« no previous file with comments | « chrome/browser/resources/file_manager/images/open_sidebar.png ('k') | chrome/browser/resources/file_manager/js/harness.js » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698