blob: e427a2bb466a94e23490efd174d0e710faf385e0 [file] [log] [blame]
// Copyright (c) 2012 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
'use strict';
/**
* Butter bar is shown on top of the file list and is used to show the copy
* progress and other messages.
* @param {HTMLElement} dialogDom FileManager top-level div.
* @param {FileOperationManagerWrapper} fileOperationManager The operation
* manager.
* @constructor
*/
function ButterBar(dialogDom, fileOperationManager) {
this.dialogDom_ = dialogDom;
this.butter_ = this.dialogDom_.querySelector('#butter-bar');
this.document_ = this.butter_.ownerDocument;
this.fileOperationManager_ = fileOperationManager;
this.hideTimeout_ = null;
this.showTimeout_ = null;
this.lastShowTime_ = 0;
this.deleteTaskId_ = null;
this.currentMode_ = null;
this.totalDeleted_ = 0;
this.lastProgressValue_ = 0;
this.alert_ = new ErrorDialog(this.dialogDom_);
this.onCopyProgressBound_ = this.onCopyProgress_.bind(this);
this.fileOperationManager_.addEventListener(
'copy-progress', this.onCopyProgressBound_);
this.onDeleteBound_ = this.onDelete_.bind(this);
this.fileOperationManager_.addEventListener('delete', this.onDeleteBound_);
}
/**
* The default amount of milliseconds time, before a butter bar will hide after
* the last update.
* @type {number}
* @private
* @const
*/
ButterBar.HIDE_DELAY_TIME_MS_ = 2000;
/**
* Name of action which should be displayed as an 'x' button instead of
* link with text.
* @const
*/
ButterBar.ACTION_X = '--action--x--';
/**
* Butter bar mode.
* @const
*/
ButterBar.Mode = {
COPY: 1,
DELETE: 2
};
/**
* Disposes the instance. No methods should be called after this method's
* invocation.
*/
ButterBar.prototype.dispose = function() {
// Unregister listeners from FileOperationManager.
this.fileOperationManager_.removeEventListener(
'copy-progress', this.onCopyProgressBound_);
this.fileOperationManager_.removeEventListener('delete', this.onDeleteBound_);
};
/**
* @return {boolean} True if visible.
* @private
*/
ButterBar.prototype.isVisible_ = function() {
return this.butter_.classList.contains('visible');
};
/**
* Show butter bar.
* @param {ButterBar.Mode} mode Butter bar mode.
* @param {string} message The message to be shown.
* @param {Object=} opt_options Options: 'actions', 'progress', 'timeout'. If
* 'timeout' is not specified, HIDE_DELAY_TIME_MS_ is used. If 'timeout' is
* false, the butter bar will not be hidden.
*/
ButterBar.prototype.show = function(mode, message, opt_options) {
this.currentMode_ = mode;
this.clearShowTimeout_();
this.clearHideTimeout_();
var actions = this.butter_.querySelector('.actions');
actions.textContent = '';
if (opt_options && 'actions' in opt_options) {
for (var label in opt_options.actions) {
var link = this.document_.createElement('a');
link.addEventListener('click', function(callback) {
callback();
return false;
}.bind(null, opt_options.actions[label]));
if (label == ButterBar.ACTION_X) {
link.className = 'x';
} else {
link.textContent = label;
}
actions.appendChild(link);
}
actions.hidden = false;
} else {
actions.hidden = true;
}
this.butter_.querySelector('.progress-bar').hidden =
!(opt_options && 'progress' in opt_options);
this.butter_.classList.remove('error');
this.butter_.classList.remove('visible'); // Will be shown in update_
this.update_(message, opt_options);
};
/**
* Show an error message in a popup dialog.
* @param {string} message Message.
* @private
*/
ButterBar.prototype.showError_ = function(message) {
// Wait in case there are previous dialogs being closed.
setTimeout(function() {
this.alert_.showHtml('', // Title.
message);
this.hide_();
}.bind(this), cr.ui.dialogs.BaseDialog.ANIMATE_STABLE_DURATION);
};
/**
* Set message and/or progress.
* @param {string} message Message.
* @param {Object=} opt_options Same as in show().
* @private
*/
ButterBar.prototype.update_ = function(message, opt_options) {
if (!opt_options)
opt_options = {};
this.clearHideTimeout_();
var butterMessage = this.butter_.querySelector('.butter-message');
butterMessage.textContent = message;
if (message && !this.isVisible_()) {
// The butter bar is made visible on the first non-empty message.
this.butter_.classList.add('visible');
}
if (opt_options && 'progress' in opt_options) {
butterMessage.classList.add('single-line');
var progressTrack = this.butter_.querySelector('.progress-track');
// Smoothen the progress only when it goes forward. Especially do not
// do the transition effect if resetting to 0.
if (opt_options.progress > this.lastProgressValue_)
progressTrack.classList.add('smoothed');
else
progressTrack.classList.remove('smoothed');
progressTrack.style.width = (opt_options.progress * 100) + '%';
this.lastProgressValue_ = opt_options.progress;
} else {
butterMessage.classList.remove('single-line');
}
if (opt_options.timeout !== false)
this.hide_(opt_options.timeout);
};
/**
* Hide butter bar. There might be the delay before hiding so that butter bar
* would be shown for no less than the minimal time.
* @param {number=} opt_timeout Delay time in milliseconds before hidding. If it
* is zero, butter bar is hidden immediatelly. If it is not specified,
* HIDE_DELAY_TIME_MS_ is used.
* @private
*/
ButterBar.prototype.hide_ = function(opt_timeout) {
this.clearHideTimeout_();
if (!this.isVisible_())
return;
var delay = typeof opt_timeout != 'undefined' ?
opt_timeout : ButterBar.HIDE_DELAY_TIME_MS_;
if (delay <= 0) {
this.currentMode_ = null;
this.butter_.classList.remove('visible');
this.butter_.querySelector('.progress-bar').hidden = true;
} else {
// Reschedule hide to comply with the minimal display time.
this.hideTimeout_ = setTimeout(function() {
this.hideTimeout_ = null;
this.hide_(0);
}.bind(this), delay);
}
};
/**
* Clear the show timeout if it is set.
* @private
*/
ButterBar.prototype.clearShowTimeout_ = function() {
if (this.showTimeout_) {
clearTimeout(this.showTimeout_);
this.showTimeout_ = null;
}
};
/**
* Clear the hide timeout if it is set.
* @private
*/
ButterBar.prototype.clearHideTimeout_ = function() {
if (this.hideTimeout_) {
clearTimeout(this.hideTimeout_);
this.hideTimeout_ = null;
}
};
/**
* @return {string} The type of operation.
* @private
*/
ButterBar.prototype.transferType_ = function() {
var progress = this.progress_;
if (progress && progress.operationType)
return progress.operationType;
return 'TRANSFER';
};
/**
* Set up butter bar for showing copy progress.
*
* @param {Object} progress Copy status object created by
* FileOperationManager.getStatus().
* @private
*/
ButterBar.prototype.showProgress_ = function(progress) {
this.progress_ = progress;
var options = {
progress: progress.processedBytes / progress.totalBytes,
actions: {},
timeout: false
};
if (this.currentMode_ == ButterBar.Mode.COPY) {
this.update_('', options);
} else {
// Ignore the cancel behavior because the butter bar is already obsoleted.
options.actions[ButterBar.ACTION_X] = function() {
};
this.show(ButterBar.Mode.COPY, '', options);
}
};
/**
* 'copy-progress' event handler. Show progress or an appropriate message.
* @param {Event} event A 'copy-progress' event from FileOperationManager.
* @private
*/
ButterBar.prototype.onCopyProgress_ = function(event) {
// Delete operation has higher priority.
if (this.currentMode_ == ButterBar.Mode.DELETE)
return;
if (event.reason != 'PROGRESS')
this.clearShowTimeout_();
switch (event.reason) {
case 'BEGIN':
this.showTimeout_ = setTimeout(function() {
this.showTimeout_ = null;
this.showProgress_(event.status);
}.bind(this), 500);
break;
case 'PROGRESS':
this.showProgress_(event.status);
break;
case 'SUCCESS':
this.hide_();
break;
case 'CANCELLED':
this.show(ButterBar.Mode.DELETE, '');
break;
case 'ERROR':
this.progress_ = event.status;
var error = event.error;
if (error.code === util.FileOperationErrorType.TARGET_EXISTS) {
var name = error.data.name;
if (error.data.isDirectory)
name += '/';
this.showError_(
strf(this.transferType_() + '_TARGET_EXISTS_ERROR', name));
} else if (error.code === util.FileOperationErrorType.FILESYSTEM_ERROR) {
if (error.data.toDrive &&
error.data.code === FileError.QUOTA_EXCEEDED_ERR) {
// The alert will be shown in FileManager.onCopyProgress_.
this.hide_();
} else {
this.showError_(strf(this.transferType_() + '_FILESYSTEM_ERROR',
util.getFileErrorString(error.data.code)));
}
} else {
this.showError_(
strf(this.transferType_() + '_UNEXPECTED_ERROR', error));
}
break;
default:
console.warn('Unknown "copy-progress" event reason: ' + event.code);
}
};
/**
* 'delete' event handler. Shows information about deleting files.
* @param {Event} event A 'delete' event from FileOperationManager.
* @private
*/
ButterBar.prototype.onDelete_ = function(event) {
switch (event.reason) {
case 'BEGIN':
if (this.currentMode_ != ButterBar.Mode.DELETE)
this.totalDeleted_ = 0;
case 'PROGRESS':
this.totalDeleted_ += event.urls.length;
var title = '';
if (this.totalDeleted_ == 1) {
var fullPath = util.extractFilePath(event.urls[0]);
var fileName = PathUtil.split(fullPath).pop();
title = '';
}
if (this.currentMode_ == ButterBar.Mode.DELETE)
this.update_(title);
else
this.show(ButterBar.Mode.DELETE, title);
break;
case 'SUCCESS':
break;
case 'ERROR':
this.showError_(str('DELETE_ERROR'));
break;
default:
console.warn('Unknown "delete" event reason: ' + event.reason);
}
};