| // Copyright 2006 The Closure Library Authors. All Rights Reserved. |
| // |
| // Licensed under the Apache License, Version 2.0 (the "License"); |
| // you may not use this file except in compliance with the License. |
| // You may obtain a copy of the License at |
| // |
| // http://www.apache.org/licenses/LICENSE-2.0 |
| // |
| // Unless required by applicable law or agreed to in writing, software |
| // distributed under the License is distributed on an "AS-IS" BASIS, |
| // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
| // See the License for the specific language governing permissions and |
| // limitations under the License. |
| |
| /** |
| * @fileoverview Implementation of a progress bar. |
| * |
| * @author arv@google.com (Erik Arvidsson) |
| * @see ../demos/progressbar.html |
| */ |
| |
| |
| goog.provide('goog.ui.ProgressBar'); |
| goog.provide('goog.ui.ProgressBar.Orientation'); |
| |
| goog.require('goog.a11y.aria'); |
| goog.require('goog.asserts'); |
| goog.require('goog.dom'); |
| goog.require('goog.dom.classlist'); |
| goog.require('goog.events'); |
| goog.require('goog.events.EventType'); |
| goog.require('goog.ui.Component'); |
| goog.require('goog.ui.RangeModel'); |
| goog.require('goog.userAgent'); |
| |
| |
| |
| /** |
| * This creates a progress bar object. |
| * @param {goog.dom.DomHelper=} opt_domHelper Optional DOM helper. |
| * @constructor |
| * @extends {goog.ui.Component} |
| */ |
| goog.ui.ProgressBar = function(opt_domHelper) { |
| goog.ui.Component.call(this, opt_domHelper); |
| |
| /** @type {?HTMLDivElement} */ |
| this.thumbElement_; |
| |
| /** |
| * The underlying data model for the progress bar. |
| * @type {goog.ui.RangeModel} |
| * @private |
| */ |
| this.rangeModel_ = new goog.ui.RangeModel; |
| goog.events.listen(this.rangeModel_, goog.ui.Component.EventType.CHANGE, |
| this.handleChange_, false, this); |
| }; |
| goog.inherits(goog.ui.ProgressBar, goog.ui.Component); |
| goog.tagUnsealableClass(goog.ui.ProgressBar); |
| |
| |
| /** |
| * Enum for representing the orientation of the progress bar. |
| * |
| * @enum {string} |
| */ |
| goog.ui.ProgressBar.Orientation = { |
| VERTICAL: 'vertical', |
| HORIZONTAL: 'horizontal' |
| }; |
| |
| |
| /** |
| * Map from progress bar orientation to CSS class names. |
| * @type {Object} |
| * @private |
| */ |
| goog.ui.ProgressBar.ORIENTATION_TO_CSS_NAME_ = {}; |
| goog.ui.ProgressBar.ORIENTATION_TO_CSS_NAME_[ |
| goog.ui.ProgressBar.Orientation.VERTICAL] = |
| goog.getCssName('progress-bar-vertical'); |
| goog.ui.ProgressBar.ORIENTATION_TO_CSS_NAME_[ |
| goog.ui.ProgressBar.Orientation.HORIZONTAL] = |
| goog.getCssName('progress-bar-horizontal'); |
| |
| |
| /** |
| * Creates the DOM nodes needed for the progress bar |
| * @override |
| */ |
| goog.ui.ProgressBar.prototype.createDom = function() { |
| this.thumbElement_ = this.createThumb_(); |
| var cs = goog.ui.ProgressBar.ORIENTATION_TO_CSS_NAME_[this.orientation_]; |
| this.setElementInternal( |
| this.getDomHelper().createDom('div', cs, this.thumbElement_)); |
| this.setValueState_(); |
| this.setMinimumState_(); |
| this.setMaximumState_(); |
| }; |
| |
| |
| /** @override */ |
| goog.ui.ProgressBar.prototype.enterDocument = function() { |
| goog.ui.ProgressBar.superClass_.enterDocument.call(this); |
| this.attachEvents_(); |
| this.updateUi_(); |
| |
| var element = this.getElement(); |
| goog.asserts.assert(element, |
| 'The progress bar DOM element cannot be null.'); |
| // state live = polite will notify the user of updates, |
| // but will not interrupt ongoing feedback |
| goog.a11y.aria.setRole(element, 'progressbar'); |
| goog.a11y.aria.setState(element, 'live', 'polite'); |
| }; |
| |
| |
| /** @override */ |
| goog.ui.ProgressBar.prototype.exitDocument = function() { |
| goog.ui.ProgressBar.superClass_.exitDocument.call(this); |
| this.detachEvents_(); |
| }; |
| |
| |
| /** |
| * This creates the thumb element. |
| * @private |
| * @return {HTMLDivElement} The created thumb element. |
| */ |
| goog.ui.ProgressBar.prototype.createThumb_ = function() { |
| return /** @type {!HTMLDivElement} */ (this.getDomHelper().createDom('div', |
| goog.getCssName('progress-bar-thumb'))); |
| }; |
| |
| |
| /** |
| * Adds the initial event listeners to the element. |
| * @private |
| */ |
| goog.ui.ProgressBar.prototype.attachEvents_ = function() { |
| if (goog.userAgent.IE && goog.userAgent.VERSION < 7) { |
| goog.events.listen(this.getElement(), goog.events.EventType.RESIZE, |
| this.updateUi_, false, this); |
| } |
| }; |
| |
| |
| /** |
| * Removes the event listeners added by attachEvents_. |
| * @private |
| */ |
| goog.ui.ProgressBar.prototype.detachEvents_ = function() { |
| if (goog.userAgent.IE && goog.userAgent.VERSION < 7) { |
| goog.events.unlisten(this.getElement(), goog.events.EventType.RESIZE, |
| this.updateUi_, false, this); |
| } |
| }; |
| |
| |
| /** |
| * Decorates an existing HTML DIV element as a progress bar input. If the |
| * element contains a child with a class name of 'progress-bar-thumb' that will |
| * be used as the thumb. |
| * @param {Element} element The HTML element to decorate. |
| * @override |
| */ |
| goog.ui.ProgressBar.prototype.decorateInternal = function(element) { |
| goog.ui.ProgressBar.superClass_.decorateInternal.call(this, element); |
| goog.dom.classlist.add( |
| goog.asserts.assert(this.getElement()), |
| goog.ui.ProgressBar.ORIENTATION_TO_CSS_NAME_[this.orientation_]); |
| |
| // find thumb |
| var thumb = goog.dom.getElementsByTagNameAndClass( |
| null, goog.getCssName('progress-bar-thumb'), this.getElement())[0]; |
| if (!thumb) { |
| thumb = this.createThumb_(); |
| this.getElement().appendChild(thumb); |
| } |
| this.thumbElement_ = thumb; |
| }; |
| |
| |
| /** |
| * @return {number} The value. |
| */ |
| goog.ui.ProgressBar.prototype.getValue = function() { |
| return this.rangeModel_.getValue(); |
| }; |
| |
| |
| /** |
| * Sets the value |
| * @param {number} v The value. |
| */ |
| goog.ui.ProgressBar.prototype.setValue = function(v) { |
| this.rangeModel_.setValue(v); |
| if (this.getElement()) { |
| this.setValueState_(); |
| } |
| }; |
| |
| |
| /** |
| * Sets the state for a11y of the current value. |
| * @private |
| */ |
| goog.ui.ProgressBar.prototype.setValueState_ = function() { |
| var element = this.getElement(); |
| goog.asserts.assert(element, |
| 'The progress bar DOM element cannot be null.'); |
| goog.a11y.aria.setState(element, 'valuenow', this.getValue()); |
| }; |
| |
| |
| /** |
| * @return {number} The minimum value. |
| */ |
| goog.ui.ProgressBar.prototype.getMinimum = function() { |
| return this.rangeModel_.getMinimum(); |
| }; |
| |
| |
| /** |
| * Sets the minimum number |
| * @param {number} v The minimum value. |
| */ |
| goog.ui.ProgressBar.prototype.setMinimum = function(v) { |
| this.rangeModel_.setMinimum(v); |
| if (this.getElement()) { |
| this.setMinimumState_(); |
| } |
| }; |
| |
| |
| /** |
| * Sets the state for a11y of the minimum value. |
| * @private |
| */ |
| goog.ui.ProgressBar.prototype.setMinimumState_ = function() { |
| var element = this.getElement(); |
| goog.asserts.assert(element, |
| 'The progress bar DOM element cannot be null.'); |
| goog.a11y.aria.setState(element, 'valuemin', this.getMinimum()); |
| }; |
| |
| |
| /** |
| * @return {number} The maximum value. |
| */ |
| goog.ui.ProgressBar.prototype.getMaximum = function() { |
| return this.rangeModel_.getMaximum(); |
| }; |
| |
| |
| /** |
| * Sets the maximum number |
| * @param {number} v The maximum value. |
| */ |
| goog.ui.ProgressBar.prototype.setMaximum = function(v) { |
| this.rangeModel_.setMaximum(v); |
| if (this.getElement()) { |
| this.setMaximumState_(); |
| } |
| }; |
| |
| |
| /** |
| * Sets the state for a11y of the maximum valiue. |
| * @private |
| */ |
| goog.ui.ProgressBar.prototype.setMaximumState_ = function() { |
| var element = this.getElement(); |
| goog.asserts.assert(element, |
| 'The progress bar DOM element cannot be null.'); |
| goog.a11y.aria.setState(element, 'valuemax', this.getMaximum()); |
| }; |
| |
| |
| /** |
| * |
| * @type {goog.ui.ProgressBar.Orientation} |
| * @private |
| */ |
| goog.ui.ProgressBar.prototype.orientation_ = |
| goog.ui.ProgressBar.Orientation.HORIZONTAL; |
| |
| |
| /** |
| * Call back when the internal range model changes |
| * @param {goog.events.Event} e The event object. |
| * @private |
| */ |
| goog.ui.ProgressBar.prototype.handleChange_ = function(e) { |
| this.updateUi_(); |
| this.dispatchEvent(goog.ui.Component.EventType.CHANGE); |
| }; |
| |
| |
| /** |
| * This is called when we need to update the size of the thumb. This happens |
| * when first created as well as when the value and the orientation changes. |
| * @private |
| */ |
| goog.ui.ProgressBar.prototype.updateUi_ = function() { |
| if (this.thumbElement_) { |
| var min = this.getMinimum(); |
| var max = this.getMaximum(); |
| var val = this.getValue(); |
| var ratio = (val - min) / (max - min); |
| var size = Math.round(ratio * 100); |
| if (this.orientation_ == goog.ui.ProgressBar.Orientation.VERTICAL) { |
| // Note(arv): IE up to version 6 has some serious computation bugs when |
| // using percentages or bottom. We therefore first set the height to |
| // 100% and measure that and base the top and height on that size instead. |
| if (goog.userAgent.IE && goog.userAgent.VERSION < 7) { |
| this.thumbElement_.style.top = 0; |
| this.thumbElement_.style.height = '100%'; |
| var h = this.thumbElement_.offsetHeight; |
| var bottom = Math.round(ratio * h); |
| this.thumbElement_.style.top = h - bottom + 'px'; |
| this.thumbElement_.style.height = bottom + 'px'; |
| } else { |
| this.thumbElement_.style.top = (100 - size) + '%'; |
| this.thumbElement_.style.height = size + '%'; |
| } |
| } else { |
| this.thumbElement_.style.width = size + '%'; |
| } |
| } |
| }; |
| |
| |
| /** |
| * This is called when we need to setup the UI sizes and positions. This |
| * happens when we create the element and when we change the orientation. |
| * @private |
| */ |
| goog.ui.ProgressBar.prototype.initializeUi_ = function() { |
| var tStyle = this.thumbElement_.style; |
| if (this.orientation_ == goog.ui.ProgressBar.Orientation.VERTICAL) { |
| tStyle.left = 0; |
| tStyle.width = '100%'; |
| } else { |
| tStyle.top = tStyle.left = 0; |
| tStyle.height = '100%'; |
| } |
| }; |
| |
| |
| /** |
| * Changes the orientation |
| * @param {goog.ui.ProgressBar.Orientation} orient The orientation. |
| */ |
| goog.ui.ProgressBar.prototype.setOrientation = function(orient) { |
| if (this.orientation_ != orient) { |
| var oldCss = |
| goog.ui.ProgressBar.ORIENTATION_TO_CSS_NAME_[this.orientation_]; |
| var newCss = goog.ui.ProgressBar.ORIENTATION_TO_CSS_NAME_[orient]; |
| this.orientation_ = orient; |
| |
| // Update the DOM |
| var element = this.getElement(); |
| if (element) { |
| goog.dom.classlist.swap(element, oldCss, newCss); |
| this.initializeUi_(); |
| this.updateUi_(); |
| } |
| } |
| }; |
| |
| |
| /** |
| * @return {goog.ui.ProgressBar.Orientation} The orientation of the |
| * progress bar. |
| */ |
| goog.ui.ProgressBar.prototype.getOrientation = function() { |
| return this.orientation_; |
| }; |
| |
| |
| /** @override */ |
| goog.ui.ProgressBar.prototype.disposeInternal = function() { |
| this.detachEvents_(); |
| goog.ui.ProgressBar.superClass_.disposeInternal.call(this); |
| this.thumbElement_ = null; |
| this.rangeModel_.dispose(); |
| }; |
| |
| |
| /** |
| * @return {?number} The step value used to determine how to round the value. |
| */ |
| goog.ui.ProgressBar.prototype.getStep = function() { |
| return this.rangeModel_.getStep(); |
| }; |
| |
| |
| /** |
| * Sets the step value. The step value is used to determine how to round the |
| * value. |
| * @param {?number} step The step size. |
| */ |
| goog.ui.ProgressBar.prototype.setStep = function(step) { |
| this.rangeModel_.setStep(step); |
| }; |