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; |
(...skipping 13 matching lines...) Expand all Loading... |
24 * the root filesystem for the new FileManager. | 24 * the root filesystem for the new FileManager. |
25 * @param {Object} params A map of parameter names to values controlling the | 25 * @param {Object} params A map of parameter names to values controlling the |
26 * appearance of the FileManager. Names are: | 26 * appearance of the FileManager. Names are: |
27 * - type: A value from FileManager.DialogType defining what kind of | 27 * - type: A value from FileManager.DialogType defining what kind of |
28 * dialog to present. Defaults to FULL_PAGE. | 28 * dialog to present. Defaults to FULL_PAGE. |
29 * - title: The title for the dialog. Defaults to a localized string based | 29 * - title: The title for the dialog. Defaults to a localized string based |
30 * on the dialog type. | 30 * on the dialog type. |
31 * - defaultPath: The default path for the dialog. The default path should | 31 * - defaultPath: The default path for the dialog. The default path should |
32 * end with a trailing slash if it represents a directory. | 32 * end with a trailing slash if it represents a directory. |
33 */ | 33 */ |
34 function FileManager(dialogDom, filesystem, rootEntries, params) { | 34 function FileManager(dialogDom, filesystem, rootEntries) { |
35 console.log('Init FileManager: ' + dialogDom); | 35 console.log('Init FileManager: ' + dialogDom); |
36 | 36 |
37 this.dialogDom_ = dialogDom; | 37 this.dialogDom_ = dialogDom; |
38 this.rootEntries_ = rootEntries; | 38 this.rootEntries_ = rootEntries; |
39 this.filesystem_ = filesystem; | 39 this.filesystem_ = filesystem; |
40 this.params_ = params || {}; | 40 this.params_ = location.search ? |
| 41 JSON.parse(decodeURIComponent(location.search.substr(1))) : |
| 42 {}; |
41 | 43 |
42 this.listType_ = null; | 44 this.listType_ = null; |
43 | 45 |
44 this.metadataCache_ = {}; | 46 this.metadataCache_ = {}; |
45 | 47 |
46 this.selection = null; | 48 this.selection = null; |
47 | 49 |
48 this.clipboard_ = null; // Current clipboard, or null if empty. | 50 this.clipboard_ = null; // Current clipboard, or null if empty. |
49 | 51 |
50 this.butterTimer_ = null; | 52 this.butterTimer_ = null; |
(...skipping 74 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
125 chrome.fileBrowserPrivate.getMountPoints(function(mountPoints) { | 127 chrome.fileBrowserPrivate.getMountPoints(function(mountPoints) { |
126 self.mountPoints_ = mountPoints; | 128 self.mountPoints_ = mountPoints; |
127 }); | 129 }); |
128 | 130 |
129 chrome.fileBrowserHandler.onExecute.addListener( | 131 chrome.fileBrowserHandler.onExecute.addListener( |
130 this.onFileTaskExecute_.bind(this)); | 132 this.onFileTaskExecute_.bind(this)); |
131 | 133 |
132 this.initCommands_(); | 134 this.initCommands_(); |
133 this.initDom_(); | 135 this.initDom_(); |
134 this.initDialogType_(); | 136 this.initDialogType_(); |
135 this.initDefaultDirectory_(this.params_.defaultPath); | 137 this.setupCurrentDirectory_(); |
136 | 138 |
137 this.summarizeSelection_(); | 139 this.summarizeSelection_(); |
138 this.updatePreview_(); | 140 this.updatePreview_(); |
139 | 141 |
140 chrome.fileBrowserPrivate.onDiskChanged.addListener( | 142 chrome.fileBrowserPrivate.onDiskChanged.addListener( |
141 this.onDiskChanged_.bind(this)); | 143 this.onDiskChanged_.bind(this)); |
142 | 144 |
143 this.refocus(); | 145 this.refocus(); |
144 | 146 |
145 // Pass all URLs to the metadata reader until we have a correct filter. | 147 // Pass all URLs to the metadata reader until we have a correct filter. |
(...skipping 665 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
811 this.pasteButton_.disabled = !event.canExecute; | 813 this.pasteButton_.disabled = !event.canExecute; |
812 break; | 814 break; |
813 | 815 |
814 case 'rename': | 816 case 'rename': |
815 event.canExecute = | 817 event.canExecute = |
816 (// Initialized to the point where we have a current directory | 818 (// Initialized to the point where we have a current directory |
817 this.currentDirEntry_ && | 819 this.currentDirEntry_ && |
818 // Rename not in progress. | 820 // Rename not in progress. |
819 !this.renameInput_.currentEntry && | 821 !this.renameInput_.currentEntry && |
820 // Only one file selected. | 822 // Only one file selected. |
| 823 this.selection && |
821 this.selection.totalCount == 1 && | 824 this.selection.totalCount == 1 && |
822 !isSystemDirEntry(this.currentDirEntry_)); | 825 !isSystemDirEntry(this.currentDirEntry_)); |
823 break; | 826 break; |
824 | 827 |
825 case 'delete': | 828 case 'delete': |
826 event.canExecute = | 829 event.canExecute = |
827 (// Initialized to the point where we have a current directory | 830 (// Initialized to the point where we have a current directory |
828 this.currentDirEntry_ && | 831 this.currentDirEntry_ && |
829 // Rename not in progress. | 832 // Rename not in progress. |
830 !this.renameInput_.currentEntry && | 833 !this.renameInput_.currentEntry && |
(...skipping 182 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
1013 this.initiateRename_(label); | 1016 this.initiateRename_(label); |
1014 return; | 1017 return; |
1015 | 1018 |
1016 case 'delete': | 1019 case 'delete': |
1017 this.deleteEntries(this.selection.entries); | 1020 this.deleteEntries(this.selection.entries); |
1018 return; | 1021 return; |
1019 } | 1022 } |
1020 }; | 1023 }; |
1021 | 1024 |
1022 /** | 1025 /** |
1023 * Respond to the back button. | 1026 * Respond to the back and forward buttons. |
1024 */ | 1027 */ |
1025 FileManager.prototype.onPopState_ = function(event) { | 1028 FileManager.prototype.onPopState_ = function(event) { |
1026 this.changeDirectory(event.state, CD_NO_HISTORY); | 1029 // TODO(serya): We should restore selected items here. |
| 1030 this.setupCurrentDirectory_(); |
1027 }; | 1031 }; |
1028 | 1032 |
1029 FileManager.prototype.requestResize_ = function(timeout) { | 1033 FileManager.prototype.requestResize_ = function(timeout) { |
1030 var self = this; | 1034 var self = this; |
1031 setTimeout(function() { self.onResize_() }, timeout || 0); | 1035 setTimeout(function() { self.onResize_() }, timeout || 0); |
1032 }; | 1036 }; |
1033 | 1037 |
1034 /** | 1038 /** |
1035 * Resize details and thumb views to fit the new window size. | 1039 * Resize details and thumb views to fit the new window size. |
1036 */ | 1040 */ |
(...skipping 17 matching lines...) Expand all Loading... |
1054 this.currentList_.redraw(); | 1058 this.currentList_.redraw(); |
1055 } | 1059 } |
1056 }; | 1060 }; |
1057 | 1061 |
1058 FileManager.prototype.resolvePath = function( | 1062 FileManager.prototype.resolvePath = function( |
1059 path, resultCallback, errorCallback) { | 1063 path, resultCallback, errorCallback) { |
1060 return util.resolvePath(this.filesystem_.root, path, resultCallback, | 1064 return util.resolvePath(this.filesystem_.root, path, resultCallback, |
1061 errorCallback); | 1065 errorCallback); |
1062 }; | 1066 }; |
1063 | 1067 |
1064 FileManager.prototype.initDefaultDirectory_ = function(path) { | 1068 /** |
1065 if (!path) { | 1069 * Restores current directory and may be a selected item after page load (or |
1066 // No preset given, find a good place to start. | 1070 * reload) or popping a state (after click on back/forward). If location.hash |
1067 // Check for removable devices, if there are none, go to Downloads. | 1071 * is present it means that the user has navigated somewhere and that place |
1068 for (var i = 0; i != this.rootEntries_.length; i++) { | 1072 * will be restored. defaultPath primarily is used with save/open dialogs. |
1069 var rootEntry = this.rootEntries_[i]; | 1073 * Default path may also contain a file name. Freshly opened file manager |
1070 if (rootEntry.fullPath == REMOVABLE_DIRECTORY) { | 1074 * window has neither. |
1071 var foundRemovable = false; | 1075 */ |
1072 var self = this; | 1076 FileManager.prototype.setupCurrentDirectory_ = function() { |
1073 util.forEachDirEntry(rootEntry, function(result) { | 1077 if (location.hash) { |
1074 if (result) { | 1078 // Location hash has the highest priority. |
1075 foundRemovable = true; | 1079 var path = decodeURI(location.hash.substr(1)); |
1076 } else { // Done enumerating, and we know the answer. | 1080 this.changeDirectory(path, CD_NO_HISTORY); |
1077 self.initDefaultDirectory_( | 1081 return; |
1078 foundRemovable ? '/' : DOWNLOADS_DIRECTORY); | 1082 } else if (this.params_.defaultPath) { |
1079 } | 1083 this.setupPath_(this.params_.defaultPath); |
1080 }); | 1084 } else { |
1081 return; | 1085 this.setupDefaultPath_(); |
1082 } | 1086 } |
1083 } | 1087 }; |
1084 | 1088 |
1085 // Removable root directory is missing altogether. | 1089 FileManager.prototype.setupDefaultPath_ = function() { |
1086 path = DOWNLOADS_DIRECTORY; | 1090 // No preset given, find a good place to start. |
| 1091 // Check for removable devices, if there are none, go to Downloads. |
| 1092 var removableDirectoryEntry = this.rootEntries_.filter(function(rootEntry) { |
| 1093 return rootEntry.fullPath == REMOVABLE_DIRECTORY; |
| 1094 })[0]; |
| 1095 if (!removableDirectoryEntry) { |
| 1096 this.changeDirectory(DOWNLOADS_DIRECTORY, CD_NO_HISTORY); |
| 1097 return; |
1087 } | 1098 } |
1088 | 1099 |
| 1100 var foundRemovable = false; |
| 1101 util.forEachDirEntry(removableDirectoryEntry, function(result) { |
| 1102 if (result) { |
| 1103 foundRemovable = true; |
| 1104 } else { // Done enumerating, and we know the answer. |
| 1105 this.changeDirectory(foundRemovable ? '/' : DOWNLOADS_DIRECTORY, |
| 1106 CD_NO_HISTORY); |
| 1107 } |
| 1108 }.bind(this)); |
| 1109 }; |
| 1110 |
| 1111 FileManager.prototype.setupPath_ = function(path) { |
1089 // Split the dirname from the basename. | 1112 // Split the dirname from the basename. |
1090 var ary = path.match(/^(.*?)(?:\/([^\/]+))?$/); | 1113 var ary = path.match(/^(.*?)(?:\/([^\/]+))?$/); |
1091 if (!ary) { | 1114 if (!ary) { |
1092 console.warn('Unable to split default path: ' + path); | 1115 console.warn('Unable to split default path: ' + path); |
1093 self.changeDirectory('/', CD_NO_HISTORY); | 1116 self.changeDirectory('/', CD_NO_HISTORY); |
1094 return; | 1117 return; |
1095 } | 1118 } |
1096 | 1119 |
1097 var baseName = ary[1]; | 1120 var baseName = ary[1]; |
1098 var leafName = ary[2]; | 1121 var leafName = ary[2]; |
(...skipping 916 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
2015 * | 2038 * |
2016 * @param {string} path The absolute path to the new directory. | 2039 * @param {string} path The absolute path to the new directory. |
2017 * @param {bool} opt_saveHistory Save this in the history stack (defaults | 2040 * @param {bool} opt_saveHistory Save this in the history stack (defaults |
2018 * to true). | 2041 * to true). |
2019 * @param {string} opt_selectedEntry The name of the file to select after | 2042 * @param {string} opt_selectedEntry The name of the file to select after |
2020 * changing directories. | 2043 * changing directories. |
2021 */ | 2044 */ |
2022 FileManager.prototype.changeDirectoryEntry = function(dirEntry, | 2045 FileManager.prototype.changeDirectoryEntry = function(dirEntry, |
2023 opt_saveHistory, | 2046 opt_saveHistory, |
2024 opt_selectedEntry) { | 2047 opt_selectedEntry) { |
| 2048 if (typeof opt_saveHistory == 'undefined') { |
| 2049 opt_saveHistory = true; |
| 2050 } else { |
| 2051 opt_saveHistory = !!opt_saveHistory; |
| 2052 } |
| 2053 |
| 2054 var location = '#' + encodeURI(dirEntry.fullPath); |
| 2055 if (opt_saveHistory) { |
| 2056 history.pushState(undefined, dirEntry.fullPath, location); |
| 2057 } else if (window.location.hash != location) { |
| 2058 // If the user typed URL manually that is not canonical it would be fixed |
| 2059 // here. However it seems history.replaceState doesn't work properly |
| 2060 // with rewritable URLs (while does with history.pushState). It changes |
| 2061 // window.location but doesn't change content of the ombibox. |
| 2062 history.replaceState(undefined, dirEntry.fullPath, location); |
| 2063 } |
| 2064 |
2025 if (this.currentDirEntry_ && | 2065 if (this.currentDirEntry_ && |
2026 this.currentDirEntry_.fullPath == dirEntry.fullPath) { | 2066 this.currentDirEntry_.fullPath == dirEntry.fullPath) { |
2027 // Directory didn't actually change. | 2067 // Directory didn't actually change. |
2028 if (opt_selectedEntry) | 2068 if (opt_selectedEntry) |
2029 this.selectEntry(opt_selectedEntry); | 2069 this.selectEntry(opt_selectedEntry); |
2030 return; | 2070 return; |
2031 } | 2071 } |
2032 | 2072 |
2033 if (typeof opt_saveHistory == 'undefined') { | |
2034 opt_saveHistory = true; | |
2035 } else { | |
2036 opt_saveHistory = !!opt_saveHistory; | |
2037 } | |
2038 | |
2039 var e = new cr.Event('directory-changed'); | 2073 var e = new cr.Event('directory-changed'); |
2040 e.previousDirEntry = this.currentDirEntry_; | 2074 e.previousDirEntry = this.currentDirEntry_; |
2041 e.newDirEntry = dirEntry; | 2075 e.newDirEntry = dirEntry; |
2042 e.saveHistory = opt_saveHistory; | 2076 e.saveHistory = opt_saveHistory; |
2043 e.selectedEntry = opt_selectedEntry; | 2077 e.selectedEntry = opt_selectedEntry; |
2044 this.currentDirEntry_ = dirEntry; | 2078 this.currentDirEntry_ = dirEntry; |
2045 this.dispatchEvent(e); | 2079 this.dispatchEvent(e); |
2046 } | 2080 } |
2047 | 2081 |
2048 /** | 2082 /** |
2049 * Change the current directory to the directory represented by a string | 2083 * Change the current directory to the directory represented by a string |
2050 * path. | 2084 * path. |
2051 * | 2085 * |
2052 * Dispatches the 'directory-changed' event when the directory is successfully | 2086 * Dispatches the 'directory-changed' event when the directory is successfully |
2053 * changed. | 2087 * changed. |
2054 * | 2088 * |
2055 * @param {string} path The absolute path to the new directory. | 2089 * @param {string} path The absolute path to the new directory. |
2056 * @param {bool} opt_saveHistory Save this in the history stack (defaults | 2090 * @param {bool} opt_saveHistory Save this in the history stack (defaults |
2057 * to true). | 2091 * to true). |
2058 * @param {string} opt_selectedEntry The name of the file to select after | 2092 * @param {string} opt_selectedEntry The name of the file to select after |
2059 * changing directories. | 2093 * changing directories. |
2060 */ | 2094 */ |
2061 FileManager.prototype.changeDirectory = function(path, | 2095 FileManager.prototype.changeDirectory = function(path, |
2062 opt_saveHistory, | 2096 opt_saveHistory, |
2063 opt_selectedEntry) { | 2097 opt_selectedEntry) { |
2064 if (path == '/') | 2098 if (path == '/') |
2065 return this.changeDirectoryEntry(this.filesystem_.root); | 2099 return this.changeDirectoryEntry(this.filesystem_.root, |
| 2100 opt_saveHistory, |
| 2101 opt_selectedEntry); |
2066 | 2102 |
2067 var self = this; | 2103 var self = this; |
2068 | 2104 |
2069 this.filesystem_.root.getDirectory( | 2105 this.filesystem_.root.getDirectory( |
2070 path, {create: false}, | 2106 path, {create: false}, |
2071 function(dirEntry) { | 2107 function(dirEntry) { |
2072 self.changeDirectoryEntry( | 2108 self.changeDirectoryEntry( |
2073 dirEntry, opt_saveHistory, opt_selectedEntry); | 2109 dirEntry, opt_saveHistory, opt_selectedEntry); |
2074 }, | 2110 }, |
2075 function(err) { | 2111 function(err) { |
2076 console.error('Error changing directory to: ' + path + ', ' + err); | 2112 console.error('Error changing directory to: ' + path + ', ' + err); |
2077 if (!self.currentDirEntry_) { | 2113 if (self.currentDirEntry_) { |
| 2114 var location = '#' + encodeURI(self.currentDirEntry_.fullPath); |
| 2115 history.replaceState(undefined, |
| 2116 self.currentDirEntry_.fullPath, |
| 2117 location); |
| 2118 } else { |
2078 // If we've never successfully changed to a directory, force them | 2119 // If we've never successfully changed to a directory, force them |
2079 // to the root. | 2120 // to the root. |
2080 self.changeDirectory('/', false); | 2121 self.changeDirectory('/', false); |
2081 } | 2122 } |
2082 }); | 2123 }); |
2083 }; | 2124 }; |
2084 | 2125 |
2085 FileManager.prototype.deleteEntries = function(entries, force) { | 2126 FileManager.prototype.deleteEntries = function(entries, force) { |
2086 if (!force) { | 2127 if (!force) { |
2087 var self = this; | 2128 var self = this; |
(...skipping 271 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
2359 this.onOk_(); | 2400 this.onOk_(); |
2360 | 2401 |
2361 }; | 2402 }; |
2362 | 2403 |
2363 /** | 2404 /** |
2364 * Update the UI when the current directory changes. | 2405 * Update the UI when the current directory changes. |
2365 * | 2406 * |
2366 * @param {cr.Event} event The directory-changed event. | 2407 * @param {cr.Event} event The directory-changed event. |
2367 */ | 2408 */ |
2368 FileManager.prototype.onDirectoryChanged_ = function(event) { | 2409 FileManager.prototype.onDirectoryChanged_ = function(event) { |
2369 if (event.saveHistory) { | |
2370 history.pushState(this.currentDirEntry_.fullPath, | |
2371 this.currentDirEntry_.fullPath, | |
2372 location.href); | |
2373 } | |
2374 | |
2375 if (this.currentDirEntry_.fullPath.substr(0, DOWNLOADS_DIRECTORY.length) == | 2410 if (this.currentDirEntry_.fullPath.substr(0, DOWNLOADS_DIRECTORY.length) == |
2376 DOWNLOADS_DIRECTORY) { | 2411 DOWNLOADS_DIRECTORY) { |
2377 if (this.downloadsWarning_.style.height != DOWNLOADS_WARNING_HEIGHT) { | 2412 if (this.downloadsWarning_.style.height != DOWNLOADS_WARNING_HEIGHT) { |
2378 // Current path starts with DOWNLOADS_DIRECTORY, show the warning. | 2413 // Current path starts with DOWNLOADS_DIRECTORY, show the warning. |
2379 this.downloadsWarning_.style.height = DOWNLOADS_WARNING_HEIGHT; | 2414 this.downloadsWarning_.style.height = DOWNLOADS_WARNING_HEIGHT; |
2380 this.requestResize_(100); | 2415 this.requestResize_(100); |
2381 } | 2416 } |
2382 } else { | 2417 } else { |
2383 if (this.downloadsWarning_.style.height != '0') { | 2418 if (this.downloadsWarning_.style.height != '0') { |
2384 this.downloadsWarning_.style.height = '0'; | 2419 this.downloadsWarning_.style.height = '0'; |
(...skipping 462 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
2847 case 69: // Ctrl-E => Rename. | 2882 case 69: // Ctrl-E => Rename. |
2848 this.updateCommands_(); | 2883 this.updateCommands_(); |
2849 if (!this.commands_['rename'].disabled) { | 2884 if (!this.commands_['rename'].disabled) { |
2850 event.preventDefault(); | 2885 event.preventDefault(); |
2851 this.commands_['rename'].execute(); | 2886 this.commands_['rename'].execute(); |
2852 } | 2887 } |
2853 break; | 2888 break; |
2854 | 2889 |
2855 case 46: // Delete. | 2890 case 46: // Delete. |
2856 if (this.dialogType_ == FileManager.DialogType.FULL_PAGE && | 2891 if (this.dialogType_ == FileManager.DialogType.FULL_PAGE && |
2857 this.selection.totalCount > 0 && | 2892 this.selection && this.selection.totalCount > 0 && |
2858 !isSystemDirEntry(this.currentDirEntry_)) { | 2893 !isSystemDirEntry(this.currentDirEntry_)) { |
2859 event.preventDefault(); | 2894 event.preventDefault(); |
2860 this.deleteEntries(this.selection.entries); | 2895 this.deleteEntries(this.selection.entries); |
2861 } | 2896 } |
2862 break; | 2897 break; |
2863 } | 2898 } |
2864 }; | 2899 }; |
2865 | 2900 |
2866 /** | 2901 /** |
2867 * KeyPress event handler for the div.list-container element. | 2902 * KeyPress event handler for the div.list-container element. |
(...skipping 177 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
3045 | 3080 |
3046 if (msg) { | 3081 if (msg) { |
3047 console.log('no no no'); | 3082 console.log('no no no'); |
3048 this.alert.show(msg, onAccept); | 3083 this.alert.show(msg, onAccept); |
3049 return false; | 3084 return false; |
3050 } | 3085 } |
3051 | 3086 |
3052 return true; | 3087 return true; |
3053 }; | 3088 }; |
3054 })(); | 3089 })(); |
OLD | NEW |