blob: 94b30359288800b666a8e65271c8d1872ebd148a [file] [log] [blame]
// Copyright 2013 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';
/**
* Progress center panel.
*
* @param {HTMLElement} element DOM Element of the process center panel.
* @param {function(string)} cancelCallback Callback to becalled with the ID of
* the progress item when the cancel button is clicked.
* @constructor
*/
var ProgressCenterPanel = function(element, cancelCallback) {
this.element_ = element;
this.openView_ = this.element_.querySelector('#progress-center-open-view');
this.closeView_ = this.element_.querySelector('#progress-center-close-view');
this.cancelCallback_ = cancelCallback;
/**
* Reset is requested but it is pending until the transition of progress bar
* is complete.
* @type {boolean}
* @private
*/
this.resetRequested_ = false;
/**
* Only progress item in the close view.
* @type {!HTMLElement}
* @private
*/
this.closeViewItem_ = this.closeView_.querySelector('li');
Object.seal(this);
// Register event handlers.
element.addEventListener('click', this.onClick_.bind(this));
element.addEventListener(
'webkitTransitionEnd', this.onItemTransitionEnd_.bind(this));
};
/**
* Whether to use the new progress center UI or not.
* TODO(hirono): Remove the flag after replacing the old butter bar with the new
* progress center.
* @type {boolean}
* @private
*/
ProgressCenterPanel.ENABLED_ = true;
/**
* Updates attributes of the item element.
* @param {!HTMLElement} element Element to be updated.
* @param {!ProgressCenterItem} item Progress center item.
* @private
*/
ProgressCenterPanel.updateItemElement_ = function(element, item) {
// Sets element attributes.
element.setAttribute('data-progress-id', item.id);
element.setAttribute('data-progress-max', item.progressMax);
element.setAttribute('data-progress-value', item.progressValue);
element.className = '';
if (item.state === ProgressItemState.ERROR)
element.classList.add('error');
if (item.cancelable)
element.classList.add('cancelable');
// If the transition animation is needed, apply it.
var previousWidthRate =
parseInt(element.querySelector('.progress-track').style.width);
if (item.state === ProgressItemState.COMPLETE &&
previousWidthRate !== item.progressRateByPercent) {
// The attribute pre-complete means that the actual operation is already
// done but the UI transition of progress bar is not complete.
element.setAttribute('pre-complete', '');
} else {
element.querySelector('label').textContent = item.message;
}
// To commit the property change and to trigger the transition even if the
// change is done synchronously, assign the width value asynchronously.
setTimeout(function() {
var track = element.querySelector('.progress-track');
// When the progress rate is reverted, we does not use the transition
// animation. Specifying '0' overrides the CSS settings and specifying null
// re-enables it.
track.style.transitionDuration =
previousWidthRate > item.progressRateByPercent ? '0' : null;
track.style.width = item.progressRateByPercent + '%';
}, 0);
};
/**
* Updates an item to the progress center panel.
* @param {!ProgressCenterItem} item Item including new contents.
* @param {!ProgressCenterItem} summarizedItem Item to be desplayed in the close
* view.
*/
ProgressCenterPanel.prototype.updateItem = function(item, summarizedItem) {
// If reset is requested, force to reset.
if (this.resetRequested_)
this.reset(true);
// Update the item.
var itemElement = this.getItemElement_(item.id);
var removed = false;
// Check whether the item should be displayed or not by referring its state.
switch (item.state) {
// Should show the item.
case ProgressItemState.PROGRESSING:
case ProgressItemState.ERROR:
// If the item has not been added yet, create a new element and add it.
if (!itemElement) {
itemElement = this.createNewItemElement_();
this.openView_.insertBefore(itemElement, this.openView_.firstNode);
}
// Update the element by referring the item model.
ProgressCenterPanel.updateItemElement_(itemElement, item);
this.element_.hidden = false;
break;
// Should not show the item.
case ProgressItemState.COMPLETE:
case ProgressItemState.CANCELED:
// If itemElement is not shown, just break.
if (!itemElement)
break;
// If the item is complete state, once update it because it may turn to
// have the pre-complete attribute.
if (item.state == ProgressItemState.COMPLETE)
ProgressCenterPanel.updateItemElement_(itemElement, item);
// If the item has the pre-complete attribute, keep showing it. Otherwise,
// just remove it.
if (item.state !== ProgressItemState.COMPLETE ||
!itemElement.hasAttribute('pre-complete')) {
this.openView_.removeChild(itemElement);
}
break;
}
// Update close view.
this.closeView_.classList.toggle('single', !summarizedItem.summarized);
ProgressCenterPanel.updateItemElement_(this.closeViewItem_, summarizedItem);
};
/**
* Remove all the items.
* @param {boolean=} opt_force True if we force to reset and do not wait the
* transition of progress bar. False otherwise. False is default.
*/
ProgressCenterPanel.prototype.reset = function(opt_force) {
if (!opt_force && this.element_.querySelector('[pre-complete]')) {
this.resetRequested_ = true;
return;
}
// Clear the flag.
this.resetRequested_ = false;
// Clear the all compete item.
this.openView_.innerHTML = '';
// Hide the progress center.
this.element_.hidden = true;
this.element_.classList.remove('opened');
};
/**
* Gets an item element having the specified ID.
* @param {string} id progress item ID.
* @return {HTMLElement} Item element having the ID.
* @private
*/
ProgressCenterPanel.prototype.getItemElement_ = function(id) {
var query = 'li[data-progress-id="' + id + '"]';
return this.openView_.querySelector(query);
};
/**
* Creates an item element.
* @return {HTMLElement} Created item element.
* @private
*/
ProgressCenterPanel.prototype.createNewItemElement_ = function() {
var label = this.element_.ownerDocument.createElement('label');
label.className = 'label';
var progressBarIndicator = this.element_.ownerDocument.createElement('div');
progressBarIndicator.className = 'progress-track';
var progressBar = this.element_.ownerDocument.createElement('div');
progressBar.className = 'progress-bar';
progressBar.appendChild(progressBarIndicator);
var cancelButton = this.element_.ownerDocument.createElement('button');
cancelButton.className = 'cancel';
cancelButton.setAttribute('tabindex', '-1');
var progressFrame = this.element_.ownerDocument.createElement('div');
progressFrame.className = 'progress-frame';
progressFrame.appendChild(progressBar);
progressFrame.appendChild(cancelButton);
var itemElement = this.element_.ownerDocument.createElement('li');
itemElement.appendChild(label);
itemElement.appendChild(progressFrame);
return itemElement;
};
/**
* Handles the transition end event of items.
* @param {Event} event Transition end event.
* @private
*/
ProgressCenterPanel.prototype.onItemTransitionEnd_ = function(event) {
var itemElement = event.target.parentNode.parentNode.parentNode;
if (!itemElement.hasAttribute('pre-complete') ||
event.propertyName !== 'width')
return;
if (itemElement !== this.closeViewItem_)
this.openView_.removeChild(itemElement);
else
itemElement.removeAttribute('pre-complete');
if (this.resetRequested_)
this.reset();
};
/**
* Handles the click event.
* @param {Event} event Click event.
* @private
*/
ProgressCenterPanel.prototype.onClick_ = function(event) {
if (event.target.classList.contains('toggle') &&
!this.closeView_.classList.contains('single'))
this.element_.classList.toggle('opened');
else if ((event.target.classList.contains('toggle') &&
this.closeView_.classList.contains('single')) ||
event.target.classList.contains('cancel'))
this.cancelCallback_(
event.target.parentNode.parentNode.getAttribute('data-progress-id'));
};