blob: 2be649af5ab9a08385b4843bba59a4c8d19b7a65 [file] [log] [blame]
Torne (Richard Coles)58218062012-11-14 11:43:16 +00001// 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
Torne (Richard Coles)2a99a7e2013-03-28 15:31:22 +00005'use strict';
6
Torne (Richard Coles)58218062012-11-14 11:43:16 +00007/**
Torne (Richard Coles)58218062012-11-14 11:43:16 +00008 * Butter bar is shown on top of the file list and is used to show the copy
9 * progress and other messages.
Torne (Richard Coles)58218062012-11-14 11:43:16 +000010 * @param {HTMLElement} dialogDom FileManager top-level div.
11 * @param {FileCopyManagerWrapper} copyManager The copy manager.
Torne (Richard Coles)2a99a7e2013-03-28 15:31:22 +000012 * @constructor
Torne (Richard Coles)58218062012-11-14 11:43:16 +000013 */
Torne (Richard Coles)a36e5922013-08-05 13:57:33 +010014function ButterBar(dialogDom, copyManager) {
Torne (Richard Coles)58218062012-11-14 11:43:16 +000015 this.dialogDom_ = dialogDom;
16 this.butter_ = this.dialogDom_.querySelector('#butter-bar');
17 this.document_ = this.butter_.ownerDocument;
18 this.copyManager_ = copyManager;
Torne (Richard Coles)58218062012-11-14 11:43:16 +000019 this.hideTimeout_ = null;
20 this.showTimeout_ = null;
21 this.lastShowTime_ = 0;
22 this.deleteTaskId_ = null;
Torne (Richard Coles)2a99a7e2013-03-28 15:31:22 +000023 this.currentMode_ = null;
24 this.totalDeleted_ = 0;
25 this.lastProgressValue_ = 0;
Torne (Richard Coles)868fa2f2013-06-11 10:57:03 +010026 this.alert_ = new ErrorDialog(this.dialogDom_);
Torne (Richard Coles)58218062012-11-14 11:43:16 +000027
Ben Murdochd3868032013-07-31 10:55:33 +010028 this.onCopyProgressBound_ = this.onCopyProgress_.bind(this);
29 this.copyManager_.addEventListener(
30 'copy-progress', this.onCopyProgressBound_);
31 this.onDeleteBound_ = this.onDelete_.bind(this);
32 this.copyManager_.addEventListener('delete', this.onDeleteBound_);
Torne (Richard Coles)58218062012-11-14 11:43:16 +000033}
34
35/**
Ben Murdocheb525c52013-07-10 11:40:50 +010036 * The default amount of milliseconds time, before a butter bar will hide after
37 * the last update.
38 * @type {number}
39 * @private
40 * @const
41 */
42ButterBar.HIDE_DELAY_TIME_MS_ = 2000;
43
44/**
Torne (Richard Coles)58218062012-11-14 11:43:16 +000045 * Name of action which should be displayed as an 'x' button instead of
46 * link with text.
Torne (Richard Coles)2a99a7e2013-03-28 15:31:22 +000047 * @const
Torne (Richard Coles)58218062012-11-14 11:43:16 +000048 */
49ButterBar.ACTION_X = '--action--x--';
50
51/**
Torne (Richard Coles)2a99a7e2013-03-28 15:31:22 +000052 * Butter bar mode.
53 * @const
54 */
55ButterBar.Mode = {
56 COPY: 1,
Torne (Richard Coles)868fa2f2013-06-11 10:57:03 +010057 DELETE: 2
Torne (Richard Coles)2a99a7e2013-03-28 15:31:22 +000058};
59
60/**
Ben Murdochd3868032013-07-31 10:55:33 +010061 * Disposes the instance. No methods should be called after this method's
62 * invocation.
63 */
64ButterBar.prototype.dispose = function() {
65 // Unregister listeners from FileCopyManager.
66 this.copyManager_.removeEventListener(
67 'copy-progress', this.onCopyProgressBound_);
68 this.copyManager_.removeEventListener('delete', this.onDeleteBound_);
69};
70
71/**
Torne (Richard Coles)58218062012-11-14 11:43:16 +000072 * @return {boolean} True if visible.
73 * @private
74 */
75ButterBar.prototype.isVisible_ = function() {
76 return this.butter_.classList.contains('visible');
77};
78
79/**
Torne (Richard Coles)58218062012-11-14 11:43:16 +000080 * Show butter bar.
Torne (Richard Coles)2a99a7e2013-03-28 15:31:22 +000081 * @param {ButterBar.Mode} mode Butter bar mode.
Torne (Richard Coles)58218062012-11-14 11:43:16 +000082 * @param {string} message The message to be shown.
Ben Murdocheb525c52013-07-10 11:40:50 +010083 * @param {Object=} opt_options Options: 'actions', 'progress', 'timeout'. If
84 * 'timeout' is not specified, HIDE_DELAY_TIME_MS_ is used. If 'timeout' is
85 * false, the butter bar will not be hidden.
Torne (Richard Coles)58218062012-11-14 11:43:16 +000086 */
Torne (Richard Coles)2a99a7e2013-03-28 15:31:22 +000087ButterBar.prototype.show = function(mode, message, opt_options) {
88 this.currentMode_ = mode;
89
Torne (Richard Coles)58218062012-11-14 11:43:16 +000090 this.clearShowTimeout_();
91 this.clearHideTimeout_();
92
93 var actions = this.butter_.querySelector('.actions');
94 actions.textContent = '';
95 if (opt_options && 'actions' in opt_options) {
96 for (var label in opt_options.actions) {
97 var link = this.document_.createElement('a');
98 link.addEventListener('click', function(callback) {
99 callback();
100 return false;
101 }.bind(null, opt_options.actions[label]));
102 if (label == ButterBar.ACTION_X) {
103 link.className = 'x';
104 } else {
105 link.textContent = label;
106 }
107 actions.appendChild(link);
108 }
109 actions.hidden = false;
110 } else {
111 actions.hidden = true;
112 }
113
114 this.butter_.querySelector('.progress-bar').hidden =
115 !(opt_options && 'progress' in opt_options);
116
117 this.butter_.classList.remove('error');
118 this.butter_.classList.remove('visible'); // Will be shown in update_
119 this.update_(message, opt_options);
120};
121
122/**
Torne (Richard Coles)868fa2f2013-06-11 10:57:03 +0100123 * Show an error message in a popup dialog.
Torne (Richard Coles)58218062012-11-14 11:43:16 +0000124 * @param {string} message Message.
Torne (Richard Coles)2a99a7e2013-03-28 15:31:22 +0000125 * @private
Torne (Richard Coles)58218062012-11-14 11:43:16 +0000126 */
Torne (Richard Coles)868fa2f2013-06-11 10:57:03 +0100127ButterBar.prototype.showError_ = function(message) {
128 // Wait in case there are previous dialogs being closed.
129 setTimeout(function() {
Torne (Richard Coles)868fa2f2013-06-11 10:57:03 +0100130 this.alert_.showHtml('', // Title.
131 message);
Ben Murdocheb525c52013-07-10 11:40:50 +0100132 this.hide_();
Torne (Richard Coles)868fa2f2013-06-11 10:57:03 +0100133 }.bind(this), cr.ui.dialogs.BaseDialog.ANIMATE_STABLE_DURATION);
Torne (Richard Coles)58218062012-11-14 11:43:16 +0000134};
135
136/**
137 * Set message and/or progress.
Torne (Richard Coles)58218062012-11-14 11:43:16 +0000138 * @param {string} message Message.
Torne (Richard Coles)2a99a7e2013-03-28 15:31:22 +0000139 * @param {Object=} opt_options Same as in show().
140 * @private
Torne (Richard Coles)58218062012-11-14 11:43:16 +0000141 */
142ButterBar.prototype.update_ = function(message, opt_options) {
143 if (!opt_options)
144 opt_options = {};
145
146 this.clearHideTimeout_();
147
Torne (Richard Coles)2a99a7e2013-03-28 15:31:22 +0000148 var butterMessage = this.butter_.querySelector('.butter-message');
149 butterMessage.textContent = message;
Torne (Richard Coles)58218062012-11-14 11:43:16 +0000150 if (message && !this.isVisible_()) {
151 // The butter bar is made visible on the first non-empty message.
152 this.butter_.classList.add('visible');
Torne (Richard Coles)58218062012-11-14 11:43:16 +0000153 }
154 if (opt_options && 'progress' in opt_options) {
Torne (Richard Coles)2a99a7e2013-03-28 15:31:22 +0000155 butterMessage.classList.add('single-line');
156 var progressTrack = this.butter_.querySelector('.progress-track');
157 // Smoothen the progress only when it goes forward. Especially do not
158 // do the transition effect if resetting to 0.
159 if (opt_options.progress > this.lastProgressValue_)
160 progressTrack.classList.add('smoothed');
161 else
162 progressTrack.classList.remove('smoothed');
163 progressTrack.style.width = (opt_options.progress * 100) + '%';
164 this.lastProgressValue_ = opt_options.progress;
165 } else {
166 butterMessage.classList.remove('single-line');
Torne (Richard Coles)58218062012-11-14 11:43:16 +0000167 }
Ben Murdocheb525c52013-07-10 11:40:50 +0100168
169 if (opt_options.timeout !== false)
170 this.hide_(opt_options.timeout);
Torne (Richard Coles)58218062012-11-14 11:43:16 +0000171};
172
173/**
Ben Murdocheb525c52013-07-10 11:40:50 +0100174 * Hide butter bar. There might be the delay before hiding so that butter bar
Torne (Richard Coles)58218062012-11-14 11:43:16 +0000175 * would be shown for no less than the minimal time.
Ben Murdocheb525c52013-07-10 11:40:50 +0100176 * @param {number=} opt_timeout Delay time in milliseconds before hidding. If it
177 * is zero, butter bar is hidden immediatelly. If it is not specified,
178 * HIDE_DELAY_TIME_MS_ is used.
Torne (Richard Coles)58218062012-11-14 11:43:16 +0000179 * @private
180 */
Ben Murdocheb525c52013-07-10 11:40:50 +0100181ButterBar.prototype.hide_ = function(opt_timeout) {
Torne (Richard Coles)58218062012-11-14 11:43:16 +0000182 this.clearHideTimeout_();
183
184 if (!this.isVisible_())
185 return;
186
Ben Murdocheb525c52013-07-10 11:40:50 +0100187 var delay = typeof opt_timeout != 'undefined' ?
188 opt_timeout : ButterBar.HIDE_DELAY_TIME_MS_;
189 if (delay <= 0) {
Torne (Richard Coles)2a99a7e2013-03-28 15:31:22 +0000190 this.currentMode_ = null;
Torne (Richard Coles)58218062012-11-14 11:43:16 +0000191 this.butter_.classList.remove('visible');
Torne (Richard Coles)2a99a7e2013-03-28 15:31:22 +0000192 this.butter_.querySelector('.progress-bar').hidden = true;
Torne (Richard Coles)58218062012-11-14 11:43:16 +0000193 } else {
194 // Reschedule hide to comply with the minimal display time.
195 this.hideTimeout_ = setTimeout(function() {
196 this.hideTimeout_ = null;
Ben Murdocheb525c52013-07-10 11:40:50 +0100197 this.hide_(0);
Torne (Richard Coles)58218062012-11-14 11:43:16 +0000198 }.bind(this), delay);
199 }
200};
201
202/**
Torne (Richard Coles)58218062012-11-14 11:43:16 +0000203 * Clear the show timeout if it is set.
204 * @private
205 */
206ButterBar.prototype.clearShowTimeout_ = function() {
207 if (this.showTimeout_) {
208 clearTimeout(this.showTimeout_);
209 this.showTimeout_ = null;
210 }
211};
212
213/**
214 * Clear the hide timeout if it is set.
215 * @private
216 */
217ButterBar.prototype.clearHideTimeout_ = function() {
218 if (this.hideTimeout_) {
219 clearTimeout(this.hideTimeout_);
220 this.hideTimeout_ = null;
221 }
222};
223
224/**
Torne (Richard Coles)58218062012-11-14 11:43:16 +0000225 * @return {string?} The type of operation.
Torne (Richard Coles)2a99a7e2013-03-28 15:31:22 +0000226 * @private
Torne (Richard Coles)58218062012-11-14 11:43:16 +0000227 */
228ButterBar.prototype.transferType_ = function() {
229 var progress = this.progress_;
Torne (Richard Coles)2a99a7e2013-03-28 15:31:22 +0000230 if (!progress)
Torne (Richard Coles)58218062012-11-14 11:43:16 +0000231 return 'TRANSFER';
232
Torne (Richard Coles)2a99a7e2013-03-28 15:31:22 +0000233 var pendingTransferTypesCount =
234 (progress.pendingMoves === 0 ? 0 : 1) +
235 (progress.pendingCopies === 0 ? 0 : 1) +
236 (progress.pendingZips === 0 ? 0 : 1);
Torne (Richard Coles)58218062012-11-14 11:43:16 +0000237
Torne (Richard Coles)2a99a7e2013-03-28 15:31:22 +0000238 if (pendingTransferTypesCount != 1)
239 return 'TRANSFER';
240 else if (progress.pendingMoves > 0)
241 return 'MOVE';
242 else if (progress.pendingCopies > 0)
243 return 'COPY';
244 else
245 return 'ZIP';
Torne (Richard Coles)58218062012-11-14 11:43:16 +0000246};
247
248/**
249 * Set up butter bar for showing copy progress.
Ben Murdocha3f7b4e2013-07-24 10:36:34 +0100250 *
251 * @param {Object} progress Copy status object created by
252 * FileCopyManager.getStatus().
Torne (Richard Coles)58218062012-11-14 11:43:16 +0000253 * @private
254 */
Ben Murdocha3f7b4e2013-07-24 10:36:34 +0100255ButterBar.prototype.showProgress_ = function(progress) {
256 this.progress_ = progress;
Ben Murdocheb525c52013-07-10 11:40:50 +0100257 var options = {
Ben Murdochbb1529c2013-08-08 10:24:53 +0100258 progress: progress.completedBytes / progress.totalBytes,
Ben Murdocheb525c52013-07-10 11:40:50 +0100259 actions: {},
260 timeout: false
261 };
Torne (Richard Coles)58218062012-11-14 11:43:16 +0000262
Ben Murdochbb1529c2013-08-08 10:24:53 +0100263 var pendingItems = progress.totalItems - progress.completedItems;
Torne (Richard Coles)58218062012-11-14 11:43:16 +0000264 var type = this.transferType_();
Ben Murdochbb1529c2013-08-08 10:24:53 +0100265 var progressString = (pendingItems === 1) ?
266 strf(type + '_FILE_NAME', progress.filename) :
267 strf(type + '_ITEMS_REMAINING', pendingItems);
Torne (Richard Coles)58218062012-11-14 11:43:16 +0000268
Torne (Richard Coles)2a99a7e2013-03-28 15:31:22 +0000269 if (this.currentMode_ == ButterBar.Mode.COPY) {
Torne (Richard Coles)58218062012-11-14 11:43:16 +0000270 this.update_(progressString, options);
271 } else {
272 options.actions[ButterBar.ACTION_X] =
273 this.copyManager_.requestCancel.bind(this.copyManager_);
Torne (Richard Coles)2a99a7e2013-03-28 15:31:22 +0000274 this.show(ButterBar.Mode.COPY, progressString, options);
Torne (Richard Coles)58218062012-11-14 11:43:16 +0000275 }
276};
277
278/**
279 * 'copy-progress' event handler. Show progress or an appropriate message.
Torne (Richard Coles)58218062012-11-14 11:43:16 +0000280 * @param {cr.Event} event A 'copy-progress' event from FileCopyManager.
Torne (Richard Coles)2a99a7e2013-03-28 15:31:22 +0000281 * @private
Torne (Richard Coles)58218062012-11-14 11:43:16 +0000282 */
283ButterBar.prototype.onCopyProgress_ = function(event) {
284 // Delete operation has higher priority.
Torne (Richard Coles)2a99a7e2013-03-28 15:31:22 +0000285 if (this.currentMode_ == ButterBar.Mode.DELETE)
286 return;
Torne (Richard Coles)58218062012-11-14 11:43:16 +0000287
288 if (event.reason != 'PROGRESS')
289 this.clearShowTimeout_();
290
291 switch (event.reason) {
292 case 'BEGIN':
293 this.showTimeout_ = setTimeout(function() {
294 this.showTimeout_ = null;
Ben Murdocha3f7b4e2013-07-24 10:36:34 +0100295 this.showProgress_(event.status);
Torne (Richard Coles)58218062012-11-14 11:43:16 +0000296 }.bind(this), 500);
297 break;
298
299 case 'PROGRESS':
Ben Murdocha3f7b4e2013-07-24 10:36:34 +0100300 this.showProgress_(event.status);
Torne (Richard Coles)58218062012-11-14 11:43:16 +0000301 break;
302
303 case 'SUCCESS':
304 this.hide_();
305 break;
306
307 case 'CANCELLED':
Torne (Richard Coles)2a99a7e2013-03-28 15:31:22 +0000308 this.show(ButterBar.Mode.DELETE,
Ben Murdocheb525c52013-07-10 11:40:50 +0100309 str(this.transferType_() + '_CANCELLED'));
Torne (Richard Coles)58218062012-11-14 11:43:16 +0000310 break;
311
312 case 'ERROR':
Ben Murdocha3f7b4e2013-07-24 10:36:34 +0100313 this.progress_ = event.status;
Ben Murdoch558790d2013-07-30 15:19:42 +0100314 var error = event.error;
315 if (error.code === util.FileOperationErrorType.TARGET_EXISTS) {
316 var name = error.data.name;
317 if (error.data.isDirectory)
Torne (Richard Coles)58218062012-11-14 11:43:16 +0000318 name += '/';
Ben Murdoch558790d2013-07-30 15:19:42 +0100319 this.showError_(
320 strf(this.transferType_() + '_TARGET_EXISTS_ERROR', name));
321 } else if (error.code === util.FileOperationErrorType.FILESYSTEM_ERROR) {
322 if (error.data.toDrive &&
323 error.data.code === FileError.QUOTA_EXCEEDED_ERR) {
Torne (Richard Coles)58218062012-11-14 11:43:16 +0000324 // The alert will be shown in FileManager.onCopyProgress_.
325 this.hide_();
326 } else {
327 this.showError_(strf(this.transferType_() + '_FILESYSTEM_ERROR',
Ben Murdoch558790d2013-07-30 15:19:42 +0100328 util.getFileErrorString(error.data.code)));
Torne (Richard Coles)58218062012-11-14 11:43:16 +0000329 }
330 } else {
Ben Murdoch558790d2013-07-30 15:19:42 +0100331 this.showError_(
332 strf(this.transferType_() + '_UNEXPECTED_ERROR', error));
Torne (Richard Coles)58218062012-11-14 11:43:16 +0000333 }
334 break;
335
336 default:
Ben Murdoch558790d2013-07-30 15:19:42 +0100337 console.warn('Unknown "copy-progress" event reason: ' + event.code);
Torne (Richard Coles)58218062012-11-14 11:43:16 +0000338 }
339};
340
341/**
Torne (Richard Coles)58218062012-11-14 11:43:16 +0000342 * 'delete' event handler. Shows information about deleting files.
Torne (Richard Coles)58218062012-11-14 11:43:16 +0000343 * @param {cr.Event} event A 'delete' event from FileCopyManager.
Torne (Richard Coles)2a99a7e2013-03-28 15:31:22 +0000344 * @private
Torne (Richard Coles)58218062012-11-14 11:43:16 +0000345 */
346ButterBar.prototype.onDelete_ = function(event) {
Torne (Richard Coles)58218062012-11-14 11:43:16 +0000347 switch (event.reason) {
Torne (Richard Coles)2a99a7e2013-03-28 15:31:22 +0000348 case 'BEGIN':
349 if (this.currentMode_ != ButterBar.Mode.DELETE)
350 this.totalDeleted_ = 0;
Torne (Richard Coles)868fa2f2013-06-11 10:57:03 +0100351
Torne (Richard Coles)2a99a7e2013-03-28 15:31:22 +0000352 case 'PROGRESS':
Torne (Richard Coles)a36e5922013-08-05 13:57:33 +0100353 this.totalDeleted_ += event.urls.length;
Torne (Richard Coles)2a99a7e2013-03-28 15:31:22 +0000354 var title = strf('DELETED_MESSAGE_PLURAL', this.totalDeleted_);
355 if (this.totalDeleted_ == 1) {
Torne (Richard Coles)a36e5922013-08-05 13:57:33 +0100356 var fullPath = util.extractFilePath(event.urls[0]);
Torne (Richard Coles)58218062012-11-14 11:43:16 +0000357 var fileName = PathUtil.split(fullPath).pop();
358 title = strf('DELETED_MESSAGE', fileName);
359 }
360
Torne (Richard Coles)2a99a7e2013-03-28 15:31:22 +0000361 if (this.currentMode_ == ButterBar.Mode.DELETE)
362 this.update_(title);
363 else
Ben Murdocheb525c52013-07-10 11:40:50 +0100364 this.show(ButterBar.Mode.DELETE, title);
Torne (Richard Coles)58218062012-11-14 11:43:16 +0000365 break;
366
Torne (Richard Coles)58218062012-11-14 11:43:16 +0000367 case 'SUCCESS':
Torne (Richard Coles)868fa2f2013-06-11 10:57:03 +0100368 break;
369
370 case 'ERROR':
Torne (Richard Coles)868fa2f2013-06-11 10:57:03 +0100371 this.showError_(str('DELETE_ERROR'));
Torne (Richard Coles)58218062012-11-14 11:43:16 +0000372 break;
373
374 default:
Torne (Richard Coles)c2e0dbd2013-05-09 18:35:53 +0100375 console.warn('Unknown "delete" event reason: ' + event.reason);
Torne (Richard Coles)58218062012-11-14 11:43:16 +0000376 }
377};