OLD | NEW |
---|---|
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 Loading... | |
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 Loading... | |
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 Loading... | |
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 Loading... | |
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 Loading... | |
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 Loading... | |
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 Loading... | |
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 Loading... | |
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 Loading... | |
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 Loading... | |
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 Loading... | |
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 Loading... | |
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 Loading... | |
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 Loading... | |
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 Loading... | |
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 Loading... | |
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 Loading... | |
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 Loading... | |
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 Loading... | |
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 Loading... | |
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 Loading... | |
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 Loading... | |
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 Loading... | |
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 | |
OLD | NEW |