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

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: Merge. 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).
11 * 11 *
12 * @constructor 12 * @constructor
13 * @param {HTMLElement} dialogDom The DOM node containing the prototypical 13 * @param {HTMLElement} dialogDom The DOM node containing the prototypical
14 * dialog UI. 14 * dialog UI.
15 */ 15 */
16 function FileManager(dialogDom) { 16 function FileManager(dialogDom) {
17 this.dialogDom_ = dialogDom; 17 this.dialogDom_ = dialogDom;
18 this.filesystem_ = null; 18 this.filesystem_ = null;
19 this.params_ = location.search ? 19 this.params_ = location.search ?
20 JSON.parse(decodeURIComponent(location.search.substr(1))) : 20 JSON.parse(decodeURIComponent(location.search.substr(1))) :
21 {}; 21 {};
22 22
23 this.listType_ = null; 23 this.listType_ = null;
24 this.showDelayTimeout_ = null;
24 25
25 this.selection = null; 26 this.selection = null;
26 27
27 this.butterTimer_ = null; 28 this.butterTimer_ = null;
28 this.currentButter_ = null; 29 this.currentButter_ = null;
29 this.butterLastShowTime_ = 0; 30 this.butterLastShowTime_ = 0;
30 31
31 this.watchedDirectoryUrl_ = null;
32 this.filesystemObserverId_ = null; 32 this.filesystemObserverId_ = null;
33 this.gdataObserverId_ = null; 33 this.gdataObserverId_ = null;
34 34
35 this.commands_ = {}; 35 this.commands_ = {};
36 36
37 this.thumbnailUrlCache_ = {}; 37 this.thumbnailUrlCache_ = {};
38 38
39 this.document_ = dialogDom.ownerDocument; 39 this.document_ = dialogDom.ownerDocument;
40 this.dialogType_ = this.params_.type || FileManager.DialogType.FULL_PAGE; 40 this.dialogType_ = this.params_.type || FileManager.DialogType.FULL_PAGE;
41 41
42 metrics.recordEnum('Create', this.dialogType_, 42 metrics.recordEnum('Create', this.dialogType_,
43 [FileManager.DialogType.SELECT_FOLDER, 43 [FileManager.DialogType.SELECT_FOLDER,
44 FileManager.DialogType.SELECT_SAVEAS_FILE, 44 FileManager.DialogType.SELECT_SAVEAS_FILE,
45 FileManager.DialogType.SELECT_OPEN_FILE, 45 FileManager.DialogType.SELECT_OPEN_FILE,
46 FileManager.DialogType.SELECT_OPEN_MULTI_FILE, 46 FileManager.DialogType.SELECT_OPEN_MULTI_FILE,
47 FileManager.DialogType.FULL_PAGE]); 47 FileManager.DialogType.FULL_PAGE]);
48 48
49 // TODO(dgozman): This will be changed to LocaleInfo. 49 // TODO(dgozman): This will be changed to LocaleInfo.
50 this.locale_ = new v8Locale(navigator.language); 50 this.locale_ = new v8Locale(navigator.language);
51 51
52 this.initFileSystem_(); 52 this.initFileSystem_();
53 this.volumeManager_ = VolumeManager.getInstance();
53 this.initDom_(); 54 this.initDom_();
54 this.initDialogType_(); 55 this.initDialogType_();
55 this.dialogDom_.style.opacity = '1';
56 } 56 }
57 57
58 /** 58 /**
59 * Maximum delay in milliseconds for updating thumbnails in the bottom panel 59 * Maximum delay in milliseconds for updating thumbnails in the bottom panel
60 * to mitigate flickering. If images load faster then the delay they replace 60 * to mitigate flickering. If images load faster then the delay they replace
61 * old images smoothly. On the other hand we don't want to keep old images 61 * old images smoothly. On the other hand we don't want to keep old images
62 * too long. 62 * too long.
63 */ 63 */
64 FileManager.THUMBNAIL_SHOW_DELAY = 100; 64 FileManager.THUMBNAIL_SHOW_DELAY = 100;
65 65
(...skipping 138 matching lines...) Expand 10 before | Expand all | Expand 10 after
204 204
205 if (parent_path[parent_path.length - 1] != '/') 205 if (parent_path[parent_path.length - 1] != '/')
206 parent_path += '/'; 206 parent_path += '/';
207 207
208 if (child_path[child_path.length - 1] != '/') 208 if (child_path[child_path.length - 1] != '/')
209 child_path += '/'; 209 child_path += '/';
210 210
211 return child_path.indexOf(parent_path) == 0; 211 return child_path.indexOf(parent_path) == 0;
212 } 212 }
213 213
214 /**
215 * Normalizes path not to start with /
216 *
217 * @param {string} path The file path.
218 */
219 function normalizeAbsolutePath(x) {
220 if (x[0] == '/')
221 return x.slice(1);
222 else
223 return x;
224 }
225
226 function removeChildren(element) { 214 function removeChildren(element) {
227 element.textContent = ''; 215 element.textContent = '';
228 } 216 }
229 217
230 function setClassIf(element, className, condition) { 218 function setClassIf(element, className, condition) {
231 if (condition) 219 if (condition)
232 element.classList.add(className); 220 element.classList.add(className);
233 else 221 else
234 element.classList.remove(className); 222 element.classList.remove(className);
235 } 223 }
(...skipping 22 matching lines...) Expand all
258 type == FileManager.DialogType.SELECT_OPEN_FILE || 246 type == FileManager.DialogType.SELECT_OPEN_FILE ||
259 type == FileManager.DialogType.SELECT_OPEN_MULTI_FILE; 247 type == FileManager.DialogType.SELECT_OPEN_MULTI_FILE;
260 }; 248 };
261 249
262 FileManager.ListType = { 250 FileManager.ListType = {
263 DETAIL: 'detail', 251 DETAIL: 'detail',
264 THUMBNAIL: 'thumb' 252 THUMBNAIL: 'thumb'
265 }; 253 };
266 254
267 /** 255 /**
256 * FileWatcher that also watches for metadata changes.
257 * @extends {FileWatcher}
258 */
259 FileManager.MetadataFileWatcher = function(fileManager) {
260 FileWatcher.call(this,
261 fileManager.filesystem_.root,
262 fileManager.directoryModel_,
263 fileManager.volumeManager_);
264 this.metadataCache_ = fileManager.metadataCache_;
265
266 this.filesystemChanngeHandler_ =
267 fileManager.updateMetadataInUI_.bind(fileManager, 'filesystem');
268 this.gdataChanngeHandler_ =
269 fileManager.updateMetadataInUI_.bind(fileManager, 'gdata');
270
271 this.filesystemObserverId_ = null;
272 this.gdataObserverId_ = null;
273
274 // Holds the directories known to contain files with stale metadata
275 // as URL to bool map.
276 this.directoriesWithStaleMetadata_ = {};
277 };
278
279 FileManager.MetadataFileWatcher.prototype.__proto__ = FileWatcher.prototype;
280
281 /**
282 * Changed metadata observers for the new directory.
283 * @override
284 * @param {DirectoryEntryi?} entry New watched directory entry.
285 * @override
286 */
287 FileManager.MetadataFileWatcher.prototype.changeWatchedEntry =
288 function(entry) {
289 FileWatcher.prototype.changeWatchedEntry.call(this, entry);
290
291 if (this.filesystemObserverId_)
292 this.metadataCache_.removeObserver(this.filesystemObserverId_);
293 if (this.gdataObserverId_)
294 this.metadataCache_.removeObserver(this.gdataObserverId_);
295 this.filesystemObserverId_ = null;
296 this.gdataObserverId_ = null;
297 if (!entry)
298 return;
299
300 this.filesystemObserverId_ = this.metadataCache_.addObserver(
301 entry,
302 MetadataCache.CHILDREN,
303 'filesystem',
304 this.filesystemChanngeHandler_);
305
306 if (DirectoryModel.getRootType(entry.fullPath) ==
307 DirectoryModel.RootType.GDATA) {
308 this.gdataObserverId_ = this.metadataCache_.addObserver(
309 entry,
310 MetadataCache.CHILDREN,
311 'gdata',
312 this.gdataChanngeHandler_);
313 }
314 };
315
316 /**
317 * @override
318 */
319 FileManager.MetadataFileWatcher.prototype.onFileInWatchedDirectoryChanged =
320 function() {
321 FileWatcher.prototype.onFileInWatchedDirectoryChanged.apply(this);
322 delete this.directoriesWithStaleMetadata_[
323 this.getWatchedDirectoryEntry().toURL()];
324 };
325
326 /**
327 * Ask the GData service to re-fetch the metadata for the current directory.
328 */
329 FileManager.MetadataFileWatcher.prototype.requestMetadataRefresh =
330 function(imageFileEntry) {
331 if (DirectoryModel.getRootType(imageFileEntry.fullPath) !=
332 DirectoryModel.RootType.GDATA) {
333 return;
334 }
335 // TODO(kaznacheev) This does not really work with GData search.
336 var imageURL = imageFileEntry.toURL();
337 var url = imageURL.substr(0, imageURL.lastIndexOf('/'));
Vladislav Kaznacheev 2012/05/22 09:48:30 Lets rename url to directoryUrl
338 // Skip if the current directory is now being refreshed.
339 if (this.directoriesWithStaleMetadata_[url])
340 return;
341
342 this.directoriesWithStaleMetadata_[url] = true;
343 chrome.fileBrowserPrivate.requestDirectoryRefresh(url);
344 };
345
346 /**
268 * Load translated strings. 347 * Load translated strings.
269 */ 348 */
270 FileManager.initStrings = function(callback) { 349 FileManager.initStrings = function(callback) {
271 chrome.fileBrowserPrivate.getStrings(function(strings) { 350 chrome.fileBrowserPrivate.getStrings(function(strings) {
272 loadTimeData.data = strings; 351 loadTimeData.data = strings;
273 if (callback) 352 if (callback)
274 callback(); 353 callback();
275 }); 354 });
276 }; 355 };
277 356
357 /**
358 * FileManager initially created hidden to prevent flickering.
359 * When DOM is almost constructed it need to be shown. Cancels
360 * delayed show.
361 */
362 FileManager.prototype.show_ = function() {
363 if (this.showDelayTimeout_) {
364 clearTimeout(this.showDelayTimeout_);
365 showDelayTimeout_ = null;
366 }
367 this.dialogDom_.classList.add('loaded');
368 };
369
370 /**
371 * If initialization code think that right after initialization
372 * something going to be shown instead of just a file list (like Gallery)
373 * it may delay show to prevent flickering. However initialization may take
374 * significant time and we don't want to keep it hidden for too long.
375 * So it will be shown not more than in 0.5 sec. If initialization completed
376 * the page must show immediatelly.
377 *
378 * @param {number} delay In milliseconds.
379 */
380 FileManager.prototype.delayShow_ = function(delay) {
381 if (!this.showDelayTimeout_) {
382 this.showDelayTimeout_ = setTimeout(function() {
383 this.showDelayTimeout_ = null;
384 this.show_();
385 }.bind(this), delay);
386 }
387 };
388
278 // Instance methods. 389 // Instance methods.
279 390
280 /** 391 /**
281 * Request local file system, resolve roots and init_ after that. 392 * Request local file system, resolve roots and init_ after that.
282 * @private 393 * @private
283 */ 394 */
284 FileManager.prototype.initFileSystem_ = function() { 395 FileManager.prototype.initFileSystem_ = function() {
285 util.installFileErrorToString(); 396 util.installFileErrorToString();
286 // Replace the default unit in util to translated unit. 397 // Replace the default unit in util to translated unit.
287 util.UNITS = [str('SIZE_KB'), 398 util.UNITS = [str('SIZE_KB'),
288 str('SIZE_MB'), 399 str('SIZE_MB'),
289 str('SIZE_GB'), 400 str('SIZE_GB'),
290 str('SIZE_TB'), 401 str('SIZE_TB'),
291 str('SIZE_PB')]; 402 str('SIZE_PB')];
292 403
293 metrics.startInterval('Load.FileSystem'); 404 metrics.startInterval('Load.FileSystem');
294 405
295 var self = this; 406 var self = this;
296 407 var downcount = 2;
297 // The list of active mount points to distinct them from other directories. 408 function done() {
298 chrome.fileBrowserPrivate.getMountPoints(function(mountPoints) { 409 if (--downcount == 0)
299 self.setMountPoints_(mountPoints);
300 onDone();
301 });
302
303 function onDone() {
304 if (self.mountPoints_ && self.filesystem_)
305 self.init_(); 410 self.init_();
306 } 411 }
307 412
308 chrome.fileBrowserPrivate.requestLocalFileSystem(function(filesystem) { 413 chrome.fileBrowserPrivate.requestLocalFileSystem(function(filesystem) {
309 metrics.recordInterval('Load.FileSystem'); 414 metrics.recordInterval('Load.FileSystem');
310 self.filesystem_ = filesystem; 415 self.filesystem_ = filesystem;
311 onDone(); 416 done();
417 });
418
419 // GDATA preferences should be initialized before creating DirectoryModel
420 // to tot rebuild the roots list.
421 this.updateNetworkStateAndGDataPreferences_(function() {
422 done();
312 }); 423 });
313 }; 424 };
314 425
315 FileManager.prototype.setMountPoints_ = function(mountPoints) {
316 this.mountPoints_ = mountPoints;
317 // Add gdata mount info if present.
318 if (this.gdataMounted_)
319 this.mountPoints_.push(this.gdataMountInfo_);
320 };
321
322 /** 426 /**
323 * Continue initializing the file manager after resolving roots. 427 * Continue initializing the file manager after resolving roots.
324 */ 428 */
325 FileManager.prototype.init_ = function() { 429 FileManager.prototype.init_ = function() {
326 metrics.startInterval('Load.DOM'); 430 metrics.startInterval('Load.DOM');
327 this.initCommands_(); 431 this.initCommands_();
328 432
329 this.metadataCache_ = MetadataCache.createFull(); 433 this.metadataCache_ = MetadataCache.createFull();
330 434
331 this.shortDateFormatter_ = 435 this.shortDateFormatter_ =
(...skipping 11 matching lines...) Expand all
343 447
344 this.table_.startBatchUpdates(); 448 this.table_.startBatchUpdates();
345 this.grid_.startBatchUpdates(); 449 this.grid_.startBatchUpdates();
346 450
347 this.initFileList_(); 451 this.initFileList_();
348 this.initDialogs_(); 452 this.initDialogs_();
349 453
350 window.addEventListener('popstate', this.onPopState_.bind(this)); 454 window.addEventListener('popstate', this.onPopState_.bind(this));
351 window.addEventListener('unload', this.onUnload_.bind(this)); 455 window.addEventListener('unload', this.onUnload_.bind(this));
352 456
353 this.directoryModel_.addEventListener('directory-changed', 457 var dm = this.directoryModel_;
354 this.onDirectoryChanged_.bind(this)); 458 dm.addEventListener('directory-changed',
459 this.onDirectoryChanged_.bind(this));
355 var self = this; 460 var self = this;
356 this.directoryModel_.addEventListener('begin-update-files', function() { 461 dm.addEventListener('begin-update-files', function() {
357 self.currentList_.startBatchUpdates(); 462 self.currentList_.startBatchUpdates();
358 }); 463 });
359 this.directoryModel_.addEventListener('end-update-files', function() { 464 dm.addEventListener('end-update-files', function() {
360 self.restoreItemBeingRenamed_(); 465 self.restoreItemBeingRenamed_();
361 self.currentList_.endBatchUpdates(); 466 self.currentList_.endBatchUpdates();
362 }); 467 });
363 this.directoryModel_.addEventListener('scan-started', 468 dm.addEventListener('scan-started', this.showSpinnerLater_.bind(this));
364 this.showSpinnerLater_.bind(this)); 469 dm.addEventListener('scan-completed', this.showSpinner_.bind(this, false));
365 this.directoryModel_.addEventListener('scan-completed', 470 dm.addEventListener('scan-completed',
366 this.showSpinner_.bind(this, false)); 471 this.refreshCurrentDirectoryMetadata_.bind(this));
367 this.directoryModel_.addEventListener('scan-completed', 472 dm.addEventListener('rescan-completed',
368 this.refreshCurrentDirectoryMetadata_.bind(this)); 473 this.refreshCurrentDirectoryMetadata_.bind(this));
369 this.directoryModel_.addEventListener('rescan-completed',
370 this.refreshCurrentDirectoryMetadata_.bind(this));
371 this.addEventListener('selection-summarized', 474 this.addEventListener('selection-summarized',
372 this.onSelectionSummarized_.bind(this)); 475 this.onSelectionSummarized_.bind(this));
373 476
374 // The list of archives requested to mount. We will show contents once 477 this.setupCurrentDirectory_(true /* page loading */);
375 // archive is mounted, but only for mounts from within this filebrowser tab.
376 this.mountRequests_ = [];
377 this.unmountRequests_ = [];
378 chrome.fileBrowserPrivate.onMountCompleted.addListener(
379 this.onMountCompleted_.bind(this));
380 478
381 chrome.fileBrowserPrivate.onFileChanged.addListener( 479 var stateChangeHandler =
382 this.onFileChanged_.bind(this)); 480 this.onNetworkStateOrGDataPreferencesChanged_.bind(this);
383 481 chrome.fileBrowserPrivate.onGDataPreferencesChanged.addListener(
384 var queryGDataPreferences = function() { 482 stateChangeHandler);
385 chrome.fileBrowserPrivate.getGDataPreferences( 483 chrome.fileBrowserPrivate.onNetworkConnectionChanged.addListener(
386 this.onGDataPreferencesChanged_.bind(this)); 484 stateChangeHandler);
387 }.bind(this); 485 stateChangeHandler();
388 queryGDataPreferences();
389 chrome.fileBrowserPrivate.onGDataPreferencesChanged.
390 addListener(queryGDataPreferences);
391
392 var queryNetworkConnectionState = function() {
393 chrome.fileBrowserPrivate.getNetworkConnectionState(
394 this.onNetworkConnectionChanged_.bind(this));
395 }.bind(this);
396 queryNetworkConnectionState();
397 chrome.fileBrowserPrivate.onNetworkConnectionChanged.
398 addListener(queryNetworkConnectionState);
399
400 var invokeHandler = !this.params_.selectOnly;
401 if (this.isStartingOnGData_()) {
402 // We are opening on a GData path. Mount GData and show
403 // "Loading Google Docs" message until the directory content loads.
404 this.dialogContainer_.setAttribute('unmounted', true);
405 this.initGData_(true /* dirChanged */);
406 // This is a one-time handler (will be nulled out on the first call).
407 this.setupCurrentDirectoryPostponed_ = function(event) {
408 this.directoryModel_.removeEventListener('directory-changed',
409 this.setupCurrentDirectoryPostponed_);
410 this.setupCurrentDirectoryPostponed_ = null;
411 if (event) // If called as an event handler just exit silently.
412 return;
413 this.setupCurrentDirectory_(
414 invokeHandler, false /* blankWhileOpeningAFile */);
415 }.bind(this);
416 this.directoryModel_.addEventListener('directory-changed',
417 this.setupCurrentDirectoryPostponed_);
418 } else {
419 this.setupCurrentDirectory_(
420 invokeHandler, true /* blankWhileOpeningAFile */);
421 }
422
423 if (this.isGDataEnabled())
424 this.setupGDataWelcome_();
425 486
426 this.summarizeSelection_(); 487 this.summarizeSelection_();
427 488
428 var sortField = 489 var sortField =
429 window.localStorage['sort-field-' + this.dialogType_] || 490 window.localStorage['sort-field-' + this.dialogType_] ||
430 'modificationTime'; 491 'modificationTime';
431 var sortDirection = 492 var sortDirection =
432 window.localStorage['sort-direction-' + this.dialogType_] || 'desc'; 493 window.localStorage['sort-direction-' + this.dialogType_] || 'desc';
433 this.directoryModel_.sortFileList(sortField, sortDirection); 494 this.directoryModel_.sortFileList(sortField, sortDirection);
434 495
435 this.refocus(); 496 this.refocus();
436 497
437 this.metadataProvider_ = 498 this.metadataProvider_ =
438 new MetadataProvider(this.filesystem_.root.toURL()); 499 new MetadataProvider(this.filesystem_.root.toURL());
439 500
440 // Holds the directories known to contain files with stale metadata
441 // as URL to bool map.
442 this.directoriesWithStaleMetadata_ = {};
443
444 // PyAuto tests monitor this state by polling this variable 501 // PyAuto tests monitor this state by polling this variable
445 this.__defineGetter__('workerInitialized_', function() { 502 this.__defineGetter__('workerInitialized_', function() {
446 return self.getMetadataProvider().isInitialized(); 503 return self.getMetadataProvider().isInitialized();
447 }); 504 });
448 505
449 if (this.dialogType_ == FileManager.DialogType.FULL_PAGE) 506 if (this.dialogType_ == FileManager.DialogType.FULL_PAGE)
450 this.initDataTransferOperations_(); 507 this.initDataTransferOperations_();
451 508
452 this.table_.endBatchUpdates(); 509 this.table_.endBatchUpdates();
453 this.grid_.endBatchUpdates(); 510 this.grid_.endBatchUpdates();
454 511
512 // Show the page now unless it's already delayed.
513 this.delayShow_(0);
514
455 metrics.recordInterval('Load.DOM'); 515 metrics.recordInterval('Load.DOM');
456 metrics.recordInterval('Load.Total'); 516 metrics.recordInterval('Load.Total');
457 }; 517 };
458 518
459 FileManager.prototype.initDataTransferOperations_ = function() { 519 FileManager.prototype.initDataTransferOperations_ = function() {
460 this.copyManager_ = new FileCopyManager(this.filesystem_.root); 520 this.copyManager_ = new FileCopyManager(this.filesystem_.root);
461 this.copyManager_.addEventListener('copy-progress', 521 this.copyManager_.addEventListener('copy-progress',
462 this.onCopyProgress_.bind(this)); 522 this.onCopyProgress_.bind(this));
463 this.copyManager_.addEventListener('copy-operation-complete', 523 this.copyManager_.addEventListener('copy-operation-complete',
464 this.onCopyManagerOperationComplete_.bind(this)); 524 this.onCopyManagerOperationComplete_.bind(this));
(...skipping 68 matching lines...) Expand 10 before | Expand all | Expand 10 after
533 this.filenameInput_ = this.dialogDom_.querySelector('.filename-input'); 593 this.filenameInput_ = this.dialogDom_.querySelector('.filename-input');
534 this.taskItems_ = this.dialogDom_.querySelector('#tasks'); 594 this.taskItems_ = this.dialogDom_.querySelector('#tasks');
535 this.okButton_ = this.dialogDom_.querySelector('.ok'); 595 this.okButton_ = this.dialogDom_.querySelector('.ok');
536 this.cancelButton_ = this.dialogDom_.querySelector('.cancel'); 596 this.cancelButton_ = this.dialogDom_.querySelector('.cancel');
537 this.deleteButton_ = this.dialogDom_.querySelector('#delete-button'); 597 this.deleteButton_ = this.dialogDom_.querySelector('#delete-button');
538 this.table_ = this.dialogDom_.querySelector('.detail-table'); 598 this.table_ = this.dialogDom_.querySelector('.detail-table');
539 this.grid_ = this.dialogDom_.querySelector('.thumbnail-grid'); 599 this.grid_ = this.dialogDom_.querySelector('.thumbnail-grid');
540 this.spinner_ = this.dialogDom_.querySelector('.spinner'); 600 this.spinner_ = this.dialogDom_.querySelector('.spinner');
541 this.showSpinner_(false); 601 this.showSpinner_(false);
542 this.butter_ = this.dialogDom_.querySelector('.butter-bar'); 602 this.butter_ = this.dialogDom_.querySelector('.butter-bar');
543 this.unmountedPanel_ = this.dialogDom_.querySelector('.unmounted-panel'); 603 this.unmountedPanel_ = this.dialogDom_.querySelector('#unmounted-panel');
544 604
545 cr.ui.decorate('#gdata-settings', cr.ui.MenuButton); 605 cr.ui.decorate('#gdata-settings', cr.ui.MenuButton);
546 cr.ui.Table.decorate(this.table_); 606 cr.ui.Table.decorate(this.table_);
547 cr.ui.Grid.decorate(this.grid_); 607 cr.ui.Grid.decorate(this.grid_);
548 608
549 this.document_.addEventListener('keydown', this.onKeyDown_.bind(this)); 609 this.document_.addEventListener('keydown', this.onKeyDown_.bind(this));
550 // Disable the default browser context menu. 610 // Disable the default browser context menu.
551 this.document_.addEventListener('contextmenu', 611 this.document_.addEventListener('contextmenu',
552 function(e) { e.preventDefault() }); 612 function(e) { e.preventDefault() });
553 613
(...skipping 29 matching lines...) Expand all
583 643
584 this.decorateSplitter( 644 this.decorateSplitter(
585 this.dialogDom_.querySelector('div.sidebar-splitter')); 645 this.dialogDom_.querySelector('div.sidebar-splitter'));
586 646
587 this.dialogContainer_ = this.dialogDom_.querySelector('.dialog-container'); 647 this.dialogContainer_ = this.dialogDom_.querySelector('.dialog-container');
588 this.dialogDom_.querySelector('#detail-view').addEventListener( 648 this.dialogDom_.querySelector('#detail-view').addEventListener(
589 'click', this.onDetailViewButtonClick_.bind(this)); 649 'click', this.onDetailViewButtonClick_.bind(this));
590 this.dialogDom_.querySelector('#thumbnail-view').addEventListener( 650 this.dialogDom_.querySelector('#thumbnail-view').addEventListener(
591 'click', this.onThumbnailViewButtonClick_.bind(this)); 651 'click', this.onThumbnailViewButtonClick_.bind(this));
592 652
593 // When we show the page for the first time we want to avoid
594 // the GDrive settings button animation, so we set the attribute ASAP.
595 if (this.isStartingOnGData_())
596 this.dialogContainer_.setAttribute('gdata', true);
597
598 this.syncButton = this.dialogDom_.querySelector('#gdata-sync-settings'); 653 this.syncButton = this.dialogDom_.querySelector('#gdata-sync-settings');
599 this.syncButton.addEventListener('click', this.onGDataPrefClick_.bind( 654 this.syncButton.addEventListener('click', this.onGDataPrefClick_.bind(
600 this, 'cellularDisabled', false /* not inverted */)); 655 this, 'cellularDisabled', false /* not inverted */));
601 656
602 this.hostedButton = this.dialogDom_.querySelector('#gdata-hosted-settings'); 657 this.hostedButton = this.dialogDom_.querySelector('#gdata-hosted-settings');
603 this.hostedButton.addEventListener('click', this.onGDataPrefClick_.bind( 658 this.hostedButton.addEventListener('click', this.onGDataPrefClick_.bind(
604 this, 'hostedFilesDisabled', true /* inverted */)); 659 this, 'hostedFilesDisabled', true /* inverted */));
605 660
606 cr.ui.ComboButton.decorate(this.taskItems_); 661 cr.ui.ComboButton.decorate(this.taskItems_);
607 this.taskItems_.addEventListener('select', 662 this.taskItems_.addEventListener('select',
(...skipping 25 matching lines...) Expand all
633 this.emptySelectionModel_ = new cr.ui.ListSelectionModel(); 688 this.emptySelectionModel_ = new cr.ui.ListSelectionModel();
634 689
635 var singleSelection = 690 var singleSelection =
636 this.dialogType_ == FileManager.DialogType.SELECT_OPEN_FILE || 691 this.dialogType_ == FileManager.DialogType.SELECT_OPEN_FILE ||
637 this.dialogType_ == FileManager.DialogType.SELECT_FOLDER || 692 this.dialogType_ == FileManager.DialogType.SELECT_FOLDER ||
638 this.dialogType_ == FileManager.DialogType.SELECT_SAVEAS_FILE; 693 this.dialogType_ == FileManager.DialogType.SELECT_SAVEAS_FILE;
639 694
640 this.directoryModel_ = new DirectoryModel( 695 this.directoryModel_ = new DirectoryModel(
641 this.filesystem_.root, 696 this.filesystem_.root,
642 singleSelection, 697 singleSelection,
643 this.metadataCache_); 698 this.metadataCache_,
699 this.volumeManager_,
700 this.isGDataEnabled());
701
702 this.directoryModel_.start();
703
704 this.fileWatcher_ = new FileManager.MetadataFileWatcher(this);
705 this.fileWatcher_.start();
644 706
645 var dataModel = this.directoryModel_.getFileList(); 707 var dataModel = this.directoryModel_.getFileList();
646 var collator = this.collator_; 708 var collator = this.collator_;
647 // TODO(dgozman): refactor comparison functions together with 709 // TODO(dgozman): refactor comparison functions together with
648 // render/update/display. 710 // render/update/display.
649 dataModel.setCompareFunction('name', function(a, b) { 711 dataModel.setCompareFunction('name', function(a, b) {
650 return collator.compare(a.name, b.name); 712 return collator.compare(a.name, b.name);
651 }); 713 });
652 dataModel.setCompareFunction('modificationTime', 714 dataModel.setCompareFunction('modificationTime',
653 this.compareMtime_.bind(this)); 715 this.compareMtime_.bind(this));
(...skipping 14 matching lines...) Expand all
668 this.initGrid_(); 730 this.initGrid_();
669 this.initRootsList_(); 731 this.initRootsList_();
670 732
671 var listType = FileManager.ListType.DETAIL; 733 var listType = FileManager.ListType.DETAIL;
672 if (FileManager.DialogType.isModal(this.dialogType_)) 734 if (FileManager.DialogType.isModal(this.dialogType_))
673 listType = window.localStorage['listType-' + this.dialogType_] || 735 listType = window.localStorage['listType-' + this.dialogType_] ||
674 FileManager.ListType.DETAIL; 736 FileManager.ListType.DETAIL;
675 this.setListType(listType); 737 this.setListType(listType);
676 738
677 this.textSearchState_ = {text: '', date: new Date()}; 739 this.textSearchState_ = {text: '', date: new Date()};
740
741 this.volumeManager_.addEventListener('gdata-status-changed',
742 this.updateGDataUnmountedPanel_.bind(this));
743 if (this.params_.mountTriggered) {
744 this.volumeManager_.addEventListener('externally-unmounted',
745 this.onExternallyUnmounted_.bind(this));
746 }
678 }; 747 };
679 748
680 FileManager.prototype.initRootsList_ = function() { 749 FileManager.prototype.initRootsList_ = function() {
681 this.rootsList_ = this.dialogDom_.querySelector('#roots-list'); 750 this.rootsList_ = this.dialogDom_.querySelector('#roots-list');
682 cr.ui.List.decorate(this.rootsList_); 751 cr.ui.List.decorate(this.rootsList_);
683 this.rootsList_.startBatchUpdates();
684 752
685 var self = this; 753 var self = this;
686 this.rootsList_.itemConstructor = function(entry) { 754 this.rootsList_.itemConstructor = function(entry) {
687 return self.renderRoot_(entry); 755 return self.renderRoot_(entry);
688 }; 756 };
689 757
690 this.rootsList_.selectionModel = 758 this.rootsList_.selectionModel =
691 this.directoryModel_.getRootsListSelectionModel(); 759 this.directoryModel_.getRootsListSelectionModel();
692 760
693 // TODO(dgozman): add "Add a drive" item. 761 // TODO(dgozman): add "Add a drive" item.
694 this.rootsList_.dataModel = this.directoryModel_.getRootsList(); 762 this.rootsList_.dataModel = this.directoryModel_.getRootsList();
695 this.directoryModel_.updateRoots(function() {
696 self.rootsList_.endBatchUpdates();
697 }, this.getGDataAccessMode_());
698 }; 763 };
699 764
700 /** 765 /**
701 * @param {boolean} dirChanged True if we just changed to GData directory, 766 * Shows the panel when current directory is GDATA and it's unmounted.
702 * False if "Retry" button clicked. 767 * Hides it otherwise. The pannel shows spinner if GDATA is mounting or
768 * an error message if it failed.
703 */ 769 */
704 FileManager.prototype.initGData_ = function(dirChanged) { 770 FileManager.prototype.updateGDataUnmountedPanel_ = function() {
705 this.initGDataUnmountedPanel_(); 771 var node = this.dialogContainer_;
706 772 if (this.isOnGData()) {
707 this.unmountedPanel_.removeAttribute('error'); 773 var status = this.volumeManager_.getGDataStatus();
708 if (dirChanged) { 774 if (status == VolumeManager.GDataStatus.MOUNTING ||
709 // When changing to GData directory we want to see a clear panel. 775 status == VolumeManager.GDataStatus.ERROR) {
710 this.unmountedPanel_.removeAttribute('retry'); 776 this.ensureGDataUnmountedPanelInitialized_();
711 if (this.gdataLoadingTimer_) { // Show immediately if already loading.
712 this.unmountedPanel_.setAttribute('loading', true);
713 } else {
714 this.unmountedPanel_.removeAttribute('loading');
715 setTimeout(function() {
716 if (this.gdataLoadingTimer_) { // Still loading.
717 this.unmountedPanel_.setAttribute('loading', true);
718 }
719 }.bind(this), 500);
720 } 777 }
778 if (status == VolumeManager.GDataStatus.ERROR)
779 this.unmountedPanel_.classList.add('retry-enabled');
780 node.setAttribute('gdata', status);
721 } else { 781 } else {
722 // When retrying we do not hide "Retry" and "Learn more". 782 node.removeAttribute('gdata');
723 this.unmountedPanel_.setAttribute('loading', true);
724 }
725
726 // If the user changed to another directory and then back to GData we
727 // re-enter this method while the timer is still active. In this case
728 // we only update the UI but do not request the mount again.
729 if (this.gdataLoadingTimer_)
730 return;
731
732 metrics.startInterval('Load.GData');
733 chrome.fileBrowserPrivate.addMount('', 'gdata', {},
734 function(sourcePath) {});
735
736 // This timer could fire before the mount succeeds. We will silently
737 // replace the error message with the correct directory contents.
738 this.gdataLoadingTimer_ = setTimeout(function() {
739 this.gdataLoadingTimer_ = null;
740 this.onGDataUnreachable_('GData load timeout');
741 }.bind(this),
742 15 * 60 * 1000);
743 };
744
745 FileManager.prototype.clearGDataLoadingTimer_ = function(message) {
746 if (this.gdataLoadingTimer_) {
747 clearTimeout(this.gdataLoadingTimer_);
748 this.gdataLoadingTimer_ = null;
749 } 783 }
750 }; 784 };
751 785
752 FileManager.prototype.onGDataUnreachable_ = function(message) { 786 /**
753 console.warn(message); 787 * Creates contents for the GDATA unmounted panel.
754 this.gdataMounted_ = false; 788 */
755 this.gdataMountInfo_ = null; 789 FileManager.prototype.ensureGDataUnmountedPanelInitialized_ = function() {
756 this.clearGDataLoadingTimer_(); 790 var panel = this.unmountedPanel_;
757 if (this.isOnGData()) { 791 if (panel.firstElementChild)
758 this.unmountedPanel_.removeAttribute('loading');
759 this.unmountedPanel_.setAttribute('error', true);
760 this.unmountedPanel_.setAttribute('retry', true);
761 }
762 };
763
764 FileManager.prototype.initGDataUnmountedPanel_ = function() {
765 if (this.unmountedPanel_.firstElementChild)
766 return; 792 return;
767 793
768 var loading = this.document_.createElement('div'); 794 function create(parent, tag, className, opt_textContent) {
769 loading.className = 'gdata loading'; 795 var div = panel.ownerDocument.createElement(tag);
770 loading.textContent = str('GDATA_LOADING'); 796 div.className = className;
771 this.unmountedPanel_.appendChild(loading); 797 div.textContent = opt_textContent || '';
798 parent.appendChild(div);
799 return div;
800 }
772 801
773 var spinnerBox = this.document_.createElement('div'); 802 var loading = create(panel, 'div', 'loading', str('GDATA_LOADING'));
774 spinnerBox.className = 'spinner-box'; 803 var spinnerBox = create(loading, 'div', 'spinner-box');
775 loading.appendChild(spinnerBox); 804 create(spinnerBox, 'div', 'spinner');
776 805 var progress = create(panel, 'div', 'progress');
777 var spinner = this.document_.createElement('div');
778 spinner.className = 'spinner';
779 spinnerBox.appendChild(spinner);
780
781 var progress = this.document_.createElement('div');
782 progress.className = 'gdata progress';
783 this.unmountedPanel_.appendChild(progress);
784
785 chrome.fileBrowserPrivate.onDocumentFeedFetched.addListener( 806 chrome.fileBrowserPrivate.onDocumentFeedFetched.addListener(
786 function(fileCount) { 807 function(fileCount) {
787 progress.textContent = strf('GDATA_LOADING_PROGRESS', fileCount); 808 progress.textContent = strf('GDATA_LOADING_PROGRESS', fileCount);
788 }); 809 });
789 810
790 var error = this.document_.createElement('div'); 811 create(panel, 'div', 'error',
791 error.className = 'gdata error'; 812 strf('GDATA_CANNOT_REACH', str('GDATA_PRODUCT_NAME')));
792 error.textContent = strf('GDATA_CANNOT_REACH', str('GDATA_PRODUCT_NAME'));
793 this.unmountedPanel_.appendChild(error);
794 813
795 var retry = this.document_.createElement('button'); 814 var retryButton = create(panel, 'button', 'retry', str('GDATA_RETRY'));
796 retry.className = 'gdata retry'; 815 retryButton.hidden = true;
797 retry.textContent = str('GDATA_RETRY'); 816 var vm = this.volumeManager_;
798 retry.onclick = this.initGData_.bind(this, false /* retry */); 817 retryButton.onclick = function() {
799 this.unmountedPanel_.appendChild(retry); 818 vm.mountGData(function() {}, function() {});
819 };
800 820
801 var learnMore = this.document_.createElement('div'); 821 var learnMore = create(panel, 'div', 'learn-more plain-link',
802 learnMore.className = 'gdata learn-more plain-link'; 822 str('GDATA_LEARN_MORE'));
803 learnMore.textContent = str('GDATA_LEARN_MORE'); 823 learnMore.onclick = this.onExternalLinkClick_.bind(this,
804 learnMore.addEventListener('click', 824 GOOGLE_DRIVE_ERROR_HELP_URL);
805 this.onExternalLinkClick_.bind(this, GOOGLE_DRIVE_ERROR_HELP_URL));
806 this.unmountedPanel_.appendChild(learnMore);
807 }; 825 };
808 826
809 FileManager.prototype.onDataModelSplice_ = function(event) { 827 FileManager.prototype.onDataModelSplice_ = function(event) {
810 var checkbox = this.document_.querySelector('#select-all-checkbox'); 828 var checkbox = this.document_.querySelector('#select-all-checkbox');
811 if (checkbox) 829 if (checkbox)
812 this.updateSelectAllCheckboxState_(checkbox); 830 this.updateSelectAllCheckboxState_(checkbox);
813 }; 831 };
814 832
815 FileManager.prototype.onDataModelPermuted_ = function(event) { 833 FileManager.prototype.onDataModelPermuted_ = function(event) {
816 var sortStatus = this.directoryModel_.getFileList().sortStatus; 834 var sortStatus = this.directoryModel_.getFileList().sortStatus;
(...skipping 478 matching lines...) Expand 10 before | Expand all | Expand 10 after
1295 } 1313 }
1296 } 1314 }
1297 }; 1315 };
1298 1316
1299 /** 1317 /**
1300 * Handler of file manager operations. Update directory model 1318 * Handler of file manager operations. Update directory model
1301 * to reflect operation result iimediatelly (not waiting directory 1319 * to reflect operation result iimediatelly (not waiting directory
1302 * update event). 1320 * update event).
1303 */ 1321 */
1304 FileManager.prototype.onCopyManagerOperationComplete_ = function(event) { 1322 FileManager.prototype.onCopyManagerOperationComplete_ = function(event) {
1305 var currentPath = 1323 var currentPath = this.directoryModel_.getCurrentDirPath();
1306 this.directoryModel_.getCurrentDirPath();
1307 if (this.isOnGData() && this.directoryModel_.isSearching()) 1324 if (this.isOnGData() && this.directoryModel_.isSearching())
1308 return; 1325 return;
1309 1326
1310 function inCurrentDirectory(entry) { 1327 function inCurrentDirectory(entry) {
1311 var fullPath = entry.fullPath; 1328 var fullPath = entry.fullPath;
1312 var dirPath = fullPath.substr(0, fullPath.length - 1329 var dirPath = fullPath.substr(0, fullPath.length -
1313 entry.name.length - 1); 1330 entry.name.length - 1);
1314 return dirPath == currentPath; 1331 return dirPath == currentPath;
1315 } 1332 }
1316 for (var i = 0; i < event.affectedEntries.length; i++) { 1333 for (var i = 0; i < event.affectedEntries.length; i++) {
(...skipping 35 matching lines...) Expand 10 before | Expand all | Expand 10 after
1352 1369
1353 case 'delete': 1370 case 'delete':
1354 this.deleteEntries(this.selection.entries); 1371 this.deleteEntries(this.selection.entries);
1355 return; 1372 return;
1356 1373
1357 case 'newfolder': 1374 case 'newfolder':
1358 this.onNewFolderCommand_(event); 1375 this.onNewFolderCommand_(event);
1359 return; 1376 return;
1360 1377
1361 case 'unmount': 1378 case 'unmount':
1362 this.unmountVolume_(this.directoryModel_.getCurrentRootDirEntry()); 1379 this.unmountVolume_(
1380 this.directoryModel_.getCurrentRootDirEntry());
1363 return; 1381 return;
1364 1382
1365 case 'format': 1383 case 'format':
1366 var entry = this.directoryModel_.getCurrentRootDirEntry(); 1384 var entry = this.directoryModel_.getCurrentRootDirEntry();
1367 this.confirm.show(str('FORMATTING_WARNING'), function() { 1385 this.confirm.show(str('FORMATTING_WARNING'), function() {
1368 chrome.fileBrowserPrivate.formatDevice(entry.toURL()); 1386 chrome.fileBrowserPrivate.formatDevice(entry.toURL());
1369 }); 1387 });
1370 1388
1371 return; 1389 return;
1372 } 1390 }
1373 }; 1391 };
1374 1392
1375 /** 1393 /**
1376 * Respond to the back and forward buttons. 1394 * Respond to the back and forward buttons.
1377 */ 1395 */
1378 FileManager.prototype.onPopState_ = function(event) { 1396 FileManager.prototype.onPopState_ = function(event) {
1379 this.closeFilePopup_(); 1397 this.closeFilePopup_();
1380 // Nothing left to do if the current directory is not changing. This happens 1398 this.setupCurrentDirectory_(false /* page loading */);
1381 // if we are exiting the Gallery.
1382 if (this.getPathFromUrlOrParams_() ==
1383 this.directoryModel_.getCurrentDirEntry().fullPath)
1384 return;
1385 this.setupCurrentDirectory_(true /* invokeHandler */);
1386 }; 1399 };
1387 1400
1388 FileManager.prototype.requestResize_ = function(timeout) { 1401 FileManager.prototype.requestResize_ = function(timeout) {
1389 setTimeout(this.onResize_.bind(this), timeout || 0); 1402 setTimeout(this.onResize_.bind(this), timeout || 0);
1390 }; 1403 };
1391 1404
1392 /** 1405 /**
1393 * Resize details and thumb views to fit the new window size. 1406 * Resize details and thumb views to fit the new window size.
1394 */ 1407 */
1395 FileManager.prototype.onResize_ = function() { 1408 FileManager.prototype.onResize_ = function() {
(...skipping 19 matching lines...) Expand all
1415 this.rootsList_.redraw(); 1428 this.rootsList_.redraw();
1416 this.truncateBreadcrumbs_(); 1429 this.truncateBreadcrumbs_();
1417 }; 1430 };
1418 1431
1419 FileManager.prototype.resolvePath = function( 1432 FileManager.prototype.resolvePath = function(
1420 path, resultCallback, errorCallback) { 1433 path, resultCallback, errorCallback) {
1421 return util.resolvePath(this.filesystem_.root, path, resultCallback, 1434 return util.resolvePath(this.filesystem_.root, path, resultCallback,
1422 errorCallback); 1435 errorCallback);
1423 }; 1436 };
1424 1437
1425 FileManager.prototype.getPathFromUrlOrParams_ = function() {
1426 return location.hash ? // Location hash has the highest priority.
1427 decodeURI(location.hash.substr(1)) :
1428 this.params_.defaultPath;
1429 };
1430
1431 /** 1438 /**
1432 * Restores current directory and may be a selected item after page load (or 1439 * Restores current directory and may be a selected item after page load (or
1433 * reload) or popping a state (after click on back/forward). If location.hash 1440 * reload) or popping a state (after click on back/forward). If location.hash
1434 * is present it means that the user has navigated somewhere and that place 1441 * is present it means that the user has navigated somewhere and that place
1435 * will be restored. defaultPath primarily is used with save/open dialogs. 1442 * will be restored. defaultPath primarily is used with save/open dialogs.
1436 * Default path may also contain a file name. Freshly opened file manager 1443 * Default path may also contain a file name. Freshly opened file manager
1437 * window has neither. 1444 * window has neither.
1438 * 1445 *
1439 * @param {boolean} invokeHandler Whether to invoke the default handler on 1446 * @param {boolean} pageLoading True if the page is loading,
1440 * the selected file. 1447 false if popping state.
1441 * @param {boolean} opt_blankWhileOpeningAFile Whether to show fade over
1442 * the file manager.
1443 */ 1448 */
1444 FileManager.prototype.setupCurrentDirectory_ = 1449 FileManager.prototype.setupCurrentDirectory_ = function(pageLoading) {
1445 function(invokeHandler, opt_blankWhileOpeningAFile) { 1450 var path = location.hash ? // Location hash has the highest priority.
1446 var path = this.getPathFromUrlOrParams_(); 1451 decodeURI(location.hash.substr(1)) :
1452 this.params_.defaultPath;
1453
1454 if (!pageLoading && path == this.directoryModel_.getCurrentDirPath())
1455 return;
1447 1456
1448 if (!path) { 1457 if (!path) {
1449 this.directoryModel_.setupDefaultPath(); 1458 this.directoryModel_.setupDefaultPath();
1450 return; 1459 return;
1451 } 1460 }
1452 1461
1453 // In the FULL_PAGE mode if the hash path points to a file we might have 1462 // In the FULL_PAGE mode if the hash path points to a file we might have
1454 // to invoke a task after selecting it. 1463 // to invoke a task after selecting it.
1455 // If the file path is in params_ we only want to select the file. 1464 // If the file path is in params_ we only want to select the file.
1456 if (invokeHandler && location.hash && 1465 var invokeHandlers = pageLoading && !this.params_.selectOnly &&
1457 this.dialogType_ == FileManager.DialogType.FULL_PAGE) { 1466 this.dialogType_ == FileManager.DialogType.FULL_PAGE &&
1458 // To prevent the file list flickering for a moment before the action 1467 !!location.hash;
1459 // is executed we hide it under a white div. 1468
1460 var shade; 1469 if (DirectoryModel.getRootType(path) == DirectoryModel.RootType.GDATA) {
1461 if (opt_blankWhileOpeningAFile) { 1470 var tracker = this.directoryModel_.createDirectoryChangeTracker();
1462 shade = this.document_.createElement('div'); 1471 // Expected finish of setupPath to GData.
1463 shade.className = 'overlay-pane'; 1472 tracker.exceptInitialChange = true;
1464 shade.style.backgroundColor = 'white'; 1473 tracker.start();
1465 this.document_.body.appendChild(shade); 1474 if (!this.isGDataEnabled()) {
1475 if (pageLoading)
1476 this.show_();
1477 this.directoryModel_.setupDefaultPath();
1478 return;
1466 } 1479 }
1467 function removeShade() { 1480 var gdataPath = '/' + DirectoryModel.GDATA_DIRECTORY;
1468 if (shade) 1481 if (this.volumeManager_.isMounted(gdataPath)) {
1469 shade.parentNode.removeChild(shade); 1482 this.finishSetupCurrentDirectory_(path, invokeHandlers);
1483 return;
1484 }
1485 if (pageLoading)
1486 this.delayShow_(500);
1487 // Reflect immediatelly in the UI we are on GData and display
1488 // mounting UI.
1489 this.directoryModel_.setupPath(gdataPath);
1490
1491 if (!this.isOnGData()) {
1492 // Since GDATA is not mounted it should be resolved synchronously
1493 // (no need in asynchronous calls to filesystem API). It is important
1494 // to prevent race condition.
1495 console.error('Expected path set up synchronously');
1470 } 1496 }
1471 1497
1498 var self = this;
1499 this.volumeManager_.mountGData(function() {
1500 tracker.stop();
1501 if (!tracker.hasChanged) {
1502 self.finishSetupCurrentDirectory_(path, invokeHandlers);
1503 }
1504 }, function(error) {
1505 tracker.stop();
1506 });
1507 } else {
1508 if (invokeHandlers && pageLoading)
1509 this.delayShow_(500);
1510 this.finishSetupCurrentDirectory_(path, invokeHandlers);
1511 }
1512 };
1513
1514 /**
1515 * @param {string} path Path to setup.
1516 * @param {boolean} invokeHandlers If thrue and |path| points to a file
1517 * then default handler is triggered.
1518 */
1519 FileManager.prototype.finishSetupCurrentDirectory_ = function(
1520 path, invokeHandlers) {
1521 if (invokeHandlers) {
1472 // Keep track of whether the path is identified as an existing leaf 1522 // Keep track of whether the path is identified as an existing leaf
1473 // node. Note that onResolve is guaranteed to be called (exactly once) 1523 // node. Note that onResolve is guaranteed to be called (exactly once)
1474 // before onLoadedActivateLeaf. 1524 // before onLoadedActivateLeaf.
1475 var foundLeaf = true; 1525 var foundLeaf = true;
1476 function onResolve(baseName, leafName, exists) { 1526 function onResolve(baseName, leafName, exists) {
1477 if (!exists || leafName == '') { 1527 if (!exists || leafName == '') {
1478 // Non-existent file or a directory. Remove the shade immediately. 1528 // Non-existent file or a directory. Remove the shade immediately.
1479 removeShade();
1480 foundLeaf = false; 1529 foundLeaf = false;
1530 self.show_();
1481 } 1531 }
1482 } 1532 }
1483 1533
1484 // TODO(kaznacheev): refactor dispatchDefaultTask to accept an array 1534 // TODO(kaznacheev): refactor dispatchDefaultTask to accept an array
1485 // of urls instead of a selection. This will remove the need to wait 1535 // of urls instead of a selection. This will remove the need to wait
1486 // until the selection is done. 1536 // until the selection is done.
1487 var self = this; 1537 var self = this;
1488 function onLoadedActivateLeaf() { 1538 function onLoadedActivateLeaf() {
1489 if (foundLeaf) { 1539 if (foundLeaf) {
1490 // There are 3 ways we can get here: 1540 // There are 3 ways we can get here:
1491 // 1. Invoked from file_manager_util::ViewFile. This can only 1541 // 1. Invoked from file_manager_util::ViewFile. This can only
1492 // happen for 'gallery' and 'mount-archive' actions. 1542 // happen for 'gallery' and 'mount-archive' actions.
1493 // 2. Reloading a Gallery page. Must be an image or a video file. 1543 // 2. Reloading a Gallery page. Must be an image or a video file.
1494 // 3. A user manually entered a URL pointing to a file. 1544 // 3. A user manually entered a URL pointing to a file.
1495 if (FileType.isImageOrVideo(path)) { 1545 if (FileType.isImageOrVideo(path)) {
1496 self.dispatchInternalTask_('gallery', self.selection.urls); 1546 self.dispatchInternalTask_('gallery', self.selection.urls);
1497 } else if (FileType.getMediaType(path) == 'archive') { 1547 } else if (FileType.getMediaType(path) == 'archive') {
1548 self.show_();
1498 self.dispatchInternalTask_('mount-archive', self.selection.urls); 1549 self.dispatchInternalTask_('mount-archive', self.selection.urls);
1499 } else { 1550 } else {
1500 // Manually entered path, do nothing, remove the shade ASAP. 1551 self.show_();
1501 removeShade();
1502 return; 1552 return;
1503 } 1553 }
1504 setTimeout(removeShade, 1000);
1505 } 1554 }
1506 } 1555 }
1507 this.directoryModel_.setupPath(path, onLoadedActivateLeaf, onResolve); 1556 this.directoryModel_.setupPath(path, onLoadedActivateLeaf, onResolve);
1508 return; 1557 return;
1509 } 1558 }
1510 1559
1511 if (this.dialogType_ == FileManager.DialogType.SELECT_SAVEAS_FILE) { 1560 if (this.dialogType_ == FileManager.DialogType.SELECT_SAVEAS_FILE) {
1512 this.directoryModel_.setupPath(path, undefined, 1561 this.directoryModel_.setupPath(path, undefined,
1513 function(basePath, leafName) { 1562 function(basePath, leafName) {
1514 this.filenameInput_.value = leafName; 1563 this.filenameInput_.value = leafName;
1515 this.selectDefaultPathInFilenameInput_(); 1564 this.selectDefaultPathInFilenameInput_();
1516 }.bind(this)); 1565 }.bind(this));
1517 return; 1566 return;
1518 } 1567 }
1519 1568
1569 this.show_();
1520 this.directoryModel_.setupPath(path); 1570 this.directoryModel_.setupPath(path);
1521 }; 1571 };
1522 1572
1523 /** 1573 /**
1524 * Tweak the UI to become a particular kind of dialog, as determined by the 1574 * Tweak the UI to become a particular kind of dialog, as determined by the
1525 * dialog type parameter passed to the constructor. 1575 * dialog type parameter passed to the constructor.
1526 */ 1576 */
1527 FileManager.prototype.initDialogType_ = function() { 1577 FileManager.prototype.initDialogType_ = function() {
1528 var defaultTitle; 1578 var defaultTitle;
1529 var okLabel = str('OPEN_LABEL'); 1579 var okLabel = str('OPEN_LABEL');
(...skipping 141 matching lines...) Expand 10 before | Expand all | Expand 10 after
1671 fractionX = 1; 1721 fractionX = 1;
1672 } 1722 }
1673 1723
1674 style.width = percent(fractionX); 1724 style.width = percent(fractionX);
1675 style.height = percent(fractionY); 1725 style.height = percent(fractionY);
1676 style.left = percent((1 - fractionX) / 2); 1726 style.left = percent((1 - fractionX) / 2);
1677 style.top = percent((1 - fractionY) / 2); 1727 style.top = percent((1 - fractionY) / 2);
1678 }; 1728 };
1679 1729
1680 /** 1730 /**
1681 * Ask the GData service to re-fetch the metadata for the current directory.
1682 */
1683 FileManager.prototype.requestMetadataRefresh = function() {
1684 if (!this.isOnGData())
1685 return;
1686 // TODO(kaznacheev) This does not really work with GData search.
1687 var url = this.getCurrentDirectoryURL();
1688 // Skip if the current directory is now being refreshed.
1689 if (this.directoriesWithStaleMetadata_[url])
1690 return;
1691
1692 this.directoriesWithStaleMetadata_[url] = true;
1693 chrome.fileBrowserPrivate.requestDirectoryRefresh(url);
1694 };
1695
1696 /**
1697 * Create a box containing a centered thumbnail image. 1731 * Create a box containing a centered thumbnail image.
1698 * 1732 *
1699 * @param {Entry} entry Entry which thumbnail is generating for. 1733 * @param {Entry} entry Entry which thumbnail is generating for.
1700 * @param {boolean} True if fill, false if fit. 1734 * @param {boolean} True if fill, false if fit.
1701 * @param {function(HTMLElement)} opt_imageLoadCallback Callback called when 1735 * @param {function(HTMLElement)} opt_imageLoadCallback Callback called when
1702 * the image has been loaded before inserting 1736 * the image has been loaded before inserting
1703 * it into the DOM. 1737 * it into the DOM.
1704 * @return {HTMLDivElement} Thumbnal box. 1738 * @return {HTMLDivElement} Thumbnal box.
1705 */ 1739 */
1706 FileManager.prototype.renderThumbnailBox_ = function(entry, fill, 1740 FileManager.prototype.renderThumbnailBox_ = function(entry, fill,
(...skipping 17 matching lines...) Expand all
1724 }; 1758 };
1725 img.onerror = function() { 1759 img.onerror = function() {
1726 // Use the default icon if we could not fetch the correct one. 1760 // Use the default icon if we could not fetch the correct one.
1727 img.src = FileType.getPreviewArt(iconType); 1761 img.src = FileType.getPreviewArt(iconType);
1728 transform = null; 1762 transform = null;
1729 util.applyTransform(box, transform); 1763 util.applyTransform(box, transform);
1730 1764
1731 var cached = self.thumbnailUrlCache_[entry.fullPath]; 1765 var cached = self.thumbnailUrlCache_[entry.fullPath];
1732 if (!cached.failed) { 1766 if (!cached.failed) {
1733 cached.failed = true; 1767 cached.failed = true;
1734 self.requestMetadataRefresh();
1735 // Failing to fetch a thumbnail likely means that the thumbnail URL 1768 // Failing to fetch a thumbnail likely means that the thumbnail URL
1736 // is now stale. Request a refresh of the current directory, to get 1769 // is now stale. Request a refresh of the current directory, to get
1737 // the new thumbnail URLs. Once the directory is refreshed, we'll get 1770 // the new thumbnail URLs. Once the directory is refreshed, we'll get
1738 // notified via onFileChanged event. 1771 // notified via onFileChanged event.
1739 self.requestMetadataRefresh(); 1772 self.fileWatcher_.requestMetadataRefresh(entry);
1740 } 1773 }
1741 }; 1774 };
1742 img.src = url; 1775 img.src = url;
1743 util.applyTransform(box, transform); 1776 util.applyTransform(box, transform);
1744 } 1777 }
1745 1778
1746 // TODO(dgozman): move to new metadata cache. 1779 // TODO(dgozman): move to new metadata cache.
1747 var cached = this.thumbnailUrlCache_[entry.fullPath]; 1780 var cached = this.thumbnailUrlCache_[entry.fullPath];
1748 // Don't reuse the cached URL if we are now retrying. 1781 // Don't reuse the cached URL if we are now retrying.
1749 if (cached && !cached.failed) 1782 if (cached && !cached.failed)
(...skipping 72 matching lines...) Expand 10 before | Expand all | Expand 10 after
1822 } 1855 }
1823 }; 1856 };
1824 li.addEventListener('mousedown', handleClick); 1857 li.addEventListener('mousedown', handleClick);
1825 li.addEventListener(cr.ui.TouchHandler.EventType.TOUCH_START, handleClick); 1858 li.addEventListener(cr.ui.TouchHandler.EventType.TOUCH_START, handleClick);
1826 1859
1827 var rootType = DirectoryModel.getRootType(entry.fullPath); 1860 var rootType = DirectoryModel.getRootType(entry.fullPath);
1828 1861
1829 var div = this.document_.createElement('div'); 1862 var div = this.document_.createElement('div');
1830 div.className = 'root-label'; 1863 div.className = 'root-label';
1831 1864
1832 var icon = rootType;
1833 var deviceNumber = this.getDeviceNumber(entry);
1834
1835 if (deviceNumber != undefined) {
1836 var mountCondition = this.mountPoints_[deviceNumber].mountCondition;
1837 if (mountCondition == 'unknown_filesystem' ||
1838 mountCondition == 'unsupported_filesystem')
1839 icon = 'unreadable';
1840 }
1841
1842 div.setAttribute('icon', icon);
1843
1844 div.textContent = this.getRootLabel_(entry.fullPath); 1865 div.textContent = this.getRootLabel_(entry.fullPath);
1845 li.appendChild(div); 1866 li.appendChild(div);
1846 1867
1847 if (rootType == DirectoryModel.RootType.ARCHIVE || 1868 if (rootType == DirectoryModel.RootType.ARCHIVE ||
1848 rootType == DirectoryModel.RootType.REMOVABLE) { 1869 rootType == DirectoryModel.RootType.REMOVABLE) {
1849 if (entry.unmounting) { 1870 var eject = this.document_.createElement('div');
1850 li.setAttribute('disabled', 'disabled'); 1871 eject.className = 'root-eject';
1851 } else { 1872 eject.addEventListener('click', function(event) {
1852 var eject = this.document_.createElement('div'); 1873 event.stopPropagation();
1853 eject.className = 'root-eject'; 1874 this.unmountVolume_(entry);
1854 eject.addEventListener('click', function(event) { 1875 }.bind(this));
1855 event.stopPropagation(); 1876 // Block other mouse handlers.
1856 this.unmountVolume_(entry); 1877 eject.addEventListener('mouseup', function(e) { e.stopPropagation() });
1857 }.bind(this)); 1878 eject.addEventListener('mousedown', function(e) { e.stopPropagation() });
1858 // Block other mouse handlers. 1879 li.appendChild(eject);
1859 eject.addEventListener('mouseup', function(e) { e.stopPropagation() });
1860 eject.addEventListener('mousedown', function(e) { e.stopPropagation() }) ;
1861 li.appendChild(eject);
1862 1880
1863 cr.ui.contextMenuHandler.setContextMenu(li, this.rootsContextMenu_); 1881 cr.ui.contextMenuHandler.setContextMenu(li, this.rootsContextMenu_);
1864 }
1865 } 1882 }
1866 1883
1867 cr.defineProperty(li, 'lead', cr.PropertyKind.BOOL_ATTR); 1884 cr.defineProperty(li, 'lead', cr.PropertyKind.BOOL_ATTR);
1868 cr.defineProperty(li, 'selected', cr.PropertyKind.BOOL_ATTR); 1885 cr.defineProperty(li, 'selected', cr.PropertyKind.BOOL_ATTR);
1886
1887 var icon = rootType;
1888 if (this.volumeManager_.isUnreadable(entry.fullPath)) {
1889 icon = 'unreadable';
1890 }
1891 div.setAttribute('icon', icon);
1892
1869 return li; 1893 return li;
1870 }; 1894 };
1871 1895
1872 /** 1896 /**
1873 * Unmounts device. 1897 * Unmounts device.
1874 * @param {Entry} entry The entry to unmount. 1898 * @param {Entry} entry The entry to unmount.
1875 */ 1899 */
1876 FileManager.prototype.unmountVolume_ = function(entry) { 1900 FileManager.prototype.unmountVolume_ = function(entry) {
1877 this.directoryModel_.prepareUnmount(entry.fullPath); 1901 listItem = this.rootsList_.getListItem(entry);
1878 this.unmountRequests_.push(entry.fullPath); 1902 if (listItem)
1879 chrome.fileBrowserPrivate.removeMount(entry.toURL()); 1903 listItem.setAttribute('disabled', '');
1904 var self = this;
1905 this.volumeManager_.unmount(entry.fullPath, function() {},
1906 function(error) {
1907 if (listItem)
1908 listItem.removeAttribute('disabled');
1909 self.alert.show(strf('UNMOUNT_FAILED', error.message));
1910 });
1880 }; 1911 };
1881 1912
1882 FileManager.prototype.updateGDataStyle_ = function( 1913 FileManager.prototype.updateGDataStyle_ = function(
1883 listItem, entry, gdata) { 1914 listItem, entry, gdata) {
1884 if (!this.isOnGData() || !gdata) 1915 if (!this.isOnGData() || !gdata)
1885 return; 1916 return;
1886 1917
1887 if (!entry.isDirectory) { 1918 if (!entry.isDirectory) {
1888 if (!gdata.availableOffline) 1919 if (!gdata.availableOffline)
1889 listItem.classList.add('dim-offline'); 1920 listItem.classList.add('dim-offline');
(...skipping 367 matching lines...) Expand 10 before | Expand all | Expand 10 after
2257 2288
2258 if (entry.isFile) { 2289 if (entry.isFile) {
2259 selection.bytes += filesystem.size; 2290 selection.bytes += filesystem.size;
2260 } 2291 }
2261 } 2292 }
2262 2293
2263 this.dispatchEvent(new cr.Event('selection-summarized')); 2294 this.dispatchEvent(new cr.Event('selection-summarized'));
2264 }.bind(this)); 2295 }.bind(this));
2265 2296
2266 if (this.isOnGData()) { 2297 if (this.isOnGData()) {
2298 function predicate(p) {
2299 return !(p && p.availableOffline);
2300 }
2267 this.metadataCache_.get(selection.urls, 'gdata', function(props) { 2301 this.metadataCache_.get(selection.urls, 'gdata', function(props) {
2268 selection.allGDataFilesPresent = 2302 selection.allGDataFilesPresent =
2269 props.filter(function(p) {return !p.availableOffline}).length == 0; 2303 props.filter(predicate).length == 0;
2270 this.updateOkButton_(); 2304 this.updateOkButton_();
2271 }.bind(this)); 2305 }.bind(this));
2272 } 2306 }
2273 }; 2307 };
2274 2308
2275 /** 2309 /**
2276 * Check if all the files in the current selection are available. The only 2310 * Check if all the files in the current selection are available. The only
2277 * case when files might be not available is when the selection contains 2311 * case when files might be not available is when the selection contains
2278 * uncached GData files and the browser is offline. 2312 * uncached GData files and the browser is offline.
2279 * @return {boolean} True if all files in the current selection are 2313 * @return {boolean} True if all files in the current selection are
(...skipping 118 matching lines...) Expand 10 before | Expand all | Expand 10 after
2398 setVisibility('visible'); 2432 setVisibility('visible');
2399 self.previewThumbnails_.textContent = ''; 2433 self.previewThumbnails_.textContent = '';
2400 self.onResize_(); 2434 self.onResize_();
2401 } 2435 }
2402 2436
2403 function setVisibility(visibility) { 2437 function setVisibility(visibility) {
2404 panel.setAttribute('visibility', visibility); 2438 panel.setAttribute('visibility', visibility);
2405 } 2439 }
2406 }; 2440 };
2407 2441
2408 FileManager.prototype.isGDataEnabled = function() {
2409 return this.getGDataPreferences_().driveEnabled;
2410 };
2411
2412 FileManager.prototype.updateGDataAccess_ = function() {
2413 if (this.isGDataEnabled())
2414 this.setupGDataWelcome_();
2415 else
2416 this.cleanupGDataWelcome_();
2417
2418 var changeDirectory = !this.isGDataEnabled() && this.isOnGData();
2419
2420 this.directoryModel_.updateRoots(function() {
2421 if (changeDirectory)
2422 this.directoryModel_.changeDirectory(
2423 this.directoryModel_.getDefaultDirectory());
2424 }.bind(this), this.getGDataAccessMode_());
2425 };
2426
2427 FileManager.prototype.getGDataAccessMode_ = function() {
2428 if (!this.isGDataEnabled())
2429 return DirectoryModel.GDATA_ACCESS_DISABLED;
2430 if (!this.gdataMounted_)
2431 return DirectoryModel.GDATA_ACCESS_LAZY;
2432 return DirectoryModel.GDATA_ACCESS_FULL;
2433 };
2434
2435 FileManager.prototype.isOnGData = function() { 2442 FileManager.prototype.isOnGData = function() {
2436 return this.directoryModel_ && 2443 return this.directoryModel_.getCurrentRootType() ==
2437 this.directoryModel_.getCurrentRootPath() == 2444 DirectoryModel.RootType.GDATA;
2438 '/' + DirectoryModel.GDATA_DIRECTORY;
2439 };
2440
2441 FileManager.prototype.isStartingOnGData_ = function() {
2442 var path = this.getPathFromUrlOrParams_();
2443 return path &&
2444 this.isGDataEnabled() &&
2445 DirectoryModel.getRootType(path) == DirectoryModel.RootType.GDATA;
2446 }; 2445 };
2447 2446
2448 FileManager.prototype.getMetadataProvider = function() { 2447 FileManager.prototype.getMetadataProvider = function() {
2449 return this.metadataProvider_; 2448 return this.metadataProvider_;
2450 }; 2449 };
2451 2450
2452 /** 2451 /**
2453 * Callback called when tasks for selected files are determined. 2452 * Callback called when tasks for selected files are determined.
2454 * @param {Object} selection Selection is passed here, since this.selection 2453 * @param {Object} selection Selection is passed here, since this.selection
2455 * can change before tasks were found, and we should be accurate. 2454 * can change before tasks were found, and we should be accurate.
(...skipping 213 matching lines...) Expand 10 before | Expand all | Expand 10 after
2669 }.bind(this)); 2668 }.bind(this));
2670 return; 2669 return;
2671 } 2670 }
2672 callback(urls); 2671 callback(urls);
2673 }.bind(this)); 2672 }.bind(this));
2674 } else { 2673 } else {
2675 callback(urls); 2674 callback(urls);
2676 } 2675 }
2677 }; 2676 };
2678 2677
2679 FileManager.prototype.getGDataPreferences_ = function() { 2678 FileManager.prototype.updateNetworkStateAndGDataPreferences_ = function(
2680 return this.gdataPreferences_ || 2679 callback) {
2681 { driveEnabled: loadTimeData.getBoolean('ENABLE_GDATA') }; 2680 var self = this;
2681 var downcount = 2;
2682 function done() {
2683 if (--downcount == 0)
2684 callback();
2685 }
2686
2687 chrome.fileBrowserPrivate.getGDataPreferences(function(prefs) {
2688 self.gdataPreferences_ = prefs;
2689 done();
2690 });
2691
2692 chrome.fileBrowserPrivate.getNetworkConnectionState(function(netwokState) {
2693 self.networkState_ = netwokState;
2694 done();
2695 });
2682 }; 2696 };
2683 2697
2684 FileManager.prototype.getNetworkConnectionState_ = function() { 2698 FileManager.prototype.onNetworkStateOrGDataPreferencesChanged_ = function() {
2685 return this.networkConnectionState_ || {}; 2699 var self = this;
2686 }; 2700 this.updateNetworkStateAndGDataPreferences_(function() {
2701 var gdata = self.gdataPreferences_;
2702 var network = self.networkState_;
2687 2703
2688 FileManager.prototype.onNetworkConnectionChanged_ = function(state) { 2704 self.directoryModel_.setGDataEnabled(self.isGDataEnabled());
2689 console.log(state.online ? 'online' : 'offline', state.type);
2690 this.networkConnectionState_ = state;
2691 this.directoryModel_.setOffline(!state.online);
2692 this.updateConnectionState_();
2693 };
2694 2705
2695 FileManager.prototype.onGDataPreferencesChanged_ = function(preferences) { 2706 if (self.isGDataEnabled())
2696 var gdataWasEnabled = this.isGDataEnabled(); 2707 self.setupGDataWelcome_();
2697 this.gdataPreferences_ = preferences; 2708 else
2698 if (gdataWasEnabled != this.isGDataEnabled()) 2709 self.cleanupGDataWelcome_();
2699 this.updateGDataAccess_();
2700 2710
2701 if (preferences.cellularDisabled) 2711 if (gdata.cellularDisabled)
2702 this.syncButton.setAttribute('checked', 'checked'); 2712 self.syncButton.setAttribute('checked', '');
2703 else 2713 else
2704 this.syncButton.removeAttribute('checked'); 2714 self.syncButton.removeAttribute('checked');
2705 2715
2706 if (!preferences.hostedFilesDisabled) 2716 if (!gdata.hostedFilesDisabled)
2707 this.hostedButton.setAttribute('checked', 'checked'); 2717 self.hostedButton.setAttribute('checked', '');
2708 else 2718 else
2709 this.hostedButton.removeAttribute('checked'); 2719 self.hostedButton.removeAttribute('checked');
2710 2720
2711 this.updateConnectionState_(); 2721 if (network.online) {
2712 }; 2722 if (gdata.cellularDisabled && network.type == 'cellular')
2713 2723 self.dialogContainer_.setAttribute('connection', 'metered');
2714 FileManager.prototype.updateConnectionState_ = function() { 2724 else
2715 if (this.isOffline()) 2725 self.dialogContainer_.removeAttribute('connection');
2716 this.dialogContainer_.setAttribute('connection', 'offline'); 2726 } else {
2717 else if (this.isOnMeteredConnection()) 2727 self.dialogContainer_.setAttribute('connection', 'offline');
2718 this.dialogContainer_.setAttribute('connection', 'metered'); 2728 }
2719 else 2729 });
2720 this.dialogContainer_.removeAttribute('connection');
2721 }; 2730 };
2722 2731
2723 FileManager.prototype.isOnMeteredConnection = function() { 2732 FileManager.prototype.isOnMeteredConnection = function() {
2724 return this.getGDataPreferences_().cellularDisabled && 2733 return this.gdataPreferences_.cellularDisabled &&
2725 this.getNetworkConnectionState_().online && 2734 this.networkState_.online &&
2726 this.getNetworkConnectionState_().type == 'cellular'; 2735 this.networkState_.type == 'cellular';
2727 }; 2736 };
2728 2737
2729 FileManager.prototype.isOffline = function() { 2738 FileManager.prototype.isOffline = function() {
2730 return !this.getNetworkConnectionState_().online; 2739 return !this.networkState_.online;
2740 };
2741
2742 FileManager.prototype.isGDataEnabled = function() {
2743 return !('driveEnabled' in this.gdataPreferences_) ||
2744 this.gdataPreferences_.driveEnabled;
2731 }; 2745 };
2732 2746
2733 FileManager.prototype.isOnReadonlyDirectory = function() { 2747 FileManager.prototype.isOnReadonlyDirectory = function() {
2734 return this.directoryModel_.isReadOnly(); 2748 return this.directoryModel_.isReadOnly();
2735 }; 2749 };
2736 2750
2737 /** 2751 FileManager.prototype.onExternallyUnmounted_ = function(event) {
2738 * Event handler called when some volume was mounted or unmouted. 2752 if (event.mountPath == this.directoryModel_.getCurrentRootPath()) {
2739 */ 2753 if (this.params_.mountTriggered) {
2740 FileManager.prototype.onMountCompleted_ = function(event) { 2754 // TODO(serya): What if 2 USB sticks plugged?
2741 var changeDirectoryTo = null; 2755 chrome.tabs.getCurrent(function(tab) {
2742 2756 chrome.tabs.remove(tab.id);
2743 if (event && event.mountType == 'gdata') { 2757 });
2744 var mounted = (event.eventType == 'mount');
2745 metrics.recordInterval('Load.GData');
2746 console.log('GData ' + (mounted ? 'mounted' : 'unmounted'));
2747 if (mounted && event.status == 'success') {
2748 this.gdataMounted_ = true;
2749 this.gdataMountInfo_ = {
2750 'mountPath': event.mountPath,
2751 'sourcePath': event.sourcePath,
2752 'mountType': event.mountType,
2753 'mountCondition': event.status
2754 };
2755 // Not calling clearGDataLoadingTimer_ here because we want to keep
2756 // "Loading Google Docs" message until the directory loads. It is OK if
2757 // the timer fires after the mount because onDirectoryChanged_ will hide
2758 // the unmounted panel.
2759 if (this.setupCurrentDirectoryPostponed_) {
2760 this.setupCurrentDirectoryPostponed_(false /* execute */);
2761 } else if (this.isOnGData() &&
2762 this.directoryModel_.getCurrentDirEntry().unmounted) {
2763 // We are currently on an unmounted GData directory, force a rescan.
2764 changeDirectoryTo = this.directoryModel_.getCurrentRootPath();
2765 }
2766 } else {
2767 this.onGDataUnreachable_('GData ' +
2768 (mounted ? ('mount failed: ' + event.status) : 'unmounted'));
2769 if (this.setupCurrentDirectoryPostponed_) {
2770 this.setupCurrentDirectoryPostponed_(true /* cancel */);
2771 // Change to unmounted GData root.
2772 changeDirectoryTo = '/' + DirectoryModel.GDATA_DIRECTORY;
2773 }
2774 } 2758 }
2775 } 2759 }
2776
2777 chrome.fileBrowserPrivate.getMountPoints(function(mountPoints) {
2778 this.setMountPoints_(mountPoints);
2779
2780 if (event.eventType == 'mount' && event.mountType != 'gdata') {
2781 // Mount request finished - remove it.
2782 // Currently we only request mounts for archive files.
2783 var index = this.mountRequests_.indexOf(event.sourcePath);
2784 if (index != -1) {
2785 this.mountRequests_.splice(index, 1);
2786 if (event.status == 'success') {
2787 // Successful mount requested from this tab, go to the drive root.
2788 changeDirectoryTo = event.mountPath;
2789 } else {
2790 // Request initiated from this tab failed, report the error.
2791 var fileName = event.sourcePath.split('/').pop();
2792 this.alert.show(
2793 strf('ARCHIVE_MOUNT_FAILED', fileName, event.status));
2794 }
2795 }
2796 }
2797
2798 if (event.eventType == 'unmount' && event.mountType != 'gdata') {
2799 // Unmount request finished - remove it.
2800 var index = this.unmountRequests_.indexOf(event.mountPath);
2801 if (index != -1) {
2802 this.unmountRequests_.splice(index, 1);
2803 if (event.status != 'success')
2804 this.alert.show(strf('UNMOUNT_FAILED', event.status));
2805 }
2806
2807 if (event.status == 'success' &&
2808 event.mountPath == this.directoryModel_.getCurrentRootPath()) {
2809 if (this.params_.mountTriggered && index == -1) {
2810 // This device mount was the reason this File Manager instance was
2811 // created. Now the device is unmounted from another instance
2812 // or the user removed the device manually. Close this instance.
2813 // window.close() sometimes doesn't work.
2814 chrome.tabs.getCurrent(function(tab) {
2815 chrome.tabs.remove(tab.id);
2816 });
2817 return;
2818 }
2819 // Current directory just unmounted. Move to the 'Downloads'.
2820 changeDirectoryTo = this.directoryModel_.getDefaultDirectory();
2821 }
2822 }
2823
2824 // Even if something failed root list should be rescanned.
2825 // Failed mounts can "give" us new devices which might be formatted,
2826 // so we have to refresh root list then.
2827 this.directoryModel_.updateRoots(function() {
2828 if (changeDirectoryTo) {
2829 this.directoryModel_.changeDirectory(changeDirectoryTo);
2830 }
2831 }.bind(this), this.getGDataAccessMode_());
2832 }.bind(this));
2833 }; 2760 };
2834 2761
2835 /** 2762 /**
2836 * Event handler called when some internal task should be executed. 2763 * Event handler called when some internal task should be executed.
2837 */ 2764 */
2838 FileManager.prototype.onFileTaskExecute_ = function(id, urls) { 2765 FileManager.prototype.onFileTaskExecute_ = function(id, urls) {
2839 if (id == 'play') { 2766 if (id == 'play') {
2840 var position = 0; 2767 var position = 0;
2841 if (urls.length == 1) { 2768 if (urls.length == 1) {
2842 // If just a single audio file is selected pass along every audio file 2769 // If just a single audio file is selected pass along every audio file
2843 // in the directory. 2770 // in the directory.
2844 var selectedUrl = urls[0]; 2771 var selectedUrl = urls[0];
2845 urls = this.getAllUrlsInCurrentDirectory_().filter(FileType.isAudio); 2772 urls = this.getAllUrlsInCurrentDirectory_().filter(FileType.isAudio);
2846 position = urls.indexOf(selectedUrl); 2773 position = urls.indexOf(selectedUrl);
2847 } 2774 }
2848 chrome.mediaPlayerPrivate.play(urls, position); 2775 chrome.mediaPlayerPrivate.play(urls, position);
2849 } else if (id == 'mount-archive') { 2776 } else if (id == 'mount-archive') {
2850 var self = this; 2777 var self = this;
2778 var tracker = this.directoryModel_.createDirectoryChangeTracker();
2779 tracker.start();
2851 this.resolveSelectResults_(urls, function(urls) { 2780 this.resolveSelectResults_(urls, function(urls) {
2852 for (var index = 0; index < urls.length; ++index) { 2781 for (var index = 0; index < urls.length; ++index) {
2853 // Url in MountCompleted event won't be escaped, so let's make sure 2782 var path = /^filesystem:[\w-]*:\/\/[\w]*\/external(\/.*)$/.
2854 // we don't use escaped one in mountRequests_. 2783 exec(urls[index])[1];
2855 var unescapedUrl = unescape(urls[index]); 2784 if (!path)
2856 chrome.fileBrowserPrivate.addMount(unescapedUrl, 'file', {}, 2785 continue;
2857 function(sourcePath) { 2786 path = decodeURIComponent(path);
2858 self.mountRequests_.push(sourcePath); 2787 self.volumeManager_.mountArchive(path, function(mountPath) {
2788 console.log('Mounted at: ', mountPath);
2789 tracker.stop();
2790 if (!tracker.hasChanged)
2791 self.directoryModel_.changeDirectory(mountPath);
2792 }, function(error) {
2793 tracker.stop();
2794 var namePos = path.lastIndexOf('/');
2795 self.alert.show(strf('ARCHIVE_MOUNT_FAILED',
2796 path.substr(namePos + 1), error));
2859 }); 2797 });
2860 } 2798 }
2861 }); 2799 });
2862 } else if (id == 'format-device') { 2800 } else if (id == 'format-device') {
2863 this.confirm.show(str('FORMATTING_WARNING'), function() { 2801 this.confirm.show(str('FORMATTING_WARNING'), function() {
2864 chrome.fileBrowserPrivate.formatDevice(urls[0]); 2802 chrome.fileBrowserPrivate.formatDevice(urls[0]);
2865 }); 2803 });
2866 } else if (id == 'gallery') { 2804 } else if (id == 'gallery') {
2867 this.openGallery_(urls); 2805 this.openGallery_(urls);
2868 } else if (id == 'view-pdf' || id == 'view-in-browser' || 2806 } else if (id == 'view-pdf' || id == 'view-in-browser' ||
2869 id == 'install-crx' || id == 'open-hosted') { 2807 id == 'install-crx' || id == 'open-hosted') {
2870 chrome.fileBrowserPrivate.viewFiles(urls, 'default', function(success) { 2808 chrome.fileBrowserPrivate.viewFiles(urls, 'default', function(success) {
2871 if (!success) 2809 if (!success)
2872 console.error('chrome.fileBrowserPrivate.viewFiles failed', urls); 2810 console.error('chrome.fileBrowserPrivate.viewFiles failed', urls);
2873 }); 2811 });
2874 } 2812 }
2875 }; 2813 };
2876 2814
2877 FileManager.prototype.getDeviceNumber = function(entry) {
2878 if (!entry.isDirectory) return undefined;
2879 for (var i = 0; i < this.mountPoints_.length; i++) {
2880 if (normalizeAbsolutePath(entry.fullPath) ==
2881 normalizeAbsolutePath(this.mountPoints_[i].mountPath)) {
2882 return i;
2883 }
2884 }
2885 return undefined;
2886 };
2887
2888 /** 2815 /**
2889 * Show a modal-like file viewer/editor on top of the File Manager UI. 2816 * Show a modal-like file viewer/editor on top of the File Manager UI.
2890 * 2817 *
2891 * @param {HTMLElement} popup Popup element. 2818 * @param {HTMLElement} popup Popup element.
2892 * @param {function} closeCallback Function to call after the popup is closed. 2819 * @param {function} closeCallback Function to call after the popup is closed.
2893 */ 2820 */
2894 FileManager.prototype.openFilePopup_ = function(popup, closeCallback) { 2821 FileManager.prototype.openFilePopup_ = function(popup, closeCallback) {
2895 this.closeFilePopup_(); 2822 this.closeFilePopup_();
2896 this.filePopup_ = popup; 2823 this.filePopup_ = popup;
2897 this.filePopupCloseCallback_ = closeCallback; 2824 this.filePopupCloseCallback_ = closeCallback;
(...skipping 90 matching lines...) Expand 10 before | Expand all | Expand 10 after
2988 galleryFrame.className = 'overlay-pane'; 2915 galleryFrame.className = 'overlay-pane';
2989 galleryFrame.scrolling = 'no'; 2916 galleryFrame.scrolling = 'no';
2990 galleryFrame.setAttribute('webkitallowfullscreen', true); 2917 galleryFrame.setAttribute('webkitallowfullscreen', true);
2991 2918
2992 var dirPath = this.directoryModel_.getCurrentDirEntry().fullPath; 2919 var dirPath = this.directoryModel_.getCurrentDirEntry().fullPath;
2993 // Push a temporary state which will be replaced every time an individual 2920 // Push a temporary state which will be replaced every time an individual
2994 // item is selected in the Gallery. 2921 // item is selected in the Gallery.
2995 this.updateLocation_(false /*push*/, dirPath); 2922 this.updateLocation_(false /*push*/, dirPath);
2996 2923
2997 galleryFrame.onload = function() { 2924 galleryFrame.onload = function() {
2925 self.show_();
2998 galleryFrame.contentWindow.ImageUtil.metrics = metrics; 2926 galleryFrame.contentWindow.ImageUtil.metrics = metrics;
2999 galleryFrame.contentWindow.FileType = FileType; 2927 galleryFrame.contentWindow.FileType = FileType;
3000 galleryFrame.contentWindow.util = util; 2928 galleryFrame.contentWindow.util = util;
3001 2929
3002 // Gallery shoud treat GData folder as readonly even when online 2930 // Gallery shoud treat GData folder as readonly even when online
3003 // until we learn to save files directly to GData. 2931 // until we learn to save files directly to GData.
3004 var readonly = self.isOnReadonlyDirectory() || self.isOnGData(); 2932 var readonly = self.isOnReadonlyDirectory() || self.isOnGData();
3005 var currentDir = self.directoryModel_.getCurrentDirEntry(); 2933 var currentDir = self.directoryModel_.getCurrentDirEntry();
3006 var downloadsDir = self.directoryModel_.getRootsList().item(0); 2934 var downloadsDir = self.directoryModel_.getRootsList().item(0);
3007 2935
(...skipping 29 matching lines...) Expand all
3037 }; 2965 };
3038 2966
3039 /** 2967 /**
3040 * Update the breadcrumb display to reflect the current directory. 2968 * Update the breadcrumb display to reflect the current directory.
3041 */ 2969 */
3042 FileManager.prototype.updateBreadcrumbs_ = function() { 2970 FileManager.prototype.updateBreadcrumbs_ = function() {
3043 var bc = this.dialogDom_.querySelector('.breadcrumbs'); 2971 var bc = this.dialogDom_.querySelector('.breadcrumbs');
3044 removeChildren(bc); 2972 removeChildren(bc);
3045 2973
3046 var rootPath = this.directoryModel_.getCurrentRootPath(); 2974 var rootPath = this.directoryModel_.getCurrentRootPath();
3047 var relativePath = this.directoryModel_.getCurrentDirEntry().fullPath. 2975 var relativePath = this.directoryModel_.getCurrentDirPath().
3048 substring(rootPath.length).replace(/\/$/, ''); 2976 substring(rootPath.length).replace(/\/$/, '');
3049 2977
3050 var pathNames = relativePath.replace(/\/$/, '').split('/'); 2978 var pathNames = relativePath.replace(/\/$/, '').split('/');
3051 if (pathNames[0] == '') 2979 if (pathNames[0] == '')
3052 pathNames.splice(0, 1); 2980 pathNames.splice(0, 1);
3053 2981
3054 // We need a first breadcrumb for root, so placing last name from 2982 // We need a first breadcrumb for root, so placing last name from
3055 // rootPath as first name of relativePath. 2983 // rootPath as first name of relativePath.
3056 var rootPathNames = rootPath.replace(/\/$/, '').split('/'); 2984 var rootPathNames = rootPath.replace(/\/$/, '').split('/');
3057 pathNames.splice(0, 0, rootPathNames[rootPathNames.length - 1]); 2985 pathNames.splice(0, 0, rootPathNames[rootPathNames.length - 1]);
(...skipping 151 matching lines...) Expand 10 before | Expand all | Expand 10 after
3209 this.table_.focus(); 3137 this.table_.focus();
3210 else // this.listType_ == FileManager.ListType.THUMBNAIL) 3138 else // this.listType_ == FileManager.ListType.THUMBNAIL)
3211 this.grid_.focus(); 3139 this.grid_.focus();
3212 }; 3140 };
3213 3141
3214 /** 3142 /**
3215 * Return full path of the current directory or null. 3143 * Return full path of the current directory or null.
3216 */ 3144 */
3217 FileManager.prototype.getCurrentDirectory = function() { 3145 FileManager.prototype.getCurrentDirectory = function() {
3218 return this.directoryModel_ && 3146 return this.directoryModel_ &&
3219 this.directoryModel_.getCurrentDirEntry().fullPath; 3147 this.directoryModel_.getCurrentDirPath();
3220 }; 3148 };
3221 3149
3222 /** 3150 /**
3223 * Return URL of the current directory or null. 3151 * Return URL of the current directory or null.
3224 */ 3152 */
3225 FileManager.prototype.getCurrentDirectoryURL = function() { 3153 FileManager.prototype.getCurrentDirectoryURL = function() {
3226 return this.directoryModel_ && 3154 return this.directoryModel_ &&
3227 this.directoryModel_.getCurrentDirEntry().toURL(); 3155 this.directoryModel_.getCurrentDirEntry().toURL();
3228 }; 3156 };
3229 3157
(...skipping 291 matching lines...) Expand 10 before | Expand all | Expand 10 after
3521 3449
3522 /** 3450 /**
3523 * Executes directory action (i.e. changes directory). If new directory is a 3451 * Executes directory action (i.e. changes directory). If new directory is a
3524 * search result directory, we'll have to calculate its real path before we 3452 * search result directory, we'll have to calculate its real path before we
3525 * actually do the operation. 3453 * actually do the operation.
3526 * 3454 *
3527 * @param {DirectoryEntry} entry Directory entry to which directory should be 3455 * @param {DirectoryEntry} entry Directory entry to which directory should be
3528 * changed. 3456 * changed.
3529 */ 3457 */
3530 FileManager.prototype.onDirectoryAction = function(entry) { 3458 FileManager.prototype.onDirectoryAction = function(entry) {
3459 var mountError = this.volumeManager_.getMountError(
3460 DirectoryModel.getRootPath(entry.fullPath));
3461 if (mountError == VolumeManager.Error.UNKNOWN_FILESYSTEM) {
3462 return this.showButter(str('UNKNOWN_FILESYSTEM_WARNING'));
3463 } else if (mountError == VolumeManager.Error.UNSUPPORTED_FILESYSTEM) {
3464 return this.showButter(str('UNSUPPORTED_FILESYSTEM_WARNING'));
3465 }
3531 if (!DirectoryModel.isGDataSearchPath(entry.fullPath)) 3466 if (!DirectoryModel.isGDataSearchPath(entry.fullPath))
3532 return this.directoryModel_.changeDirectory(entry.fullPath); 3467 return this.directoryModel_.changeDirectory(entry.fullPath);
3533 3468
3534 // If we are under gdata search path, the real entries file path may be 3469 // If we are under gdata search path, the real entries file path may be
3535 // different from |entry.fullPath|. 3470 // different from |entry.fullPath|.
3536 var self = this; 3471 var self = this;
3537 chrome.fileBrowserPrivate.getPathForDriveSearchResult(entry.toURL(), 3472 chrome.fileBrowserPrivate.getPathForDriveSearchResult(entry.toURL(),
3538 function(path) { 3473 function(path) {
3539 // |path| may be undefined if there was an error. If that is the case, 3474 // |path| may be undefined if there was an error. If that is the case,
3540 // change to the original file path. 3475 // change to the original file path.
(...skipping 63 matching lines...) Expand 10 before | Expand all | Expand 10 after
3604 * 3539 *
3605 * @param {cr.Event} event The directory-changed event. 3540 * @param {cr.Event} event The directory-changed event.
3606 */ 3541 */
3607 FileManager.prototype.onDirectoryChanged_ = function(event) { 3542 FileManager.prototype.onDirectoryChanged_ = function(event) {
3608 this.updateCommonActionButtons_(); 3543 this.updateCommonActionButtons_();
3609 this.updateOkButton_(); 3544 this.updateOkButton_();
3610 this.updateBreadcrumbs_(); 3545 this.updateBreadcrumbs_();
3611 this.updateColumnModel_(); 3546 this.updateColumnModel_();
3612 this.updateSearchBoxOnDirChange_(); 3547 this.updateSearchBoxOnDirChange_();
3613 3548
3614 // Sometimes we rescan the same directory (when mounting GData lazily first, 3549 this.updateLocation_(event.initial, this.getCurrentDirectory());
3615 // then for real). Do not update the location then.
3616 if (event.newDirEntry.fullPath != event.previousDirEntry.fullPath) {
3617 this.updateLocation_(event.initial, event.newDirEntry.fullPath);
3618 }
3619
3620 this.checkFreeSpace_(this.getCurrentDirectory()); 3550 this.checkFreeSpace_(this.getCurrentDirectory());
3621 3551
3622 this.updateTitle_(); 3552 this.updateTitle_();
3623 3553 this.updateGDataUnmountedPanel_();
3624 if (this.filesystemObserverId_) 3554 if (this.isOnGData())
3625 this.metadataCache_.removeObserver(this.filesystemObserverId_); 3555 this.unmountedPanel_.classList.remove('retry-enabled');
3626 if (this.gdataObserverId_)
3627 this.metadataCache_.removeObserver(this.gdataObserverId_);
3628
3629 this.filesystemObserverId_ = this.metadataCache_.addObserver(
3630 this.directoryModel_.getCurrentDirEntry(),
3631 MetadataCache.CHILDREN,
3632 'filesystem',
3633 this.updateMetadataInUI_.bind(this, 'filesystem'));
3634
3635 if (this.isOnGData()) {
3636 this.gdataObserverId_ = this.metadataCache_.addObserver(
3637 this.directoryModel_.getCurrentDirEntry(),
3638 MetadataCache.CHILDREN,
3639 'gdata',
3640 this.updateMetadataInUI_.bind(this, 'gdata'));
3641 }
3642
3643 var self = this;
3644
3645 if (this.watchedDirectoryUrl_) {
3646 if (this.watchedDirectoryUrl_ != event.previousDirEntry.toURL()) {
3647 console.warn('event.previousDirEntry does not match File Manager state',
3648 event, this.watchedDirectoryUrl_);
3649 }
3650 chrome.fileBrowserPrivate.removeFileWatch(this.watchedDirectoryUrl_,
3651 function(result) {
3652 if (!result) {
3653 console.log('Failed to remove file watch');
3654 }
3655 });
3656 this.watchedDirectoryUrl_ = null;
3657 }
3658
3659 if (event.newDirEntry.fullPath != '/' && !event.newDirEntry.unmounted) {
3660 this.watchedDirectoryUrl_ = event.newDirEntry.toURL();
3661 chrome.fileBrowserPrivate.addFileWatch(this.watchedDirectoryUrl_,
3662 function(result) {
3663 if (!result) {
3664 console.log('Failed to add file watch');
3665 this.watchedDirectoryUrl_ = null;
3666 }
3667 }.bind(this));
3668 }
3669
3670 if (event.newDirEntry.unmounted)
3671 this.dialogContainer_.setAttribute('unmounted', true);
3672 else {
3673 this.dialogContainer_.removeAttribute('unmounted');
3674 // Need to resize explicitly because the list container had display:none.
3675 this.onResize_();
3676 }
3677
3678 if (this.isOnGData()) {
3679 this.dialogContainer_.setAttribute('gdata', true);
3680 if (event.newDirEntry.unmounted) {
3681 if (event.newDirEntry.error)
3682 this.onGDataUnreachable_('File error ' + event.newDirEntry.error);
3683 else
3684 this.initGData_(true /* directory changed */);
3685 }
3686 } else {
3687 this.dialogContainer_.removeAttribute('gdata');
3688 }
3689 }; 3556 };
3690 3557
3691 FileManager.prototype.findListItemForEvent_ = function(event) { 3558 FileManager.prototype.findListItemForEvent_ = function(event) {
3692 return this.findListItemForNode_(event.srcElement); 3559 return this.findListItemForNode_(event.srcElement);
3693 }; 3560 };
3694 3561
3695 FileManager.prototype.findListItemForNode_ = function(node) { 3562 FileManager.prototype.findListItemForNode_ = function(node) {
3696 var item = this.currentList_.getListItemAncestor(node); 3563 var item = this.currentList_.getListItemAncestor(node);
3697 // TODO(serya): list should check that. 3564 // TODO(serya): list should check that.
3698 return item && this.currentList_.isItem(item) ? item : null; 3565 return item && this.currentList_.isItem(item) ? item : null;
3699 }; 3566 };
3700 3567
3701 /** 3568 /**
3702 * Unload handler for the page. May be called manually for the file picker 3569 * Unload handler for the page. May be called manually for the file picker
3703 * dialog, because it closes by calling extension API functions that do not 3570 * dialog, because it closes by calling extension API functions that do not
3704 * return. 3571 * return.
3705 */ 3572 */
3706 FileManager.prototype.onUnload_ = function() { 3573 FileManager.prototype.onUnload_ = function() {
3707 if (this.watchedDirectoryUrl_) { 3574 this.fileWatcher_.stop();
3708 chrome.fileBrowserPrivate.removeFileWatch(
3709 this.watchedDirectoryUrl_,
3710 function(result) {
3711 if (!result) {
3712 console.log('Failed to remove file watch');
3713 }
3714 });
3715 this.watchedDirectoryUrl_ = null;
3716 }
3717 };
3718
3719 FileManager.prototype.onFileChanged_ = function(event) {
3720 // We receive a lot of events even in folders we are not interested in.
3721 if (encodeURI(event.fileUrl) == this.getSearchOrCurrentDirectoryURL()) {
3722 // This event is not necessarily caused by the metadata refresh
3723 // completion. We clear the map knowing that if the metadata is still
3724 // stale then a new re-fetch will be requested.
3725 delete this.directoriesWithStaleMetadata_[
3726 this.getSearchOrCurrentDirectoryURL()];
3727 this.directoryModel_.rescanLater();
3728 }
3729 }; 3575 };
3730 3576
3731 FileManager.prototype.initiateRename_ = function() { 3577 FileManager.prototype.initiateRename_ = function() {
3732 var item = this.currentList_.ensureLeadItemExists(); 3578 var item = this.currentList_.ensureLeadItemExists();
3733 if (!item) 3579 if (!item)
3734 return; 3580 return;
3735 var label = item.querySelector('.filename-label'); 3581 var label = item.querySelector('.filename-label');
3736 var input = this.renameInput_; 3582 var input = this.renameInput_;
3737 3583
3738 input.value = label.textContent; 3584 input.value = label.textContent;
(...skipping 830 matching lines...) Expand 10 before | Expand all | Expand 10 after
4569 handleSplitterDragEnd: function(e) { 4415 handleSplitterDragEnd: function(e) {
4570 Splitter.prototype.handleSplitterDragEnd.apply(this, arguments); 4416 Splitter.prototype.handleSplitterDragEnd.apply(this, arguments);
4571 this.ownerDocument.documentElement.classList.remove('col-resize'); 4417 this.ownerDocument.documentElement.classList.remove('col-resize');
4572 } 4418 }
4573 }; 4419 };
4574 4420
4575 customSplitter.decorate(splitterElement); 4421 customSplitter.decorate(splitterElement);
4576 }; 4422 };
4577 4423
4578 FileManager.prototype.setupGDataWelcome_ = function() { 4424 FileManager.prototype.setupGDataWelcome_ = function() {
4425 if (this.gdataWelcomeHandler_)
4426 return;
4579 this.gdataWelcomeHandler_ = this.createGDataWelcomeHandler_(); 4427 this.gdataWelcomeHandler_ = this.createGDataWelcomeHandler_();
4580 if (this.gdataWelcomeHandler_) { 4428 if (this.gdataWelcomeHandler_) {
4581 this.directoryModel_.addEventListener('scan-completed', 4429 this.directoryModel_.addEventListener('scan-completed',
4582 this.gdataWelcomeHandler_); 4430 this.gdataWelcomeHandler_);
4583 this.directoryModel_.addEventListener('rescan-completed', 4431 this.directoryModel_.addEventListener('rescan-completed',
4584 this.gdataWelcomeHandler_); 4432 this.gdataWelcomeHandler_);
4585 } 4433 }
4586 }; 4434 };
4587 4435
4588 FileManager.prototype.cleanupGDataWelcome_ = function() { 4436 FileManager.prototype.cleanupGDataWelcome_ = function() {
(...skipping 109 matching lines...) Expand 10 before | Expand all | Expand 10 after
4698 4546
4699 function closeBanner() { 4547 function closeBanner() {
4700 self.cleanupGDataWelcome_(); 4548 self.cleanupGDataWelcome_();
4701 // Stop showing the welcome banner. 4549 // Stop showing the welcome banner.
4702 localStorage[WELCOME_HEADER_COUNTER_KEY] = WELCOME_HEADER_COUNTER_LIMIT; 4550 localStorage[WELCOME_HEADER_COUNTER_KEY] = WELCOME_HEADER_COUNTER_LIMIT;
4703 } 4551 }
4704 4552
4705 return maybeShowBanner; 4553 return maybeShowBanner;
4706 }; 4554 };
4707 })(); 4555 })();
4556
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698