blob: 468129ed9b29cf2e1923fd5323da575063a39773 [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.
* @constructor
*/
var ProgressCenterPanel = function(element) {
this.element_ = element;
this.openView_ = this.element_.querySelector('#progress-center-open-view');
this.closeView_ = this.element_.querySelector('#progress-center-close-view');
/**
* 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');
/**
* Callback to becalled with the ID of the progress item when the cancel
* button is clicked.
*/
this.cancelCallback = null;
Object.seal(this);
// Register event handlers.
element.addEventListener('click', this.onClick_.bind(this));
element.addEventListener(
'webkitTransitionEnd', this.onItemTransitionEnd_.bind(this));
};
/**
* 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');
// Only when the previousWidthRate is not NaN (when style width is already
// set) and the progress rate increases, we use transition animation.
var previousWidthRate =
parseInt(element.querySelector('.progress-track').style.width);
var animation = !isNaN(previousWidthRate) &&
previousWidthRate < item.progressRateInPercent;
if (item.state === ProgressItemState.COMPLETED && animation) {
// 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');
track.classList.toggle('animated', animation);
track.style.width = item.progressRateInPercent + '%';
track.hidden = false;
}, 0);
};
/**
* Updates an item to the progress center panel.
* @param {!ProgressCenterItem} item Item including new contents.
*/
ProgressCenterPanel.prototype.updateItem = function(item) {
// 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.COMPLETED:
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.COMPLETED)
ProgressCenterPanel.updateItemElement_(itemElement, item);
// If the item has the pre-complete attribute, keep showing it. Otherwise,
// just remove it.
if (item.state !== ProgressItemState.COMPLETED ||
!itemElement.hasAttribute('pre-complete')) {
this.openView_.removeChild(itemElement);
}
break;
}
};
/**
* Updates close showing summarized item.
* @param {!ProgressCenterItem} summarizedItem Item to be displayed in the close
* view.
*/
ProgressCenterPanel.prototype.updateCloseView = function(summarizedItem) {
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 = '';
// Clear track width of close view.
this.closeViewItem_.querySelector('.progress-track').style.width = '';
// Hide the progress center.
this.element_.hidden = true;
this.closeViewItem_.querySelector('.progress-track').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) {
// Toggle button.
if (event.target.classList.contains('toggle') &&
(!this.closeView_.classList.contains('single') ||
this.element_.classList.contains('opened'))) {
this.element_.classList.toggle('opened');
return;
}
// Cancel button.
if (this.cancelCallback)
this.cancelCallback(
event.target.parentNode.parentNode.getAttribute('data-progress-id'));
};