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

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 49 matching lines...) Expand 10 before | Expand all | Expand 10 after
60 metrics.recordEnum('Create', this.dialogType_, 60 metrics.recordEnum('Create', this.dialogType_,
61 [FileManager.DialogType.SELECT_FOLDER, 61 [FileManager.DialogType.SELECT_FOLDER,
62 FileManager.DialogType.SELECT_SAVEAS_FILE, 62 FileManager.DialogType.SELECT_SAVEAS_FILE,
63 FileManager.DialogType.SELECT_OPEN_FILE, 63 FileManager.DialogType.SELECT_OPEN_FILE,
64 FileManager.DialogType.SELECT_OPEN_MULTI_FILE, 64 FileManager.DialogType.SELECT_OPEN_MULTI_FILE,
65 FileManager.DialogType.FULL_PAGE]); 65 FileManager.DialogType.FULL_PAGE]);
66 66
67 // TODO(dgozman): This will be changed to LocaleInfo. 67 // TODO(dgozman): This will be changed to LocaleInfo.
68 this.locale_ = new v8Locale(navigator.language); 68 this.locale_ = new v8Locale(navigator.language);
69 69
70 this.resolveRoots_(); 70 this.requestFileSystem_();
71 this.initDom_(); 71 this.initDom_();
72 this.initDialogType_(); 72 this.initDialogType_();
73 this.dialogDom_.style.opacity = '1'; 73 this.dialogDom_.style.opacity = '1';
74 } 74 }
75 75
76 FileManager.prototype = { 76 FileManager.prototype = {
77 __proto__: cr.EventTarget.prototype 77 __proto__: cr.EventTarget.prototype
78 }; 78 };
79 79
80 // Anonymous "namespace". 80 // Anonymous "namespace".
(...skipping 381 matching lines...) Expand 10 before | Expand all | Expand 10 after
462 chrome.fileBrowserPrivate.getStrings(function(strings) { 462 chrome.fileBrowserPrivate.getStrings(function(strings) {
463 localStrings = new LocalStrings(strings); 463 localStrings = new LocalStrings(strings);
464 if (callback) 464 if (callback)
465 callback(); 465 callback();
466 }); 466 });
467 }; 467 };
468 468
469 // Instance methods. 469 // Instance methods.
470 470
471 /** 471 /**
472 * Request file system and get root entries asynchronously. Invokes init_ 472 * Request local file system, resolve roots and init_ after that.
473 * when have finished.
474 */ 473 */
475 FileManager.prototype.resolveRoots_ = function(callback) { 474 FileManager.prototype.requestFileSystem_ = function() {
Dmitry Zvorygin 2011/11/22 14:42:47 It might be better to move requestFileSystem_ to i
dgozman 2011/11/22 14:51:37 Renamed.
476 var rootPaths = ['Downloads', 'removable', 'archive']; 475 util.installFileErrorToString();
476 metrics.startInterval('RequestLocalFileSystem');
477 477
478 metrics.startInterval('RequestLocalFileSystem');
479 var self = this; 478 var self = this;
480 chrome.fileBrowserPrivate.requestLocalFileSystem(function(filesystem) { 479 chrome.fileBrowserPrivate.requestLocalFileSystem(function(filesystem) {
481 self.filesystem_ = filesystem;
482 util.installFileErrorToString();
483
484 metrics.recordTime('RequestLocalFileSystem'); 480 metrics.recordTime('RequestLocalFileSystem');
485 console.log('Found filesystem: ' + filesystem.name, filesystem); 481 console.log('Found filesystem: ' + filesystem.name, filesystem);
486 482
487 var rootEntries = []; 483 self.filesystem_ = filesystem;
488 484 self.resolveRoots_(function(rootEntries) {
489 function onAllRootsFound() {
490 self.rootEntries_ = rootEntries; 485 self.rootEntries_ = rootEntries;
491 self.init_(); 486 self.init_();
492 } 487 });
493
494 function onPathError(path, err) {
495 console.error('Error locating root path: ' + path + ': ' + err);
496 }
497
498 function onEntryFound(entry) {
499 if (entry) {
500 rootEntries.push(entry);
501 } else {
502 onAllRootsFound();
503 }
504 }
505
506 metrics.startInterval('EnumerateRoots');
507 if (filesystem.name.match(/^chrome-extension_\S+:external/i)) {
508 // We've been handed the local filesystem, whose root directory
509 // cannot be enumerated.
510 util.getDirectories(filesystem.root, {create: false}, rootPaths,
511 onEntryFound, onPathError);
512 } else {
513 util.forEachDirEntry(filesystem.root, onEntryFound);
514 }
515 }); 488 });
516 }; 489 };
517 490
518 /** 491 /**
492 * Get root entries asynchronously. Invokes callback
493 * when have finished.
494 */
495 FileManager.prototype.resolveRoots_ = function(callback) {
496 var rootPaths = [DOWNLOADS_DIRECTORY, ARCHIVE_DIRECTORY,
497 REMOVABLE_DIRECTORY].map(function(s) { return s.substring(1); });
498 var rootEntries = [];
499
500 // The number of entries left to enumerate to get all roots.
501 // When equals to zero, we are done.
502 // Initially is 100 to prevent finishing before these entries themselves
503 // are found.
504 var entriesToEnumerate = 100;
Vladislav Kaznacheev 2011/11/22 13:16:35 Seems quite hacky. How about having a set of direc
dgozman 2011/11/22 14:15:14 We do not know the whole set in advance. Even wors
505
506 function onAllRootsFound() {
Vladislav Kaznacheev 2011/11/22 13:16:35 It is only called from one place, lets inline it.
dgozman 2011/11/22 14:15:14 Done.
507 callback(rootEntries);
508 }
509
510 function onPathError(path, err) {
511 console.error('Error locating root path: ' + path + ': ' + err);
512 }
513
514 function onRootFound(root) {
515 if (root) {
516 rootEntries.push(root);
517 } else {
518 entriesToEnumerate--;
519 if (entriesToEnumerate == 0)
520 onAllRootsFound();
521 }
522 }
523
524 function onEntryFound(entry) {
525 if (entry) {
526 entriesToEnumerate++;
527 var path = entry.fullPath;
528 if (path == ARCHIVE_DIRECTORY || path == REMOVABLE_DIRECTORY) {
529 // All removable devices and mounted archives are considered
530 // roots, and are shown in the sidebar.
531 util.forEachDirEntry(entry, onRootFound);
532 } else {
533 onRootFound(entry);
534 onRootFound(null);
535 }
536 } else {
537 // No more entries to enumerate - remove fake 100.
538 entriesToEnumerate -= 100;
539 }
540 }
541
542 metrics.startInterval('EnumerateRoots');
Dmitry Zvorygin 2011/11/22 14:42:47 Should be corresponding metrics.recordTime or some
dgozman 2011/11/22 14:51:37 Done.
543 if (this.filesystem_.name.match(/^chrome-extension_\S+:external/i)) {
544 // We've been handed the local filesystem, whose root directory
545 // cannot be enumerated.
546 util.getDirectories(this.filesystem_.root, {create: false}, rootPaths,
547 onEntryFound, onPathError);
548 } else {
549 util.forEachDirEntry(this.filesystem_.root, onEntryFound);
550 }
551 };
552
553 /**
519 * Continue initializing the file manager after resolving roots. 554 * Continue initializing the file manager after resolving roots.
520 */ 555 */
521 FileManager.prototype.init_ = function() { 556 FileManager.prototype.init_ = function() {
522 metrics.startInterval('InitFileManager'); 557 metrics.startInterval('InitFileManager');
558 this.initCommands_();
523 this.initFileList_(); 559 this.initFileList_();
524 this.initDialogs_(); 560 this.initDialogs_();
525 561
526 // TODO(rginda): 6/22/11: Remove this test when createDateTimeFormat is 562 // TODO(rginda): 6/22/11: Remove this test when createDateTimeFormat is
527 // available in all chrome trunk builds. 563 // available in all chrome trunk builds.
528 if ('createDateTimeFormat' in this.locale_) { 564 if ('createDateTimeFormat' in this.locale_) {
529 this.shortDateFormatter_ = 565 this.shortDateFormatter_ =
530 this.locale_.createDateTimeFormat({'dateType': 'medium'}); 566 this.locale_.createDateTimeFormat({'dateType': 'medium'});
531 } else { 567 } else {
532 this.shortDateFormatter_ = { 568 this.shortDateFormatter_ = {
(...skipping 53 matching lines...) Expand 10 before | Expand all | Expand 10 after
586 622
587 // The list of callbacks to be invoked during the directory rescan after 623 // The list of callbacks to be invoked during the directory rescan after
588 // all paste tasks are complete. 624 // all paste tasks are complete.
589 this.pasteSuccessCallbacks_ = []; 625 this.pasteSuccessCallbacks_ = [];
590 626
591 // The list of active mount points to distinct them from other directories. 627 // The list of active mount points to distinct them from other directories.
592 chrome.fileBrowserPrivate.getMountPoints(function(mountPoints) { 628 chrome.fileBrowserPrivate.getMountPoints(function(mountPoints) {
593 self.mountPoints_ = mountPoints; 629 self.mountPoints_ = mountPoints;
594 }); 630 });
595 631
596 this.initCommands_();
597
598 this.setupCurrentDirectory_(); 632 this.setupCurrentDirectory_();
599 633
600 this.summarizeSelection_(); 634 this.summarizeSelection_();
601 635
602 this.dataModel_.sort('cachedMtime_', 'desc'); 636 this.dataModel_.sort('cachedMtime_', 'desc');
603 637
604 this.refocus(); 638 this.refocus();
605 639
606 this.createMetadataProvider_(); 640 this.createMetadataProvider_();
607 metrics.recordTime('InitFileManager'); 641 metrics.recordTime('InitFileManager');
608 metrics.recordTime('TotalLoad'); 642 metrics.recordTime('TotalLoad');
609 }; 643 };
610 644
611 /** 645 /**
612 * One-time initialization of commands. 646 * One-time initialization of commands.
613 */ 647 */
614 FileManager.prototype.initCommands_ = function() { 648 FileManager.prototype.initCommands_ = function() {
615 var commands = this.dialogDom_.querySelectorAll('command'); 649 var commands = this.dialogDom_.querySelectorAll('command');
616 for (var i = 0; i < commands.length; i++) { 650 for (var i = 0; i < commands.length; i++) {
617 var command = commands[i]; 651 var command = commands[i];
618 cr.ui.Command.decorate(command); 652 cr.ui.Command.decorate(command);
653 command.label = command.textContent;
654 command.textContent = '';
619 this.commands_[command.id] = command; 655 this.commands_[command.id] = command;
620 } 656 }
621 657
622 this.fileContextMenu_ = this.dialogDom_.querySelector('.file-context-menu'); 658 this.fileContextMenu_ = this.dialogDom_.querySelector('.file-context-menu');
623 cr.ui.Menu.decorate(this.fileContextMenu_); 659 cr.ui.Menu.decorate(this.fileContextMenu_);
624 660
625 this.document_.addEventListener('canExecute', 661 this.document_.addEventListener('canExecute',
626 this.onCanExecute_.bind(this)); 662 this.onCanExecute_.bind(this));
627 this.document_.addEventListener('command', this.onCommand_.bind(this)); 663 this.document_.addEventListener('command', this.onCommand_.bind(this));
628 } 664 }
(...skipping 17 matching lines...) Expand all
646 this.previewThumbnails_ = 682 this.previewThumbnails_ =
647 this.dialogDom_.querySelector('.preview-thumbnails'); 683 this.dialogDom_.querySelector('.preview-thumbnails');
648 this.previewPanel_ = this.dialogDom_.querySelector('.preview-panel'); 684 this.previewPanel_ = this.dialogDom_.querySelector('.preview-panel');
649 this.previewFilename_ = this.dialogDom_.querySelector('.preview-filename'); 685 this.previewFilename_ = this.dialogDom_.querySelector('.preview-filename');
650 this.previewSummary_ = this.dialogDom_.querySelector('.preview-summary'); 686 this.previewSummary_ = this.dialogDom_.querySelector('.preview-summary');
651 this.previewMetadata_ = this.dialogDom_.querySelector('.preview-metadata'); 687 this.previewMetadata_ = this.dialogDom_.querySelector('.preview-metadata');
652 this.filenameInput_ = this.dialogDom_.querySelector('.filename-input'); 688 this.filenameInput_ = this.dialogDom_.querySelector('.filename-input');
653 this.taskButtons_ = this.dialogDom_.querySelector('.task-buttons'); 689 this.taskButtons_ = this.dialogDom_.querySelector('.task-buttons');
654 this.okButton_ = this.dialogDom_.querySelector('.ok'); 690 this.okButton_ = this.dialogDom_.querySelector('.ok');
655 this.cancelButton_ = this.dialogDom_.querySelector('.cancel'); 691 this.cancelButton_ = this.dialogDom_.querySelector('.cancel');
656 this.newFolderButton_ = this.dialogDom_.querySelector('.new-folder');
657 this.deleteButton_ = this.dialogDom_.querySelector('.delete-button'); 692 this.deleteButton_ = this.dialogDom_.querySelector('.delete-button');
658 693
659 this.downloadsWarning_ = 694 this.downloadsWarning_ =
660 this.dialogDom_.querySelector('.downloads-warning'); 695 this.dialogDom_.querySelector('.downloads-warning');
661 var html = util.htmlUnescape(str('DOWNLOADS_DIRECTORY_WARNING')); 696 var html = util.htmlUnescape(str('DOWNLOADS_DIRECTORY_WARNING'));
662 this.downloadsWarning_.lastElementChild.innerHTML = html; 697 this.downloadsWarning_.lastElementChild.innerHTML = html;
663 var link = this.downloadsWarning_.querySelector('a'); 698 var link = this.downloadsWarning_.querySelector('a');
664 link.addEventListener('click', this.onDownloadsWarningClick_.bind(this)); 699 link.addEventListener('click', this.onDownloadsWarningClick_.bind(this));
665 700
666 this.document_.addEventListener('keydown', this.onKeyDown_.bind(this)); 701 this.document_.addEventListener('keydown', this.onKeyDown_.bind(this));
(...skipping 10 matching lines...) Expand all
677 'keyup', this.onFilenameInputKeyUp_.bind(this)); 712 'keyup', this.onFilenameInputKeyUp_.bind(this));
678 this.filenameInput_.addEventListener( 713 this.filenameInput_.addEventListener(
679 'focus', this.onFilenameInputFocus_.bind(this)); 714 'focus', this.onFilenameInputFocus_.bind(this));
680 715
681 var listContainer = this.dialogDom_.querySelector('.list-container'); 716 var listContainer = this.dialogDom_.querySelector('.list-container');
682 listContainer.addEventListener('keydown', this.onListKeyDown_.bind(this)); 717 listContainer.addEventListener('keydown', this.onListKeyDown_.bind(this));
683 listContainer.addEventListener('keypress', this.onListKeyPress_.bind(this)); 718 listContainer.addEventListener('keypress', this.onListKeyPress_.bind(this));
684 this.okButton_.addEventListener('click', this.onOk_.bind(this)); 719 this.okButton_.addEventListener('click', this.onOk_.bind(this));
685 this.cancelButton_.addEventListener('click', this.onCancel_.bind(this)); 720 this.cancelButton_.addEventListener('click', this.onCancel_.bind(this));
686 721
687 this.dialogDom_.querySelector('button.new-folder').addEventListener( 722 this.dialogDom_.querySelector('div.open-sidebar').addEventListener(
688 'click', this.onNewFolderButtonClick_.bind(this)); 723 'click', this.onToggleSidebar_.bind(this));
724 this.dialogDom_.querySelector('div.close-sidebar').addEventListener(
725 'click', this.onToggleSidebar_.bind(this));
726 this.dialogContainer_ = this.dialogDom_.querySelector('.dialog-container');
689 727
690 this.dialogDom_.querySelector('button.detail-view').addEventListener( 728 this.dialogDom_.querySelector('button.detail-view').addEventListener(
691 'click', this.onDetailViewButtonClick_.bind(this)); 729 'click', this.onDetailViewButtonClick_.bind(this));
692 this.dialogDom_.querySelector('button.thumbnail-view').addEventListener( 730 this.dialogDom_.querySelector('button.thumbnail-view').addEventListener(
693 'click', this.onThumbnailViewButtonClick_.bind(this)); 731 'click', this.onThumbnailViewButtonClick_.bind(this));
694 732
695 this.dialogDom_.ownerDocument.defaultView.addEventListener( 733 this.dialogDom_.ownerDocument.defaultView.addEventListener(
696 'resize', this.onResize_.bind(this)); 734 'resize', this.onResize_.bind(this));
697 735
698 var ary = this.dialogDom_.querySelectorAll('[visibleif]'); 736 var ary = this.dialogDom_.querySelectorAll('[visibleif]');
(...skipping 32 matching lines...) Expand 10 before | Expand all | Expand 10 after
731 if (this.dialogType_ == FileManager.DialogType.SELECT_OPEN_FILE || 769 if (this.dialogType_ == FileManager.DialogType.SELECT_OPEN_FILE ||
732 this.dialogType_ == FileManager.DialogType.SELECT_OPEN_FOLDER || 770 this.dialogType_ == FileManager.DialogType.SELECT_OPEN_FOLDER ||
733 this.dialogType_ == FileManager.DialogType.SELECT_SAVEAS_FILE) { 771 this.dialogType_ == FileManager.DialogType.SELECT_SAVEAS_FILE) {
734 this.selectionModelClass_ = cr.ui.ListSingleSelectionModel; 772 this.selectionModelClass_ = cr.ui.ListSingleSelectionModel;
735 } else { 773 } else {
736 this.selectionModelClass_ = cr.ui.ListSelectionModel; 774 this.selectionModelClass_ = cr.ui.ListSelectionModel;
737 } 775 }
738 776
739 this.initTable_(); 777 this.initTable_();
740 this.initGrid_(); 778 this.initGrid_();
779 this.initRootsList_();
741 780
742 this.setListType(FileManager.ListType.DETAIL); 781 this.setListType(FileManager.ListType.DETAIL);
743 782
744 this.onResize_(); 783 this.onResize_();
745 784
746 this.textSearchState_ = {text: '', date: new Date()}; 785 this.textSearchState_ = {text: '', date: new Date()};
747 }; 786 };
748 787
788 FileManager.prototype.initRootsList_ = function() {
789 this.rootsList_ = this.dialogDom_.querySelector('.roots-list');
790 cr.ui.List.decorate(this.rootsList_);
791
792 var self = this;
793 this.rootsList_.itemConstructor = function(entry) {
794 return self.renderRoot_(entry);
795 };
796
797 this.rootsList_.selectionModel = new cr.ui.ListSingleSelectionModel();
798 this.rootsList_.selectionModel.addEventListener(
799 'change', this.onRootsSelectionChanged_.bind(this));
800
801 // TODO(dgozman): add "Add a drive" item.
802 this.rootsList_.dataModel = new cr.ui.ArrayDataModel(this.rootEntries_);
803 };
804
805 FileManager.prototype.updateRoots_ = function(opt_changeDirectoryTo) {
806 var self = this;
807 this.resolveRoots_(function(rootEntries) {
808 self.rootEntries_ = rootEntries;
809
810 var dataModel = this.rootsList_.dataModel;
811 var args = [0, dataModel.length].concat(rootEntries);
812 dataModel.splice.apply(dataModel, args);
813
814 this.updateRootsListSelection_();
815
816 if (opt_changeDirectoryTo)
817 self.changeDirectory(opt_changeDirectoryTo);
818 });
819 };
820
749 /** 821 /**
750 * Get the icon type for a given Entry. 822 * Get the icon type for a given Entry.
751 * 823 *
752 * @param {Entry} entry An Entry subclass (FileEntry or DirectoryEntry). 824 * @param {Entry} entry An Entry subclass (FileEntry or DirectoryEntry).
753 * @return {string} 825 * @return {string}
754 */ 826 */
755 FileManager.prototype.getIconType = function(entry) { 827 FileManager.prototype.getIconType = function(entry) {
756 if (!('cachedIconType_' in entry)) 828 if (!('cachedIconType_' in entry))
757 entry.cachedIconType_ = this.computeIconType_(entry); 829 entry.cachedIconType_ = this.computeIconType_(entry);
758 return entry.cachedIconType_; 830 return entry.cachedIconType_;
(...skipping 299 matching lines...) Expand 10 before | Expand all | Expand 10 after
1058 !isSystemDirEntry(this.currentDirEntry_)); 1130 !isSystemDirEntry(this.currentDirEntry_));
1059 1131
1060 case 'delete': 1132 case 'delete':
1061 return (// Initialized to the point where we have a current directory 1133 return (// Initialized to the point where we have a current directory
1062 this.currentDirEntry_ && 1134 this.currentDirEntry_ &&
1063 // Rename not in progress. 1135 // Rename not in progress.
1064 !this.renameInput_.currentEntry && 1136 !this.renameInput_.currentEntry &&
1065 !isSystemDirEntry(this.currentDirEntry_)) && 1137 !isSystemDirEntry(this.currentDirEntry_)) &&
1066 this.selection && 1138 this.selection &&
1067 this.selection.totalCount > 0; 1139 this.selection.totalCount > 0;
1140
1141 case 'newfolder':
1142 return this.currentDirEntry_ &&
1143 (this.dialogType_ == 'saveas-file' ||
1144 this.dialogType_ == 'full-page');
1068 } 1145 }
1069 }; 1146 };
1070 1147
1071 FileManager.prototype.updateCommonActionButtons_ = function() { 1148 FileManager.prototype.updateCommonActionButtons_ = function() {
1072 if (this.deleteButton_) 1149 if (this.deleteButton_)
1073 this.deleteButton_.disabled = !this.canExecute_('delete'); 1150 this.deleteButton_.disabled = !this.canExecute_('delete');
1074 }; 1151 };
1075 1152
1076 FileManager.prototype.setListType = function(type) { 1153 FileManager.prototype.setListType = function(type) {
1077 if (type && type == this.listType_) 1154 if (type && type == this.listType_)
(...skipping 185 matching lines...) Expand 10 before | Expand all | Expand 10 after
1263 leadIndex); 1340 leadIndex);
1264 return; 1341 return;
1265 } 1342 }
1266 1343
1267 this.initiateRename_(label); 1344 this.initiateRename_(label);
1268 return; 1345 return;
1269 1346
1270 case 'delete': 1347 case 'delete':
1271 this.deleteEntries(this.selection.entries); 1348 this.deleteEntries(this.selection.entries);
1272 return; 1349 return;
1350
1351 case 'newfolder':
1352 this.onNewFolderCommand_(event);
1353 return;
1273 } 1354 }
1274 }; 1355 };
1275 1356
1276 /** 1357 /**
1277 * Respond to the back and forward buttons. 1358 * Respond to the back and forward buttons.
1278 */ 1359 */
1279 FileManager.prototype.onPopState_ = function(event) { 1360 FileManager.prototype.onPopState_ = function(event) {
1280 // TODO(serya): We should restore selected items here. 1361 // TODO(serya): We should restore selected items here.
1281 if (this.rootEntries_) 1362 if (this.rootEntries_)
1282 this.setupCurrentDirectory_(); 1363 this.setupCurrentDirectory_();
1283 }; 1364 };
1284 1365
1285 FileManager.prototype.requestResize_ = function(timeout) { 1366 FileManager.prototype.requestResize_ = function(timeout) {
1286 var self = this; 1367 var self = this;
1287 setTimeout(function() { self.onResize_() }, timeout || 0); 1368 setTimeout(function() { self.onResize_() }, timeout || 0);
1288 }; 1369 };
1289 1370
1290 /** 1371 /**
1291 * Resize details and thumb views to fit the new window size. 1372 * Resize details and thumb views to fit the new window size.
1292 */ 1373 */
1293 FileManager.prototype.onResize_ = function() { 1374 FileManager.prototype.onResize_ = function() {
1294 this.table_.style.height = this.grid_.style.height = 1375 this.table_.style.height = this.grid_.style.height =
1295 this.grid_.parentNode.clientHeight + 'px'; 1376 this.grid_.parentNode.clientHeight + 'px';
1296 this.table_.style.width = this.grid_.style.width =
1297 this.grid_.parentNode.clientWidth + 'px';
1298
1299 this.table_.list_.style.width = this.table_.parentNode.clientWidth + 'px';
1300 this.table_.list_.style.height = (this.table_.clientHeight - 1 - 1377 this.table_.list_.style.height = (this.table_.clientHeight - 1 -
1301 this.table_.header_.clientHeight) + 'px'; 1378 this.table_.header_.clientHeight) + 'px';
1302 1379
1303 if (this.listType_ == FileManager.ListType.THUMBNAIL) { 1380 if (this.listType_ == FileManager.ListType.THUMBNAIL) {
1304 var self = this; 1381 var self = this;
1305 setTimeout(function() { 1382 setTimeout(function() {
1306 self.grid_.columns = 0; 1383 self.grid_.columns = 0;
1307 self.grid_.redraw(); 1384 self.grid_.redraw();
1308 }, 0); 1385 }, 0);
1309 } else { 1386 } else {
1310 this.currentList_.redraw(); 1387 this.currentList_.redraw();
1311 } 1388 }
1389
1390 this.rootsList_.style.height =
1391 this.rootsList_.parentNode.clientHeight + 'px';
1392 this.rootsList_.redraw();
1312 }; 1393 };
1313 1394
1314 FileManager.prototype.resolvePath = function( 1395 FileManager.prototype.resolvePath = function(
1315 path, resultCallback, errorCallback) { 1396 path, resultCallback, errorCallback) {
1316 return util.resolvePath(this.filesystem_.root, path, resultCallback, 1397 return util.resolvePath(this.filesystem_.root, path, resultCallback,
1317 errorCallback); 1398 errorCallback);
1318 }; 1399 };
1319 1400
1320 /** 1401 /**
1321 * Restores current directory and may be a selected item after page load (or 1402 * Restores current directory and may be a selected item after page load (or
(...skipping 13 matching lines...) Expand all
1335 this.setupPath_(this.params_.defaultPath); 1416 this.setupPath_(this.params_.defaultPath);
1336 } else { 1417 } else {
1337 this.setupDefaultPath_(); 1418 this.setupDefaultPath_();
1338 } 1419 }
1339 }; 1420 };
1340 1421
1341 FileManager.prototype.setupDefaultPath_ = function() { 1422 FileManager.prototype.setupDefaultPath_ = function() {
1342 // No preset given, find a good place to start. 1423 // No preset given, find a good place to start.
1343 // Check for removable devices, if there are none, go to Downloads. 1424 // Check for removable devices, if there are none, go to Downloads.
1344 var removableDirectoryEntry = this.rootEntries_.filter(function(rootEntry) { 1425 var removableDirectoryEntry = this.rootEntries_.filter(function(rootEntry) {
1345 return rootEntry.fullPath == REMOVABLE_DIRECTORY; 1426 return isParentPath(REMOVABLE_DIRECTORY, rootEntry.fullPath);
1346 })[0]; 1427 })[0];
1347 if (!removableDirectoryEntry) { 1428 this.changeDirectory(
1348 this.changeDirectory(DOWNLOADS_DIRECTORY, CD_NO_HISTORY); 1429 removableDirectoryEntry.fullPath || DOWNLOADS_DIRECTORY,
1349 return; 1430 CD_NO_HISTORY);
1350 }
1351
1352 var foundRemovable = false;
1353 util.forEachDirEntry(removableDirectoryEntry, function(result) {
1354 if (result) {
1355 foundRemovable = true;
1356 } else { // Done enumerating, and we know the answer.
1357 this.changeDirectory(foundRemovable ? '/' : DOWNLOADS_DIRECTORY,
1358 CD_NO_HISTORY);
1359 }
1360 }.bind(this));
1361 }; 1431 };
1362 1432
1363 FileManager.prototype.setupPath_ = function(path) { 1433 FileManager.prototype.setupPath_ = function(path) {
1364 // Split the dirname from the basename. 1434 // Split the dirname from the basename.
1365 var ary = path.match(/^(?:(.*)\/)?([^\/]*)$/); 1435 var ary = path.match(/^(?:(.*)\/)?([^\/]*)$/);
1366 if (!ary) { 1436 if (!ary) {
1367 console.warn('Unable to split default path: ' + path); 1437 console.warn('Unable to split default path: ' + path);
1368 self.changeDirectory('/', CD_NO_HISTORY); 1438 self.changeDirectory('/', CD_NO_HISTORY);
1369 return; 1439 return;
1370 } 1440 }
(...skipping 16 matching lines...) Expand all
1387 return; 1457 return;
1388 } 1458 }
1389 1459
1390 // Leaf is an existing file, cd to its parent directory and select it. 1460 // Leaf is an existing file, cd to its parent directory and select it.
1391 self.changeDirectoryEntry(baseDirEntry, CD_NO_HISTORY, leafEntry.name); 1461 self.changeDirectoryEntry(baseDirEntry, CD_NO_HISTORY, leafEntry.name);
1392 } 1462 }
1393 1463
1394 function onLeafError(err) { 1464 function onLeafError(err) {
1395 // Set filename first so OK button will update in changeDirectoryEntry. 1465 // Set filename first so OK button will update in changeDirectoryEntry.
1396 self.filenameInput_.value = leafName; 1466 self.filenameInput_.value = leafName;
1467 self.selectDefaultPathInFilenameInput_();
1397 if (err = FileError.NOT_FOUND_ERR) { 1468 if (err = FileError.NOT_FOUND_ERR) {
1398 // Leaf does not exist, it's just a suggested file name. 1469 // Leaf does not exist, it's just a suggested file name.
1399 self.changeDirectoryEntry(baseDirEntry, CD_NO_HISTORY); 1470 self.changeDirectoryEntry(baseDirEntry, CD_NO_HISTORY);
1400 } else { 1471 } else {
1401 console.log('Unexpected error resolving default leaf: ' + err); 1472 console.log('Unexpected error resolving default leaf: ' + err);
1402 self.changeDirectoryEntry('/', CD_NO_HISTORY); 1473 self.changeDirectoryEntry('/', CD_NO_HISTORY);
1403 } 1474 }
1404 } 1475 }
1405 1476
1406 self.resolvePath(path, onLeafFound, onLeafError); 1477 self.resolvePath(path, onLeafFound, onLeafError);
1407 } 1478 }
1408 1479
1409 function onBaseError(err) { 1480 function onBaseError(err) {
1410 // Set filename first so OK button will update in changeDirectory. 1481 // Set filename first so OK button will update in changeDirectory.
1411 self.filenameInput_.value = leafName; 1482 self.filenameInput_.value = leafName;
1483 self.selectDefaultPathInFilenameInput_();
1412 console.log('Unexpected error resolving default base "' + 1484 console.log('Unexpected error resolving default base "' +
1413 baseName + '": ' + err); 1485 baseName + '": ' + err);
1414 self.changeDirectory('/', CD_NO_HISTORY); 1486 self.changeDirectory('/', CD_NO_HISTORY);
1415 } 1487 }
1416 1488
1417 if (baseName) { 1489 if (baseName) {
1418 this.filesystem_.root.getDirectory( 1490 this.filesystem_.root.getDirectory(
1419 baseName, {create: false}, onBaseFound, onBaseError); 1491 baseName, {create: false}, onBaseFound, onBaseError);
1420 } else { 1492 } else {
1421 onBaseFound(this.filesystem_.root); 1493 onBaseFound(this.filesystem_.root);
(...skipping 220 matching lines...) Expand 10 before | Expand all | Expand 10 after
1642 icon.className = 'detail-icon'; 1714 icon.className = 'detail-icon';
1643 this.getIconType(entry); 1715 this.getIconType(entry);
1644 icon.setAttribute('iconType', entry.cachedIconType_); 1716 icon.setAttribute('iconType', entry.cachedIconType_);
1645 div.appendChild(icon); 1717 div.appendChild(icon);
1646 1718
1647 return div; 1719 return div;
1648 }; 1720 };
1649 1721
1650 FileManager.prototype.getLabelForRootPath_ = function(path) { 1722 FileManager.prototype.getLabelForRootPath_ = function(path) {
1651 // This hack lets us localize the top level directories. 1723 // This hack lets us localize the top level directories.
1652 if (path == 'Downloads') 1724 if (path == DOWNLOADS_DIRECTORY)
1653 return str('DOWNLOADS_DIRECTORY_LABEL'); 1725 return str('CHROMEBOOK_DIRECTORY_LABEL');
1654 1726
1655 if (path == 'archive') 1727 if (path == ARCHIVE_DIRECTORY)
1656 return str('ARCHIVE_DIRECTORY_LABEL'); 1728 return str('ARCHIVE_DIRECTORY_LABEL');
1729 if (isParentPath(ARCHIVE_DIRECTORY, path))
1730 return path.substring(ARCHIVE_DIRECTORY.length + 1);
1657 1731
1658 if (path == 'removable') 1732 if (path == REMOVABLE_DIRECTORY)
1659 return str('REMOVABLE_DIRECTORY_LABEL'); 1733 return str('REMOVABLE_DIRECTORY_LABEL');
1734 if (isParentPath(REMOVABLE_DIRECTORY, path))
1735 return path.substring(REMOVABLE_DIRECTORY.length + 1);
1660 1736
1661 return path || str('ROOT_DIRECTORY_LABEL'); 1737 return path || str('ROOT_DIRECTORY_LABEL');
1662 }; 1738 };
1663 1739
1740 FileManager.prototype.getRootIconUrl_ = function(path, opt_small) {
1741 var iconUrl = opt_small ? 'images/chromebook_28x28.png' :
1742 'images/chromebook_24x24.png';
1743 if (isParentPath(REMOVABLE_DIRECTORY, path))
1744 iconUrl = 'images/filetype_device.png';
1745 if (isParentPath(ARCHIVE_DIRECTORY, path))
1746 iconUrl = 'images/icon_mount_archive_16x16.png';
1747 return chrome.extension.getURL(iconUrl);
1748 };
1749
1750 FileManager.prototype.renderRoot_ = function(entry) {
1751 var li = this.document_.createElement('li');
1752 li.className = 'root-item';
1753
1754 var icon = this.document_.createElement('img');
1755 icon.setAttribute('src', this.getRootIconUrl_(entry.fullPath, false));
1756 li.appendChild(icon);
1757
1758 var div = this.document_.createElement('div');
1759 div.textContent = this.getLabelForRootPath_(entry.fullPath);
1760 li.appendChild(div);
1761
1762 if (isParentPath(REMOVABLE_DIRECTORY, entry.fullPath) ||
1763 isParentPath(ARCHIVE_DIRECTORY, entry.fullPath)) {
1764 var spacer = this.document_.createElement('div');
1765 spacer.className = 'spacer';
1766 li.appendChild(spacer);
1767
1768 var eject = this.document_.createElement('img');
1769 eject.className = 'root-eject';
1770 eject.setAttribute('src', chrome.extension.getURL('images/eject.png'));
1771 eject.addEventListener('click', this.onEjectClick_.bind(this, entry));
1772 li.appendChild(eject);
1773 }
1774
1775 cr.defineProperty(li, 'lead', cr.PropertyKind.BOOL_ATTR);
1776 cr.defineProperty(li, 'selected', cr.PropertyKind.BOOL_ATTR);
1777 return li;
1778 };
1779
1780 /**
1781 * Handler for eject button clicked.
1782 * @param {Entry} entry Entry to eject.
1783 * @param {Event} event The event.
1784 */
1785 FileManager.prototype.onEjectClick_ = function(entry, event) {
1786 chrome.fileBrowserPrivate.removeMount(entry.fullPath);
1787 };
1788
1664 /** 1789 /**
1665 * Render the Name column of the detail table. 1790 * Render the Name column of the detail table.
1666 * 1791 *
1667 * Invoked by cr.ui.Table when a file needs to be rendered. 1792 * Invoked by cr.ui.Table when a file needs to be rendered.
1668 * 1793 *
1669 * @param {Entry} entry The Entry object to render. 1794 * @param {Entry} entry The Entry object to render.
1670 * @param {string} columnId The id of the column to be rendered. 1795 * @param {string} columnId The id of the column to be rendered.
1671 * @param {cr.ui.Table} table The table doing the rendering. 1796 * @param {cr.ui.Table} table The table doing the rendering.
1672 */ 1797 */
1673 FileManager.prototype.renderName_ = function(entry, columnId, table) { 1798 FileManager.prototype.renderName_ = function(entry, columnId, table) {
1674 var label = this.document_.createElement('div'); 1799 var label = this.document_.createElement('div');
1675 label.entry = entry; 1800 label.entry = entry;
1676 label.className = 'detail-name filename-label'; 1801 label.className = 'detail-name filename-label';
1677 if (this.currentDirEntry_.name == '') { 1802 label.textContent = entry.name;
1678 label.textContent = this.getLabelForRootPath_(entry.name);
1679 } else {
1680 label.textContent = entry.name;
1681 }
1682 1803
1683 return label; 1804 return label;
1684 }; 1805 };
1685 1806
1686 /** 1807 /**
1687 * Render the Size column of the detail table. 1808 * Render the Size column of the detail table.
1688 * 1809 *
1689 * @param {Entry} entry The Entry object to render. 1810 * @param {Entry} entry The Entry object to render.
1690 * @param {string} columnId The id of the column to be rendered. 1811 * @param {string} columnId The id of the column to be rendered.
1691 * @param {cr.ui.Table} table The table doing the rendering. 1812 * @param {cr.ui.Table} table The table doing the rendering.
(...skipping 291 matching lines...) Expand 10 before | Expand all | Expand 10 after
1983 // Skip the button creation. 2104 // Skip the button creation.
1984 if (!str('ENABLE_PHOTO_EDITOR')) continue; 2105 if (!str('ENABLE_PHOTO_EDITOR')) continue;
1985 this.galleryTask_ = task; 2106 this.galleryTask_ = task;
1986 } 2107 }
1987 } 2108 }
1988 this.renderTaskButton_(task); 2109 this.renderTaskButton_(task);
1989 } 2110 }
1990 2111
1991 // These are done in separate functions, as the checks require 2112 // These are done in separate functions, as the checks require
1992 // asynchronous function calls. 2113 // asynchronous function calls.
1993 this.maybeRenderUnmountTask_(selection);
1994 this.maybeRenderFormattingTask_(selection); 2114 this.maybeRenderFormattingTask_(selection);
1995 }; 2115 };
1996 2116
1997 FileManager.prototype.renderTaskButton_ = function(task) { 2117 FileManager.prototype.renderTaskButton_ = function(task) {
1998 var button = this.document_.createElement('button'); 2118 var button = this.document_.createElement('button');
1999 button.addEventListener('click', 2119 button.addEventListener('click',
2000 this.onTaskButtonClicked_.bind(this, task)); 2120 this.onTaskButtonClicked_.bind(this, task));
2001 button.className = 'task-button'; 2121 button.className = 'task-button';
2002 2122
2003 var img = this.document_.createElement('img'); 2123 var img = this.document_.createElement('img');
2004 img.src = task.iconUrl; 2124 img.src = task.iconUrl;
2005 2125
2006 button.appendChild(img); 2126 button.appendChild(img);
2007 var label = this.document_.createElement('div'); 2127 var label = this.document_.createElement('div');
2008 label.appendChild(this.document_.createTextNode(task.title)) 2128 label.appendChild(this.document_.createTextNode(task.title))
2009 button.appendChild(label); 2129 button.appendChild(label);
2010 2130
2011 this.taskButtons_.appendChild(button); 2131 this.taskButtons_.appendChild(button);
2012 }; 2132 };
2013 2133
2014 /** 2134 /**
2015 * Checks whether unmount task should be displayed and if the answer is
2016 * affirmative renders it.
2017 * @param {Object} selection Selected files object.
2018 */
2019 FileManager.prototype.maybeRenderUnmountTask_ = function(selection) {
2020 for (var index = 0; index < selection.urls.length; ++index) {
2021 // Each url should be a mount point.
2022 var path = selection.entries[index].fullPath;
2023 var found = false;
2024 for (var i = 0; i < this.mountPoints_.length; i++) {
2025 var mountPath = this.mountPoints_[i].mountPath;
2026 if (mountPath[0] != '/') {
2027 mountPath = '/' + mountPath;
2028 }
2029 if (mountPath == path && this.mountPoints_[i].mountType == 'file') {
2030 found = true;
2031 break;
2032 }
2033 }
2034 if (!found)
2035 return;
2036 }
2037 this.renderTaskButton_({
2038 taskId: this.getExtensionId_() + '|unmount-archive',
2039 iconUrl:
2040 chrome.extension.getURL('images/icon_unmount_archive_16x16.png'),
2041 title: str('UNMOUNT_ARCHIVE'),
2042 internal: true
2043 });
2044 };
2045
2046 /**
2047 * Checks whether formatting task should be displayed and if the answer is 2135 * Checks whether formatting task should be displayed and if the answer is
2048 * affirmative renders it. Includes asynchronous calls, so it's splitted into 2136 * affirmative renders it. Includes asynchronous calls, so it's splitted into
2049 * three parts. 2137 * three parts.
2050 * @param {Object} selection Selected files object. 2138 * @param {Object} selection Selected files object.
2051 */ 2139 */
2052 FileManager.prototype.maybeRenderFormattingTask_ = function(selection) { 2140 FileManager.prototype.maybeRenderFormattingTask_ = function(selection) {
2053 // Not to make unnecessary getMountPoints() call we doublecheck if there is 2141 // Not to make unnecessary getMountPoints() call we doublecheck if there is
2054 // only one selected entry. 2142 // only one selected entry.
2055 if (selection.entries.length != 1) 2143 if (selection.entries.length != 1)
2056 return; 2144 return;
(...skipping 59 matching lines...) Expand 10 before | Expand all | Expand 10 after
2116 } 2204 }
2117 chrome.fileBrowserPrivate.executeTask(task.taskId, urls); 2205 chrome.fileBrowserPrivate.executeTask(task.taskId, urls);
2118 }; 2206 };
2119 2207
2120 /** 2208 /**
2121 * Event handler called when some volume was mounted or unmouted. 2209 * Event handler called when some volume was mounted or unmouted.
2122 */ 2210 */
2123 FileManager.prototype.onMountCompleted_ = function(event) { 2211 FileManager.prototype.onMountCompleted_ = function(event) {
2124 var self = this; 2212 var self = this;
2125 chrome.fileBrowserPrivate.getMountPoints(function(mountPoints) { 2213 chrome.fileBrowserPrivate.getMountPoints(function(mountPoints) {
2214 // TODO(dgozman): update rootsList in sidebar.
2126 self.mountPoints_ = mountPoints; 2215 self.mountPoints_ = mountPoints;
2127 if (event.eventType == 'mount') { 2216 if (event.eventType == 'mount') {
2128 for (var index = 0; index < self.mountRequests_.length; ++index) { 2217 for (var index = 0; index < self.mountRequests_.length; ++index) {
2129 if (self.mountRequests_[index] == event.sourceUrl) { 2218 if (self.mountRequests_[index] == event.sourceUrl) {
2130 self.mountRequests_.splice(index, 1); 2219 self.mountRequests_.splice(index, 1);
2131 if (event.status == 'success') { 2220 if (event.status == 'success') {
2132 self.changeDirectory(event.mountPath); 2221 self.updateRoots_(event.mountPath);
2133 } else { 2222 } else {
2134 // Report mount error. 2223 // Report mount error.
2135 if (event.mountType == 'file') { 2224 if (event.mountType == 'file') {
2136 var fileName = event.sourceUrl.substr( 2225 var fileName = event.sourceUrl.substr(
2137 event.sourceUrl.lastIndexOf('/') + 1); 2226 event.sourceUrl.lastIndexOf('/') + 1);
2138 self.alert.show(strf('ARCHIVE_MOUNT_FAILED', fileName, 2227 self.alert.show(strf('ARCHIVE_MOUNT_FAILED', fileName,
2139 event.status)); 2228 event.status));
2140 } 2229 }
2141 } 2230 }
2142 return; 2231 return;
2143 } 2232 }
2144 } 2233 }
2145 } 2234 }
2146 2235
2147 if (event.eventType == 'unmount' && event.status == 'success' && 2236 if (event.eventType == 'unmount' && event.status == 'success' &&
2148 self.currentDirEntry_ && 2237 self.currentDirEntry_ &&
2149 isParentPath(event.mountPath, self.currentDirEntry_.fullPath)) { 2238 isParentPath(event.mountPath, self.currentDirEntry_.fullPath)) {
2150 self.changeDirectory(getParentPath(event.mountPath)); 2239 self.changeDirectory(getParentPath(event.mountPath));
2151 return; 2240 return;
2152 } 2241 }
2153 2242
2154 var rescanDirectoryNeeded = (event.status == 'success'); 2243 var rescanDirectoryNeeded = (event.status == 'success');
2155 for (var i = 0; i < mountPoints.length; i++) { 2244 for (var i = 0; i < mountPoints.length; i++) {
2156 if (event.sourceUrl == mountPoints[i].sourceUrl && 2245 if (event.sourceUrl == mountPoints[i].sourceUrl &&
2157 mountPoints[i].mountCondition != '') { 2246 mountPoints[i].mountCondition != '') {
2158 rescanDirectoryNeeded = true; 2247 rescanDirectoryNeeded = true;
2159 } 2248 }
2160 } 2249 }
2161 // TODO(dgozman): rescan directory, only if it contains mounted points,
2162 // when mounts location will be decided.
2163 if (rescanDirectoryNeeded) 2250 if (rescanDirectoryNeeded)
2164 self.rescanDirectory_(null, 300); 2251 self.rescanDirectory_(null, 300);
2165 }); 2252 });
2166 }; 2253 };
2167 2254
2168 /** 2255 /**
2169 * Event handler called when some internal task should be executed. 2256 * Event handler called when some internal task should be executed.
2170 */ 2257 */
2171 FileManager.prototype.onFileTaskExecute_ = function(id, details) { 2258 FileManager.prototype.onFileTaskExecute_ = function(id, details) {
2172 var urls = details.urls; 2259 var urls = details.urls;
2173 if (id == 'preview') { 2260 if (id == 'preview') {
2174 g_slideshow_data = urls; 2261 g_slideshow_data = urls;
2175 chrome.tabs.create({url: "slideshow.html"}); 2262 chrome.tabs.create({url: "slideshow.html"});
2176 } else if (id == 'play' || id == 'enqueue') { 2263 } else if (id == 'play' || id == 'enqueue') {
2177 chrome.fileBrowserPrivate.viewFiles(urls, id); 2264 chrome.fileBrowserPrivate.viewFiles(urls, id);
2178 } else if (id == 'mount-archive') { 2265 } else if (id == 'mount-archive') {
2179 for (var index = 0; index < urls.length; ++index) { 2266 for (var index = 0; index < urls.length; ++index) {
2180 this.mountRequests_.push(urls[index]); 2267 this.mountRequests_.push(urls[index]);
2181 chrome.fileBrowserPrivate.addMount(urls[index], 'file', {}); 2268 chrome.fileBrowserPrivate.addMount(urls[index], 'file', {});
2182 } 2269 }
2183 } else if (id == 'unmount-archive') {
2184 for (var index = 0; index < urls.length; ++index) {
2185 chrome.fileBrowserPrivate.removeMount(urls[index]);
2186 }
2187 } else if (id == 'format-device') { 2270 } else if (id == 'format-device') {
2188 this.confirm.show(str('FORMATTING_WARNING'), function() { 2271 this.confirm.show(str('FORMATTING_WARNING'), function() {
2189 chrome.fileBrowserPrivate.formatDevice(urls[0]); 2272 chrome.fileBrowserPrivate.formatDevice(urls[0]);
2190 }); 2273 });
2191 } else if (id == 'gallery') { 2274 } else if (id == 'gallery') {
2192 // Pass to gallery all possible tasks except the gallery itself. 2275 // Pass to gallery all possible tasks except the gallery itself.
2193 var noGallery = []; 2276 var noGallery = [];
2194 for (var index = 0; index < details.task.allTasks.length; index++) { 2277 for (var index = 0; index < details.task.allTasks.length; index++) {
2195 var task = details.task.allTasks[index]; 2278 var task = details.task.allTasks[index];
2196 if (task.taskId != this.getExtensionId_() + '|gallery') { 2279 if (task.taskId != this.getExtensionId_() + '|gallery') {
(...skipping 57 matching lines...) Expand 10 before | Expand all | Expand 10 after
2254 self.metadataProvider_, 2337 self.metadataProvider_,
2255 shareActions, 2338 shareActions,
2256 str); 2339 str);
2257 }; 2340 };
2258 2341
2259 galleryFrame.src = 'js/image_editor/gallery.html'; 2342 galleryFrame.src = 'js/image_editor/gallery.html';
2260 this.dialogDom_.appendChild(galleryFrame); 2343 this.dialogDom_.appendChild(galleryFrame);
2261 galleryFrame.focus(); 2344 galleryFrame.focus();
2262 }; 2345 };
2263 2346
2347 FileManager.prototype.getRootForPath_ = function(path) {
2348 for (var index = 0; index < this.rootEntries_.length; index++) {
2349 if (isParentPath(this.rootEntries_[index].fullPath, path)) {
2350 return this.rootEntries_[index];
2351 }
2352 }
2353 return null;
2354 };
2355
2264 /** 2356 /**
2265 * Update the breadcrumb display to reflect the current directory. 2357 * Update the breadcrumb display to reflect the current directory.
2266 */ 2358 */
2267 FileManager.prototype.updateBreadcrumbs_ = function() { 2359 FileManager.prototype.updateBreadcrumbs_ = function() {
2268 var bc = this.dialogDom_.querySelector('.breadcrumbs'); 2360 var bc = this.dialogDom_.querySelector('.breadcrumbs');
2269 bc.innerHTML = ''; 2361 bc.innerHTML = '';
2270 2362
2271 var fullPath = this.currentDirEntry_.fullPath.replace(/\/$/, ''); 2363 var fullPath = this.currentDirEntry_.fullPath;
2272 var pathNames = fullPath.split('/'); 2364 var root = this.getRootForPath_(fullPath);
2273 var path = ''; 2365 if (!root) {
2366 console.error('Not root for: ' + fullPath);
2367 return;
2368 }
2369
2370 var icon = this.document_.createElement('img');
2371 icon.className = 'breadcrumb-icon';
2372 icon.setAttribute('src', this.getRootIconUrl_(root.fullPath, true));
2373 bc.appendChild(icon);
2374
2375 var rootPath = root.fullPath;
2376 var relativePath = fullPath.substring(rootPath.length);
2377 var pathNames = relativePath.replace(/\/$/, '').split('/');
2378 if (pathNames[0] == '')
2379 pathNames.splice(0, 1);
2380
2381 // We need a first breadcrumb for root, so placing last name from
2382 // rootPath as first name of relativePath.
2383 var rootPathNames = rootPath.replace(/\/$/, '').split('/');
2384 pathNames.splice(0, 0, rootPathNames[rootPathNames.length - 1]);
2385 rootPathNames.splice(rootPathNames.length - 1, 1);
2386 var path = rootPathNames.join('/') + '/';
2274 2387
2275 for (var i = 0; i < pathNames.length; i++) { 2388 for (var i = 0; i < pathNames.length; i++) {
2276 var pathName = pathNames[i]; 2389 var pathName = pathNames[i];
2277 path += pathName + '/'; 2390 path += pathName;
2278 2391
2279 var div = this.document_.createElement('div'); 2392 var div = this.document_.createElement('div');
2280 div.className = 'breadcrumb-path'; 2393 div.className = 'breadcrumb-path';
2281 if (i <= 1) { 2394 if (i == 0) {
2282 // i == 0: root directory itself, i == 1: the files it contains. 2395 div.textContent = this.getLabelForRootPath_(path);
2283 div.textContent = this.getLabelForRootPath_(pathName);
2284 } else { 2396 } else {
2285 div.textContent = pathName; 2397 div.textContent = pathName;
2286 } 2398 }
2287 2399
2400 path = path + '/';
2288 div.path = path; 2401 div.path = path;
2289 div.addEventListener('click', this.onBreadcrumbClick_.bind(this)); 2402 div.addEventListener('click', this.onBreadcrumbClick_.bind(this));
2290 2403
2291 bc.appendChild(div); 2404 bc.appendChild(div);
2292 2405
2293 if (i == pathNames.length - 1) { 2406 if (i == pathNames.length - 1) {
2294 div.classList.add('breadcrumb-last'); 2407 div.classList.add('breadcrumb-last');
2295 } else { 2408 } else {
2296 var spacer = this.document_.createElement('div'); 2409 var spacer = this.document_.createElement('div');
2297 spacer.className = 'breadcrumb-spacer'; 2410 spacer.className = 'breadcrumb-spacer';
(...skipping 112 matching lines...) Expand 10 before | Expand all | Expand 10 after
2410 for (var i = 0; i < this.dataModel_.length; i++) { 2523 for (var i = 0; i < this.dataModel_.length; i++) {
2411 if (this.dataModel_.item(i).name == name) { 2524 if (this.dataModel_.item(i).name == name) {
2412 this.currentList_.selectionModel.selectedIndex = i; 2525 this.currentList_.selectionModel.selectedIndex = i;
2413 this.currentList_.scrollIndexIntoView(i); 2526 this.currentList_.scrollIndexIntoView(i);
2414 this.currentList_.focus(); 2527 this.currentList_.focus();
2415 return; 2528 return;
2416 } 2529 }
2417 } 2530 }
2418 }; 2531 };
2419 2532
2533 FileManager.prototype.updateRootsListSelection_ = function() {
2534 if (!this.currentDirEntry_) return;
2535 for (var index = 0; index < this.rootEntries_.length; index++) {
Vladislav Kaznacheev 2011/11/22 13:16:35 Consider having getRootPathFor_ return an index an
dgozman 2011/11/22 14:15:14 Done.
2536 if (isParentPath(this.rootEntries_[index].fullPath,
2537 this.currentDirEntry_.fullPath)) {
2538 this.rootsList_.selectionModel.selectedIndex = index;
2539 return;
2540 }
2541 }
2542 this.rootsList_.selectionModel.selectedIndex = 0;
2543 console.error('No corresponding root for: ' +
2544 this.currentDirEntry_.fullPath);
2545 };
2546
2420 /** 2547 /**
2421 * Add the file/directory with given name to the current selection. 2548 * Add the file/directory with given name to the current selection.
2422 * 2549 *
2423 * @param {string} name The name of the entry to select. 2550 * @param {string} name The name of the entry to select.
2424 * @return {boolean} Whether entry exists. 2551 * @return {boolean} Whether entry exists.
2425 */ 2552 */
2426 FileManager.prototype.addItemToSelection = function(name) { 2553 FileManager.prototype.addItemToSelection = function(name) {
2427 var entryExists = false; 2554 var entryExists = false;
2428 for (var i = 0; i < this.dataModel_.length; i++) { 2555 for (var i = 0; i < this.dataModel_.length; i++) {
2429 if (this.dataModel_.item(i).name == name) { 2556 if (this.dataModel_.item(i).name == name) {
(...skipping 95 matching lines...) Expand 10 before | Expand all | Expand 10 after
2525 FileManager.prototype.changeDirectoryEntry = function(dirEntry, 2652 FileManager.prototype.changeDirectoryEntry = function(dirEntry,
2526 opt_saveHistory, 2653 opt_saveHistory,
2527 opt_selectedEntry, 2654 opt_selectedEntry,
2528 opt_callback) { 2655 opt_callback) {
2529 if (typeof opt_saveHistory == 'undefined') { 2656 if (typeof opt_saveHistory == 'undefined') {
2530 opt_saveHistory = true; 2657 opt_saveHistory = true;
2531 } else { 2658 } else {
2532 opt_saveHistory = !!opt_saveHistory; 2659 opt_saveHistory = !!opt_saveHistory;
2533 } 2660 }
2534 2661
2662 if (dirEntry.fullPath == '/' || dirEntry.fullPath == DOWNLOADS_DIRECTORY ||
2663 dirEntry.fullPath == ARCHIVE_DIRECTORY) {
Vladislav Kaznacheev 2011/11/22 13:16:35 Why? It would be nice to have a comment here.
dgozman 2011/11/22 14:15:14 Comment added.
2664 dirEntry = this.rootEntries_[0] || dirEntry;
2665 }
2666
2535 var location = '#' + encodeURI(dirEntry.fullPath); 2667 var location = '#' + encodeURI(dirEntry.fullPath);
2536 if (opt_saveHistory) { 2668 if (opt_saveHistory) {
2537 history.pushState(undefined, dirEntry.fullPath, location); 2669 history.pushState(undefined, dirEntry.fullPath, location);
2538 } else if (window.location.hash != location) { 2670 } else if (window.location.hash != location) {
2539 // If the user typed URL manually that is not canonical it would be fixed 2671 // If the user typed URL manually that is not canonical it would be fixed
2540 // here. However it seems history.replaceState doesn't work properly 2672 // here. However it seems history.replaceState doesn't work properly
2541 // with rewritable URLs (while does with history.pushState). It changes 2673 // with rewritable URLs (while does with history.pushState). It changes
2542 // window.location but doesn't change content of the ombibox. 2674 // window.location but doesn't change content of the ombibox.
2543 history.replaceState(undefined, dirEntry.fullPath, location); 2675 history.replaceState(undefined, dirEntry.fullPath, location);
2544 } 2676 }
(...skipping 216 matching lines...) Expand 10 before | Expand all | Expand 10 after
2761 // then the default action of this click event fires and toggles the 2893 // then the default action of this click event fires and toggles the
2762 // checkbox back off. 2894 // checkbox back off.
2763 // 2895 //
2764 // Since we're going to force checkboxes into the correct state for any 2896 // Since we're going to force checkboxes into the correct state for any
2765 // multi-selection, we can prevent this shift click from toggling the 2897 // multi-selection, we can prevent this shift click from toggling the
2766 // checkbox and avoid the trouble. 2898 // checkbox and avoid the trouble.
2767 event.preventDefault(); 2899 event.preventDefault();
2768 } 2900 }
2769 }; 2901 };
2770 2902
2903 FileManager.prototype.onRootsSelectionChanged_ = function(event) {
2904 var root = this.rootEntries_[this.rootsList_.selectionModel.selectedIndex];
2905 this.changeDirectory(root.fullPath);
2906 };
2907
2908 FileManager.prototype.selectDefaultPathInFilenameInput_ = function() {
2909 var input = this.filenameInput_;
2910 input.focus();
2911 var selectionEnd = input.value.lastIndexOf('.');
2912 if (selectionEnd == -1) {
2913 input.select();
2914 } else {
2915 input.selectionStart = 0;
2916 input.selectionEnd = selectionEnd;
2917 }
2918 // Clear, so we never do this again.
2919 this.params_.defaultPath = '';
2920 };
2921
2771 /** 2922 /**
2772 * Update the UI when the selection model changes. 2923 * Update the UI when the selection model changes.
2773 * 2924 *
2774 * @param {cr.Event} event The change event. 2925 * @param {cr.Event} event The change event.
2775 */ 2926 */
2776 FileManager.prototype.onSelectionChanged_ = function(event) { 2927 FileManager.prototype.onSelectionChanged_ = function(event) {
2777 this.summarizeSelection_(); 2928 this.summarizeSelection_();
2778 2929
2779 if (this.dialogType_ == FileManager.DialogType.SELECT_SAVEAS_FILE) { 2930 if (this.dialogType_ == FileManager.DialogType.SELECT_SAVEAS_FILE) {
2780 // If this is a save-as dialog, copy the selected file into the filename 2931 // If this is a save-as dialog, copy the selected file into the filename
2781 // input text box. 2932 // input text box.
2782 2933
2783 if (this.selection && 2934 if (this.selection &&
2784 this.selection.totalCount == 1 && 2935 this.selection.totalCount == 1 &&
2785 this.selection.entries[0].isFile) 2936 this.selection.entries[0].isFile &&
2937 this.filenameInput_.value != this.selection.entries[0].name) {
2786 this.filenameInput_.value = this.selection.entries[0].name; 2938 this.filenameInput_.value = this.selection.entries[0].name;
2939 if (this.params_.defaultPath == this.selection.entries[0].fullPath)
2940 this.selectDefaultPathInFilenameInput_();
2941 }
2787 } 2942 }
2788 2943
2789 this.updateOkButton_(); 2944 this.updateOkButton_();
2790 2945
2791 var self = this; 2946 var self = this;
2792 setTimeout(function() { self.onSelectionChangeComplete_(event) }, 0); 2947 setTimeout(function() { self.onSelectionChangeComplete_(event) }, 0);
2793 }; 2948 };
2794 2949
2795 /** 2950 /**
2796 * Handle selection change related tasks that won't run properly during 2951 * Handle selection change related tasks that won't run properly during
(...skipping 128 matching lines...) Expand 10 before | Expand all | Expand 10 after
2925 * Update the UI when the current directory changes. 3080 * Update the UI when the current directory changes.
2926 * 3081 *
2927 * @param {cr.Event} event The directory-changed event. 3082 * @param {cr.Event} event The directory-changed event.
2928 */ 3083 */
2929 FileManager.prototype.onDirectoryChanged_ = function(event) { 3084 FileManager.prototype.onDirectoryChanged_ = function(event) {
2930 this.updateCommands_(); 3085 this.updateCommands_();
2931 this.updateOkButton_(); 3086 this.updateOkButton_();
2932 3087
2933 this.checkFreeSpace_(this.currentDirEntry_.fullPath); 3088 this.checkFreeSpace_(this.currentDirEntry_.fullPath);
2934 3089
2935 // New folder should never be enabled in the root or media/ directories. 3090 // TODO(dgozman): title may be better than this.
2936 this.newFolderButton_.disabled = isSystemDirEntry(this.currentDirEntry_);
2937
2938 this.document_.title = this.currentDirEntry_.fullPath; 3091 this.document_.title = this.currentDirEntry_.fullPath;
2939 3092
2940 var self = this; 3093 var self = this;
2941 3094
2942 if (this.subscribedOnDirectoryChanges_) { 3095 if (this.subscribedOnDirectoryChanges_) {
2943 chrome.fileBrowserPrivate.removeFileWatch(event.previousDirEntry.toURL(), 3096 chrome.fileBrowserPrivate.removeFileWatch(event.previousDirEntry.toURL(),
2944 function(result) { 3097 function(result) {
2945 if (!result) { 3098 if (!result) {
2946 console.log('Failed to remove file watch'); 3099 console.log('Failed to remove file watch');
2947 } 3100 }
(...skipping 75 matching lines...) Expand 10 before | Expand all | Expand 10 after
3023 // Updated when a user clicks on the label of a file, used to detect 3176 // Updated when a user clicks on the label of a file, used to detect
3024 // when a click is eligible to trigger a rename. Can be null, or 3177 // when a click is eligible to trigger a rename. Can be null, or
3025 // an object with 'path' and 'date' properties. 3178 // an object with 'path' and 'date' properties.
3026 this.lastLabelClick_ = null; 3179 this.lastLabelClick_ = null;
3027 3180
3028 // Clear the table first. 3181 // Clear the table first.
3029 this.dataModel_.splice(0, this.dataModel_.length); 3182 this.dataModel_.splice(0, this.dataModel_.length);
3030 this.currentList_.selectionModel.clear(); 3183 this.currentList_.selectionModel.clear();
3031 3184
3032 this.updateBreadcrumbs_(); 3185 this.updateBreadcrumbs_();
3186 this.updateRootsListSelection_();
3033 3187
3034 if (this.currentDirEntry_.fullPath != '/') { 3188 if (this.currentDirEntry_.fullPath != '/') {
3035 // Add current request to pending result list 3189 // Add current request to pending result list
3036 this.pendingRescanQueue_.push({ 3190 this.pendingRescanQueue_.push({
3037 onSuccess:opt_callback, 3191 onSuccess:opt_callback,
3038 onError:opt_onError 3192 onError:opt_onError
3039 }); 3193 });
3040 3194
3041 if (this.rescanRunning_) 3195 if (this.rescanRunning_)
3042 return; 3196 return;
(...skipping 315 matching lines...) Expand 10 before | Expand all | Expand 10 after
3358 var selectionEnd = input.value.lastIndexOf('.'); 3512 var selectionEnd = input.value.lastIndexOf('.');
3359 if (selectionEnd == -1) { 3513 if (selectionEnd == -1) {
3360 input.select(); 3514 input.select();
3361 } else { 3515 } else {
3362 input.selectionStart = 0; 3516 input.selectionStart = 0;
3363 input.selectionEnd = selectionEnd; 3517 input.selectionEnd = selectionEnd;
3364 } 3518 }
3365 }, 0); 3519 }, 0);
3366 }; 3520 };
3367 3521
3368 FileManager.prototype.onNewFolderButtonClick_ = function(event) { 3522 FileManager.prototype.onToggleSidebar_ = function(event) {
3523 if (this.dialogContainer_.hasAttribute('sidebar')) {
3524 this.dialogContainer_.removeAttribute('sidebar');
3525 } else {
3526 this.dialogContainer_.setAttribute('sidebar', 'sidebar');
3527 }
3528 setTimeout(this.onResize_.bind(this), 300);
3529 };
3530
3531 FileManager.prototype.onNewFolderCommand_ = function(event) {
3369 var self = this; 3532 var self = this;
3370 3533
3371 function onNameSelected(name) { 3534 function onNameSelected(name) {
3372 var valid = self.validateFileName_(name, function() { 3535 var valid = self.validateFileName_(name, function() {
3373 promptForName(name); 3536 promptForName(name);
3374 }); 3537 });
3375 3538
3376 if (!valid) { 3539 if (!valid) {
3377 // Validation failed. User will be prompted for a new name after they 3540 // Validation failed. User will be prompted for a new name after they
3378 // dismiss the validation error dialog. 3541 // dismiss the validation error dialog.
(...skipping 108 matching lines...) Expand 10 before | Expand all | Expand 10 after
3487 this.dialogType_ != FileManager.SELECT_FOLDER) { 3650 this.dialogType_ != FileManager.SELECT_FOLDER) {
3488 event.preventDefault(); 3651 event.preventDefault();
3489 this.onDirectoryAction(this.selection.entries[0]); 3652 this.onDirectoryAction(this.selection.entries[0]);
3490 } else if (!this.okButton_.disabled) { 3653 } else if (!this.okButton_.disabled) {
3491 event.preventDefault(); 3654 event.preventDefault();
3492 this.onOk_(); 3655 this.onOk_();
3493 } 3656 }
3494 break; 3657 break;
3495 3658
3496 case 32: // Ctrl-Space => New Folder. 3659 case 32: // Ctrl-Space => New Folder.
3497 if (this.newFolderButton_.style.display != 'none' && event.ctrlKey) { 3660 if ((this.dialogType_ == 'saveas-file' ||
3661 this.dialogType_ == 'full-page') && event.ctrlKey) {
3498 event.preventDefault(); 3662 event.preventDefault();
3499 this.onNewFolderButtonClick_(); 3663 this.onNewFolderCommand_();
3500 } 3664 }
3501 break; 3665 break;
3502 3666
3503 case 88: // Ctrl-X => Cut. 3667 case 88: // Ctrl-X => Cut.
3504 this.updateCommands_(); 3668 this.updateCommands_();
3505 if (!this.commands_['cut'].disabled) { 3669 if (!this.commands_['cut'].disabled) {
3506 event.preventDefault(); 3670 event.preventDefault();
3507 this.commands_['cut'].execute(); 3671 this.commands_['cut'].execute();
3508 } 3672 }
3509 break; 3673 break;
(...skipping 283 matching lines...) Expand 10 before | Expand all | Expand 10 after
3793 }); 3957 });
3794 }, onError); 3958 }, onError);
3795 3959
3796 function onError(err) { 3960 function onError(err) {
3797 console.log('Error while checking free space: ' + err); 3961 console.log('Error while checking free space: ' + err);
3798 setTimeout(doCheck, 1000 * 60); 3962 setTimeout(doCheck, 1000 * 60);
3799 } 3963 }
3800 } 3964 }
3801 } 3965 }
3802 })(); 3966 })();
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698