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

Side by Side Diff: chrome/browser/resources/file_manager/js/file_selection.js

Issue 39123003: [Files.app] Split the JavaScript files into subdirectories: common, background, and foreground (Closed) Base URL: svn://svn.chromium.org/chrome/trunk/src
Patch Set: fixed test failure. Created 7 years, 1 month ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch | Annotate | Revision Log
OLDNEW
(Empty)
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
3 // found in the LICENSE file.
4
5 'use strict';
6
7 /**
8 * The current selection object.
9 *
10 * @param {FileManager} fileManager FileManager instance.
11 * @param {Array.<number>} indexes Selected indexes.
12 * @constructor
13 */
14 function FileSelection(fileManager, indexes) {
15 this.fileManager_ = fileManager;
16 this.computeBytesSequence_ = 0;
17 this.indexes = indexes;
18 this.entries = [];
19 this.urls = [];
20 this.totalCount = 0;
21 this.fileCount = 0;
22 this.directoryCount = 0;
23 this.bytes = 0;
24 this.showBytes = false;
25 this.allDriveFilesPresent = false,
26 this.iconType = null;
27 this.bytesKnown = false;
28 this.mustBeHidden_ = false;
29 this.mimeTypes = null;
30
31 // Synchronously compute what we can.
32 for (var i = 0; i < this.indexes.length; i++) {
33 var entry = fileManager.getFileList().item(this.indexes[i]);
34 if (!entry)
35 continue;
36
37 this.entries.push(entry);
38 this.urls.push(entry.toURL());
39
40 if (this.iconType == null) {
41 this.iconType = FileType.getIcon(entry);
42 } else if (this.iconType != 'unknown') {
43 var iconType = FileType.getIcon(entry);
44 if (this.iconType != iconType)
45 this.iconType = 'unknown';
46 }
47
48 if (entry.isFile) {
49 this.fileCount += 1;
50 } else {
51 this.directoryCount += 1;
52 }
53 this.totalCount++;
54 }
55
56 this.tasks = new FileTasks(this.fileManager_);
57
58 Object.seal(this);
59 }
60
61 /**
62 * Computes data required to get file tasks and requests the tasks.
63 *
64 * @param {function} callback The callback.
65 */
66 FileSelection.prototype.createTasks = function(callback) {
67 if (!this.fileManager_.isOnDrive()) {
68 this.tasks.init(this.urls);
69 callback();
70 return;
71 }
72
73 this.fileManager_.metadataCache_.get(this.urls, 'drive', function(props) {
74 var present = props.filter(function(p) { return p && p.availableOffline });
75 this.allDriveFilesPresent = present.length == props.length;
76
77 // Collect all of the mime types and push that info into the selection.
78 this.mimeTypes = props.map(function(value) {
79 return (value && value.contentMimeType) || '';
80 });
81
82 this.tasks.init(this.urls, this.mimeTypes);
83 callback();
84 }.bind(this));
85 };
86
87 /**
88 * Computes the total size of selected files.
89 *
90 * @param {function} callback Completion callback. Not called when cancelled,
91 * or a new call has been invoked in the meantime.
92 */
93 FileSelection.prototype.computeBytes = function(callback) {
94 if (this.entries.length == 0) {
95 this.bytesKnown = true;
96 this.showBytes = false;
97 this.bytes = 0;
98 return;
99 }
100
101 var computeBytesSequence = ++this.computeBytesSequence_;
102 var pendingMetadataCount = 0;
103
104 var maybeDone = function() {
105 if (pendingMetadataCount == 0) {
106 this.bytesKnown = true;
107 callback();
108 }
109 }.bind(this);
110
111 var onProps = function(properties) {
112 // Ignore if the call got cancelled, or there is another new one fired.
113 if (computeBytesSequence != this.computeBytesSequence_)
114 return;
115
116 // It may happen that the metadata is not available because a file has been
117 // deleted in the meantime.
118 if (properties)
119 this.bytes += properties.size;
120 pendingMetadataCount--;
121 maybeDone();
122 }.bind(this);
123
124 for (var index = 0; index < this.entries.length; index++) {
125 var entry = this.entries[index];
126 if (entry.isFile) {
127 this.showBytes |= !FileType.isHosted(entry);
128 pendingMetadataCount++;
129 this.fileManager_.metadataCache_.get(entry, 'filesystem', onProps);
130 } else if (entry.isDirectory) {
131 // Don't compute the directory size as it's expensive.
132 // crbug.com/179073.
133 this.showBytes = false;
134 break;
135 }
136 }
137 maybeDone();
138 };
139
140 /**
141 * Cancels any async computation by increasing the sequence number. Results
142 * of any previous call to computeBytes() will be discarded.
143 *
144 * @private
145 */
146 FileSelection.prototype.cancelComputing_ = function() {
147 this.computeBytesSequence_++;
148 };
149
150 /**
151 * This object encapsulates everything related to current selection.
152 *
153 * @param {FileManager} fileManager File manager instance.
154 * @extends {cr.EventTarget}
155 * @constructor
156 */
157 function FileSelectionHandler(fileManager) {
158 this.fileManager_ = fileManager;
159 // TODO(dgozman): create a shared object with most of UI elements.
160 this.okButton_ = fileManager.okButton_;
161 this.filenameInput_ = fileManager.filenameInput_;
162 this.previewPanel_ = fileManager.previewPanel_;
163 this.taskItems_ = fileManager.taskItems_;
164 }
165
166 /**
167 * Create the temporary disabled action menu item.
168 * @return {Object} Created disabled item.
169 * @private
170 */
171 FileSelectionHandler.createTemporaryDisabledActionMenuItem_ = function() {
172 if (!FileSelectionHandler.cachedDisabledActionMenuItem_) {
173 FileSelectionHandler.cachedDisabledActionMenuItem_ = {
174 label: str('ACTION_OPEN'),
175 disabled: true
176 };
177 }
178
179 return FileSelectionHandler.cachedDisabledActionMenuItem_;
180 };
181
182 /**
183 * Cached the temporary disabled action menu item. Used inside
184 * FileSelectionHandler.createTemporaryDisabledActionMenuItem_().
185 * @private
186 */
187 FileSelectionHandler.cachedDisabledActionMenuItem_ = null;
188
189 /**
190 * FileSelectionHandler extends cr.EventTarget.
191 */
192 FileSelectionHandler.prototype.__proto__ = cr.EventTarget.prototype;
193
194 /**
195 * Maximum amount of thumbnails in the preview pane.
196 *
197 * @const
198 * @type {number}
199 */
200 FileSelectionHandler.MAX_PREVIEW_THUMBNAIL_COUNT = 4;
201
202 /**
203 * Maximum width or height of an image what pops up when the mouse hovers
204 * thumbnail in the bottom panel (in pixels).
205 *
206 * @const
207 * @type {number}
208 */
209 FileSelectionHandler.IMAGE_HOVER_PREVIEW_SIZE = 200;
210
211 /**
212 * Update the UI when the selection model changes.
213 *
214 * @param {Event} event The change event.
215 */
216 FileSelectionHandler.prototype.onFileSelectionChanged = function(event) {
217 var indexes =
218 this.fileManager_.getCurrentList().selectionModel.selectedIndexes;
219 if (this.selection) this.selection.cancelComputing_();
220 var selection = new FileSelection(this.fileManager_, indexes);
221 this.selection = selection;
222
223 if (this.fileManager_.dialogType == DialogType.SELECT_SAVEAS_FILE) {
224 // If this is a save-as dialog, copy the selected file into the filename
225 // input text box.
226 if (this.selection.totalCount == 1 &&
227 this.selection.entries[0].isFile &&
228 this.filenameInput_.value != this.selection.entries[0].name) {
229 this.filenameInput_.value = this.selection.entries[0].name;
230 }
231 }
232
233 this.updateOkButton();
234
235 if (this.selectionUpdateTimer_) {
236 clearTimeout(this.selectionUpdateTimer_);
237 this.selectionUpdateTimer_ = null;
238 }
239
240 // The rest of the selection properties are computed via (sometimes lengthy)
241 // asynchronous calls. We initiate these calls after a timeout. If the
242 // selection is changing quickly we only do this once when it slows down.
243
244 var updateDelay = 200;
245 var now = Date.now();
246 if (now > (this.lastFileSelectionTime_ || 0) + updateDelay) {
247 // The previous selection change happened a while ago. Update the UI soon.
248 updateDelay = 0;
249 }
250 this.lastFileSelectionTime_ = now;
251
252 if (this.fileManager_.dialogType === DialogType.FULL_PAGE &&
253 selection.directoryCount === 0 && selection.fileCount > 0) {
254 // Show disabled items for position calculation of the menu. They will be
255 // overridden in this.updateFileSelectionAsync().
256 this.fileManager_.updateContextMenuActionItems(
257 FileSelectionHandler.createTemporaryDisabledActionMenuItem_(), true);
258 } else {
259 // Update context menu.
260 this.fileManager_.updateContextMenuActionItems(null, false);
261 }
262
263 this.selectionUpdateTimer_ = setTimeout(function() {
264 this.selectionUpdateTimer_ = null;
265 if (this.selection == selection)
266 this.updateFileSelectionAsync(selection);
267 }.bind(this), updateDelay);
268 };
269
270 /**
271 * Updates the Ok button enabled state.
272 *
273 * @return {boolean} Whether button is enabled.
274 */
275 FileSelectionHandler.prototype.updateOkButton = function() {
276 var selectable;
277 var dialogType = this.fileManager_.dialogType;
278
279 if (dialogType == DialogType.SELECT_FOLDER ||
280 dialogType == DialogType.SELECT_UPLOAD_FOLDER) {
281 // In SELECT_FOLDER mode, we allow to select current directory
282 // when nothing is selected.
283 selectable = this.selection.directoryCount <= 1 &&
284 this.selection.fileCount == 0;
285 } else if (dialogType == DialogType.SELECT_OPEN_FILE) {
286 selectable = (this.isFileSelectionAvailable() &&
287 this.selection.directoryCount == 0 &&
288 this.selection.fileCount == 1);
289 } else if (dialogType == DialogType.SELECT_OPEN_MULTI_FILE) {
290 selectable = (this.isFileSelectionAvailable() &&
291 this.selection.directoryCount == 0 &&
292 this.selection.fileCount >= 1);
293 } else if (dialogType == DialogType.SELECT_SAVEAS_FILE) {
294 if (this.fileManager_.isOnReadonlyDirectory()) {
295 selectable = false;
296 } else {
297 selectable = !!this.filenameInput_.value;
298 }
299 } else if (dialogType == DialogType.FULL_PAGE) {
300 // No "select" buttons on the full page UI.
301 selectable = true;
302 } else {
303 throw new Error('Unknown dialog type');
304 }
305
306 this.okButton_.disabled = !selectable;
307 return selectable;
308 };
309
310 /**
311 * Check if all the files in the current selection are available. The only
312 * case when files might be not available is when the selection contains
313 * uncached Drive files and the browser is offline.
314 *
315 * @return {boolean} True if all files in the current selection are
316 * available.
317 */
318 FileSelectionHandler.prototype.isFileSelectionAvailable = function() {
319 return !this.fileManager_.isOnDrive() ||
320 !this.fileManager_.isDriveOffline() ||
321 this.selection.allDriveFilesPresent;
322 };
323
324 /**
325 * Calculates async selection stats and updates secondary UI elements.
326 *
327 * @param {FileSelection} selection The selection object.
328 */
329 FileSelectionHandler.prototype.updateFileSelectionAsync = function(selection) {
330 if (this.selection != selection) return;
331
332 // Update the file tasks.
333 if (this.fileManager_.dialogType === DialogType.FULL_PAGE &&
334 selection.directoryCount === 0 && selection.fileCount > 0) {
335 selection.createTasks(function() {
336 if (this.selection != selection)
337 return;
338 selection.tasks.display(this.taskItems_);
339 selection.tasks.updateMenuItem();
340 }.bind(this));
341 } else {
342 this.taskItems_.hidden = true;
343 }
344
345 // Update preview panels.
346 var wasVisible = this.previewPanel_.visible;
347 this.previewPanel_.setSelection(selection);
348 this.previewPanel_.thumbnails.selection = (selection.totalCount != 0) ?
349 selection :
350 {entries: [this.fileManager_.getCurrentDirectoryEntry()]};
351
352 // Scroll to item
353 if (!wasVisible && this.selection.totalCount == 1) {
354 var list = this.fileManager_.getCurrentList();
355 list.scrollIndexIntoView(list.selectionModel.selectedIndex);
356 }
357
358 // Sync the commands availability.
359 if (this.fileManager_.commandHandler)
360 this.fileManager_.commandHandler.updateAvailability();
361
362 // Inform tests it's OK to click buttons now.
363 if (selection.totalCount > 0) {
364 chrome.test.sendMessage('selection-change-complete');
365 }
366 };
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698