OLD | NEW |
| (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 * Butter bar is shown on top of the file list and is used to show the copy | |
9 * progress and other messages. | |
10 * @param {HTMLElement} dialogDom FileManager top-level div. | |
11 * @param {FileOperationManagerWrapper} fileOperationManager The operation | |
12 * manager. | |
13 * @constructor | |
14 */ | |
15 function ButterBar(dialogDom, fileOperationManager) { | |
16 this.dialogDom_ = dialogDom; | |
17 this.butter_ = this.dialogDom_.querySelector('#butter-bar'); | |
18 this.document_ = this.butter_.ownerDocument; | |
19 this.fileOperationManager_ = fileOperationManager; | |
20 this.hideTimeout_ = null; | |
21 this.showTimeout_ = null; | |
22 this.lastShowTime_ = 0; | |
23 this.deleteTaskId_ = null; | |
24 this.currentMode_ = null; | |
25 this.totalDeleted_ = 0; | |
26 this.lastProgressValue_ = 0; | |
27 this.alert_ = new ErrorDialog(this.dialogDom_); | |
28 | |
29 this.onCopyProgressBound_ = this.onCopyProgress_.bind(this); | |
30 this.fileOperationManager_.addEventListener( | |
31 'copy-progress', this.onCopyProgressBound_); | |
32 this.onDeleteBound_ = this.onDelete_.bind(this); | |
33 this.fileOperationManager_.addEventListener('delete', this.onDeleteBound_); | |
34 } | |
35 | |
36 /** | |
37 * The default amount of milliseconds time, before a butter bar will hide after | |
38 * the last update. | |
39 * @type {number} | |
40 * @private | |
41 * @const | |
42 */ | |
43 ButterBar.HIDE_DELAY_TIME_MS_ = 2000; | |
44 | |
45 /** | |
46 * Name of action which should be displayed as an 'x' button instead of | |
47 * link with text. | |
48 * @const | |
49 */ | |
50 ButterBar.ACTION_X = '--action--x--'; | |
51 | |
52 /** | |
53 * Butter bar mode. | |
54 * @const | |
55 */ | |
56 ButterBar.Mode = { | |
57 COPY: 1, | |
58 DELETE: 2 | |
59 }; | |
60 | |
61 /** | |
62 * Disposes the instance. No methods should be called after this method's | |
63 * invocation. | |
64 */ | |
65 ButterBar.prototype.dispose = function() { | |
66 // Unregister listeners from FileOperationManager. | |
67 this.fileOperationManager_.removeEventListener( | |
68 'copy-progress', this.onCopyProgressBound_); | |
69 this.fileOperationManager_.removeEventListener('delete', this.onDeleteBound_); | |
70 }; | |
71 | |
72 /** | |
73 * @return {boolean} True if visible. | |
74 * @private | |
75 */ | |
76 ButterBar.prototype.isVisible_ = function() { | |
77 return this.butter_.classList.contains('visible'); | |
78 }; | |
79 | |
80 /** | |
81 * Show butter bar. | |
82 * @param {ButterBar.Mode} mode Butter bar mode. | |
83 * @param {string} message The message to be shown. | |
84 * @param {Object=} opt_options Options: 'actions', 'progress', 'timeout'. If | |
85 * 'timeout' is not specified, HIDE_DELAY_TIME_MS_ is used. If 'timeout' is | |
86 * false, the butter bar will not be hidden. | |
87 */ | |
88 ButterBar.prototype.show = function(mode, message, opt_options) { | |
89 this.currentMode_ = mode; | |
90 | |
91 this.clearShowTimeout_(); | |
92 this.clearHideTimeout_(); | |
93 | |
94 var actions = this.butter_.querySelector('.actions'); | |
95 actions.textContent = ''; | |
96 if (opt_options && 'actions' in opt_options) { | |
97 for (var label in opt_options.actions) { | |
98 var link = this.document_.createElement('a'); | |
99 link.addEventListener('click', function(callback) { | |
100 callback(); | |
101 return false; | |
102 }.bind(null, opt_options.actions[label])); | |
103 if (label == ButterBar.ACTION_X) { | |
104 link.className = 'x'; | |
105 } else { | |
106 link.textContent = label; | |
107 } | |
108 actions.appendChild(link); | |
109 } | |
110 actions.hidden = false; | |
111 } else { | |
112 actions.hidden = true; | |
113 } | |
114 | |
115 this.butter_.querySelector('.progress-bar').hidden = | |
116 !(opt_options && 'progress' in opt_options); | |
117 | |
118 this.butter_.classList.remove('error'); | |
119 this.butter_.classList.remove('visible'); // Will be shown in update_ | |
120 this.update_(message, opt_options); | |
121 }; | |
122 | |
123 /** | |
124 * Show an error message in a popup dialog. | |
125 * @param {string} message Message. | |
126 * @private | |
127 */ | |
128 ButterBar.prototype.showError_ = function(message) { | |
129 // Wait in case there are previous dialogs being closed. | |
130 setTimeout(function() { | |
131 this.alert_.showHtml('', // Title. | |
132 message); | |
133 this.hide_(); | |
134 }.bind(this), cr.ui.dialogs.BaseDialog.ANIMATE_STABLE_DURATION); | |
135 }; | |
136 | |
137 /** | |
138 * Set message and/or progress. | |
139 * @param {string} message Message. | |
140 * @param {Object=} opt_options Same as in show(). | |
141 * @private | |
142 */ | |
143 ButterBar.prototype.update_ = function(message, opt_options) { | |
144 if (!opt_options) | |
145 opt_options = {}; | |
146 | |
147 this.clearHideTimeout_(); | |
148 | |
149 var butterMessage = this.butter_.querySelector('.butter-message'); | |
150 butterMessage.textContent = message; | |
151 if (message && !this.isVisible_()) { | |
152 // The butter bar is made visible on the first non-empty message. | |
153 this.butter_.classList.add('visible'); | |
154 } | |
155 if (opt_options && 'progress' in opt_options) { | |
156 butterMessage.classList.add('single-line'); | |
157 var progressTrack = this.butter_.querySelector('.progress-track'); | |
158 // Smoothen the progress only when it goes forward. Especially do not | |
159 // do the transition effect if resetting to 0. | |
160 if (opt_options.progress > this.lastProgressValue_) | |
161 progressTrack.classList.add('smoothed'); | |
162 else | |
163 progressTrack.classList.remove('smoothed'); | |
164 progressTrack.style.width = (opt_options.progress * 100) + '%'; | |
165 this.lastProgressValue_ = opt_options.progress; | |
166 } else { | |
167 butterMessage.classList.remove('single-line'); | |
168 } | |
169 | |
170 if (opt_options.timeout !== false) | |
171 this.hide_(opt_options.timeout); | |
172 }; | |
173 | |
174 /** | |
175 * Hide butter bar. There might be the delay before hiding so that butter bar | |
176 * would be shown for no less than the minimal time. | |
177 * @param {number=} opt_timeout Delay time in milliseconds before hidding. If it | |
178 * is zero, butter bar is hidden immediatelly. If it is not specified, | |
179 * HIDE_DELAY_TIME_MS_ is used. | |
180 * @private | |
181 */ | |
182 ButterBar.prototype.hide_ = function(opt_timeout) { | |
183 this.clearHideTimeout_(); | |
184 | |
185 if (!this.isVisible_()) | |
186 return; | |
187 | |
188 var delay = typeof opt_timeout != 'undefined' ? | |
189 opt_timeout : ButterBar.HIDE_DELAY_TIME_MS_; | |
190 if (delay <= 0) { | |
191 this.currentMode_ = null; | |
192 this.butter_.classList.remove('visible'); | |
193 this.butter_.querySelector('.progress-bar').hidden = true; | |
194 } else { | |
195 // Reschedule hide to comply with the minimal display time. | |
196 this.hideTimeout_ = setTimeout(function() { | |
197 this.hideTimeout_ = null; | |
198 this.hide_(0); | |
199 }.bind(this), delay); | |
200 } | |
201 }; | |
202 | |
203 /** | |
204 * Clear the show timeout if it is set. | |
205 * @private | |
206 */ | |
207 ButterBar.prototype.clearShowTimeout_ = function() { | |
208 if (this.showTimeout_) { | |
209 clearTimeout(this.showTimeout_); | |
210 this.showTimeout_ = null; | |
211 } | |
212 }; | |
213 | |
214 /** | |
215 * Clear the hide timeout if it is set. | |
216 * @private | |
217 */ | |
218 ButterBar.prototype.clearHideTimeout_ = function() { | |
219 if (this.hideTimeout_) { | |
220 clearTimeout(this.hideTimeout_); | |
221 this.hideTimeout_ = null; | |
222 } | |
223 }; | |
224 | |
225 /** | |
226 * @return {string} The type of operation. | |
227 * @private | |
228 */ | |
229 ButterBar.prototype.transferType_ = function() { | |
230 var progress = this.progress_; | |
231 if (progress && progress.operationType) | |
232 return progress.operationType; | |
233 | |
234 return 'TRANSFER'; | |
235 }; | |
236 | |
237 /** | |
238 * Set up butter bar for showing copy progress. | |
239 * | |
240 * @param {Object} progress Copy status object created by | |
241 * FileOperationManager.getStatus(). | |
242 * @private | |
243 */ | |
244 ButterBar.prototype.showProgress_ = function(progress) { | |
245 this.progress_ = progress; | |
246 var options = { | |
247 progress: progress.processedBytes / progress.totalBytes, | |
248 actions: {}, | |
249 timeout: false | |
250 }; | |
251 | |
252 var pendingItems = progress.numRemainingItems; | |
253 var type = this.transferType_(); | |
254 var progressString = (pendingItems === 1) ? | |
255 strf(type + '_FILE_NAME', progress.processingEntry.name) : | |
256 strf(type + '_ITEMS_REMAINING', pendingItems); | |
257 | |
258 if (this.currentMode_ == ButterBar.Mode.COPY) { | |
259 this.update_(progressString, options); | |
260 } else { | |
261 options.actions[ButterBar.ACTION_X] = | |
262 this.fileOperationManager_.requestCancel.bind( | |
263 this.fileOperationManager_); | |
264 this.show(ButterBar.Mode.COPY, progressString, options); | |
265 } | |
266 }; | |
267 | |
268 /** | |
269 * 'copy-progress' event handler. Show progress or an appropriate message. | |
270 * @param {Event} event A 'copy-progress' event from FileOperationManager. | |
271 * @private | |
272 */ | |
273 ButterBar.prototype.onCopyProgress_ = function(event) { | |
274 // Delete operation has higher priority. | |
275 if (this.currentMode_ == ButterBar.Mode.DELETE) | |
276 return; | |
277 | |
278 if (event.reason != 'PROGRESS') | |
279 this.clearShowTimeout_(); | |
280 | |
281 switch (event.reason) { | |
282 case 'BEGIN': | |
283 this.showTimeout_ = setTimeout(function() { | |
284 this.showTimeout_ = null; | |
285 this.showProgress_(event.status); | |
286 }.bind(this), 500); | |
287 break; | |
288 | |
289 case 'PROGRESS': | |
290 this.showProgress_(event.status); | |
291 break; | |
292 | |
293 case 'SUCCESS': | |
294 this.hide_(); | |
295 break; | |
296 | |
297 case 'CANCELLED': | |
298 this.show(ButterBar.Mode.DELETE, | |
299 str(this.transferType_() + '_CANCELLED')); | |
300 break; | |
301 | |
302 case 'ERROR': | |
303 this.progress_ = event.status; | |
304 var error = event.error; | |
305 if (error.code === util.FileOperationErrorType.TARGET_EXISTS) { | |
306 var name = error.data.name; | |
307 if (error.data.isDirectory) | |
308 name += '/'; | |
309 this.showError_( | |
310 strf(this.transferType_() + '_TARGET_EXISTS_ERROR', name)); | |
311 } else if (error.code === util.FileOperationErrorType.FILESYSTEM_ERROR) { | |
312 if (error.data.toDrive && | |
313 error.data.code === FileError.QUOTA_EXCEEDED_ERR) { | |
314 // The alert will be shown in FileManager.onCopyProgress_. | |
315 this.hide_(); | |
316 } else { | |
317 this.showError_(strf(this.transferType_() + '_FILESYSTEM_ERROR', | |
318 util.getFileErrorString(error.data.code))); | |
319 } | |
320 } else { | |
321 this.showError_( | |
322 strf(this.transferType_() + '_UNEXPECTED_ERROR', error)); | |
323 } | |
324 break; | |
325 | |
326 default: | |
327 console.warn('Unknown "copy-progress" event reason: ' + event.code); | |
328 } | |
329 }; | |
330 | |
331 /** | |
332 * 'delete' event handler. Shows information about deleting files. | |
333 * @param {Event} event A 'delete' event from FileOperationManager. | |
334 * @private | |
335 */ | |
336 ButterBar.prototype.onDelete_ = function(event) { | |
337 switch (event.reason) { | |
338 case 'BEGIN': | |
339 if (this.currentMode_ != ButterBar.Mode.DELETE) | |
340 this.totalDeleted_ = 0; | |
341 | |
342 case 'PROGRESS': | |
343 this.totalDeleted_ += event.urls.length; | |
344 var title = strf('DELETED_MESSAGE_PLURAL', this.totalDeleted_); | |
345 if (this.totalDeleted_ == 1) { | |
346 var fullPath = util.extractFilePath(event.urls[0]); | |
347 var fileName = PathUtil.split(fullPath).pop(); | |
348 title = strf('DELETED_MESSAGE', fileName); | |
349 } | |
350 | |
351 if (this.currentMode_ == ButterBar.Mode.DELETE) | |
352 this.update_(title); | |
353 else | |
354 this.show(ButterBar.Mode.DELETE, title); | |
355 break; | |
356 | |
357 case 'SUCCESS': | |
358 break; | |
359 | |
360 case 'ERROR': | |
361 this.showError_(str('DELETE_ERROR')); | |
362 break; | |
363 | |
364 default: | |
365 console.warn('Unknown "delete" event reason: ' + event.reason); | |
366 } | |
367 }; | |
OLD | NEW |