- add sources.
[platform/framework/web/crosswalk.git] / src / chrome / browser / resources / file_manager / foreground / js / butter_bar.js
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   if (this.currentMode_ == ButterBar.Mode.COPY) {
253     this.update_('', options);
254   } else {
255     // Ignore the cancel behavior because the butter bar is already obsoleted.
256     options.actions[ButterBar.ACTION_X] = function() {
257     };
258     this.show(ButterBar.Mode.COPY, '', options);
259   }
260 };
261
262 /**
263  * 'copy-progress' event handler. Show progress or an appropriate message.
264  * @param {Event} event A 'copy-progress' event from FileOperationManager.
265  * @private
266  */
267 ButterBar.prototype.onCopyProgress_ = function(event) {
268   // Delete operation has higher priority.
269   if (this.currentMode_ == ButterBar.Mode.DELETE)
270     return;
271
272   if (event.reason != 'PROGRESS')
273     this.clearShowTimeout_();
274
275   switch (event.reason) {
276     case 'BEGIN':
277       this.showTimeout_ = setTimeout(function() {
278         this.showTimeout_ = null;
279         this.showProgress_(event.status);
280       }.bind(this), 500);
281       break;
282
283     case 'PROGRESS':
284       this.showProgress_(event.status);
285       break;
286
287     case 'SUCCESS':
288       this.hide_();
289       break;
290
291     case 'CANCELLED':
292       this.show(ButterBar.Mode.DELETE, '');
293       break;
294
295     case 'ERROR':
296       this.progress_ = event.status;
297       var error = event.error;
298       if (error.code === util.FileOperationErrorType.TARGET_EXISTS) {
299         var name = error.data.name;
300         if (error.data.isDirectory)
301           name += '/';
302         this.showError_(
303             strf(this.transferType_() + '_TARGET_EXISTS_ERROR', name));
304       } else if (error.code === util.FileOperationErrorType.FILESYSTEM_ERROR) {
305         if (error.data.toDrive &&
306             error.data.code === FileError.QUOTA_EXCEEDED_ERR) {
307           // The alert will be shown in FileManager.onCopyProgress_.
308           this.hide_();
309         } else {
310           this.showError_(strf(this.transferType_() + '_FILESYSTEM_ERROR',
311                                util.getFileErrorString(error.data.code)));
312           }
313       } else {
314         this.showError_(
315             strf(this.transferType_() + '_UNEXPECTED_ERROR', error));
316       }
317       break;
318
319     default:
320       console.warn('Unknown "copy-progress" event reason: ' + event.code);
321   }
322 };
323
324 /**
325  * 'delete' event handler. Shows information about deleting files.
326  * @param {Event} event A 'delete' event from FileOperationManager.
327  * @private
328  */
329 ButterBar.prototype.onDelete_ = function(event) {
330   switch (event.reason) {
331     case 'BEGIN':
332       if (this.currentMode_ != ButterBar.Mode.DELETE)
333         this.totalDeleted_ = 0;
334
335     case 'PROGRESS':
336       this.totalDeleted_ += event.urls.length;
337       var title = '';
338       if (this.totalDeleted_ == 1) {
339         var fullPath = util.extractFilePath(event.urls[0]);
340         var fileName = PathUtil.split(fullPath).pop();
341         title = '';
342       }
343
344       if (this.currentMode_ == ButterBar.Mode.DELETE)
345         this.update_(title);
346       else
347         this.show(ButterBar.Mode.DELETE, title);
348       break;
349
350     case 'SUCCESS':
351       break;
352
353     case 'ERROR':
354       this.showError_(str('DELETE_ERROR'));
355       break;
356
357     default:
358       console.warn('Unknown "delete" event reason: ' + event.reason);
359   }
360 };