OLD | NEW |
1 // Copyright (c) 2011 The Chromium Authors. All rights reserved. | 1 // Copyright (c) 2011 The Chromium Authors. All rights reserved. |
2 // Use of this source code is governed by a BSD-style license that can be | 2 // Use of this source code is governed by a BSD-style license that can be |
3 // found in the LICENSE file. | 3 // found in the LICENSE file. |
4 | 4 |
5 // Setting the src of an img to an empty string can crash the browser, so we | 5 // Setting the src of an img to an empty string can crash the browser, so we |
6 // use an empty 1x1 gif instead. | 6 // use an empty 1x1 gif instead. |
7 const EMPTY_IMAGE_URI = 'data:image/gif;base64,' | 7 const EMPTY_IMAGE_URI = 'data:image/gif;base64,' |
8 + 'R0lGODlhAQABAPABAP///wAAACH5BAEKAAAALAAAAAABAAEAAAICRAEAOw%3D%3D'; | 8 + 'R0lGODlhAQABAPABAP///wAAACH5BAEKAAAALAAAAAABAAEAAAICRAEAOw%3D%3D'; |
9 | 9 |
10 var g_slideshow_data = null; | 10 var g_slideshow_data = null; |
11 | 11 |
12 const GALLERY_ENABLED = true; | 12 const GALLERY_ENABLED = true; |
13 | 13 |
14 // If directory files changes too often, don't rescan directory more than once | 14 // If directory files changes too often, don't rescan directory more than once |
15 // per specified interval | 15 // per specified interval |
16 const SIMULTANEOUS_RESCAN_INTERVAL = 1000; | 16 const SIMULTANEOUS_RESCAN_INTERVAL = 1000; |
17 | 17 |
18 /** | 18 /** |
19 * FileManager constructor. | 19 * FileManager constructor. |
20 * | 20 * |
21 * FileManager objects encapsulate the functionality of the file selector | 21 * FileManager objects encapsulate the functionality of the file selector |
22 * dialogs, as well as the full screen file manager application (though the | 22 * dialogs, as well as the full screen file manager application (though the |
23 * latter is not yet implemented). | 23 * latter is not yet implemented). |
24 * | 24 * |
25 * @param {HTMLElement} dialogDom The DOM node containing the prototypical | 25 * @param {HTMLElement} dialogDom The DOM node containing the prototypical |
26 * dialog UI. | 26 * dialog UI. |
27 * @param {DOMFileSystem} filesystem The HTML5 filesystem object representing | 27 * @param {DOMFileSystem} filesystem The HTML5 filesystem object representing |
28 * the root filesystem for the new FileManager. | 28 * the root filesystem for the new FileManager. |
29 * @param {Object} params A map of parameter names to values controlling the | |
30 * appearance of the FileManager. Names are: | |
31 * - type: A value from FileManager.DialogType defining what kind of | |
32 * dialog to present. Defaults to FULL_PAGE. | |
33 * - title: The title for the dialog. Defaults to a localized string based | |
34 * on the dialog type. | |
35 * - defaultPath: The default path for the dialog. The default path should | |
36 * end with a trailing slash if it represents a directory. | |
37 */ | 29 */ |
38 function FileManager(dialogDom, filesystem, rootEntries) { | 30 function FileManager(dialogDom) { |
39 console.log('Init FileManager: ' + dialogDom); | 31 console.log('Init FileManager: ' + dialogDom); |
40 | 32 |
41 this.dialogDom_ = dialogDom; | 33 this.dialogDom_ = dialogDom; |
42 this.rootEntries_ = rootEntries; | 34 this.rootEntries_ = null; |
43 this.filesystem_ = filesystem; | 35 this.filesystem_ = null; |
44 this.params_ = location.search ? | 36 this.params_ = location.search ? |
45 JSON.parse(decodeURIComponent(location.search.substr(1))) : | 37 JSON.parse(decodeURIComponent(location.search.substr(1))) : |
46 {}; | 38 {}; |
47 | 39 |
48 this.listType_ = null; | 40 this.listType_ = null; |
49 | 41 |
50 this.selection = null; | 42 this.selection = null; |
51 | 43 |
52 this.clipboard_ = null; // Current clipboard, or null if empty. | 44 this.clipboard_ = null; // Current clipboard, or null if empty. |
53 | 45 |
(...skipping 11 matching lines...) Expand all Loading... |
65 this.document_ = dialogDom.ownerDocument; | 57 this.document_ = dialogDom.ownerDocument; |
66 this.dialogType_ = this.params_.type || FileManager.DialogType.FULL_PAGE; | 58 this.dialogType_ = this.params_.type || FileManager.DialogType.FULL_PAGE; |
67 | 59 |
68 metrics.recordEnum('Create', this.dialogType_, | 60 metrics.recordEnum('Create', this.dialogType_, |
69 [FileManager.DialogType.SELECT_FOLDER, | 61 [FileManager.DialogType.SELECT_FOLDER, |
70 FileManager.DialogType.SELECT_SAVEAS_FILE, | 62 FileManager.DialogType.SELECT_SAVEAS_FILE, |
71 FileManager.DialogType.SELECT_OPEN_FILE, | 63 FileManager.DialogType.SELECT_OPEN_FILE, |
72 FileManager.DialogType.SELECT_OPEN_MULTI_FILE, | 64 FileManager.DialogType.SELECT_OPEN_MULTI_FILE, |
73 FileManager.DialogType.FULL_PAGE]); | 65 FileManager.DialogType.FULL_PAGE]); |
74 | 66 |
75 this.initDialogs_(); | |
76 | |
77 // TODO(dgozman): This will be changed to LocaleInfo. | 67 // TODO(dgozman): This will be changed to LocaleInfo. |
78 this.locale_ = new v8Locale(navigator.language); | 68 this.locale_ = new v8Locale(navigator.language); |
79 | 69 |
80 // TODO(rginda): 6/22/11: Remove this test when createDateTimeFormat is | 70 this.resolveRoots_(); |
81 // available in all chrome trunk builds. | |
82 if ('createDateTimeFormat' in this.locale_) { | |
83 this.shortDateFormatter_ = | |
84 this.locale_.createDateTimeFormat({'dateType': 'medium'}); | |
85 } else { | |
86 this.shortDateFormatter_ = { | |
87 format: function(d) { | |
88 return (d.getMonth() + 1) + '/' + d.getDate() + '/' + d.getFullYear(); | |
89 } | |
90 }; | |
91 } | |
92 | |
93 // TODO(rginda): 6/22/11: Remove this test when createCollator is | |
94 // available in all chrome trunk builds. | |
95 if ('createCollator' in this.locale_) { | |
96 this.collator_ = this.locale_.createCollator({ | |
97 'numeric': true, 'ignoreCase': true, 'ignoreAccents': true}); | |
98 } else { | |
99 this.collator_ = { | |
100 compare: function(a, b) { | |
101 if (a > b) return 1; | |
102 if (a < b) return -1; | |
103 return 0; | |
104 } | |
105 }; | |
106 } | |
107 | |
108 // Optional list of file types. | |
109 this.fileTypes_ = this.params_.typeList; | |
110 | |
111 this.showCheckboxes_ = | |
112 (this.dialogType_ == FileManager.DialogType.FULL_PAGE || | |
113 this.dialogType_ == FileManager.DialogType.SELECT_OPEN_MULTI_FILE); | |
114 | |
115 // DirectoryEntry representing the current directory of the dialog. | |
116 this.currentDirEntry_ = null; | |
117 | |
118 this.copyManager_ = new FileCopyManager(); | |
119 this.copyManager_.addEventListener('copy-progress', | |
120 this.onCopyProgress_.bind(this)); | |
121 | |
122 window.addEventListener('popstate', this.onPopState_.bind(this)); | |
123 window.addEventListener('unload', this.onUnload_.bind(this)); | |
124 | |
125 this.addEventListener('directory-changed', | |
126 this.onDirectoryChanged_.bind(this)); | |
127 this.addEventListener('selection-summarized', | |
128 this.onSelectionSummarized_.bind(this)); | |
129 | |
130 // The list of archives requested to mount. We will show contents once | |
131 // archive is mounted, but only for mounts from within this filebrowser tab. | |
132 this.mountRequests_ = []; | |
133 chrome.fileBrowserPrivate.onMountCompleted.addListener( | |
134 this.onMountCompleted_.bind(this)); | |
135 | |
136 chrome.fileBrowserPrivate.onFileChanged.addListener( | |
137 this.onFileChanged_.bind(this)); | |
138 | |
139 var self = this; | |
140 | |
141 // The list of callbacks to be invoked during the directory rescan after | |
142 // all paste tasks are complete. | |
143 this.pasteSuccessCallbacks_ = []; | |
144 | |
145 // The list of active mount points to distinct them from other directories. | |
146 chrome.fileBrowserPrivate.getMountPoints(function(mountPoints) { | |
147 self.mountPoints_ = mountPoints; | |
148 }); | |
149 | |
150 this.initCommands_(); | |
151 this.initDom_(); | 71 this.initDom_(); |
152 this.initDialogType_(); | 72 this.initDialogType_(); |
153 this.setupCurrentDirectory_(); | 73 this.dialogDom_.style.opacity = '1'; |
154 | |
155 this.summarizeSelection_(); | |
156 | |
157 this.dataModel_.sort('cachedMtime_', 'desc'); | |
158 | |
159 this.refocus(); | |
160 | |
161 this.createMetadataProvider_(); | |
162 } | 74 } |
163 | 75 |
164 FileManager.prototype = { | 76 FileManager.prototype = { |
165 __proto__: cr.EventTarget.prototype | 77 __proto__: cr.EventTarget.prototype |
166 }; | 78 }; |
167 | 79 |
168 // Anonymous "namespace". | 80 // Anonymous "namespace". |
169 (function() { | 81 (function() { |
170 | 82 |
171 // Private variables and helper functions. | 83 // Private variables and helper functions. |
(...skipping 378 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
550 chrome.fileBrowserPrivate.getStrings(function(strings) { | 462 chrome.fileBrowserPrivate.getStrings(function(strings) { |
551 localStrings = new LocalStrings(strings); | 463 localStrings = new LocalStrings(strings); |
552 if (callback) | 464 if (callback) |
553 callback(); | 465 callback(); |
554 }); | 466 }); |
555 }; | 467 }; |
556 | 468 |
557 // Instance methods. | 469 // Instance methods. |
558 | 470 |
559 /** | 471 /** |
| 472 * Request file system and get root entries asynchronously. Invokes init_ |
| 473 * when have finished. |
| 474 */ |
| 475 FileManager.prototype.resolveRoots_ = function(callback) { |
| 476 var rootPaths = ['Downloads', 'removable', 'archive']; |
| 477 |
| 478 metrics.startInterval('RequestLocalFileSystem'); |
| 479 var self = this; |
| 480 chrome.fileBrowserPrivate.requestLocalFileSystem(function(filesystem) { |
| 481 self.filesystem_ = filesystem; |
| 482 util.installFileErrorToString(); |
| 483 |
| 484 metrics.recordTime('RequestLocalFileSystem'); |
| 485 console.log('Found filesystem: ' + filesystem.name, filesystem); |
| 486 |
| 487 var rootEntries = []; |
| 488 |
| 489 function onAllRootsFound() { |
| 490 self.rootEntries_ = rootEntries; |
| 491 self.init_(); |
| 492 } |
| 493 |
| 494 function onPathError(path, err) { |
| 495 console.error('Error locating root path: ' + path + ': ' + err); |
| 496 } |
| 497 |
| 498 function onEntryFound(entry) { |
| 499 if (entry) { |
| 500 rootEntries.push(entry); |
| 501 } else { |
| 502 onAllRootsFound(); |
| 503 } |
| 504 } |
| 505 |
| 506 metrics.startInterval('EnumerateRoots'); |
| 507 if (filesystem.name.match(/^chrome-extension_\S+:external/i)) { |
| 508 // We've been handed the local filesystem, whose root directory |
| 509 // cannot be enumerated. |
| 510 util.getDirectories(filesystem.root, {create: false}, rootPaths, |
| 511 onEntryFound, onPathError); |
| 512 } else { |
| 513 util.forEachDirEntry(filesystem.root, onEntryFound); |
| 514 } |
| 515 }); |
| 516 }; |
| 517 |
| 518 /** |
| 519 * Continue initializing the file manager after resolving roots. |
| 520 */ |
| 521 FileManager.prototype.init_ = function() { |
| 522 metrics.startInterval('InitFileManager'); |
| 523 this.initFileList_(); |
| 524 this.initDialogs_(); |
| 525 |
| 526 // TODO(rginda): 6/22/11: Remove this test when createDateTimeFormat is |
| 527 // available in all chrome trunk builds. |
| 528 if ('createDateTimeFormat' in this.locale_) { |
| 529 this.shortDateFormatter_ = |
| 530 this.locale_.createDateTimeFormat({'dateType': 'medium'}); |
| 531 } else { |
| 532 this.shortDateFormatter_ = { |
| 533 format: function(d) { |
| 534 return (d.getMonth() + 1) + '/' + d.getDate() + '/' + d.getFullYear(); |
| 535 } |
| 536 }; |
| 537 } |
| 538 |
| 539 // TODO(rginda): 6/22/11: Remove this test when createCollator is |
| 540 // available in all chrome trunk builds. |
| 541 if ('createCollator' in this.locale_) { |
| 542 this.collator_ = this.locale_.createCollator({ |
| 543 'numeric': true, 'ignoreCase': true, 'ignoreAccents': true}); |
| 544 } else { |
| 545 this.collator_ = { |
| 546 compare: function(a, b) { |
| 547 if (a > b) return 1; |
| 548 if (a < b) return -1; |
| 549 return 0; |
| 550 } |
| 551 }; |
| 552 } |
| 553 |
| 554 // Optional list of file types. |
| 555 this.fileTypes_ = this.params_.typeList; |
| 556 |
| 557 this.showCheckboxes_ = |
| 558 (this.dialogType_ == FileManager.DialogType.FULL_PAGE || |
| 559 this.dialogType_ == FileManager.DialogType.SELECT_OPEN_MULTI_FILE); |
| 560 |
| 561 // DirectoryEntry representing the current directory of the dialog. |
| 562 this.currentDirEntry_ = null; |
| 563 |
| 564 this.copyManager_ = new FileCopyManager(); |
| 565 this.copyManager_.addEventListener('copy-progress', |
| 566 this.onCopyProgress_.bind(this)); |
| 567 |
| 568 window.addEventListener('popstate', this.onPopState_.bind(this)); |
| 569 window.addEventListener('unload', this.onUnload_.bind(this)); |
| 570 |
| 571 this.addEventListener('directory-changed', |
| 572 this.onDirectoryChanged_.bind(this)); |
| 573 this.addEventListener('selection-summarized', |
| 574 this.onSelectionSummarized_.bind(this)); |
| 575 |
| 576 // The list of archives requested to mount. We will show contents once |
| 577 // archive is mounted, but only for mounts from within this filebrowser tab. |
| 578 this.mountRequests_ = []; |
| 579 chrome.fileBrowserPrivate.onMountCompleted.addListener( |
| 580 this.onMountCompleted_.bind(this)); |
| 581 |
| 582 chrome.fileBrowserPrivate.onFileChanged.addListener( |
| 583 this.onFileChanged_.bind(this)); |
| 584 |
| 585 var self = this; |
| 586 |
| 587 // The list of callbacks to be invoked during the directory rescan after |
| 588 // all paste tasks are complete. |
| 589 this.pasteSuccessCallbacks_ = []; |
| 590 |
| 591 // The list of active mount points to distinct them from other directories. |
| 592 chrome.fileBrowserPrivate.getMountPoints(function(mountPoints) { |
| 593 self.mountPoints_ = mountPoints; |
| 594 }); |
| 595 |
| 596 this.initCommands_(); |
| 597 |
| 598 this.setupCurrentDirectory_(); |
| 599 |
| 600 this.summarizeSelection_(); |
| 601 |
| 602 this.dataModel_.sort('cachedMtime_', 'desc'); |
| 603 |
| 604 this.refocus(); |
| 605 |
| 606 this.createMetadataProvider_(); |
| 607 metrics.recordTime('InitFileManager'); |
| 608 metrics.recordTime('TotalLoad'); |
| 609 }; |
| 610 |
| 611 /** |
560 * One-time initialization of commands. | 612 * One-time initialization of commands. |
561 */ | 613 */ |
562 FileManager.prototype.initCommands_ = function() { | 614 FileManager.prototype.initCommands_ = function() { |
563 var commands = this.dialogDom_.querySelectorAll('command'); | 615 var commands = this.dialogDom_.querySelectorAll('command'); |
564 for (var i = 0; i < commands.length; i++) { | 616 for (var i = 0; i < commands.length; i++) { |
565 var command = commands[i]; | 617 var command = commands[i]; |
566 cr.ui.Command.decorate(command); | 618 cr.ui.Command.decorate(command); |
567 this.commands_[command.id] = command; | 619 this.commands_[command.id] = command; |
568 } | 620 } |
569 | 621 |
(...skipping 75 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
645 | 697 |
646 var ary = this.dialogDom_.querySelectorAll('[visibleif]'); | 698 var ary = this.dialogDom_.querySelectorAll('[visibleif]'); |
647 for (var i = 0; i < ary.length; i++) { | 699 for (var i = 0; i < ary.length; i++) { |
648 var expr = ary[i].getAttribute('visibleif'); | 700 var expr = ary[i].getAttribute('visibleif'); |
649 if (!eval(expr)) | 701 if (!eval(expr)) |
650 ary[i].style.display = 'none'; | 702 ary[i].style.display = 'none'; |
651 } | 703 } |
652 | 704 |
653 // Populate the static localized strings. | 705 // Populate the static localized strings. |
654 i18nTemplate.process(this.document_, localStrings.templateData); | 706 i18nTemplate.process(this.document_, localStrings.templateData); |
| 707 }; |
655 | 708 |
| 709 /** |
| 710 * Constructs table and grid (heavy operation). |
| 711 **/ |
| 712 FileManager.prototype.initFileList_ = function() { |
656 // Always sharing the data model between the detail/thumb views confuses | 713 // Always sharing the data model between the detail/thumb views confuses |
657 // them. Instead we maintain this bogus data model, and hook it up to the | 714 // them. Instead we maintain this bogus data model, and hook it up to the |
658 // view that is not in use. | 715 // view that is not in use. |
659 this.emptyDataModel_ = new cr.ui.ArrayDataModel([]); | 716 this.emptyDataModel_ = new cr.ui.ArrayDataModel([]); |
660 | 717 |
661 this.dataModel_ = new cr.ui.ArrayDataModel([]); | 718 this.dataModel_ = new cr.ui.ArrayDataModel([]); |
662 var collator = this.collator_; | 719 var collator = this.collator_; |
663 this.dataModel_.setCompareFunction('name', function(a, b) { | 720 this.dataModel_.setCompareFunction('name', function(a, b) { |
664 return collator.compare(a.name, b.name); | 721 return collator.compare(a.name, b.name); |
665 }); | 722 }); |
(...skipping 12 matching lines...) Expand all Loading... |
678 } else { | 735 } else { |
679 this.selectionModelClass_ = cr.ui.ListSelectionModel; | 736 this.selectionModelClass_ = cr.ui.ListSelectionModel; |
680 } | 737 } |
681 | 738 |
682 this.initTable_(); | 739 this.initTable_(); |
683 this.initGrid_(); | 740 this.initGrid_(); |
684 | 741 |
685 this.setListType(FileManager.ListType.DETAIL); | 742 this.setListType(FileManager.ListType.DETAIL); |
686 | 743 |
687 this.onResize_(); | 744 this.onResize_(); |
688 this.dialogDom_.style.opacity = '1'; | |
689 | 745 |
690 this.textSearchState_ = {text: '', date: new Date()}; | 746 this.textSearchState_ = {text: '', date: new Date()}; |
691 }; | 747 }; |
692 | 748 |
693 /** | 749 /** |
694 * Get the icon type for a given Entry. | 750 * Get the icon type for a given Entry. |
695 * | 751 * |
696 * @param {Entry} entry An Entry subclass (FileEntry or DirectoryEntry). | 752 * @param {Entry} entry An Entry subclass (FileEntry or DirectoryEntry). |
697 * @return {string} | 753 * @return {string} |
698 */ | 754 */ |
(...skipping 516 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
1215 this.deleteEntries(this.selection.entries); | 1271 this.deleteEntries(this.selection.entries); |
1216 return; | 1272 return; |
1217 } | 1273 } |
1218 }; | 1274 }; |
1219 | 1275 |
1220 /** | 1276 /** |
1221 * Respond to the back and forward buttons. | 1277 * Respond to the back and forward buttons. |
1222 */ | 1278 */ |
1223 FileManager.prototype.onPopState_ = function(event) { | 1279 FileManager.prototype.onPopState_ = function(event) { |
1224 // TODO(serya): We should restore selected items here. | 1280 // TODO(serya): We should restore selected items here. |
1225 this.setupCurrentDirectory_(); | 1281 if (this.rootEntries_) |
| 1282 this.setupCurrentDirectory_(); |
1226 }; | 1283 }; |
1227 | 1284 |
1228 FileManager.prototype.requestResize_ = function(timeout) { | 1285 FileManager.prototype.requestResize_ = function(timeout) { |
1229 var self = this; | 1286 var self = this; |
1230 setTimeout(function() { self.onResize_() }, timeout || 0); | 1287 setTimeout(function() { self.onResize_() }, timeout || 0); |
1231 }; | 1288 }; |
1232 | 1289 |
1233 /** | 1290 /** |
1234 * Resize details and thumb views to fit the new window size. | 1291 * Resize details and thumb views to fit the new window size. |
1235 */ | 1292 */ |
(...skipping 2500 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
3736 }); | 3793 }); |
3737 }, onError); | 3794 }, onError); |
3738 | 3795 |
3739 function onError(err) { | 3796 function onError(err) { |
3740 console.log('Error while checking free space: ' + err); | 3797 console.log('Error while checking free space: ' + err); |
3741 setTimeout(doCheck, 1000 * 60); | 3798 setTimeout(doCheck, 1000 * 60); |
3742 } | 3799 } |
3743 } | 3800 } |
3744 } | 3801 } |
3745 })(); | 3802 })(); |
OLD | NEW |