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). |
(...skipping 10 matching lines...) Expand all Loading... | |
21 {}; | 21 {}; |
22 | 22 |
23 this.listType_ = null; | 23 this.listType_ = null; |
24 | 24 |
25 this.selection = null; | 25 this.selection = null; |
26 | 26 |
27 this.butterTimer_ = null; | 27 this.butterTimer_ = null; |
28 this.currentButter_ = null; | 28 this.currentButter_ = null; |
29 this.butterLastShowTime_ = 0; | 29 this.butterLastShowTime_ = 0; |
30 | 30 |
31 this.watchedDirectoryUrl_ = null; | |
32 this.filesystemObserverId_ = null; | 31 this.filesystemObserverId_ = null; |
33 this.gdataObserverId_ = null; | 32 this.gdataObserverId_ = null; |
34 | 33 |
35 this.commands_ = {}; | 34 this.commands_ = {}; |
36 | 35 |
37 this.thumbnailUrlCache_ = {}; | 36 this.thumbnailUrlCache_ = {}; |
38 | 37 |
39 this.document_ = dialogDom.ownerDocument; | 38 this.document_ = dialogDom.ownerDocument; |
40 this.dialogType_ = this.params_.type || FileManager.DialogType.FULL_PAGE; | 39 this.dialogType_ = this.params_.type || FileManager.DialogType.FULL_PAGE; |
41 | 40 |
42 metrics.recordEnum('Create', this.dialogType_, | 41 metrics.recordEnum('Create', this.dialogType_, |
43 [FileManager.DialogType.SELECT_FOLDER, | 42 [FileManager.DialogType.SELECT_FOLDER, |
44 FileManager.DialogType.SELECT_SAVEAS_FILE, | 43 FileManager.DialogType.SELECT_SAVEAS_FILE, |
45 FileManager.DialogType.SELECT_OPEN_FILE, | 44 FileManager.DialogType.SELECT_OPEN_FILE, |
46 FileManager.DialogType.SELECT_OPEN_MULTI_FILE, | 45 FileManager.DialogType.SELECT_OPEN_MULTI_FILE, |
47 FileManager.DialogType.FULL_PAGE]); | 46 FileManager.DialogType.FULL_PAGE]); |
48 | 47 |
49 // TODO(dgozman): This will be changed to LocaleInfo. | 48 // TODO(dgozman): This will be changed to LocaleInfo. |
50 this.locale_ = new v8Locale(navigator.language); | 49 this.locale_ = new v8Locale(navigator.language); |
51 | 50 |
52 this.initFileSystem_(); | 51 this.initFileSystem_(); |
52 this.volumeManager_ = VolumeManager.getInstance(); | |
53 this.initDom_(); | 53 this.initDom_(); |
54 this.initDialogType_(); | 54 this.initDialogType_(); |
55 this.dialogDom_.style.opacity = '1'; | 55 this.dialogDom_.style.opacity = '1'; |
56 } | 56 } |
57 | 57 |
58 FileManager.prototype = { | 58 FileManager.prototype = { |
59 __proto__: cr.EventTarget.prototype | 59 __proto__: cr.EventTarget.prototype |
60 }; | 60 }; |
61 | 61 |
62 // Anonymous "namespace". | 62 // Anonymous "namespace". |
(...skipping 138 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
201 | 201 |
202 if (parent_path[parent_path.length - 1] != '/') | 202 if (parent_path[parent_path.length - 1] != '/') |
203 parent_path += '/'; | 203 parent_path += '/'; |
204 | 204 |
205 if (child_path[child_path.length - 1] != '/') | 205 if (child_path[child_path.length - 1] != '/') |
206 child_path += '/'; | 206 child_path += '/'; |
207 | 207 |
208 return child_path.indexOf(parent_path) == 0; | 208 return child_path.indexOf(parent_path) == 0; |
209 } | 209 } |
210 | 210 |
211 /** | |
212 * Normalizes path not to start with / | |
213 * | |
214 * @param {string} path The file path. | |
215 */ | |
216 function normalizeAbsolutePath(x) { | |
217 if (x[0] == '/') | |
218 return x.slice(1); | |
219 else | |
220 return x; | |
221 } | |
222 | |
223 function removeChildren(element) { | 211 function removeChildren(element) { |
224 element.textContent = ''; | 212 element.textContent = ''; |
225 } | 213 } |
226 | 214 |
227 // Public statics. | 215 // Public statics. |
228 | 216 |
229 /** | 217 /** |
230 * List of dialog types. | 218 * List of dialog types. |
231 * | 219 * |
232 * Keep this in sync with FileManagerDialog::GetDialogTypeAsString, except | 220 * Keep this in sync with FileManagerDialog::GetDialogTypeAsString, except |
(...skipping 14 matching lines...) Expand all Loading... | |
247 type == FileManager.DialogType.SELECT_SAVEAS_FILE || | 235 type == FileManager.DialogType.SELECT_SAVEAS_FILE || |
248 type == FileManager.DialogType.SELECT_OPEN_FILE || | 236 type == FileManager.DialogType.SELECT_OPEN_FILE || |
249 type == FileManager.DialogType.SELECT_OPEN_MULTI_FILE; | 237 type == FileManager.DialogType.SELECT_OPEN_MULTI_FILE; |
250 }; | 238 }; |
251 | 239 |
252 FileManager.ListType = { | 240 FileManager.ListType = { |
253 DETAIL: 'detail', | 241 DETAIL: 'detail', |
254 THUMBNAIL: 'thumb' | 242 THUMBNAIL: 'thumb' |
255 }; | 243 }; |
256 | 244 |
245 FileManager.FileWatcher = function(fileManager) { | |
246 FileWatcher.call(this, | |
247 fileManager.filesystem_.root, | |
248 fileManager.directoryModel_, | |
249 fileManager.volumeManager_); | |
250 this.metadataCache_ = fileManager.metadataCache_; | |
251 | |
252 this.filesystemChanngeHandler_ = | |
dgozman
2012/05/16 14:31:46
typo here and below: chaNNge
| |
253 fileManager.updateMetadataInUI_.bind(fileManager, 'filesystem'); | |
254 this.gdataChanngeHandler_ = | |
255 fileManager.updateMetadataInUI_.bind(fileManager, 'gdata'); | |
256 | |
257 this.filesystemObserverId_ = null; | |
258 this.gdataObserverId_ = null; | |
259 }; | |
260 | |
261 FileManager.FileWatcher.prototype.__proto__ = FileWatcher.prototype; | |
262 | |
263 FileManager.FileWatcher.prototype.changeWatchedEntry = function(entry) { | |
264 FileWatcher.prototype.changeWatchedEntry.call(this, entry); | |
265 | |
266 if (this.filesystemObserverId_) | |
267 this.metadataCache_.removeObserver(this.filesystemObserverId_); | |
268 if (this.gdataObserverId_) | |
269 this.metadataCache_.removeObserver(this.gdataObserverId_); | |
270 this.filesystemObserverId_ = null; | |
271 this.gdataObserverId_ = null; | |
272 if (!entry) | |
273 return; | |
274 | |
275 this.filesystemObserverId_ = this.metadataCache_.addObserver( | |
276 entry, | |
277 MetadataCache.CHILDREN, | |
278 'filesystem', | |
279 this.filesystemChanngeHandler_); | |
280 | |
281 if (DirectoryModel.getRootType(entry.fullPath) == | |
282 DirectoryModel.RootType.GDATA) { | |
283 this.gdataObserverId_ = this.metadataCache_.addObserver( | |
284 entry, | |
285 MetadataCache.CHILDREN, | |
286 'gdata', | |
287 this.gdataChanngeHandler_); | |
288 } | |
289 }; | |
290 | |
257 /** | 291 /** |
258 * Load translated strings. | 292 * Load translated strings. |
259 */ | 293 */ |
260 FileManager.initStrings = function(callback) { | 294 FileManager.initStrings = function(callback) { |
261 chrome.fileBrowserPrivate.getStrings(function(strings) { | 295 chrome.fileBrowserPrivate.getStrings(function(strings) { |
262 localStrings = new LocalStrings(strings); | 296 localStrings = new LocalStrings(strings); |
263 if (callback) | 297 if (callback) |
264 callback(); | 298 callback(); |
265 }); | 299 }); |
266 }; | 300 }; |
(...skipping 10 matching lines...) Expand all Loading... | |
277 util.UNITS = [str('SIZE_KB'), | 311 util.UNITS = [str('SIZE_KB'), |
278 str('SIZE_MB'), | 312 str('SIZE_MB'), |
279 str('SIZE_GB'), | 313 str('SIZE_GB'), |
280 str('SIZE_TB'), | 314 str('SIZE_TB'), |
281 str('SIZE_PB')]; | 315 str('SIZE_PB')]; |
282 | 316 |
283 metrics.startInterval('Load.FileSystem'); | 317 metrics.startInterval('Load.FileSystem'); |
284 | 318 |
285 var self = this; | 319 var self = this; |
286 | 320 |
287 // The list of active mount points to distinct them from other directories. | |
288 chrome.fileBrowserPrivate.getMountPoints(function(mountPoints) { | |
289 self.setMountPoints_(mountPoints); | |
290 onDone(); | |
291 }); | |
292 | |
293 function onDone() { | |
294 if (self.mountPoints_ && self.filesystem_) | |
295 self.init_(); | |
296 } | |
297 | |
298 chrome.fileBrowserPrivate.requestLocalFileSystem(function(filesystem) { | 321 chrome.fileBrowserPrivate.requestLocalFileSystem(function(filesystem) { |
299 metrics.recordInterval('Load.FileSystem'); | 322 metrics.recordInterval('Load.FileSystem'); |
300 self.filesystem_ = filesystem; | 323 self.filesystem_ = filesystem; |
301 onDone(); | 324 self.init_(); |
302 }); | 325 }); |
303 }; | 326 }; |
304 | 327 |
305 FileManager.prototype.setMountPoints_ = function(mountPoints) { | |
306 this.mountPoints_ = mountPoints; | |
307 // Add gdata mount info if present. | |
308 if (this.gdataMounted_) | |
309 this.mountPoints_.push(this.gdataMountInfo_); | |
310 }; | |
311 | |
312 /** | 328 /** |
313 * Continue initializing the file manager after resolving roots. | 329 * Continue initializing the file manager after resolving roots. |
314 */ | 330 */ |
315 FileManager.prototype.init_ = function() { | 331 FileManager.prototype.init_ = function() { |
316 metrics.startInterval('Load.DOM'); | 332 metrics.startInterval('Load.DOM'); |
317 this.initCommands_(); | 333 this.initCommands_(); |
318 | 334 |
319 this.metadataCache_ = MetadataCache.createFull(); | 335 this.metadataCache_ = MetadataCache.createFull(); |
320 | 336 |
321 this.shortDateFormatter_ = | 337 this.shortDateFormatter_ = |
(...skipping 11 matching lines...) Expand all Loading... | |
333 | 349 |
334 this.table_.startBatchUpdates(); | 350 this.table_.startBatchUpdates(); |
335 this.grid_.startBatchUpdates(); | 351 this.grid_.startBatchUpdates(); |
336 | 352 |
337 this.initFileList_(); | 353 this.initFileList_(); |
338 this.initDialogs_(); | 354 this.initDialogs_(); |
339 | 355 |
340 window.addEventListener('popstate', this.onPopState_.bind(this)); | 356 window.addEventListener('popstate', this.onPopState_.bind(this)); |
341 window.addEventListener('unload', this.onUnload_.bind(this)); | 357 window.addEventListener('unload', this.onUnload_.bind(this)); |
342 | 358 |
343 this.directoryModel_.addEventListener('directory-changed', | 359 var dm = this.directoryModel_; |
360 dm.addEventListener('directory-changed', | |
344 this.onDirectoryChanged_.bind(this)); | 361 this.onDirectoryChanged_.bind(this)); |
345 var self = this; | 362 var self = this; |
346 this.directoryModel_.addEventListener('begin-update-files', function() { | 363 dm.addEventListener('begin-update-files', function() { |
347 self.currentList_.startBatchUpdates(); | 364 self.currentList_.startBatchUpdates(); |
348 }); | 365 }); |
349 this.directoryModel_.addEventListener('end-update-files', function() { | 366 dm.addEventListener('end-update-files', function() { |
350 self.restoreItemBeingRenamed_(); | 367 self.restoreItemBeingRenamed_(); |
351 self.currentList_.endBatchUpdates(); | 368 self.currentList_.endBatchUpdates(); |
352 }); | 369 }); |
353 this.directoryModel_.addEventListener('scan-started', | 370 dm.addEventListener('scan-started', this.showSpinnerLater_.bind(this)); |
354 this.showSpinnerLater_.bind(this)); | 371 dm.addEventListener('scan-completed', this.showSpinner_.bind(this, false)); |
355 this.directoryModel_.addEventListener('scan-completed', | 372 dm.addEventListener('scan-completed', |
356 this.showSpinner_.bind(this, false)); | 373 this.refreshCurrentDirectoryMetadata_.bind(this)); |
357 this.directoryModel_.addEventListener('scan-completed', | 374 dm.addEventListener('rescan-completed', |
358 this.refreshCurrentDirectoryMetadata_.bind(this)); | 375 this.refreshCurrentDirectoryMetadata_.bind(this)); |
359 this.directoryModel_.addEventListener('rescan-completed', | |
360 this.refreshCurrentDirectoryMetadata_.bind(this)); | |
361 this.addEventListener('selection-summarized', | 376 this.addEventListener('selection-summarized', |
362 this.onSelectionSummarized_.bind(this)); | 377 this.onSelectionSummarized_.bind(this)); |
363 | 378 |
364 // The list of archives requested to mount. We will show contents once | 379 this.setupCurrentDirectory_(true /* page loading */); |
365 // archive is mounted, but only for mounts from within this filebrowser tab. | |
366 this.mountRequests_ = []; | |
367 this.unmountRequests_ = []; | |
368 chrome.fileBrowserPrivate.onMountCompleted.addListener( | |
369 this.onMountCompleted_.bind(this)); | |
370 | |
371 chrome.fileBrowserPrivate.onFileChanged.addListener( | |
372 this.onFileChanged_.bind(this)); | |
373 | |
374 this.networkConnectionState_ = null; | 380 this.networkConnectionState_ = null; |
375 var queryNetworkConnectionState = function() { | 381 var queryNetworkConnectionState = function() { |
376 chrome.fileBrowserPrivate.getNetworkConnectionState( | 382 chrome.fileBrowserPrivate.getNetworkConnectionState( |
377 this.onNetworkConnectionChanged_.bind(this)); | 383 this.onNetworkConnectionChanged_.bind(this)); |
378 }.bind(this); | 384 }.bind(this); |
379 queryNetworkConnectionState(); | 385 queryNetworkConnectionState(); |
380 chrome.fileBrowserPrivate.onNetworkConnectionChanged. | 386 chrome.fileBrowserPrivate.onNetworkConnectionChanged. |
381 addListener(queryNetworkConnectionState); | 387 addListener(queryNetworkConnectionState); |
382 | 388 |
383 var invokeHandler = !this.params_.selectOnly; | |
384 if (this.isStartingOnGData_()) { | |
385 // We are opening on a GData path. Mount GData and show | |
386 // "Loading Google Docs" message until the directory content loads. | |
387 this.dialogContainer_.setAttribute('unmounted', true); | |
388 this.initGData_(true /* dirChanged */); | |
389 // This is a one-time handler (will be nulled out on the first call). | |
390 this.setupCurrentDirectoryPostponed_ = function(event) { | |
391 this.directoryModel_.removeEventListener('directory-changed', | |
392 this.setupCurrentDirectoryPostponed_); | |
393 this.setupCurrentDirectoryPostponed_ = null; | |
394 if (event) // If called as an event handler just exit silently. | |
395 return; | |
396 this.setupCurrentDirectory_( | |
397 invokeHandler, false /* blankWhileOpeningAFile */); | |
398 }.bind(this); | |
399 this.directoryModel_.addEventListener('directory-changed', | |
400 this.setupCurrentDirectoryPostponed_); | |
401 } else { | |
402 this.setupCurrentDirectory_( | |
403 invokeHandler, true /* blankWhileOpeningAFile */); | |
404 } | |
405 | |
406 this.summarizeSelection_(); | 389 this.summarizeSelection_(); |
407 | 390 |
408 var sortField = | 391 var sortField = |
409 window.localStorage['sort-field-' + this.dialogType_] || | 392 window.localStorage['sort-field-' + this.dialogType_] || |
410 'modificationTime'; | 393 'modificationTime'; |
411 var sortDirection = | 394 var sortDirection = |
412 window.localStorage['sort-direction-' + this.dialogType_] || 'desc'; | 395 window.localStorage['sort-direction-' + this.dialogType_] || 'desc'; |
413 this.directoryModel_.sortFileList(sortField, sortDirection); | 396 this.directoryModel_.sortFileList(sortField, sortDirection); |
414 | 397 |
415 this.refocus(); | 398 this.refocus(); |
(...skipping 93 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
509 this.filenameInput_ = this.dialogDom_.querySelector('.filename-input'); | 492 this.filenameInput_ = this.dialogDom_.querySelector('.filename-input'); |
510 this.taskItems_ = this.dialogDom_.querySelector('#tasks'); | 493 this.taskItems_ = this.dialogDom_.querySelector('#tasks'); |
511 this.okButton_ = this.dialogDom_.querySelector('.ok'); | 494 this.okButton_ = this.dialogDom_.querySelector('.ok'); |
512 this.cancelButton_ = this.dialogDom_.querySelector('.cancel'); | 495 this.cancelButton_ = this.dialogDom_.querySelector('.cancel'); |
513 this.deleteButton_ = this.dialogDom_.querySelector('#delete-button'); | 496 this.deleteButton_ = this.dialogDom_.querySelector('#delete-button'); |
514 this.table_ = this.dialogDom_.querySelector('.detail-table'); | 497 this.table_ = this.dialogDom_.querySelector('.detail-table'); |
515 this.grid_ = this.dialogDom_.querySelector('.thumbnail-grid'); | 498 this.grid_ = this.dialogDom_.querySelector('.thumbnail-grid'); |
516 this.spinner_ = this.dialogDom_.querySelector('.spinner'); | 499 this.spinner_ = this.dialogDom_.querySelector('.spinner'); |
517 this.showSpinner_(false); | 500 this.showSpinner_(false); |
518 this.butter_ = this.dialogDom_.querySelector('.butter-bar'); | 501 this.butter_ = this.dialogDom_.querySelector('.butter-bar'); |
519 this.unmountedPanel_ = this.dialogDom_.querySelector('.unmounted-panel'); | |
520 | 502 |
521 cr.ui.decorate('#gdata-settings', cr.ui.MenuButton); | 503 cr.ui.decorate('#gdata-settings', cr.ui.MenuButton); |
522 cr.ui.Table.decorate(this.table_); | 504 cr.ui.Table.decorate(this.table_); |
523 cr.ui.Grid.decorate(this.grid_); | 505 cr.ui.Grid.decorate(this.grid_); |
524 | 506 |
525 this.document_.addEventListener('keydown', this.onKeyDown_.bind(this)); | 507 this.document_.addEventListener('keydown', this.onKeyDown_.bind(this)); |
526 // Disable the default browser context menu. | 508 // Disable the default browser context menu. |
527 this.document_.addEventListener('contextmenu', | 509 this.document_.addEventListener('contextmenu', |
528 function(e) { e.preventDefault() }); | 510 function(e) { e.preventDefault() }); |
529 | 511 |
(...skipping 29 matching lines...) Expand all Loading... | |
559 | 541 |
560 this.decorateSplitter( | 542 this.decorateSplitter( |
561 this.dialogDom_.querySelector('div.sidebar-splitter')); | 543 this.dialogDom_.querySelector('div.sidebar-splitter')); |
562 | 544 |
563 this.dialogContainer_ = this.dialogDom_.querySelector('.dialog-container'); | 545 this.dialogContainer_ = this.dialogDom_.querySelector('.dialog-container'); |
564 this.dialogDom_.querySelector('#detail-view').addEventListener( | 546 this.dialogDom_.querySelector('#detail-view').addEventListener( |
565 'click', this.onDetailViewButtonClick_.bind(this)); | 547 'click', this.onDetailViewButtonClick_.bind(this)); |
566 this.dialogDom_.querySelector('#thumbnail-view').addEventListener( | 548 this.dialogDom_.querySelector('#thumbnail-view').addEventListener( |
567 'click', this.onThumbnailViewButtonClick_.bind(this)); | 549 'click', this.onThumbnailViewButtonClick_.bind(this)); |
568 | 550 |
569 // When we show the page for the first time we want to avoid | |
570 // the GDrive settings button animation, so we set the attribute ASAP. | |
571 if (this.isStartingOnGData_()) | |
572 this.dialogContainer_.setAttribute('gdata', true); | |
573 | |
574 this.syncButton = this.dialogDom_.querySelector('#gdata-sync-settings'); | 551 this.syncButton = this.dialogDom_.querySelector('#gdata-sync-settings'); |
575 this.syncButton.addEventListener('click', this.onGDataPrefClick_.bind( | 552 this.syncButton.addEventListener('click', this.onGDataPrefClick_.bind( |
576 this, 'cellularDisabled', false /* not inverted */)); | 553 this, 'cellularDisabled', false /* not inverted */)); |
577 | 554 |
578 this.hostedButton = this.dialogDom_.querySelector('#gdata-hosted-settings'); | 555 this.hostedButton = this.dialogDom_.querySelector('#gdata-hosted-settings'); |
579 this.hostedButton.addEventListener('click', this.onGDataPrefClick_.bind( | 556 this.hostedButton.addEventListener('click', this.onGDataPrefClick_.bind( |
580 this, 'hostedFilesDisabled', true /* inverted */)); | 557 this, 'hostedFilesDisabled', true /* inverted */)); |
581 | 558 |
582 this.gdataPreferences_ = null; | 559 this.gdataPreferences_ = null; |
583 var queryGDataPreferences = function() { | 560 var queryGDataPreferences = function() { |
584 chrome.fileBrowserPrivate.getGDataPreferences( | 561 chrome.fileBrowserPrivate.getGDataPreferences( |
585 this.onGDataPreferencesChanged_.bind(this)); | 562 this.onGDataPreferencesChanged_.bind(this)); |
586 }.bind(this); | 563 }.bind(this); |
587 queryGDataPreferences(); | 564 queryGDataPreferences(); |
588 chrome.fileBrowserPrivate.onGDataPreferencesChanged. | 565 // TODO(serya): uncomment |
589 addListener(queryGDataPreferences); | 566 //chrome.fileBrowserPrivate.onGDataPreferencesChanged. |
567 // addListener(queryGDataPreferences); | |
590 | 568 |
591 cr.ui.ComboButton.decorate(this.taskItems_); | 569 cr.ui.ComboButton.decorate(this.taskItems_); |
592 this.taskItems_.addEventListener('select', | 570 this.taskItems_.addEventListener('select', |
593 this.onTaskItemClicked_.bind(this)); | 571 this.onTaskItemClicked_.bind(this)); |
594 | 572 |
595 this.dialogDom_.ownerDocument.defaultView.addEventListener( | 573 this.dialogDom_.ownerDocument.defaultView.addEventListener( |
596 'resize', this.onResize_.bind(this)); | 574 'resize', this.onResize_.bind(this)); |
597 | 575 |
598 if (str('ASH') == '1') | 576 if (str('ASH') == '1') |
599 this.dialogDom_.setAttribute('ash', 'true'); | 577 this.dialogDom_.setAttribute('ash', 'true'); |
(...skipping 19 matching lines...) Expand all Loading... | |
619 | 597 |
620 var sigleSelection = | 598 var sigleSelection = |
621 this.dialogType_ == FileManager.DialogType.SELECT_OPEN_FILE || | 599 this.dialogType_ == FileManager.DialogType.SELECT_OPEN_FILE || |
622 this.dialogType_ == FileManager.DialogType.SELECT_FOLDER || | 600 this.dialogType_ == FileManager.DialogType.SELECT_FOLDER || |
623 this.dialogType_ == FileManager.DialogType.SELECT_SAVEAS_FILE; | 601 this.dialogType_ == FileManager.DialogType.SELECT_SAVEAS_FILE; |
624 | 602 |
625 this.directoryModel_ = new DirectoryModel( | 603 this.directoryModel_ = new DirectoryModel( |
626 this.filesystem_.root, | 604 this.filesystem_.root, |
627 sigleSelection, | 605 sigleSelection, |
628 FileManager.isGDataEnabled(), | 606 FileManager.isGDataEnabled(), |
629 this.metadataCache_); | 607 this.metadataCache_, |
608 this.volumeManager_); | |
609 | |
610 this.fileWatcher_ = new FileManager.FileWatcher(this); | |
611 this.fileWatcher_.start(); | |
630 | 612 |
631 if (FileManager.isGDataEnabled()) | 613 if (FileManager.isGDataEnabled()) |
632 this.initGDataWelcomeBanners_(); | 614 this.initGDataWelcomeBanners_(); |
633 | 615 |
634 var dataModel = this.directoryModel_.getFileList(); | 616 var dataModel = this.directoryModel_.getFileList(); |
635 var collator = this.collator_; | 617 var collator = this.collator_; |
636 // TODO(dgozman): refactor comparison functions together with | 618 // TODO(dgozman): refactor comparison functions together with |
637 // render/update/display. | 619 // render/update/display. |
638 dataModel.setCompareFunction('name', function(a, b) { | 620 dataModel.setCompareFunction('name', function(a, b) { |
639 return collator.compare(a.name, b.name); | 621 return collator.compare(a.name, b.name); |
(...skipping 17 matching lines...) Expand all Loading... | |
657 this.initGrid_(); | 639 this.initGrid_(); |
658 this.initRootsList_(); | 640 this.initRootsList_(); |
659 | 641 |
660 var listType = FileManager.ListType.DETAIL; | 642 var listType = FileManager.ListType.DETAIL; |
661 if (FileManager.DialogType.isModal(this.dialogType_)) | 643 if (FileManager.DialogType.isModal(this.dialogType_)) |
662 listType = window.localStorage['listType-' + this.dialogType_] || | 644 listType = window.localStorage['listType-' + this.dialogType_] || |
663 FileManager.ListType.DETAIL; | 645 FileManager.ListType.DETAIL; |
664 this.setListType(listType); | 646 this.setListType(listType); |
665 | 647 |
666 this.textSearchState_ = {text: '', date: new Date()}; | 648 this.textSearchState_ = {text: '', date: new Date()}; |
649 | |
650 this.volumeManager_.addEventListener('gdata-status-changed', | |
651 this.updateGDataStatus_.bind(this, 'status-change')); | |
667 }; | 652 }; |
668 | 653 |
669 FileManager.prototype.initRootsList_ = function() { | 654 FileManager.prototype.initRootsList_ = function() { |
670 this.rootsList_ = this.dialogDom_.querySelector('#roots-list'); | 655 this.rootsList_ = this.dialogDom_.querySelector('#roots-list'); |
671 cr.ui.List.decorate(this.rootsList_); | 656 cr.ui.List.decorate(this.rootsList_); |
672 this.rootsList_.startBatchUpdates(); | |
673 | 657 |
674 var self = this; | 658 var self = this; |
675 this.rootsList_.itemConstructor = function(entry) { | 659 this.rootsList_.itemConstructor = function(entry) { |
676 return self.renderRoot_(entry); | 660 return self.renderRoot_(entry); |
677 }; | 661 }; |
678 | 662 |
679 this.rootsList_.selectionModel = | 663 this.rootsList_.selectionModel = |
680 this.directoryModel_.getRootsListSelectionModel(); | 664 this.directoryModel_.getRootsListSelectionModel(); |
681 | 665 |
682 // TODO(dgozman): add "Add a drive" item. | 666 // TODO(dgozman): add "Add a drive" item. |
683 this.rootsList_.dataModel = this.directoryModel_.getRootsList(); | 667 this.rootsList_.dataModel = this.directoryModel_.getRootsList(); |
684 this.directoryModel_.updateRoots(function() { | |
685 self.rootsList_.endBatchUpdates(); | |
686 }, false); | |
687 }; | 668 }; |
688 | 669 |
689 /** | 670 FileManager.prototype.updateGDataStatus_ = function(reason) { |
690 * @param {boolean} dirChanged True if we just changed to GData directory, | 671 var node = this.dialogContainer_; |
691 * False if "Retry" button clicked. | 672 if (this.isOnGData()) { |
692 */ | 673 var status = this.volumeManager_.getGDataStatus(); |
693 FileManager.prototype.initGData_ = function(dirChanged) { | 674 if (status == VolumeManager.GDataStatus.MOUNTING || |
694 this.initGDataUnmountedPanel_(); | 675 status == VolumeManager.GDataStatus.ERROR) { |
695 | 676 this.initGDataUnmountedPanel_(); |
696 this.unmountedPanel_.removeAttribute('error'); | 677 var retryButton = this.document_.querySelector( |
697 if (dirChanged) { | 678 '#unmounted-panel button.retry'); |
698 // When changing to GData directory we want to see a clear panel. | 679 if (status == VolumeManager.GDataStatus.ERROR) |
699 this.unmountedPanel_.removeAttribute('retry'); | 680 retryButton.hidden = false; |
700 if (this.gdataLoadingTimer_) { // Show immediately if already loading. | 681 else if (reason == 'directory-change') |
701 this.unmountedPanel_.setAttribute('loading', true); | 682 retryButton.hidden = true; |
702 } else { | |
703 this.unmountedPanel_.removeAttribute('loading'); | |
704 setTimeout(function() { | |
705 if (this.gdataLoadingTimer_) { // Still loading. | |
706 this.unmountedPanel_.setAttribute('loading', true); | |
707 } | |
708 }.bind(this), 500); | |
709 } | 683 } |
684 node.setAttribute('gdata', status); | |
710 } else { | 685 } else { |
711 // When retrying we do not hide "Retry" and "Learn more". | 686 node.removeAttribute('gdata'); |
712 this.unmountedPanel_.setAttribute('loading', true); | |
713 } | 687 } |
714 | 688 console.log('gdata status: ', node.getAttribute('gdata')); |
715 // If the user changed to another directory and then back to GData we | |
716 // re-enter this method while the timer is still active. In this case | |
717 // we only update the UI but do not request the mount again. | |
718 if (this.gdataLoadingTimer_) | |
719 return; | |
720 | |
721 metrics.startInterval('Load.GData'); | |
722 chrome.fileBrowserPrivate.addMount('', 'gdata', {}, | |
723 function(sourcePath) {}); | |
724 | |
725 // This timer could fire before the mount succeeds. We will silently | |
726 // replace the error message with the correct directory contents. | |
727 this.gdataLoadingTimer_ = setTimeout(function() { | |
728 this.gdataLoadingTimer_ = null; | |
729 this.onGDataUnreachable_('GData load timeout'); | |
730 }.bind(this), | |
731 15 * 60 * 1000); | |
732 }; | |
733 | |
734 FileManager.prototype.clearGDataLoadingTimer_ = function(message) { | |
735 if (this.gdataLoadingTimer_) { | |
736 clearTimeout(this.gdataLoadingTimer_); | |
737 this.gdataLoadingTimer_ = null; | |
738 } | |
739 }; | |
740 | |
741 FileManager.prototype.onGDataUnreachable_ = function(message) { | |
742 console.warn(message); | |
743 if (this.isOnGData()) { | |
744 this.unmountedPanel_.removeAttribute('loading'); | |
745 this.unmountedPanel_.setAttribute('error', true); | |
746 this.unmountedPanel_.setAttribute('retry', true); | |
747 } | |
748 }; | 689 }; |
749 | 690 |
750 FileManager.prototype.initGDataUnmountedPanel_ = function() { | 691 FileManager.prototype.initGDataUnmountedPanel_ = function() { |
751 if (this.unmountedPanel_.firstElementChild) | 692 var panel = this.document_.querySelector('#unmounted-panel'); |
693 if (panel.firstElementChild) | |
752 return; | 694 return; |
753 | 695 |
754 var loading = this.document_.createElement('div'); | 696 function create(tag, className) { |
755 loading.className = 'gdata loading'; | 697 var div = panel.ownerDocument.createElement(tag); |
756 loading.textContent = str('GDATA_LOADING'); | 698 div.className = className; |
757 this.unmountedPanel_.appendChild(loading); | 699 return div; |
700 } | |
758 | 701 |
759 var spinnerBox = this.document_.createElement('div'); | 702 function append(tag, className, opt_textContent) { |
760 spinnerBox.className = 'spinner-box'; | 703 var div = create(tag, className); |
761 loading.appendChild(spinnerBox); | 704 div.textContent = opt_textContent || ''; |
705 panel.appendChild(div); | |
706 return div; | |
707 } | |
762 | 708 |
763 var spinner = this.document_.createElement('div'); | 709 append('div', 'gdata loading', str('GDATA_LOADING')); |
764 spinner.className = 'spinner'; | 710 append('div', 'spinner-box').appendChild(create('div', 'spinner')); |
765 spinnerBox.appendChild(spinner); | 711 var progress = append('div', 'gdata progress'); |
766 | |
767 var progress = this.document_.createElement('div'); | |
768 progress.className = 'gdata progress'; | |
769 this.unmountedPanel_.appendChild(progress); | |
770 | |
771 chrome.fileBrowserPrivate.onDocumentFeedFetched.addListener( | 712 chrome.fileBrowserPrivate.onDocumentFeedFetched.addListener( |
772 function(fileCount) { | 713 function(fileCount) { |
773 progress.textContent = strf('GDATA_LOADING_PROGRESS', fileCount); | 714 progress.textContent = strf('GDATA_LOADING_PROGRESS', fileCount); |
774 }); | 715 }); |
775 | 716 |
776 var error = this.document_.createElement('div'); | 717 append('div', 'gdata error', strf('GDATA_CANNOT_REACH', |
777 error.className = 'gdata error'; | 718 str('GDATA_PRODUCT_NAME'))); |
778 error.textContent = strf('GDATA_CANNOT_REACH', str('GDATA_PRODUCT_NAME')); | |
779 this.unmountedPanel_.appendChild(error); | |
780 | 719 |
781 var retry = this.document_.createElement('button'); | 720 append('button', 'gdata retry', str('GDATA_RETRY')); |
782 retry.className = 'gdata retry'; | 721 // onclick = this.initGData_.bind(this, false); |
783 retry.textContent = str('GDATA_RETRY'); | |
784 retry.onclick = this.initGData_.bind(this, false /* retry */); | |
785 this.unmountedPanel_.appendChild(retry); | |
786 | 722 |
787 var learnMore = this.document_.createElement('div'); | 723 append('div', 'gdata learn-more plain-link', str('GDATA_LEARN_MORE')). |
788 learnMore.className = 'gdata learn-more plain-link'; | 724 onclick = this.onExternalLinkClick_.bind(this, |
789 learnMore.textContent = str('GDATA_LEARN_MORE'); | 725 GOOGLE_DRIVE_ERROR_HELP_URL); |
790 learnMore.addEventListener('click', | |
791 this.onExternalLinkClick_.bind(this, GOOGLE_DRIVE_ERROR_HELP_URL)); | |
792 this.unmountedPanel_.appendChild(learnMore); | |
793 }; | 726 }; |
794 | 727 |
795 FileManager.prototype.onDataModelSplice_ = function(event) { | 728 FileManager.prototype.onDataModelSplice_ = function(event) { |
796 var checkbox = this.document_.querySelector('#select-all-checkbox'); | 729 var checkbox = this.document_.querySelector('#select-all-checkbox'); |
797 if (checkbox) | 730 if (checkbox) |
798 this.updateSelectAllCheckboxState_(checkbox); | 731 this.updateSelectAllCheckboxState_(checkbox); |
799 }; | 732 }; |
800 | 733 |
801 FileManager.prototype.onDataModelPermuted_ = function(event) { | 734 FileManager.prototype.onDataModelPermuted_ = function(event) { |
802 var sortStatus = this.directoryModel_.getFileList().sortStatus; | 735 var sortStatus = this.directoryModel_.getFileList().sortStatus; |
(...skipping 530 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
1333 | 1266 |
1334 case 'delete': | 1267 case 'delete': |
1335 this.deleteEntries(this.selection.entries); | 1268 this.deleteEntries(this.selection.entries); |
1336 return; | 1269 return; |
1337 | 1270 |
1338 case 'newfolder': | 1271 case 'newfolder': |
1339 this.onNewFolderCommand_(event); | 1272 this.onNewFolderCommand_(event); |
1340 return; | 1273 return; |
1341 | 1274 |
1342 case 'unmount': | 1275 case 'unmount': |
1343 this.unmountVolume_(this.directoryModel_.getCurrentRootDirEntry()); | 1276 this.unmountVolume_( |
1277 this.directoryModel_.getCurrentRootDirEntry()); | |
1344 return; | 1278 return; |
1345 | 1279 |
1346 case 'format': | 1280 case 'format': |
1347 var entry = this.directoryModel_.getCurrentRootDirEntry(); | 1281 var entry = this.directoryModel_.getCurrentRootDirEntry(); |
1348 this.confirm.show(str('FORMATTING_WARNING'), function() { | 1282 this.confirm.show(str('FORMATTING_WARNING'), function() { |
1349 chrome.fileBrowserPrivate.formatDevice(entry.toURL()); | 1283 chrome.fileBrowserPrivate.formatDevice(entry.toURL()); |
1350 }); | 1284 }); |
1351 | 1285 |
1352 return; | 1286 return; |
1353 } | 1287 } |
1354 }; | 1288 }; |
1355 | 1289 |
1356 /** | 1290 /** |
1357 * Respond to the back and forward buttons. | 1291 * Respond to the back and forward buttons. |
1358 */ | 1292 */ |
1359 FileManager.prototype.onPopState_ = function(event) { | 1293 FileManager.prototype.onPopState_ = function(event) { |
1360 this.closeFilePopup_(); | 1294 this.closeFilePopup_(); |
1361 // Nothing left to do if the current directory is not changing. This happens | 1295 // Nothing left to do if the current directory is not changing. This happens |
1362 // if we are exiting the Gallery. | 1296 // if we are exiting the Gallery. |
1363 if (this.getPathFromUrlOrParams_() == | 1297 if (this.getPathFromUrlOrParams_() == |
1364 this.directoryModel_.getCurrentDirEntry().fullPath) | 1298 this.directoryModel_.getCurrentDirEntry().fullPath) |
1365 return; | 1299 return; |
1366 this.setupCurrentDirectory_(true /* invokeHandler */); | 1300 this.setupCurrentDirectory_(false /* page loading */); |
1367 }; | 1301 }; |
1368 | 1302 |
1369 FileManager.prototype.requestResize_ = function(timeout) { | 1303 FileManager.prototype.requestResize_ = function(timeout) { |
1370 setTimeout(this.onResize_.bind(this), timeout || 0); | 1304 setTimeout(this.onResize_.bind(this), timeout || 0); |
1371 }; | 1305 }; |
1372 | 1306 |
1373 /** | 1307 /** |
1374 * Resize details and thumb views to fit the new window size. | 1308 * Resize details and thumb views to fit the new window size. |
1375 */ | 1309 */ |
1376 FileManager.prototype.onResize_ = function() { | 1310 FileManager.prototype.onResize_ = function() { |
(...skipping 33 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
1410 }; | 1344 }; |
1411 | 1345 |
1412 /** | 1346 /** |
1413 * Restores current directory and may be a selected item after page load (or | 1347 * Restores current directory and may be a selected item after page load (or |
1414 * reload) or popping a state (after click on back/forward). If location.hash | 1348 * reload) or popping a state (after click on back/forward). If location.hash |
1415 * is present it means that the user has navigated somewhere and that place | 1349 * is present it means that the user has navigated somewhere and that place |
1416 * will be restored. defaultPath primarily is used with save/open dialogs. | 1350 * will be restored. defaultPath primarily is used with save/open dialogs. |
1417 * Default path may also contain a file name. Freshly opened file manager | 1351 * Default path may also contain a file name. Freshly opened file manager |
1418 * window has neither. | 1352 * window has neither. |
1419 * | 1353 * |
1420 * @param {boolean} invokeHandler Whether to invoke the default handler on | 1354 * @param {boolean} pageLoading True if pase is loading, |
1421 * the selected file. | 1355 false if popping state. |
1422 * @param {boolean} opt_blankWhileOpeningAFile Whether to show fade over | |
1423 * the file manager. | |
1424 */ | 1356 */ |
1425 FileManager.prototype.setupCurrentDirectory_ = | 1357 FileManager.prototype.setupCurrentDirectory_ = function(pageLoading) { |
1426 function(invokeHandler, opt_blankWhileOpeningAFile) { | |
1427 var path = this.getPathFromUrlOrParams_(); | 1358 var path = this.getPathFromUrlOrParams_(); |
1428 | 1359 |
1429 if (!path) { | 1360 if (!path) { |
1430 this.directoryModel_.setupDefaultPath(); | 1361 this.directoryModel_.setupDefaultPath(); |
1431 return; | 1362 return; |
1432 } | 1363 } |
1433 | 1364 |
1365 if (DirectoryModel.getRootType(path) == DirectoryModel.RootType.GDATA) { | |
1366 if (!FileManager.isGDataEnabled()) { | |
1367 this.directoryModel_.setupDefaultPath(); | |
1368 return; | |
1369 } | |
1370 | |
1371 // Reflect immediatelly in the UI we are on GData and display | |
1372 // mounting UI. | |
1373 this.directoryModel_.setupPath('/' + DirectoryModel.GDATA_DIRECTORY); | |
1374 | |
1375 var tracker = this.directoryModel_.createDirectoryChangeTracker(); | |
1376 // Expected finish of setupPath to GData. | |
1377 tracker.exceptInitialChange = true; | |
1378 tracker.start(); | |
1379 this.volumeManager_.mountGData(function() { | |
1380 tracker.stop(); | |
1381 if (!tracker.hasChanged) { | |
1382 this.finishSetupCurrentDirectory_(path, pageLoading); | |
1383 } | |
1384 }.bind(this), function(error) { | |
1385 tracker.stop(); | |
1386 }); | |
1387 } else { | |
1388 this.finishSetupCurrentDirectory_(path, pageLoading); | |
1389 } | |
1390 }; | |
1391 | |
1392 FileManager.prototype.finishSetupCurrentDirectory_ = function( | |
1393 path, pageLoading) { | |
1394 var invokeHandler = pageLoading && !this.params_.selectOnly; | |
1434 // In the FULL_PAGE mode if the hash path points to a file we might have | 1395 // In the FULL_PAGE mode if the hash path points to a file we might have |
1435 // to invoke a task after selecting it. | 1396 // to invoke a task after selecting it. |
1436 // If the file path is in params_ we only want to select the file. | 1397 // If the file path is in params_ we only want to select the file. |
1437 if (invokeHandler && location.hash && | 1398 if (this.dialogType_ == FileManager.DialogType.FULL_PAGE && |
1438 this.dialogType_ == FileManager.DialogType.FULL_PAGE) { | 1399 location.hash && invokeHandler) { |
1439 // To prevent the file list flickering for a moment before the action | 1400 // To prevent the file list flickering for a moment before the action |
1440 // is executed we hide it under a white div. | 1401 // is executed we hide it under a white div. |
1441 var shade; | 1402 var shade; |
1442 if (opt_blankWhileOpeningAFile) { | 1403 if (pageLoading) { |
1443 shade = this.document_.createElement('div'); | 1404 shade = this.document_.createElement('div'); |
1444 shade.className = 'overlay-pane'; | 1405 shade.className = 'overlay-pane'; |
1445 shade.style.backgroundColor = 'white'; | 1406 shade.style.backgroundColor = 'white'; |
1446 this.document_.body.appendChild(shade); | 1407 this.document_.body.appendChild(shade); |
1447 } | 1408 } |
1448 function removeShade() { | 1409 function removeShade() { |
1449 if (shade) | 1410 if (shade) |
1450 shade.parentNode.removeChild(shade); | 1411 shade.parentNode.removeChild(shade); |
1451 } | 1412 } |
1452 | 1413 |
(...skipping 315 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
1768 } | 1729 } |
1769 }; | 1730 }; |
1770 li.addEventListener('mousedown', handleClick); | 1731 li.addEventListener('mousedown', handleClick); |
1771 li.addEventListener(cr.ui.TouchHandler.EventType.TOUCH_START, handleClick); | 1732 li.addEventListener(cr.ui.TouchHandler.EventType.TOUCH_START, handleClick); |
1772 | 1733 |
1773 var rootType = DirectoryModel.getRootType(entry.fullPath); | 1734 var rootType = DirectoryModel.getRootType(entry.fullPath); |
1774 | 1735 |
1775 var div = this.document_.createElement('div'); | 1736 var div = this.document_.createElement('div'); |
1776 div.className = 'root-label'; | 1737 div.className = 'root-label'; |
1777 | 1738 |
1778 var icon = rootType; | |
1779 var deviceNumber = this.getDeviceNumber(entry); | |
1780 | |
1781 if (deviceNumber != undefined) { | |
1782 var mountCondition = this.mountPoints_[deviceNumber].mountCondition; | |
1783 if (mountCondition == 'unknown_filesystem' || | |
1784 mountCondition == 'unsupported_filesystem') | |
1785 icon = 'unreadable'; | |
1786 } | |
1787 | |
1788 div.setAttribute('icon', icon); | |
1789 | |
1790 div.textContent = this.getRootLabel_(entry.fullPath); | 1739 div.textContent = this.getRootLabel_(entry.fullPath); |
1791 li.appendChild(div); | 1740 li.appendChild(div); |
1792 | 1741 |
1793 if (rootType == DirectoryModel.RootType.ARCHIVE || | 1742 if (rootType == DirectoryModel.RootType.ARCHIVE || |
1794 rootType == DirectoryModel.RootType.REMOVABLE) { | 1743 rootType == DirectoryModel.RootType.REMOVABLE) { |
1795 if (entry.unmounting) { | 1744 var eject = this.document_.createElement('div'); |
1796 li.setAttribute('disabled', 'disabled'); | 1745 eject.className = 'root-eject'; |
1797 } else { | 1746 eject.addEventListener('click', function(event) { |
1798 var eject = this.document_.createElement('div'); | 1747 event.stopPropagation(); |
1799 eject.className = 'root-eject'; | 1748 this.unmountVolume_(entry); |
1800 eject.addEventListener('click', function(event) { | 1749 }.bind(this)); |
1801 event.stopPropagation(); | 1750 // Block other mouse handlers. |
1802 this.unmountVolume_(entry); | 1751 eject.addEventListener('mouseup', function(e) { e.stopPropagation() }); |
1803 }.bind(this)); | 1752 eject.addEventListener('mousedown', function(e) { e.stopPropagation() }); |
1804 // Block other mouse handlers. | 1753 li.appendChild(eject); |
1805 eject.addEventListener('mouseup', function(e) { e.stopPropagation() }); | |
1806 eject.addEventListener('mousedown', function(e) { e.stopPropagation() }) ; | |
1807 li.appendChild(eject); | |
1808 | 1754 |
1809 cr.ui.contextMenuHandler.setContextMenu(li, this.rootsContextMenu_); | 1755 cr.ui.contextMenuHandler.setContextMenu(li, this.rootsContextMenu_); |
1810 } | |
1811 } | 1756 } |
1812 | 1757 |
1813 cr.defineProperty(li, 'lead', cr.PropertyKind.BOOL_ATTR); | 1758 cr.defineProperty(li, 'lead', cr.PropertyKind.BOOL_ATTR); |
1814 cr.defineProperty(li, 'selected', cr.PropertyKind.BOOL_ATTR); | 1759 cr.defineProperty(li, 'selected', cr.PropertyKind.BOOL_ATTR); |
1760 | |
1761 var icon = rootType; | |
1762 var mountCondition = this.volumeManager_.getMountCondition(entry.fullPath); | |
1763 | |
1764 if (mountCondition == 'unknown_filesystem' || | |
1765 mountCondition == 'unsupported_filesystem') { | |
1766 icon = 'unreadable'; | |
1767 } | |
1768 div.setAttribute('icon', icon); | |
1769 | |
1815 return li; | 1770 return li; |
1816 }; | 1771 }; |
1817 | 1772 |
1818 /** | 1773 /** |
1819 * Unmounts device. | 1774 * Unmounts device. |
1820 * @param {Entry} entry The entry to unmount. | 1775 * @param {Entry} entry The entry to unmount. |
1821 */ | 1776 */ |
1822 FileManager.prototype.unmountVolume_ = function(entry) { | 1777 FileManager.prototype.unmountVolume_ = function(entry) { |
1823 this.directoryModel_.prepareUnmount(entry.fullPath); | 1778 listItem = this.rootsList_.getListItem(entry); |
1824 this.unmountRequests_.push(entry.fullPath); | 1779 listItem && listItem.setAttribute('disabled'); |
dgozman
2012/05/16 14:31:46
Don't like this syntax.
| |
1825 chrome.fileBrowserPrivate.removeMount(entry.toURL()); | 1780 var self = this; |
1781 this.volumeManager_.unmount(entry.fullPath, function() {}, | |
1782 function(error) { | |
1783 listItem && listItem.removeAttribute('disabled'); | |
1784 self.alert.show(strf('UNMOUNT_FAILED', error.message)); | |
1785 }); | |
1826 }; | 1786 }; |
1827 | 1787 |
1828 FileManager.prototype.updateGDataStyle_ = function( | 1788 FileManager.prototype.updateGDataStyle_ = function( |
1829 listItem, entry, gdata) { | 1789 listItem, entry, gdata) { |
1830 if (!this.isOnGData() || !gdata) | 1790 if (!this.isOnGData() || !gdata) |
1831 return; | 1791 return; |
1832 | 1792 |
1833 if (!entry.isDirectory) { | 1793 if (!entry.isDirectory) { |
1834 if (!gdata.availableOffline) | 1794 if (!gdata.availableOffline) |
1835 listItem.classList.add('dim-offline'); | 1795 listItem.classList.add('dim-offline'); |
(...skipping 357 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
2193 selection.bytes += filesystem.size; | 2153 selection.bytes += filesystem.size; |
2194 } | 2154 } |
2195 } | 2155 } |
2196 | 2156 |
2197 this.dispatchEvent(new cr.Event('selection-summarized')); | 2157 this.dispatchEvent(new cr.Event('selection-summarized')); |
2198 }.bind(this)); | 2158 }.bind(this)); |
2199 | 2159 |
2200 if (this.isOnGData()) { | 2160 if (this.isOnGData()) { |
2201 this.metadataCache_.get(selection.urls, 'gdata', function(props) { | 2161 this.metadataCache_.get(selection.urls, 'gdata', function(props) { |
2202 selection.allGDataFilesPresent = | 2162 selection.allGDataFilesPresent = |
2203 props.filter(function(p) {return !p.availableOffline}).length == 0; | 2163 props.filter(function(p) {return p && !p.availableOffline}).length = = 0; |
dgozman
2012/05/16 14:31:46
- long line
- this actually should be |!(p && p.av
| |
2204 this.updateOkButton_(); | 2164 this.updateOkButton_(); |
2205 }.bind(this)); | 2165 }.bind(this)); |
2206 } | 2166 } |
2207 }; | 2167 }; |
2208 | 2168 |
2209 /** | 2169 /** |
2210 * Check if all the files in the current selection are available. The only | 2170 * Check if all the files in the current selection are available. The only |
2211 * case when files might be not available is when the selection contains | 2171 * case when files might be not available is when the selection contains |
2212 * uncached GData files and the browser is offline. | 2172 * uncached GData files and the browser is offline. |
2213 * @return {boolean} True if all files in the current selection are | 2173 * @return {boolean} True if all files in the current selection are |
(...skipping 411 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
2625 }; | 2585 }; |
2626 | 2586 |
2627 FileManager.prototype.isOffline = function() { | 2587 FileManager.prototype.isOffline = function() { |
2628 return this.networkConnectionState_ && !this.networkConnectionState_.online; | 2588 return this.networkConnectionState_ && !this.networkConnectionState_.online; |
2629 }; | 2589 }; |
2630 | 2590 |
2631 FileManager.prototype.isOnReadonlyDirectory = function() { | 2591 FileManager.prototype.isOnReadonlyDirectory = function() { |
2632 return this.directoryModel_.isReadOnly(); | 2592 return this.directoryModel_.isReadOnly(); |
2633 }; | 2593 }; |
2634 | 2594 |
2635 /** | 2595 // TODO(serya): close tab on unmount. |
2636 * Event handler called when some volume was mounted or unmouted. | 2596 FileManager.prototype.onVolumeUnmounted_ = function(event) { |
2637 */ | 2597 if (event.mountPath == dm.getCurrentRootPath()) { |
2638 FileManager.prototype.onMountCompleted_ = function(event) { | 2598 if (this.params_.mountTriggered && !event.requested) { |
2639 var changeDirectoryTo = null; | 2599 // TODO(serya): What if 2 USB sticks plugged? |
2640 | 2600 chrome.tabs.getCurrent(function(tab) { |
2641 if (event && event.mountType == 'gdata') { | 2601 chrome.tabs.remove(tab.id); |
2642 var mounted = (event.eventType == 'mount'); | 2602 }); |
2643 metrics.recordInterval('Load.GData'); | |
2644 console.log('GData ' + (mounted ? 'mounted' : 'unmounted')); | |
2645 if (mounted && event.status == 'success') { | |
2646 this.gdataMounted_ = true; | |
2647 this.gdataMountInfo_ = { | |
2648 'mountPath': event.mountPath, | |
2649 'sourcePath': event.sourcePath, | |
2650 'mountType': event.mountType, | |
2651 'mountCondition': event.status | |
2652 }; | |
2653 // Not calling clearGDataLoadingTimer_ here because we want to keep | |
2654 // "Loading Google Docs" message until the directory loads. It is OK if | |
2655 // the timer fires after the mount because onDirectoryChanged_ will hide | |
2656 // the unmounted panel. | |
2657 if (this.setupCurrentDirectoryPostponed_) { | |
2658 this.setupCurrentDirectoryPostponed_(false /* execute */); | |
2659 } else if (this.isOnGData() && | |
2660 this.directoryModel_.getCurrentDirEntry().unmounted) { | |
2661 // We are currently on an unmounted GData directory, force a rescan. | |
2662 changeDirectoryTo = this.directoryModel_.getCurrentRootPath(); | |
2663 } | |
2664 } else { | |
2665 this.gdataMounted_ = false; | |
2666 this.gdataMountInfo_ = null; | |
2667 this.clearGDataLoadingTimer_(); | |
2668 this.onGDataUnreachable_('GData ' + | |
2669 (mounted ? ('mount failed: ' + event.status) : 'unmounted')); | |
2670 if (this.setupCurrentDirectoryPostponed_) { | |
2671 this.setupCurrentDirectoryPostponed_(true /* cancel */); | |
2672 // Change to unmounted GData root. | |
2673 changeDirectoryTo = '/' + DirectoryModel.GDATA_DIRECTORY; | |
2674 } | |
2675 } | 2603 } |
2676 } | 2604 } |
2677 | |
2678 chrome.fileBrowserPrivate.getMountPoints(function(mountPoints) { | |
2679 this.setMountPoints_(mountPoints); | |
2680 | |
2681 if (event.eventType == 'mount') { | |
2682 // Mount request finished - remove it. | |
2683 // Currently we only request mounts for archive files. | |
2684 var index = this.mountRequests_.indexOf(event.sourcePath); | |
2685 if (index != -1) { | |
2686 this.mountRequests_.splice(index, 1); | |
2687 if (event.status == 'success') { | |
2688 // Successful mount requested from this tab, go to the drive root. | |
2689 changeDirectoryTo = event.mountPath; | |
2690 } else { | |
2691 // Request initiated from this tab failed, report the error. | |
2692 var fileName = event.sourcePath.split('/').pop(); | |
2693 this.alert.show( | |
2694 strf('ARCHIVE_MOUNT_FAILED', fileName, event.status)); | |
2695 } | |
2696 } | |
2697 } | |
2698 | |
2699 if (event.eventType == 'unmount') { | |
2700 // Unmount request finished - remove it. | |
2701 var index = this.unmountRequests_.indexOf(event.mountPath); | |
2702 if (index != -1) { | |
2703 this.unmountRequests_.splice(index, 1); | |
2704 if (event.status != 'success') | |
2705 this.alert.show(strf('UNMOUNT_FAILED', event.status)); | |
2706 } | |
2707 | |
2708 if (event.status == 'success' && | |
2709 event.mountPath == this.directoryModel_.getCurrentRootPath()) { | |
2710 if (this.params_.mountTriggered && index == -1) { | |
2711 // This device mount was the reason this File Manager instance was | |
2712 // created. Now the device is unmounted from another instance | |
2713 // or the user removed the device manually. Close this instance. | |
2714 // window.close() sometimes doesn't work. | |
2715 chrome.tabs.getCurrent(function(tab) { | |
2716 chrome.tabs.remove(tab.id); | |
2717 }); | |
2718 return; | |
2719 } | |
2720 // Current directory just unmounted. Move to the 'Downloads'. | |
2721 changeDirectoryTo = this.directoryModel_.getDefaultDirectory(); | |
2722 } | |
2723 } | |
2724 | |
2725 // Even if something failed root list should be rescanned. | |
2726 // Failed mounts can "give" us new devices which might be formatted, | |
2727 // so we have to refresh root list then. | |
2728 this.directoryModel_.updateRoots(function() { | |
2729 if (changeDirectoryTo) { | |
2730 this.directoryModel_.changeDirectory(changeDirectoryTo); | |
2731 } | |
2732 }.bind(this), this.gdataMounted_); | |
2733 }.bind(this)); | |
2734 }; | 2605 }; |
2735 | 2606 |
2736 /** | 2607 /** |
2737 * Event handler called when some internal task should be executed. | 2608 * Event handler called when some internal task should be executed. |
2738 */ | 2609 */ |
2739 FileManager.prototype.onFileTaskExecute_ = function(id, urls) { | 2610 FileManager.prototype.onFileTaskExecute_ = function(id, urls) { |
2740 if (id == 'play') { | 2611 if (id == 'play') { |
2741 var position = 0; | 2612 var position = 0; |
2742 if (urls.length == 1) { | 2613 if (urls.length == 1) { |
2743 // If just a single audio file is selected pass along every audio file | 2614 // If just a single audio file is selected pass along every audio file |
2744 // in the directory. | 2615 // in the directory. |
2745 var selectedUrl = urls[0]; | 2616 var selectedUrl = urls[0]; |
2746 urls = this.getAllUrlsInCurrentDirectory_().filter(FileType.isAudio); | 2617 urls = this.getAllUrlsInCurrentDirectory_().filter(FileType.isAudio); |
2747 position = urls.indexOf(selectedUrl); | 2618 position = urls.indexOf(selectedUrl); |
2748 } | 2619 } |
2749 chrome.mediaPlayerPrivate.play(urls, position); | 2620 chrome.mediaPlayerPrivate.play(urls, position); |
2750 } else if (id == 'mount-archive') { | 2621 } else if (id == 'mount-archive') { |
2751 var self = this; | 2622 var self = this; |
2623 var tracker = this.directoryModel_.createDirectoryChangeTracker(); | |
2624 tracker.start(); | |
2752 this.resolveSelectResults_(urls, function(urls) { | 2625 this.resolveSelectResults_(urls, function(urls) { |
2753 for (var index = 0; index < urls.length; ++index) { | 2626 for (var index = 0; index < urls.length; ++index) { |
2754 // Url in MountCompleted event won't be escaped, so let's make sure | 2627 var path = /^filesystem:[\w-]*:\/\/[\w]*\/external(\/.*)$/. |
2755 // we don't use escaped one in mountRequests_. | 2628 exec(urls[index])[1]; |
2756 var unescapedUrl = unescape(urls[index]); | 2629 if (!path) |
2757 chrome.fileBrowserPrivate.addMount(unescapedUrl, 'file', {}, | 2630 continue; |
2758 function(sourcePath) { | 2631 path = decodeURIComponent(path); |
2759 self.mountRequests_.push(sourcePath); | 2632 self.volumeManager_.mountArchive(path, function(mountPath) { |
2633 console.log('Mounted at: ', mountPath); | |
2634 tracker.stop(); | |
2635 if (!tracker.hasChanged) | |
2636 self.directoryModel_.changeDirectory(mountPath); | |
2637 }, function(error) { | |
2638 tracker.stop(); | |
2639 self.alert.show(strf('ARCHIVE_MOUNT_FAILED', | |
2640 error.fileName, error.message)); | |
2760 }); | 2641 }); |
2761 } | 2642 } |
2762 }); | 2643 }); |
2763 } else if (id == 'format-device') { | 2644 } else if (id == 'format-device') { |
2764 this.confirm.show(str('FORMATTING_WARNING'), function() { | 2645 this.confirm.show(str('FORMATTING_WARNING'), function() { |
2765 chrome.fileBrowserPrivate.formatDevice(urls[0]); | 2646 chrome.fileBrowserPrivate.formatDevice(urls[0]); |
2766 }); | 2647 }); |
2767 } else if (id == 'gallery') { | 2648 } else if (id == 'gallery') { |
2768 this.openGallery_(urls); | 2649 this.openGallery_(urls); |
2769 } else if (id == 'view-pdf' || id == 'view-in-browser' || | 2650 } else if (id == 'view-pdf' || id == 'view-in-browser' || |
2770 id == 'install-crx' || id == 'open-hosted') { | 2651 id == 'install-crx' || id == 'open-hosted') { |
2771 chrome.fileBrowserPrivate.viewFiles(urls, 'default', function(success) { | 2652 chrome.fileBrowserPrivate.viewFiles(urls, 'default', function(success) { |
2772 if (!success) | 2653 if (!success) |
2773 console.error('chrome.fileBrowserPrivate.viewFiles failed', urls); | 2654 console.error('chrome.fileBrowserPrivate.viewFiles failed', urls); |
2774 }); | 2655 }); |
2775 } | 2656 } |
2776 }; | 2657 }; |
2777 | 2658 |
2778 FileManager.prototype.getDeviceNumber = function(entry) { | |
2779 if (!entry.isDirectory) return undefined; | |
2780 for (var i = 0; i < this.mountPoints_.length; i++) { | |
2781 if (normalizeAbsolutePath(entry.fullPath) == | |
2782 normalizeAbsolutePath(this.mountPoints_[i].mountPath)) { | |
2783 return i; | |
2784 } | |
2785 } | |
2786 return undefined; | |
2787 }; | |
2788 | |
2789 /** | 2659 /** |
2790 * Show a modal-like file viewer/editor on top of the File Manager UI. | 2660 * Show a modal-like file viewer/editor on top of the File Manager UI. |
2791 * | 2661 * |
2792 * @param {HTMLElement} popup Popup element. | 2662 * @param {HTMLElement} popup Popup element. |
2793 * @param {function} closeCallback Function to call after the popup is closed. | 2663 * @param {function} closeCallback Function to call after the popup is closed. |
2794 */ | 2664 */ |
2795 FileManager.prototype.openFilePopup_ = function(popup, closeCallback) { | 2665 FileManager.prototype.openFilePopup_ = function(popup, closeCallback) { |
2796 this.closeFilePopup_(); | 2666 this.closeFilePopup_(); |
2797 this.filePopup_ = popup; | 2667 this.filePopup_ = popup; |
2798 this.filePopupCloseCallback_ = closeCallback; | 2668 this.filePopupCloseCallback_ = closeCallback; |
(...skipping 577 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
3376 return true; | 3246 return true; |
3377 } | 3247 } |
3378 if (!this.okButton_.disabled) { | 3248 if (!this.okButton_.disabled) { |
3379 this.onOk_(); | 3249 this.onOk_(); |
3380 return true; | 3250 return true; |
3381 } | 3251 } |
3382 return false; | 3252 return false; |
3383 }; | 3253 }; |
3384 | 3254 |
3385 FileManager.prototype.onDirectoryAction = function(entry) { | 3255 FileManager.prototype.onDirectoryAction = function(entry) { |
3386 var deviceNumber = this.getDeviceNumber(entry); | 3256 var mountCondition = this.volumeManager_.getMountCondition(entry.fullPath); |
3387 if (deviceNumber != undefined && | 3257 if (mountCondition == 'unknown_filesystem') { |
3388 this.mountPoints_[deviceNumber].mountCondition == | |
3389 'unknown_filesystem') { | |
3390 return this.showButter(str('UNKNOWN_FILESYSTEM_WARNING')); | 3258 return this.showButter(str('UNKNOWN_FILESYSTEM_WARNING')); |
3391 } else if (deviceNumber != undefined && | 3259 } else if (mountCondition == 'unsupported_filesystem') { |
3392 this.mountPoints_[deviceNumber].mountCondition == | |
3393 'unsupported_filesystem') { | |
3394 return this.showButter(str('UNSUPPORTED_FILESYSTEM_WARNING')); | 3260 return this.showButter(str('UNSUPPORTED_FILESYSTEM_WARNING')); |
3395 } else { | 3261 } else { |
3396 return this.directoryModel_.changeDirectory(entry.fullPath); | 3262 return this.directoryModel_.changeDirectory(entry.fullPath); |
3397 } | 3263 } |
3398 }; | 3264 }; |
3399 | 3265 |
3400 /** | 3266 /** |
3401 * Show or hide the "Low disk space" warning. | 3267 * Show or hide the "Low disk space" warning. |
3402 * @param {boolean} show True if the box need to be shown. | 3268 * @param {boolean} show True if the box need to be shown. |
3403 */ | 3269 */ |
(...skipping 53 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
3457 | 3323 |
3458 // Sometimes we rescan the same directory (when mounting GData lazily first, | 3324 // Sometimes we rescan the same directory (when mounting GData lazily first, |
3459 // then for real). Do not update the location then. | 3325 // then for real). Do not update the location then. |
3460 if (event.newDirEntry.fullPath != event.previousDirEntry.fullPath) { | 3326 if (event.newDirEntry.fullPath != event.previousDirEntry.fullPath) { |
3461 this.updateLocation_(event.initial, event.newDirEntry.fullPath); | 3327 this.updateLocation_(event.initial, event.newDirEntry.fullPath); |
3462 } | 3328 } |
3463 | 3329 |
3464 this.checkFreeSpace_(this.getCurrentDirectory()); | 3330 this.checkFreeSpace_(this.getCurrentDirectory()); |
3465 | 3331 |
3466 this.updateTitle_(); | 3332 this.updateTitle_(); |
3467 | 3333 this.updateGDataStatus_('directory-change'); |
3468 if (this.filesystemObserverId_) | |
3469 this.metadataCache_.removeObserver(this.filesystemObserverId_); | |
3470 if (this.gdataObserverId_) | |
3471 this.metadataCache_.removeObserver(this.gdataObserverId_); | |
3472 | |
3473 this.filesystemObserverId_ = this.metadataCache_.addObserver( | |
3474 this.directoryModel_.getCurrentDirEntry(), | |
3475 MetadataCache.CHILDREN, | |
3476 'filesystem', | |
3477 this.updateMetadataInUI_.bind(this, 'filesystem')); | |
3478 | |
3479 if (this.isOnGData()) { | |
3480 this.gdataObserverId_ = this.metadataCache_.addObserver( | |
3481 this.directoryModel_.getCurrentDirEntry(), | |
3482 MetadataCache.CHILDREN, | |
3483 'gdata', | |
3484 this.updateMetadataInUI_.bind(this, 'gdata')); | |
3485 } | |
3486 | |
3487 var self = this; | |
3488 | |
3489 if (this.watchedDirectoryUrl_) { | |
3490 if (this.watchedDirectoryUrl_ != event.previousDirEntry.toURL()) { | |
3491 console.warn('event.previousDirEntry does not match File Manager state', | |
3492 event, this.watchedDirectoryUrl_); | |
3493 } | |
3494 chrome.fileBrowserPrivate.removeFileWatch(this.watchedDirectoryUrl_, | |
3495 function(result) { | |
3496 if (!result) { | |
3497 console.log('Failed to remove file watch'); | |
3498 } | |
3499 }); | |
3500 this.watchedDirectoryUrl_ = null; | |
3501 } | |
3502 | |
3503 if (event.newDirEntry.fullPath != '/' && !event.newDirEntry.unmounted) { | |
3504 this.watchedDirectoryUrl_ = event.newDirEntry.toURL(); | |
3505 chrome.fileBrowserPrivate.addFileWatch(this.watchedDirectoryUrl_, | |
3506 function(result) { | |
3507 if (!result) { | |
3508 console.log('Failed to add file watch'); | |
3509 this.watchedDirectoryUrl_ = null; | |
3510 } | |
3511 }.bind(this)); | |
3512 } | |
3513 | |
3514 if (event.newDirEntry.unmounted) | |
3515 this.dialogContainer_.setAttribute('unmounted', true); | |
3516 else { | |
3517 this.dialogContainer_.removeAttribute('unmounted'); | |
3518 // Need to resize explicitly because the list container had display:none. | |
3519 this.onResize_(); | |
3520 } | |
3521 | |
3522 if (this.isOnGData()) { | |
3523 this.dialogContainer_.setAttribute('gdata', true); | |
3524 if (event.newDirEntry.unmounted) { | |
3525 this.initGData_(true /* directory changed */); | |
3526 } | |
3527 } else { | |
3528 this.dialogContainer_.removeAttribute('gdata'); | |
3529 } | |
3530 }; | 3334 }; |
3531 | 3335 |
3532 FileManager.prototype.findListItemForEvent_ = function(event) { | 3336 FileManager.prototype.findListItemForEvent_ = function(event) { |
3533 return this.findListItemForNode_(event.srcElement); | 3337 return this.findListItemForNode_(event.srcElement); |
3534 }; | 3338 }; |
3535 | 3339 |
3536 FileManager.prototype.findListItemForNode_ = function(node) { | 3340 FileManager.prototype.findListItemForNode_ = function(node) { |
3537 var item = this.currentList_.getListItemAncestor(node); | 3341 var item = this.currentList_.getListItemAncestor(node); |
3538 // TODO(serya): list should check that. | 3342 // TODO(serya): list should check that. |
3539 return item && this.currentList_.isItem(item) ? item : null; | 3343 return item && this.currentList_.isItem(item) ? item : null; |
3540 }; | 3344 }; |
3541 | 3345 |
3542 /** | 3346 /** |
3543 * Unload handler for the page. May be called manually for the file picker | 3347 * Unload handler for the page. May be called manually for the file picker |
3544 * dialog, because it closes by calling extension API functions that do not | 3348 * dialog, because it closes by calling extension API functions that do not |
3545 * return. | 3349 * return. |
3546 */ | 3350 */ |
3547 FileManager.prototype.onUnload_ = function() { | 3351 FileManager.prototype.onUnload_ = function() { |
3548 if (this.watchedDirectoryUrl_) { | 3352 this.fileWatcher_.stop(); |
3549 chrome.fileBrowserPrivate.removeFileWatch( | |
3550 this.watchedDirectoryUrl_, | |
3551 function(result) { | |
3552 if (!result) { | |
3553 console.log('Failed to remove file watch'); | |
3554 } | |
3555 }); | |
3556 this.watchedDirectoryUrl_ = null; | |
3557 } | |
3558 }; | |
3559 | |
3560 FileManager.prototype.onFileChanged_ = function(event) { | |
3561 // We receive a lot of events even in folders we are not interested in. | |
3562 if (encodeURI(event.fileUrl) == this.getCurrentDirectoryURL()) | |
3563 this.directoryModel_.rescanLater(); | |
3564 }; | 3353 }; |
3565 | 3354 |
3566 FileManager.prototype.initiateRename_ = function() { | 3355 FileManager.prototype.initiateRename_ = function() { |
3567 var item = this.currentList_.ensureLeadItemExists(); | 3356 var item = this.currentList_.ensureLeadItemExists(); |
3568 if (!item) | 3357 if (!item) |
3569 return; | 3358 return; |
3570 var label = item.querySelector('.filename-label'); | 3359 var label = item.querySelector('.filename-label'); |
3571 var input = this.renameInput_; | 3360 var input = this.renameInput_; |
3572 | 3361 |
3573 input.value = label.textContent; | 3362 input.value = label.textContent; |
(...skipping 905 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
4479 | 4268 |
4480 this.directoryModel_.addEventListener('scan-completed', maybeShowBanner); | 4269 this.directoryModel_.addEventListener('scan-completed', maybeShowBanner); |
4481 this.directoryModel_.addEventListener('rescan-completed', maybeShowBanner); | 4270 this.directoryModel_.addEventListener('rescan-completed', maybeShowBanner); |
4482 | 4271 |
4483 var style = this.document_.createElement('link'); | 4272 var style = this.document_.createElement('link'); |
4484 style.rel = 'stylesheet'; | 4273 style.rel = 'stylesheet'; |
4485 style.href = 'css/gdrive_welcome.css'; | 4274 style.href = 'css/gdrive_welcome.css'; |
4486 this.document_.head.appendChild(style); | 4275 this.document_.head.appendChild(style); |
4487 }; | 4276 }; |
4488 })(); | 4277 })(); |
4278 | |
OLD | NEW |