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

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

Issue 10310163: Refactoring file manager: moving volume mounting related code to a separate class. (Closed) Base URL: svn://svn.chromium.org/chrome/trunk/src
Patch Set: Created 8 years, 7 months 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) 2012 The Chromium Authors. All rights reserved. 1 // Copyright (c) 2012 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 /** 5 /**
6 * FileManager constructor. 6 * FileManager constructor.
7 * 7 *
8 * FileManager objects encapsulate the functionality of the file selector 8 * FileManager objects encapsulate the functionality of the file selector
9 * dialogs, as well as the full screen file manager application (though the 9 * dialogs, as well as the full screen file manager application (though the
10 * latter is not yet implemented). 10 * latter is not yet implemented).
(...skipping 10 matching lines...) Expand all
21 {}; 21 {};
22 22
23 this.listType_ = null; 23 this.listType_ = null;
24 24
25 this.selection = null; 25 this.selection = null;
26 26
27 this.butterTimer_ = null; 27 this.butterTimer_ = null;
28 this.currentButter_ = null; 28 this.currentButter_ = null;
29 this.butterLastShowTime_ = 0; 29 this.butterLastShowTime_ = 0;
30 30
31 this.watchedDirectoryUrl_ = null;
32 this.filesystemObserverId_ = null; 31 this.filesystemObserverId_ = null;
33 this.gdataObserverId_ = null; 32 this.gdataObserverId_ = null;
34 33
35 this.commands_ = {}; 34 this.commands_ = {};
36 35
37 this.thumbnailUrlCache_ = {}; 36 this.thumbnailUrlCache_ = {};
38 37
39 this.document_ = dialogDom.ownerDocument; 38 this.document_ = dialogDom.ownerDocument;
40 this.dialogType_ = this.params_.type || FileManager.DialogType.FULL_PAGE; 39 this.dialogType_ = this.params_.type || FileManager.DialogType.FULL_PAGE;
41 40
42 metrics.recordEnum('Create', this.dialogType_, 41 metrics.recordEnum('Create', this.dialogType_,
43 [FileManager.DialogType.SELECT_FOLDER, 42 [FileManager.DialogType.SELECT_FOLDER,
44 FileManager.DialogType.SELECT_SAVEAS_FILE, 43 FileManager.DialogType.SELECT_SAVEAS_FILE,
45 FileManager.DialogType.SELECT_OPEN_FILE, 44 FileManager.DialogType.SELECT_OPEN_FILE,
46 FileManager.DialogType.SELECT_OPEN_MULTI_FILE, 45 FileManager.DialogType.SELECT_OPEN_MULTI_FILE,
47 FileManager.DialogType.FULL_PAGE]); 46 FileManager.DialogType.FULL_PAGE]);
48 47
49 // TODO(dgozman): This will be changed to LocaleInfo. 48 // TODO(dgozman): This will be changed to LocaleInfo.
50 this.locale_ = new v8Locale(navigator.language); 49 this.locale_ = new v8Locale(navigator.language);
51 50
52 this.initFileSystem_(); 51 this.initFileSystem_();
52 this.volumeManager_ = VolumeManager.getInstance();
53 this.initDom_(); 53 this.initDom_();
54 this.initDialogType_(); 54 this.initDialogType_();
55 this.dialogDom_.style.opacity = '1'; 55 this.dialogDom_.style.opacity = '1';
56 } 56 }
57 57
58 FileManager.prototype = { 58 FileManager.prototype = {
59 __proto__: cr.EventTarget.prototype 59 __proto__: cr.EventTarget.prototype
60 }; 60 };
61 61
62 // Anonymous "namespace". 62 // Anonymous "namespace".
(...skipping 214 matching lines...) Expand 10 before | Expand all | Expand 10 after
277 util.UNITS = [str('SIZE_KB'), 277 util.UNITS = [str('SIZE_KB'),
278 str('SIZE_MB'), 278 str('SIZE_MB'),
279 str('SIZE_GB'), 279 str('SIZE_GB'),
280 str('SIZE_TB'), 280 str('SIZE_TB'),
281 str('SIZE_PB')]; 281 str('SIZE_PB')];
282 282
283 metrics.startInterval('Load.FileSystem'); 283 metrics.startInterval('Load.FileSystem');
284 284
285 var self = this; 285 var self = this;
286 286
287 // The list of active mount points to distinct them from other directories.
288 chrome.fileBrowserPrivate.getMountPoints(function(mountPoints) {
289 self.setMountPoints_(mountPoints);
290 onDone();
291 });
292
293 function onDone() {
294 if (self.mountPoints_ && self.filesystem_)
295 self.init_();
296 }
297
298 chrome.fileBrowserPrivate.requestLocalFileSystem(function(filesystem) { 287 chrome.fileBrowserPrivate.requestLocalFileSystem(function(filesystem) {
299 metrics.recordInterval('Load.FileSystem'); 288 metrics.recordInterval('Load.FileSystem');
300 self.filesystem_ = filesystem; 289 self.filesystem_ = filesystem;
301 onDone(); 290 self.init_();
302 }); 291 });
303 }; 292 };
304 293
305 FileManager.prototype.setMountPoints_ = function(mountPoints) {
306 this.mountPoints_ = mountPoints;
307 // Add gdata mount info if present.
308 if (this.gdataMounted_)
309 this.mountPoints_.push(this.gdataMountInfo_);
310 };
311
312 /** 294 /**
313 * Continue initializing the file manager after resolving roots. 295 * Continue initializing the file manager after resolving roots.
314 */ 296 */
315 FileManager.prototype.init_ = function() { 297 FileManager.prototype.init_ = function() {
316 metrics.startInterval('Load.DOM'); 298 metrics.startInterval('Load.DOM');
317 this.initCommands_(); 299 this.initCommands_();
318 300
319 this.metadataCache_ = MetadataCache.createFull(); 301 this.metadataCache_ = MetadataCache.createFull();
320 302
321 this.shortDateFormatter_ = 303 this.shortDateFormatter_ =
(...skipping 11 matching lines...) Expand all
333 315
334 this.table_.startBatchUpdates(); 316 this.table_.startBatchUpdates();
335 this.grid_.startBatchUpdates(); 317 this.grid_.startBatchUpdates();
336 318
337 this.initFileList_(); 319 this.initFileList_();
338 this.initDialogs_(); 320 this.initDialogs_();
339 321
340 window.addEventListener('popstate', this.onPopState_.bind(this)); 322 window.addEventListener('popstate', this.onPopState_.bind(this));
341 window.addEventListener('unload', this.onUnload_.bind(this)); 323 window.addEventListener('unload', this.onUnload_.bind(this));
342 324
343 this.directoryModel_.addEventListener('directory-changed', 325 var dm = this.directoryModel_;
326 dm.addEventListener('directory-changed',
344 this.onDirectoryChanged_.bind(this)); 327 this.onDirectoryChanged_.bind(this));
345 var self = this; 328 var self = this;
346 this.directoryModel_.addEventListener('begin-update-files', function() { 329 dm.addEventListener('begin-update-files', function() {
347 self.currentList_.startBatchUpdates(); 330 self.currentList_.startBatchUpdates();
348 }); 331 });
349 this.directoryModel_.addEventListener('end-update-files', function() { 332 dm.addEventListener('end-update-files', function() {
350 self.restoreItemBeingRenamed_(); 333 self.restoreItemBeingRenamed_();
351 self.currentList_.endBatchUpdates(); 334 self.currentList_.endBatchUpdates();
352 }); 335 });
353 this.directoryModel_.addEventListener('scan-started', 336 dm.addEventListener('scan-started', this.showSpinnerLater_.bind(this));
354 this.showSpinnerLater_.bind(this)); 337 dm.addEventListener('scan-completed', this.showSpinner_.bind(this, false));
355 this.directoryModel_.addEventListener('scan-completed', 338 dm.addEventListener('scan-completed',
356 this.showSpinner_.bind(this, false)); 339 this.refreshCurrentDirectoryMetadata_.bind(this));
357 this.directoryModel_.addEventListener('scan-completed', 340 dm.addEventListener('rescan-completed',
358 this.refreshCurrentDirectoryMetadata_.bind(this)); 341 this.refreshCurrentDirectoryMetadata_.bind(this));
359 this.directoryModel_.addEventListener('rescan-completed',
360 this.refreshCurrentDirectoryMetadata_.bind(this));
361 this.addEventListener('selection-summarized', 342 this.addEventListener('selection-summarized',
362 this.onSelectionSummarized_.bind(this)); 343 this.onSelectionSummarized_.bind(this));
363 344
364 // The list of archives requested to mount. We will show contents once 345 this.setupCurrentDirectory_(true /* page loading */);
365 // archive is mounted, but only for mounts from within this filebrowser tab.
366 this.mountRequests_ = [];
367 this.unmountRequests_ = [];
368 chrome.fileBrowserPrivate.onMountCompleted.addListener(
369 this.onMountCompleted_.bind(this));
370
371 chrome.fileBrowserPrivate.onFileChanged.addListener(
372 this.onFileChanged_.bind(this));
373
374 this.networkConnectionState_ = null; 346 this.networkConnectionState_ = null;
375 var queryNetworkConnectionState = function() { 347 var queryNetworkConnectionState = function() {
376 chrome.fileBrowserPrivate.getNetworkConnectionState( 348 // TODO(serya): uncomment
377 this.onNetworkConnectionChanged_.bind(this)); 349 // chrome.fileBrowserPrivate.getNetworkConnectionState(
350 // this.onNetworkConnectionChanged_.bind(this));
378 }.bind(this); 351 }.bind(this);
379 queryNetworkConnectionState(); 352 queryNetworkConnectionState();
380 chrome.fileBrowserPrivate.onNetworkConnectionChanged. 353 // TODO(serya): uncomment
381 addListener(queryNetworkConnectionState); 354 // chrome.fileBrowserPrivate.onNetworkConnectionChanged.
382 355 // addListener(queryNetworkConnectionState);
383 var invokeHandler = !this.params_.selectOnly;
384 if (this.isStartingOnGData_()) {
385 // We are opening on a GData path. Mount GData and show
386 // "Loading Google Docs" message until the directory content loads.
387 this.dialogContainer_.setAttribute('unmounted', true);
388 this.initGData_(true /* dirChanged */);
389 // This is a one-time handler (will be nulled out on the first call).
390 this.setupCurrentDirectoryPostponed_ = function(event) {
391 this.directoryModel_.removeEventListener('directory-changed',
392 this.setupCurrentDirectoryPostponed_);
393 this.setupCurrentDirectoryPostponed_ = null;
394 if (event) // If called as an event handler just exit silently.
395 return;
396 this.setupCurrentDirectory_(
397 invokeHandler, false /* blankWhileOpeningAFile */);
398 }.bind(this);
399 this.directoryModel_.addEventListener('directory-changed',
400 this.setupCurrentDirectoryPostponed_);
401 } else {
402 this.setupCurrentDirectory_(
403 invokeHandler, true /* blankWhileOpeningAFile */);
404 }
405 356
406 this.summarizeSelection_(); 357 this.summarizeSelection_();
407 358
408 var sortField = 359 var sortField =
409 window.localStorage['sort-field-' + this.dialogType_] || 360 window.localStorage['sort-field-' + this.dialogType_] ||
410 'modificationTime'; 361 'modificationTime';
411 var sortDirection = 362 var sortDirection =
412 window.localStorage['sort-direction-' + this.dialogType_] || 'desc'; 363 window.localStorage['sort-direction-' + this.dialogType_] || 'desc';
413 this.directoryModel_.sortFileList(sortField, sortDirection); 364 this.directoryModel_.sortFileList(sortField, sortDirection);
414 365
(...skipping 94 matching lines...) Expand 10 before | Expand all | Expand 10 after
509 this.filenameInput_ = this.dialogDom_.querySelector('.filename-input'); 460 this.filenameInput_ = this.dialogDom_.querySelector('.filename-input');
510 this.taskItems_ = this.dialogDom_.querySelector('#tasks'); 461 this.taskItems_ = this.dialogDom_.querySelector('#tasks');
511 this.okButton_ = this.dialogDom_.querySelector('.ok'); 462 this.okButton_ = this.dialogDom_.querySelector('.ok');
512 this.cancelButton_ = this.dialogDom_.querySelector('.cancel'); 463 this.cancelButton_ = this.dialogDom_.querySelector('.cancel');
513 this.deleteButton_ = this.dialogDom_.querySelector('#delete-button'); 464 this.deleteButton_ = this.dialogDom_.querySelector('#delete-button');
514 this.table_ = this.dialogDom_.querySelector('.detail-table'); 465 this.table_ = this.dialogDom_.querySelector('.detail-table');
515 this.grid_ = this.dialogDom_.querySelector('.thumbnail-grid'); 466 this.grid_ = this.dialogDom_.querySelector('.thumbnail-grid');
516 this.spinner_ = this.dialogDom_.querySelector('.spinner'); 467 this.spinner_ = this.dialogDom_.querySelector('.spinner');
517 this.showSpinner_(false); 468 this.showSpinner_(false);
518 this.butter_ = this.dialogDom_.querySelector('.butter-bar'); 469 this.butter_ = this.dialogDom_.querySelector('.butter-bar');
519 this.unmountedPanel_ = this.dialogDom_.querySelector('.unmounted-panel');
520 470
521 cr.ui.decorate('#gdata-settings', cr.ui.MenuButton); 471 cr.ui.decorate('#gdata-settings', cr.ui.MenuButton);
522 cr.ui.Table.decorate(this.table_); 472 cr.ui.Table.decorate(this.table_);
523 cr.ui.Grid.decorate(this.grid_); 473 cr.ui.Grid.decorate(this.grid_);
524 474
525 this.document_.addEventListener('keydown', this.onKeyDown_.bind(this)); 475 this.document_.addEventListener('keydown', this.onKeyDown_.bind(this));
526 // Disable the default browser context menu. 476 // Disable the default browser context menu.
527 this.document_.addEventListener('contextmenu', 477 this.document_.addEventListener('contextmenu',
528 function(e) { e.preventDefault() }); 478 function(e) { e.preventDefault() });
529 479
(...skipping 29 matching lines...) Expand all
559 509
560 this.decorateSplitter( 510 this.decorateSplitter(
561 this.dialogDom_.querySelector('div.sidebar-splitter')); 511 this.dialogDom_.querySelector('div.sidebar-splitter'));
562 512
563 this.dialogContainer_ = this.dialogDom_.querySelector('.dialog-container'); 513 this.dialogContainer_ = this.dialogDom_.querySelector('.dialog-container');
564 this.dialogDom_.querySelector('#detail-view').addEventListener( 514 this.dialogDom_.querySelector('#detail-view').addEventListener(
565 'click', this.onDetailViewButtonClick_.bind(this)); 515 'click', this.onDetailViewButtonClick_.bind(this));
566 this.dialogDom_.querySelector('#thumbnail-view').addEventListener( 516 this.dialogDom_.querySelector('#thumbnail-view').addEventListener(
567 'click', this.onThumbnailViewButtonClick_.bind(this)); 517 'click', this.onThumbnailViewButtonClick_.bind(this));
568 518
569 // When we show the page for the first time we want to avoid
570 // the GDrive settings button animation, so we set the attribute ASAP.
571 if (this.isStartingOnGData_())
572 this.dialogContainer_.setAttribute('gdata', true);
573
574 this.syncButton = this.dialogDom_.querySelector('#gdata-sync-settings'); 519 this.syncButton = this.dialogDom_.querySelector('#gdata-sync-settings');
575 this.syncButton.addEventListener('click', this.onGDataPrefClick_.bind( 520 this.syncButton.addEventListener('click', this.onGDataPrefClick_.bind(
576 this, 'cellularDisabled', false /* not inverted */)); 521 this, 'cellularDisabled', false /* not inverted */));
577 522
578 this.hostedButton = this.dialogDom_.querySelector('#gdata-hosted-settings'); 523 this.hostedButton = this.dialogDom_.querySelector('#gdata-hosted-settings');
579 this.hostedButton.addEventListener('click', this.onGDataPrefClick_.bind( 524 this.hostedButton.addEventListener('click', this.onGDataPrefClick_.bind(
580 this, 'hostedFilesDisabled', true /* inverted */)); 525 this, 'hostedFilesDisabled', true /* inverted */));
581 526
582 this.gdataPreferences_ = null; 527 this.gdataPreferences_ = null;
583 var queryGDataPreferences = function() { 528 var queryGDataPreferences = function() {
584 chrome.fileBrowserPrivate.getGDataPreferences( 529 chrome.fileBrowserPrivate.getGDataPreferences(
585 this.onGDataPreferencesChanged_.bind(this)); 530 this.onGDataPreferencesChanged_.bind(this));
586 }.bind(this); 531 }.bind(this);
587 queryGDataPreferences(); 532 queryGDataPreferences();
588 chrome.fileBrowserPrivate.onGDataPreferencesChanged. 533 // TODO(serya): uncomment
589 addListener(queryGDataPreferences); 534 //chrome.fileBrowserPrivate.onGDataPreferencesChanged.
535 // addListener(queryGDataPreferences);
590 536
591 cr.ui.ComboButton.decorate(this.taskItems_); 537 cr.ui.ComboButton.decorate(this.taskItems_);
592 this.taskItems_.addEventListener('select', 538 this.taskItems_.addEventListener('select',
593 this.onTaskItemClicked_.bind(this)); 539 this.onTaskItemClicked_.bind(this));
594 540
595 this.dialogDom_.ownerDocument.defaultView.addEventListener( 541 this.dialogDom_.ownerDocument.defaultView.addEventListener(
596 'resize', this.onResize_.bind(this)); 542 'resize', this.onResize_.bind(this));
597 543
598 if (str('ASH') == '1') 544 if (str('ASH') == '1')
599 this.dialogDom_.setAttribute('ash', 'true'); 545 this.dialogDom_.setAttribute('ash', 'true');
(...skipping 19 matching lines...) Expand all
619 565
620 var sigleSelection = 566 var sigleSelection =
621 this.dialogType_ == FileManager.DialogType.SELECT_OPEN_FILE || 567 this.dialogType_ == FileManager.DialogType.SELECT_OPEN_FILE ||
622 this.dialogType_ == FileManager.DialogType.SELECT_FOLDER || 568 this.dialogType_ == FileManager.DialogType.SELECT_FOLDER ||
623 this.dialogType_ == FileManager.DialogType.SELECT_SAVEAS_FILE; 569 this.dialogType_ == FileManager.DialogType.SELECT_SAVEAS_FILE;
624 570
625 this.directoryModel_ = new DirectoryModel( 571 this.directoryModel_ = new DirectoryModel(
626 this.filesystem_.root, 572 this.filesystem_.root,
627 sigleSelection, 573 sigleSelection,
628 FileManager.isGDataEnabled(), 574 FileManager.isGDataEnabled(),
629 this.metadataCache_); 575 this.metadataCache_,
576 VolumeManager.getInstance());
577
578 this.fileWatcher_ = new FileWatcher(this.filesystem_.root,
579 this.directoryModel_,
580 VolumeManager.getInstance());
581 this.fileWatcher_.start();
630 582
631 if (FileManager.isGDataEnabled()) 583 if (FileManager.isGDataEnabled())
632 this.initGDataWelcomeBanners_(); 584 this.initGDataWelcomeBanners_();
633 585
634 var dataModel = this.directoryModel_.getFileList(); 586 var dataModel = this.directoryModel_.getFileList();
635 var collator = this.collator_; 587 var collator = this.collator_;
636 // TODO(dgozman): refactor comparison functions together with 588 // TODO(dgozman): refactor comparison functions together with
637 // render/update/display. 589 // render/update/display.
638 dataModel.setCompareFunction('name', function(a, b) { 590 dataModel.setCompareFunction('name', function(a, b) {
639 return collator.compare(a.name, b.name); 591 return collator.compare(a.name, b.name);
(...skipping 17 matching lines...) Expand all
657 this.initGrid_(); 609 this.initGrid_();
658 this.initRootsList_(); 610 this.initRootsList_();
659 611
660 var listType = FileManager.ListType.DETAIL; 612 var listType = FileManager.ListType.DETAIL;
661 if (FileManager.DialogType.isModal(this.dialogType_)) 613 if (FileManager.DialogType.isModal(this.dialogType_))
662 listType = window.localStorage['listType-' + this.dialogType_] || 614 listType = window.localStorage['listType-' + this.dialogType_] ||
663 FileManager.ListType.DETAIL; 615 FileManager.ListType.DETAIL;
664 this.setListType(listType); 616 this.setListType(listType);
665 617
666 this.textSearchState_ = {text: '', date: new Date()}; 618 this.textSearchState_ = {text: '', date: new Date()};
619
620 this.volumeManager_.addEventListener('gdata-status-changed',
621 this.updateGDataStatus_.bind(this));
667 }; 622 };
668 623
669 FileManager.prototype.initRootsList_ = function() { 624 FileManager.prototype.initRootsList_ = function() {
670 this.rootsList_ = this.dialogDom_.querySelector('#roots-list'); 625 this.rootsList_ = this.dialogDom_.querySelector('#roots-list');
671 cr.ui.List.decorate(this.rootsList_); 626 cr.ui.List.decorate(this.rootsList_);
672 this.rootsList_.startBatchUpdates();
673 627
674 var self = this; 628 var self = this;
675 this.rootsList_.itemConstructor = function(entry) { 629 this.rootsList_.itemConstructor = function(entry) {
676 return self.renderRoot_(entry); 630 return self.renderRoot_(entry);
677 }; 631 };
678 632
679 this.rootsList_.selectionModel = 633 this.rootsList_.selectionModel =
680 this.directoryModel_.getRootsListSelectionModel(); 634 this.directoryModel_.getRootsListSelectionModel();
681 635
682 // TODO(dgozman): add "Add a drive" item. 636 // TODO(dgozman): add "Add a drive" item.
683 this.rootsList_.dataModel = this.directoryModel_.getRootsList(); 637 this.rootsList_.dataModel = this.directoryModel_.getRootsList();
684 this.directoryModel_.updateRoots(function() {
685 self.rootsList_.endBatchUpdates();
686 }, false);
687 }; 638 };
688 639
689 /** 640 FileManager.prototype.updateGDataStatus_ = function() {
690 * @param {boolean} dirChanged True if we just changed to GData directory, 641 var node = this.dialogContainer_;
691 * False if "Retry" button clicked. 642 if (this.isOnGData()) {
692 */ 643 var status = this.volumeManager_.getGDataStatus();
693 FileManager.prototype.initGData_ = function(dirChanged) { 644 if (status == VolumeManager.GDataStatus.MOUNTING ||
694 this.initGDataUnmountedPanel_(); 645 status == VolumeManager.GDataStatus.ERROR) {
695 646 this.initGDataUnmountedPanel_();
696 this.unmountedPanel_.removeAttribute('error');
697 if (dirChanged) {
698 // When changing to GData directory we want to see a clear panel.
699 this.unmountedPanel_.removeAttribute('retry');
700 if (this.gdataLoadingTimer_) { // Show immediately if already loading.
701 this.unmountedPanel_.setAttribute('loading', true);
702 } else {
703 this.unmountedPanel_.removeAttribute('loading');
704 setTimeout(function() {
705 if (this.gdataLoadingTimer_) { // Still loading.
706 this.unmountedPanel_.setAttribute('loading', true);
707 }
708 }.bind(this), 500);
709 } 647 }
648 node.setAttribute('gdata', status);
710 } else { 649 } else {
711 // When retrying we do not hide "Retry" and "Learn more". 650 node.removeAttribute('gdata');
712 this.unmountedPanel_.setAttribute('loading', true);
713 } 651 }
714 652 console.log('gdata status: ', node.getAttribute('gdata'));
715 // If the user changed to another directory and then back to GData we
716 // re-enter this method while the timer is still active. In this case
717 // we only update the UI but do not request the mount again.
718 if (this.gdataLoadingTimer_)
719 return;
720
721 metrics.startInterval('Load.GData');
722 chrome.fileBrowserPrivate.addMount('', 'gdata', {},
723 function(sourcePath) {});
724
725 // This timer could fire before the mount succeeds. We will silently
726 // replace the error message with the correct directory contents.
727 this.gdataLoadingTimer_ = setTimeout(function() {
728 this.gdataLoadingTimer_ = null;
729 this.onGDataUnreachable_('GData load timeout');
730 }.bind(this),
731 15 * 60 * 1000);
732 };
733
734 FileManager.prototype.clearGDataLoadingTimer_ = function(message) {
735 if (this.gdataLoadingTimer_) {
736 clearTimeout(this.gdataLoadingTimer_);
737 this.gdataLoadingTimer_ = null;
738 }
739 };
740
741 FileManager.prototype.onGDataUnreachable_ = function(message) {
742 console.warn(message);
743 if (this.isOnGData()) {
744 this.unmountedPanel_.removeAttribute('loading');
745 this.unmountedPanel_.setAttribute('error', true);
746 this.unmountedPanel_.setAttribute('retry', true);
747 }
748 }; 653 };
749 654
750 FileManager.prototype.initGDataUnmountedPanel_ = function() { 655 FileManager.prototype.initGDataUnmountedPanel_ = function() {
751 if (this.unmountedPanel_.firstElementChild) 656 var panel = this.document_.querySelector('#unmounted-panel');
657 if (panel.firstElementChild)
752 return; 658 return;
753 659
754 var loading = this.document_.createElement('div'); 660 function create(tag, className) {
755 loading.className = 'gdata loading'; 661 var div = panel.ownerDocument.createElement(tag);
756 loading.textContent = str('GDATA_LOADING'); 662 div.className = className;
757 this.unmountedPanel_.appendChild(loading); 663 return div;
664 }
758 665
759 var spinnerBox = this.document_.createElement('div'); 666 function append(tag, className, opt_textContent) {
760 spinnerBox.className = 'spinner-box'; 667 var div = create(tag, className);
761 loading.appendChild(spinnerBox); 668 div.textContent = opt_textContent | '';
669 panel.appendChild(div);
670 return div;
671 }
762 672
763 var spinner = this.document_.createElement('div'); 673 append('div', 'gdata loading', str('GDATA_LOADING'));
764 spinner.className = 'spinner'; 674 append('div', 'spinner-box').appendChild(create('div', 'spinner'));
765 spinnerBox.appendChild(spinner); 675 var progress = append('div', 'gdata progress');
766
767 var progress = this.document_.createElement('div');
768 progress.className = 'gdata progress';
769 this.unmountedPanel_.appendChild(progress);
770
771 chrome.fileBrowserPrivate.onDocumentFeedFetched.addListener( 676 chrome.fileBrowserPrivate.onDocumentFeedFetched.addListener(
772 function(fileCount) { 677 function(fileCount) {
773 progress.textContent = strf('GDATA_LOADING_PROGRESS', fileCount); 678 progress.textContent = strf('GDATA_LOADING_PROGRESS', fileCount);
774 }); 679 });
775 680
776 var error = this.document_.createElement('div'); 681 append('div', 'gdata error', strf('GDATA_CANNOT_REACH',
777 error.className = 'gdata error'; 682 str('GDATA_PRODUCT_NAME')));
778 error.textContent = strf('GDATA_CANNOT_REACH', str('GDATA_PRODUCT_NAME'));
779 this.unmountedPanel_.appendChild(error);
780 683
781 var retry = this.document_.createElement('button'); 684 append('button', 'gdata retry', str('GDATA_RETRY'));
782 retry.className = 'gdata retry'; 685 // onclick = this.initGData_.bind(this, false);
783 retry.textContent = str('GDATA_RETRY');
784 retry.onclick = this.initGData_.bind(this, false /* retry */);
785 this.unmountedPanel_.appendChild(retry);
786 686
787 var learnMore = this.document_.createElement('div'); 687 append('div', 'gdata learn-more plain-link', str('GDATA_LEARN_MORE')).
788 learnMore.className = 'gdata learn-more plain-link'; 688 onclick = this.onExternalLinkClick_.bind(this,
789 learnMore.textContent = str('GDATA_LEARN_MORE'); 689 GOOGLE_DRIVE_ERROR_HELP_URL);
790 learnMore.addEventListener('click',
791 this.onExternalLinkClick_.bind(this, GOOGLE_DRIVE_ERROR_HELP_URL));
792 this.unmountedPanel_.appendChild(learnMore);
793 }; 690 };
794 691
795 FileManager.prototype.onDataModelSplice_ = function(event) { 692 FileManager.prototype.onDataModelSplice_ = function(event) {
796 var checkbox = this.document_.querySelector('#select-all-checkbox'); 693 var checkbox = this.document_.querySelector('#select-all-checkbox');
797 if (checkbox) 694 if (checkbox)
798 this.updateSelectAllCheckboxState_(checkbox); 695 this.updateSelectAllCheckboxState_(checkbox);
799 }; 696 };
800 697
801 FileManager.prototype.onDataModelPermuted_ = function(event) { 698 FileManager.prototype.onDataModelPermuted_ = function(event) {
802 var sortStatus = this.directoryModel_.getFileList().sortStatus; 699 var sortStatus = this.directoryModel_.getFileList().sortStatus;
(...skipping 530 matching lines...) Expand 10 before | Expand all | Expand 10 after
1333 1230
1334 case 'delete': 1231 case 'delete':
1335 this.deleteEntries(this.selection.entries); 1232 this.deleteEntries(this.selection.entries);
1336 return; 1233 return;
1337 1234
1338 case 'newfolder': 1235 case 'newfolder':
1339 this.onNewFolderCommand_(event); 1236 this.onNewFolderCommand_(event);
1340 return; 1237 return;
1341 1238
1342 case 'unmount': 1239 case 'unmount':
1343 this.unmountVolume_(this.directoryModel_.getCurrentRootDirEntry()); 1240 this.unmountVolume_(
1241 this.directoryModel_.getCurrentRootDirEntry());
1344 return; 1242 return;
1345 1243
1346 case 'format': 1244 case 'format':
1347 var entry = this.directoryModel_.getCurrentRootDirEntry(); 1245 var entry = this.directoryModel_.getCurrentRootDirEntry();
1348 this.confirm.show(str('FORMATTING_WARNING'), function() { 1246 this.confirm.show(str('FORMATTING_WARNING'), function() {
1349 chrome.fileBrowserPrivate.formatDevice(entry.toURL()); 1247 chrome.fileBrowserPrivate.formatDevice(entry.toURL());
1350 }); 1248 });
1351 1249
1352 return; 1250 return;
1353 } 1251 }
1354 }; 1252 };
1355 1253
1356 /** 1254 /**
1357 * Respond to the back and forward buttons. 1255 * Respond to the back and forward buttons.
1358 */ 1256 */
1359 FileManager.prototype.onPopState_ = function(event) { 1257 FileManager.prototype.onPopState_ = function(event) {
1360 this.closeFilePopup_(); 1258 this.closeFilePopup_();
1361 // Nothing left to do if the current directory is not changing. This happens 1259 // Nothing left to do if the current directory is not changing. This happens
1362 // if we are exiting the Gallery. 1260 // if we are exiting the Gallery.
1363 if (this.getPathFromUrlOrParams_() == 1261 if (this.getPathFromUrlOrParams_() ==
1364 this.directoryModel_.getCurrentDirEntry().fullPath) 1262 this.directoryModel_.getCurrentDirEntry().fullPath)
1365 return; 1263 return;
1366 this.setupCurrentDirectory_(true /* invokeHandler */); 1264 this.setupCurrentDirectory_(false /* page loading */);
1367 }; 1265 };
1368 1266
1369 FileManager.prototype.requestResize_ = function(timeout) { 1267 FileManager.prototype.requestResize_ = function(timeout) {
1370 setTimeout(this.onResize_.bind(this), timeout || 0); 1268 setTimeout(this.onResize_.bind(this), timeout || 0);
1371 }; 1269 };
1372 1270
1373 /** 1271 /**
1374 * Resize details and thumb views to fit the new window size. 1272 * Resize details and thumb views to fit the new window size.
1375 */ 1273 */
1376 FileManager.prototype.onResize_ = function() { 1274 FileManager.prototype.onResize_ = function() {
(...skipping 33 matching lines...) Expand 10 before | Expand all | Expand 10 after
1410 }; 1308 };
1411 1309
1412 /** 1310 /**
1413 * Restores current directory and may be a selected item after page load (or 1311 * Restores current directory and may be a selected item after page load (or
1414 * reload) or popping a state (after click on back/forward). If location.hash 1312 * reload) or popping a state (after click on back/forward). If location.hash
1415 * is present it means that the user has navigated somewhere and that place 1313 * is present it means that the user has navigated somewhere and that place
1416 * will be restored. defaultPath primarily is used with save/open dialogs. 1314 * will be restored. defaultPath primarily is used with save/open dialogs.
1417 * Default path may also contain a file name. Freshly opened file manager 1315 * Default path may also contain a file name. Freshly opened file manager
1418 * window has neither. 1316 * window has neither.
1419 * 1317 *
1420 * @param {boolean} invokeHandler Whether to invoke the default handler on 1318 * @param {boolean} invokeHandler Whether to invoke the default handler on
Vladislav Kaznacheev 2012/05/15 11:11:54 We do need the |invokeHandler| parameter. I cannot
SeRya 2012/05/15 18:16:20 |invokeHandler| was inlined in place. I added invo
1421 * the selected file. 1319 * the selected file.
1422 * @param {boolean} opt_blankWhileOpeningAFile Whether to show fade over 1320 * @param {boolean} opt_blankWhileOpeningAFile Whether to show fade over
1423 * the file manager. 1321 * the file manager.
1424 */ 1322 */
1425 FileManager.prototype.setupCurrentDirectory_ = 1323 FileManager.prototype.setupCurrentDirectory_ = function(pageLoading) {
1426 function(invokeHandler, opt_blankWhileOpeningAFile) {
1427 var path = this.getPathFromUrlOrParams_(); 1324 var path = this.getPathFromUrlOrParams_();
1428 1325
1429 if (!path) { 1326 if (!path) {
1430 this.directoryModel_.setupDefaultPath(); 1327 this.directoryModel_.setupDefaultPath();
1431 return; 1328 return;
1432 } 1329 }
1433 1330
1331 if (DirectoryModel.getRootType(path) == DirectoryModel.RootType.GDATA) {
1332 if (!FileManager.isGDataEnabled()) {
1333 this.directoryModel_.setupDefaultPath();
1334 return;
1335 }
1336
1337 // Reflect immediatelly in the UI we are on GData and display
1338 // mounting UI.
1339 this.directoryModel_.setupPath('/' + DirectoryModel.GDATA_DIRECTORY);
1340
1341 var tracker = this.directoryModel_.createDirectoryChangeTracker();
1342 // Expected finish of setupPath to GData.
1343 tracker.exceptInitialChange = true;
1344 tracker.start();
1345 this.volumeManager_.mountGData(function() {
1346 tracker.stop();
1347 if (!tracker.hasChanged) {
1348 this.finishSetupCurrentDirectory_(path, pageLoading);
1349 }
1350 }.bind(this), function(error) {
1351 tracker.stop();
1352 });
1353 } else {
1354 this.finishSetupCurrentDirectory_(path, pageLoading);
1355 }
1356 };
1357
1358 FileManager.prototype.finishSetupCurrentDirectory_ = function(
1359 path, pageLoading) {
1434 // In the FULL_PAGE mode if the hash path points to a file we might have 1360 // In the FULL_PAGE mode if the hash path points to a file we might have
1435 // to invoke a task after selecting it. 1361 // to invoke a task after selecting it.
1436 // If the file path is in params_ we only want to select the file. 1362 // If the file path is in params_ we only want to select the file.
1437 if (invokeHandler && location.hash && 1363 if (this.dialogType_ == FileManager.DialogType.FULL_PAGE &&
1438 this.dialogType_ == FileManager.DialogType.FULL_PAGE) { 1364 location.hash && (!pageLoading || !this.params_.selectOnly)) {
1439 // To prevent the file list flickering for a moment before the action 1365 // To prevent the file list flickering for a moment before the action
1440 // is executed we hide it under a white div. 1366 // is executed we hide it under a white div.
1441 var shade; 1367 var shade;
1442 if (opt_blankWhileOpeningAFile) { 1368 if (pageLoading) {
1443 shade = this.document_.createElement('div'); 1369 shade = this.document_.createElement('div');
1444 shade.className = 'overlay-pane'; 1370 shade.className = 'overlay-pane';
1445 shade.style.backgroundColor = 'white'; 1371 shade.style.backgroundColor = 'white';
1446 this.document_.body.appendChild(shade); 1372 this.document_.body.appendChild(shade);
1447 } 1373 }
1448 function removeShade() { 1374 function removeShade() {
1449 if (shade) 1375 if (shade)
1450 shade.parentNode.removeChild(shade); 1376 shade.parentNode.removeChild(shade);
1451 } 1377 }
1452 1378
(...skipping 315 matching lines...) Expand 10 before | Expand all | Expand 10 after
1768 } 1694 }
1769 }; 1695 };
1770 li.addEventListener('mousedown', handleClick); 1696 li.addEventListener('mousedown', handleClick);
1771 li.addEventListener(cr.ui.TouchHandler.EventType.TOUCH_START, handleClick); 1697 li.addEventListener(cr.ui.TouchHandler.EventType.TOUCH_START, handleClick);
1772 1698
1773 var rootType = DirectoryModel.getRootType(entry.fullPath); 1699 var rootType = DirectoryModel.getRootType(entry.fullPath);
1774 1700
1775 var div = this.document_.createElement('div'); 1701 var div = this.document_.createElement('div');
1776 div.className = 'root-label'; 1702 div.className = 'root-label';
1777 1703
1778 var icon = rootType;
1779 var deviceNumber = this.getDeviceNumber(entry);
1780
1781 if (deviceNumber != undefined) {
1782 var mountCondition = this.mountPoints_[deviceNumber].mountCondition;
1783 if (mountCondition == 'unknown_filesystem' ||
1784 mountCondition == 'unsupported_filesystem')
1785 icon = 'unreadable';
1786 }
1787
1788 div.setAttribute('icon', icon);
1789
1790 div.textContent = this.getRootLabel_(entry.fullPath); 1704 div.textContent = this.getRootLabel_(entry.fullPath);
1791 li.appendChild(div); 1705 li.appendChild(div);
1792 1706
1793 if (rootType == DirectoryModel.RootType.ARCHIVE || 1707 if (rootType == DirectoryModel.RootType.ARCHIVE ||
1794 rootType == DirectoryModel.RootType.REMOVABLE) { 1708 rootType == DirectoryModel.RootType.REMOVABLE) {
1795 if (entry.unmounting) { 1709 var eject = this.document_.createElement('div');
1796 li.setAttribute('disabled', 'disabled'); 1710 eject.className = 'root-eject';
1797 } else { 1711 eject.addEventListener('click', function(event) {
1798 var eject = this.document_.createElement('div'); 1712 event.stopPropagation();
1799 eject.className = 'root-eject'; 1713 this.unmountVolume_(entry);
1800 eject.addEventListener('click', function(event) { 1714 }.bind(this));
1801 event.stopPropagation(); 1715 // Block other mouse handlers.
1802 this.unmountVolume_(entry); 1716 eject.addEventListener('mouseup', function(e) { e.stopPropagation() });
1803 }.bind(this)); 1717 eject.addEventListener('mousedown', function(e) { e.stopPropagation() });
1804 // Block other mouse handlers. 1718 li.appendChild(eject);
1805 eject.addEventListener('mouseup', function(e) { e.stopPropagation() });
1806 eject.addEventListener('mousedown', function(e) { e.stopPropagation() }) ;
1807 li.appendChild(eject);
1808 1719
1809 cr.ui.contextMenuHandler.setContextMenu(li, this.rootsContextMenu_); 1720 cr.ui.contextMenuHandler.setContextMenu(li, this.rootsContextMenu_);
1810 }
1811 } 1721 }
1812 1722
1813 cr.defineProperty(li, 'lead', cr.PropertyKind.BOOL_ATTR); 1723 cr.defineProperty(li, 'lead', cr.PropertyKind.BOOL_ATTR);
1814 cr.defineProperty(li, 'selected', cr.PropertyKind.BOOL_ATTR); 1724 cr.defineProperty(li, 'selected', cr.PropertyKind.BOOL_ATTR);
1725
1726 var icon = rootType;
1727 var mountCondition = this.volumeManager_.getMountCondition(entry.fullPath);
1728
1729 if (mountCondition == 'unknown_filesystem' ||
1730 mountCondition == 'unsupported_filesystem') {
1731 icon = 'unreadable';
1732 }
1733 div.setAttribute('icon', icon);
1734
1815 return li; 1735 return li;
1816 }; 1736 };
1817 1737
1818 /** 1738 /**
1819 * Unmounts device. 1739 * Unmounts device.
1820 * @param {Entry} entry The entry to unmount. 1740 * @param {Entry} entry The entry to unmount.
1821 */ 1741 */
1822 FileManager.prototype.unmountVolume_ = function(entry) { 1742 FileManager.prototype.unmountVolume_ = function(entry) {
1823 this.directoryModel_.prepareUnmount(entry.fullPath); 1743 listItem = this.rootsList_.fileListItem(entry);
1824 this.unmountRequests_.push(entry.fullPath); 1744 listItem && listItem.setAttribute('disabled');
1825 chrome.fileBrowserPrivate.removeMount(entry.toURL()); 1745 var self = this;
1746 this.volumeManager_.unmount(entry.fullPath, function() {},
1747 function(error) {
1748 listItem && listItem.removeAttribute('disabled');
1749 self.alert.show(strf('UNMOUNT_FAILED', error.message));
1750 });
1826 }; 1751 };
1827 1752
1828 FileManager.prototype.updateGDataStyle_ = function( 1753 FileManager.prototype.updateGDataStyle_ = function(
1829 listItem, entry, gdata) { 1754 listItem, entry, gdata) {
1830 if (!this.isOnGData() || !gdata) 1755 if (!this.isOnGData() || !gdata)
1831 return; 1756 return;
1832 1757
1833 if (!entry.isDirectory) { 1758 if (!entry.isDirectory) {
1834 if (!gdata.availableOffline) 1759 if (!gdata.availableOffline)
1835 listItem.classList.add('dim-offline'); 1760 listItem.classList.add('dim-offline');
(...skipping 357 matching lines...) Expand 10 before | Expand all | Expand 10 after
2193 selection.bytes += filesystem.size; 2118 selection.bytes += filesystem.size;
2194 } 2119 }
2195 } 2120 }
2196 2121
2197 this.dispatchEvent(new cr.Event('selection-summarized')); 2122 this.dispatchEvent(new cr.Event('selection-summarized'));
2198 }.bind(this)); 2123 }.bind(this));
2199 2124
2200 if (this.isOnGData()) { 2125 if (this.isOnGData()) {
2201 this.metadataCache_.get(selection.urls, 'gdata', function(props) { 2126 this.metadataCache_.get(selection.urls, 'gdata', function(props) {
2202 selection.allGDataFilesPresent = 2127 selection.allGDataFilesPresent =
2203 props.filter(function(p) {return !p.availableOffline}).length == 0; 2128 props.filter(function(p) {return p && !p.availableOffline}).length = = 0;
2204 this.updateOkButton_(); 2129 this.updateOkButton_();
2205 }.bind(this)); 2130 }.bind(this));
2206 } 2131 }
2207 }; 2132 };
2208 2133
2209 /** 2134 /**
2210 * Check if all the files in the current selection are available. The only 2135 * Check if all the files in the current selection are available. The only
2211 * case when files might be not available is when the selection contains 2136 * case when files might be not available is when the selection contains
2212 * uncached GData files and the browser is offline. 2137 * uncached GData files and the browser is offline.
2213 * @return {boolean} True if all files in the current selection are 2138 * @return {boolean} True if all files in the current selection are
(...skipping 411 matching lines...) Expand 10 before | Expand all | Expand 10 after
2625 }; 2550 };
2626 2551
2627 FileManager.prototype.isOffline = function() { 2552 FileManager.prototype.isOffline = function() {
2628 return this.networkConnectionState_ && !this.networkConnectionState_.online; 2553 return this.networkConnectionState_ && !this.networkConnectionState_.online;
2629 }; 2554 };
2630 2555
2631 FileManager.prototype.isOnReadonlyDirectory = function() { 2556 FileManager.prototype.isOnReadonlyDirectory = function() {
2632 return this.directoryModel_.isReadOnly(); 2557 return this.directoryModel_.isReadOnly();
2633 }; 2558 };
2634 2559
2635 /** 2560 // TODO(serya): close tab on unmount.
2636 * Event handler called when some volume was mounted or unmouted. 2561 FileManager.prototype.onVolumeUnmounted_ = function(event) {
2637 */ 2562 if (event.mountPath == dm.getCurrentRootPath()) {
2638 FileManager.prototype.onMountCompleted_ = function(event) { 2563 if (this.params_.mountTriggered && !event.requested) {
2639 var changeDirectoryTo = null; 2564 // TODO(serya): What if 2 USB sticks plugged?
2640 2565 chrome.tabs.getCurrent(function(tab) {
2641 if (event && event.mountType == 'gdata') { 2566 chrome.tabs.remove(tab.id);
2642 var mounted = (event.eventType == 'mount'); 2567 });
2643 metrics.recordInterval('Load.GData');
2644 console.log('GData ' + (mounted ? 'mounted' : 'unmounted'));
2645 if (mounted && event.status == 'success') {
2646 this.gdataMounted_ = true;
2647 this.gdataMountInfo_ = {
2648 'mountPath': event.mountPath,
2649 'sourcePath': event.sourcePath,
2650 'mountType': event.mountType,
2651 'mountCondition': event.status
2652 };
2653 // Not calling clearGDataLoadingTimer_ here because we want to keep
2654 // "Loading Google Docs" message until the directory loads. It is OK if
2655 // the timer fires after the mount because onDirectoryChanged_ will hide
2656 // the unmounted panel.
2657 if (this.setupCurrentDirectoryPostponed_) {
2658 this.setupCurrentDirectoryPostponed_(false /* execute */);
2659 } else if (this.isOnGData() &&
2660 this.directoryModel_.getCurrentDirEntry().unmounted) {
2661 // We are currently on an unmounted GData directory, force a rescan.
2662 changeDirectoryTo = this.directoryModel_.getCurrentRootPath();
2663 }
2664 } else {
2665 this.gdataMounted_ = false;
2666 this.gdataMountInfo_ = null;
2667 this.clearGDataLoadingTimer_();
2668 this.onGDataUnreachable_('GData ' +
2669 (mounted ? ('mount failed: ' + event.status) : 'unmounted'));
2670 if (this.setupCurrentDirectoryPostponed_) {
2671 this.setupCurrentDirectoryPostponed_(true /* cancel */);
2672 // Change to unmounted GData root.
2673 changeDirectoryTo = '/' + DirectoryModel.GDATA_DIRECTORY;
2674 }
2675 } 2568 }
2676 } 2569 }
2677
2678 chrome.fileBrowserPrivate.getMountPoints(function(mountPoints) {
2679 this.setMountPoints_(mountPoints);
2680
2681 if (event.eventType == 'mount') {
2682 // Mount request finished - remove it.
2683 // Currently we only request mounts for archive files.
2684 var index = this.mountRequests_.indexOf(event.sourcePath);
2685 if (index != -1) {
2686 this.mountRequests_.splice(index, 1);
2687 if (event.status == 'success') {
2688 // Successful mount requested from this tab, go to the drive root.
2689 changeDirectoryTo = event.mountPath;
2690 } else {
2691 // Request initiated from this tab failed, report the error.
2692 var fileName = event.sourcePath.split('/').pop();
2693 this.alert.show(
2694 strf('ARCHIVE_MOUNT_FAILED', fileName, event.status));
2695 }
2696 }
2697 }
2698
2699 if (event.eventType == 'unmount') {
2700 // Unmount request finished - remove it.
2701 var index = this.unmountRequests_.indexOf(event.mountPath);
2702 if (index != -1) {
2703 this.unmountRequests_.splice(index, 1);
2704 if (event.status != 'success')
2705 this.alert.show(strf('UNMOUNT_FAILED', event.status));
2706 }
2707
2708 if (event.status == 'success' &&
2709 event.mountPath == this.directoryModel_.getCurrentRootPath()) {
2710 if (this.params_.mountTriggered && index == -1) {
2711 // This device mount was the reason this File Manager instance was
2712 // created. Now the device is unmounted from another instance
2713 // or the user removed the device manually. Close this instance.
2714 // window.close() sometimes doesn't work.
2715 chrome.tabs.getCurrent(function(tab) {
2716 chrome.tabs.remove(tab.id);
2717 });
2718 return;
2719 }
2720 // Current directory just unmounted. Move to the 'Downloads'.
2721 changeDirectoryTo = this.directoryModel_.getDefaultDirectory();
2722 }
2723 }
2724
2725 // Even if something failed root list should be rescanned.
2726 // Failed mounts can "give" us new devices which might be formatted,
2727 // so we have to refresh root list then.
2728 this.directoryModel_.updateRoots(function() {
2729 if (changeDirectoryTo) {
2730 this.directoryModel_.changeDirectory(changeDirectoryTo);
2731 }
2732 }.bind(this), this.gdataMounted_);
2733 }.bind(this));
2734 }; 2570 };
2735 2571
2736 /** 2572 /**
2737 * Event handler called when some internal task should be executed. 2573 * Event handler called when some internal task should be executed.
2738 */ 2574 */
2739 FileManager.prototype.onFileTaskExecute_ = function(id, urls) { 2575 FileManager.prototype.onFileTaskExecute_ = function(id, urls) {
2740 if (id == 'play') { 2576 if (id == 'play') {
2741 var position = 0; 2577 var position = 0;
2742 if (urls.length == 1) { 2578 if (urls.length == 1) {
2743 // If just a single audio file is selected pass along every audio file 2579 // If just a single audio file is selected pass along every audio file
2744 // in the directory. 2580 // in the directory.
2745 var selectedUrl = urls[0]; 2581 var selectedUrl = urls[0];
2746 urls = this.getAllUrlsInCurrentDirectory_().filter(FileType.isAudio); 2582 urls = this.getAllUrlsInCurrentDirectory_().filter(FileType.isAudio);
2747 position = urls.indexOf(selectedUrl); 2583 position = urls.indexOf(selectedUrl);
2748 } 2584 }
2749 chrome.mediaPlayerPrivate.play(urls, position); 2585 chrome.mediaPlayerPrivate.play(urls, position);
2750 } else if (id == 'mount-archive') { 2586 } else if (id == 'mount-archive') {
2751 var self = this; 2587 var self = this;
2588 var tracker = this.directoryModel_.createDirectoryChangeTracker();
2589 tracker.start();
2752 this.resolveSelectResults_(urls, function(urls) { 2590 this.resolveSelectResults_(urls, function(urls) {
2753 for (var index = 0; index < urls.length; ++index) { 2591 for (var index = 0; index < urls.length; ++index) {
2754 // Url in MountCompleted event won't be escaped, so let's make sure 2592 var path = /^filesystem:[\w-]*:\/\/[\w]*\/external(\/.*)$/.
2755 // we don't use escaped one in mountRequests_. 2593 exec(urls[index])[1];
2756 var unescapedUrl = unescape(urls[index]); 2594 if (!path)
2757 chrome.fileBrowserPrivate.addMount(unescapedUrl, 'file', {}, 2595 continue;
2758 function(sourcePath) { 2596 path = path.decodeURLComponent(path);
2759 self.mountRequests_.push(sourcePath); 2597 this.volumeManager_.mountArchive(path, function(mountPath) {
2598 tracker.stop();
2599 if (!tracker.hasChanged)
2600 self.directoryModel_.changeDirectory(mountPath);
2601 }, function(error) {
2602 tracker.stop();
2603 self.alert.show(strf('ARCHIVE_MOUNT_FAILED',
2604 error.fileName, error.message));
2760 }); 2605 });
2761 } 2606 }
2762 }); 2607 });
2763 } else if (id == 'format-device') { 2608 } else if (id == 'format-device') {
2764 this.confirm.show(str('FORMATTING_WARNING'), function() { 2609 this.confirm.show(str('FORMATTING_WARNING'), function() {
2765 chrome.fileBrowserPrivate.formatDevice(urls[0]); 2610 chrome.fileBrowserPrivate.formatDevice(urls[0]);
2766 }); 2611 });
2767 } else if (id == 'gallery') { 2612 } else if (id == 'gallery') {
2768 this.openGallery_(urls); 2613 this.openGallery_(urls);
2769 } else if (id == 'view-pdf' || id == 'view-in-browser' || 2614 } else if (id == 'view-pdf' || id == 'view-in-browser' ||
2770 id == 'install-crx' || id == 'open-hosted') { 2615 id == 'install-crx' || id == 'open-hosted') {
2771 chrome.fileBrowserPrivate.viewFiles(urls, 'default', function(success) { 2616 chrome.fileBrowserPrivate.viewFiles(urls, 'default', function(success) {
2772 if (!success) 2617 if (!success)
2773 console.error('chrome.fileBrowserPrivate.viewFiles failed', urls); 2618 console.error('chrome.fileBrowserPrivate.viewFiles failed', urls);
2774 }); 2619 });
2775 } 2620 }
2776 }; 2621 };
2777 2622
2778 FileManager.prototype.getDeviceNumber = function(entry) {
2779 if (!entry.isDirectory) return undefined;
2780 for (var i = 0; i < this.mountPoints_.length; i++) {
2781 if (normalizeAbsolutePath(entry.fullPath) ==
2782 normalizeAbsolutePath(this.mountPoints_[i].mountPath)) {
2783 return i;
2784 }
2785 }
2786 return undefined;
2787 };
2788
2789 /** 2623 /**
2790 * Show a modal-like file viewer/editor on top of the File Manager UI. 2624 * Show a modal-like file viewer/editor on top of the File Manager UI.
2791 * 2625 *
2792 * @param {HTMLElement} popup Popup element. 2626 * @param {HTMLElement} popup Popup element.
2793 * @param {function} closeCallback Function to call after the popup is closed. 2627 * @param {function} closeCallback Function to call after the popup is closed.
2794 */ 2628 */
2795 FileManager.prototype.openFilePopup_ = function(popup, closeCallback) { 2629 FileManager.prototype.openFilePopup_ = function(popup, closeCallback) {
2796 this.closeFilePopup_(); 2630 this.closeFilePopup_();
2797 this.filePopup_ = popup; 2631 this.filePopup_ = popup;
2798 this.filePopupCloseCallback_ = closeCallback; 2632 this.filePopupCloseCallback_ = closeCallback;
(...skipping 577 matching lines...) Expand 10 before | Expand all | Expand 10 after
3376 return true; 3210 return true;
3377 } 3211 }
3378 if (!this.okButton_.disabled) { 3212 if (!this.okButton_.disabled) {
3379 this.onOk_(); 3213 this.onOk_();
3380 return true; 3214 return true;
3381 } 3215 }
3382 return false; 3216 return false;
3383 }; 3217 };
3384 3218
3385 FileManager.prototype.onDirectoryAction = function(entry) { 3219 FileManager.prototype.onDirectoryAction = function(entry) {
3386 var deviceNumber = this.getDeviceNumber(entry); 3220 var mountCondition = this.volumeManager_.getMountCondition(entry.fullPath);
3387 if (deviceNumber != undefined && 3221 if (mountCondition == 'unknown_filesystem') {
3388 this.mountPoints_[deviceNumber].mountCondition ==
3389 'unknown_filesystem') {
3390 return this.showButter(str('UNKNOWN_FILESYSTEM_WARNING')); 3222 return this.showButter(str('UNKNOWN_FILESYSTEM_WARNING'));
3391 } else if (deviceNumber != undefined && 3223 } else if (mountCondition == 'unsupported_filesystem') {
3392 this.mountPoints_[deviceNumber].mountCondition ==
3393 'unsupported_filesystem') {
3394 return this.showButter(str('UNSUPPORTED_FILESYSTEM_WARNING')); 3224 return this.showButter(str('UNSUPPORTED_FILESYSTEM_WARNING'));
3395 } else { 3225 } else {
3396 return this.directoryModel_.changeDirectory(entry.fullPath); 3226 return this.directoryModel_.changeDirectory(entry.fullPath);
3397 } 3227 }
3398 }; 3228 };
3399 3229
3400 /** 3230 /**
3401 * Show or hide the "Low disk space" warning. 3231 * Show or hide the "Low disk space" warning.
3402 * @param {boolean} show True if the box need to be shown. 3232 * @param {boolean} show True if the box need to be shown.
3403 */ 3233 */
(...skipping 53 matching lines...) Expand 10 before | Expand all | Expand 10 after
3457 3287
3458 // Sometimes we rescan the same directory (when mounting GData lazily first, 3288 // Sometimes we rescan the same directory (when mounting GData lazily first,
3459 // then for real). Do not update the location then. 3289 // then for real). Do not update the location then.
3460 if (event.newDirEntry.fullPath != event.previousDirEntry.fullPath) { 3290 if (event.newDirEntry.fullPath != event.previousDirEntry.fullPath) {
3461 this.updateLocation_(event.initial, event.newDirEntry.fullPath); 3291 this.updateLocation_(event.initial, event.newDirEntry.fullPath);
3462 } 3292 }
3463 3293
3464 this.checkFreeSpace_(this.getCurrentDirectory()); 3294 this.checkFreeSpace_(this.getCurrentDirectory());
3465 3295
3466 this.updateTitle_(); 3296 this.updateTitle_();
3297 this.updateGDataStatus_();
3467 3298
3468 if (this.filesystemObserverId_) 3299 if (this.filesystemObserverId_)
3469 this.metadataCache_.removeObserver(this.filesystemObserverId_); 3300 this.metadataCache_.removeObserver(this.filesystemObserverId_);
3470 if (this.gdataObserverId_) 3301 if (this.gdataObserverId_)
3471 this.metadataCache_.removeObserver(this.gdataObserverId_); 3302 this.metadataCache_.removeObserver(this.gdataObserverId_);
3472 3303
3473 this.filesystemObserverId_ = this.metadataCache_.addObserver( 3304 this.filesystemObserverId_ = this.metadataCache_.addObserver(
3474 this.directoryModel_.getCurrentDirEntry(), 3305 this.directoryModel_.getCurrentDirEntry(),
3475 MetadataCache.CHILDREN, 3306 MetadataCache.CHILDREN,
3476 'filesystem', 3307 'filesystem',
3477 this.updateMetadataInUI_.bind(this, 'filesystem')); 3308 this.updateMetadataInUI_.bind(this, 'filesystem'));
3478 3309
3479 if (this.isOnGData()) { 3310 if (this.isOnGData()) {
3480 this.gdataObserverId_ = this.metadataCache_.addObserver( 3311 this.gdataObserverId_ = this.metadataCache_.addObserver(
3481 this.directoryModel_.getCurrentDirEntry(), 3312 this.directoryModel_.getCurrentDirEntry(),
3482 MetadataCache.CHILDREN, 3313 MetadataCache.CHILDREN,
3483 'gdata', 3314 'gdata',
3484 this.updateMetadataInUI_.bind(this, 'gdata')); 3315 this.updateMetadataInUI_.bind(this, 'gdata'));
3485 } 3316 }
3486
3487 var self = this;
3488
3489 if (this.watchedDirectoryUrl_) {
3490 if (this.watchedDirectoryUrl_ != event.previousDirEntry.toURL()) {
3491 console.warn('event.previousDirEntry does not match File Manager state',
3492 event, this.watchedDirectoryUrl_);
3493 }
3494 chrome.fileBrowserPrivate.removeFileWatch(this.watchedDirectoryUrl_,
3495 function(result) {
3496 if (!result) {
3497 console.log('Failed to remove file watch');
3498 }
3499 });
3500 this.watchedDirectoryUrl_ = null;
3501 }
3502
3503 if (event.newDirEntry.fullPath != '/' && !event.newDirEntry.unmounted) {
3504 this.watchedDirectoryUrl_ = event.newDirEntry.toURL();
3505 chrome.fileBrowserPrivate.addFileWatch(this.watchedDirectoryUrl_,
3506 function(result) {
3507 if (!result) {
3508 console.log('Failed to add file watch');
3509 this.watchedDirectoryUrl_ = null;
3510 }
3511 }.bind(this));
3512 }
3513
3514 if (event.newDirEntry.unmounted)
3515 this.dialogContainer_.setAttribute('unmounted', true);
3516 else {
3517 this.dialogContainer_.removeAttribute('unmounted');
3518 // Need to resize explicitly because the list container had display:none.
3519 this.onResize_();
3520 }
3521
3522 if (this.isOnGData()) {
3523 this.dialogContainer_.setAttribute('gdata', true);
3524 if (event.newDirEntry.unmounted) {
3525 this.initGData_(true /* directory changed */);
3526 }
3527 } else {
3528 this.dialogContainer_.removeAttribute('gdata');
3529 }
3530 }; 3317 };
3531 3318
3532 FileManager.prototype.findListItemForEvent_ = function(event) { 3319 FileManager.prototype.findListItemForEvent_ = function(event) {
3533 return this.findListItemForNode_(event.srcElement); 3320 return this.findListItemForNode_(event.srcElement);
3534 }; 3321 };
3535 3322
3536 FileManager.prototype.findListItemForNode_ = function(node) { 3323 FileManager.prototype.findListItemForNode_ = function(node) {
3537 var item = this.currentList_.getListItemAncestor(node); 3324 var item = this.currentList_.getListItemAncestor(node);
3538 // TODO(serya): list should check that. 3325 // TODO(serya): list should check that.
3539 return item && this.currentList_.isItem(item) ? item : null; 3326 return item && this.currentList_.isItem(item) ? item : null;
3540 }; 3327 };
3541 3328
3542 /** 3329 /**
3543 * Unload handler for the page. May be called manually for the file picker 3330 * Unload handler for the page. May be called manually for the file picker
3544 * dialog, because it closes by calling extension API functions that do not 3331 * dialog, because it closes by calling extension API functions that do not
3545 * return. 3332 * return.
3546 */ 3333 */
3547 FileManager.prototype.onUnload_ = function() { 3334 FileManager.prototype.onUnload_ = function() {
3548 if (this.watchedDirectoryUrl_) { 3335 this.fileWatcher_.stop();
3549 chrome.fileBrowserPrivate.removeFileWatch(
3550 this.watchedDirectoryUrl_,
3551 function(result) {
3552 if (!result) {
3553 console.log('Failed to remove file watch');
3554 }
3555 });
3556 this.watchedDirectoryUrl_ = null;
3557 }
3558 };
3559
3560 FileManager.prototype.onFileChanged_ = function(event) {
3561 // We receive a lot of events even in folders we are not interested in.
3562 if (encodeURI(event.fileUrl) == this.getCurrentDirectoryURL())
3563 this.directoryModel_.rescanLater();
3564 }; 3336 };
3565 3337
3566 FileManager.prototype.initiateRename_ = function() { 3338 FileManager.prototype.initiateRename_ = function() {
3567 var item = this.currentList_.ensureLeadItemExists(); 3339 var item = this.currentList_.ensureLeadItemExists();
3568 if (!item) 3340 if (!item)
3569 return; 3341 return;
3570 var label = item.querySelector('.filename-label'); 3342 var label = item.querySelector('.filename-label');
3571 var input = this.renameInput_; 3343 var input = this.renameInput_;
3572 3344
3573 input.value = label.textContent; 3345 input.value = label.textContent;
(...skipping 905 matching lines...) Expand 10 before | Expand all | Expand 10 after
4479 4251
4480 this.directoryModel_.addEventListener('scan-completed', maybeShowBanner); 4252 this.directoryModel_.addEventListener('scan-completed', maybeShowBanner);
4481 this.directoryModel_.addEventListener('rescan-completed', maybeShowBanner); 4253 this.directoryModel_.addEventListener('rescan-completed', maybeShowBanner);
4482 4254
4483 var style = this.document_.createElement('link'); 4255 var style = this.document_.createElement('link');
4484 style.rel = 'stylesheet'; 4256 style.rel = 'stylesheet';
4485 style.href = 'css/gdrive_welcome.css'; 4257 style.href = 'css/gdrive_welcome.css';
4486 this.document_.head.appendChild(style); 4258 this.document_.head.appendChild(style);
4487 }; 4259 };
4488 })(); 4260 })();
4261
4262 function FileChangeWatcher(directoryModel, volumeManager) {
4263
4264 this.dm_ = directoryModel;
4265 this.vm_ = volumeManager;
4266
4267 this.dm_.addEventListener('directory-changed',
4268 this.update_.bind(this));
4269 this.vm_.addEventListener('changed',
4270 this.update_.bind(this));
4271 };
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698