// Copyright 2007 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 Gauge UI component, using browser vector graphics. * @see ../demos/gauge.html */ goog.provide('goog.ui.Gauge'); goog.provide('goog.ui.GaugeColoredRange'); goog.require('goog.a11y.aria'); goog.require('goog.asserts'); goog.require('goog.dom.TagName'); goog.require('goog.events'); goog.require('goog.fx.Animation'); goog.require('goog.fx.Transition'); goog.require('goog.fx.easing'); goog.require('goog.graphics'); goog.require('goog.graphics.Font'); goog.require('goog.graphics.Path'); goog.require('goog.graphics.SolidFill'); goog.require('goog.math'); goog.require('goog.ui.Component'); goog.require('goog.ui.GaugeTheme'); /** * Information on how to decorate a range in the gauge. * This is an internal-only class. * @param {number} fromValue The range start (minimal) value. * @param {number} toValue The range end (maximal) value. * @param {string} backgroundColor Color to fill the range background with. * @constructor * @final */ goog.ui.GaugeColoredRange = function(fromValue, toValue, backgroundColor) { /** * The range start (minimal) value. * @type {number} */ this.fromValue = fromValue; /** * The range end (maximal) value. * @type {number} */ this.toValue = toValue; /** * Color to fill the range background with. * @type {string} */ this.backgroundColor = backgroundColor; }; /** * A UI component that displays a gauge. * A gauge displayes a current value within a round axis that represents a * given range. * The gauge is built from an external border, and internal border inside it, * ticks and labels inside the internal border, and a needle that points to * the current value. * @param {number} width The width in pixels. * @param {number} height The height in pixels. * @param {goog.dom.DomHelper=} opt_domHelper The DOM helper object for the * document we want to render in. * @constructor * @extends {goog.ui.Component} * @final */ goog.ui.Gauge = function(width, height, opt_domHelper) { goog.ui.Component.call(this, opt_domHelper); /** * The width in pixels of this component. * @type {number} * @private */ this.width_ = width; /** * The height in pixels of this component. * @type {number} * @private */ this.height_ = height; /** * The underlying graphics. * @type {goog.graphics.AbstractGraphics} * @private */ this.graphics_ = goog.graphics.createGraphics(width, height, null, null, opt_domHelper); /** * Colors to paint the background of certain ranges (optional). * @type {Array} * @private */ this.rangeColors_ = []; }; goog.inherits(goog.ui.Gauge, goog.ui.Component); /** * Constant for a background color for a gauge area. */ goog.ui.Gauge.RED = '#ffc0c0'; /** * Constant for a background color for a gauge area. */ goog.ui.Gauge.GREEN = '#c0ffc0'; /** * Constant for a background color for a gauge area. */ goog.ui.Gauge.YELLOW = '#ffffa0'; /** * The radius of the entire gauge from the canvas size. * @type {number} */ goog.ui.Gauge.FACTOR_RADIUS_FROM_SIZE = 0.45; /** * The ratio of internal gauge radius from entire radius. * The remaining area is the border around the gauge. * @type {number} */ goog.ui.Gauge.FACTOR_MAIN_AREA = 0.9; /** * The ratio of the colored background area for value ranges. * The colored area width is computed as * InternalRadius * (1 - FACTOR_COLOR_RADIUS) * @type {number} */ goog.ui.Gauge.FACTOR_COLOR_RADIUS = 0.75; /** * The ratio of the major ticks length start position, from the radius. * The major ticks length width is computed as * InternalRadius * (1 - FACTOR_MAJOR_TICKS) * @type {number} */ goog.ui.Gauge.FACTOR_MAJOR_TICKS = 0.8; /** * The ratio of the minor ticks length start position, from the radius. * The minor ticks length width is computed as * InternalRadius * (1 - FACTOR_MINOR_TICKS) * @type {number} */ goog.ui.Gauge.FACTOR_MINOR_TICKS = 0.9; /** * The length of the needle front (value facing) from the internal radius. * The needle front is the part of the needle that points to the value. * @type {number} */ goog.ui.Gauge.FACTOR_NEEDLE_FRONT = 0.95; /** * The length of the needle back relative to the internal radius. * The needle back is the part of the needle that points away from the value. * @type {number} */ goog.ui.Gauge.FACTOR_NEEDLE_BACK = 0.3; /** * The width of the needle front at the hinge. * This is the width of the curve control point, the actual width is * computed by the curve itself. * @type {number} */ goog.ui.Gauge.FACTOR_NEEDLE_WIDTH = 0.07; /** * The width (radius) of the needle hinge from the gauge radius. * @type {number} */ goog.ui.Gauge.FACTOR_NEEDLE_HINGE = 0.15; /** * The title font size (height) for titles relative to the internal radius. * @type {number} */ goog.ui.Gauge.FACTOR_TITLE_FONT_SIZE = 0.16; /** * The offset of the title from the center, relative to the internal radius. * @type {number} */ goog.ui.Gauge.FACTOR_TITLE_OFFSET = 0.35; /** * The formatted value font size (height) relative to the internal radius. * @type {number} */ goog.ui.Gauge.FACTOR_VALUE_FONT_SIZE = 0.18; /** * The title font size (height) for tick labels relative to the internal radius. * @type {number} */ goog.ui.Gauge.FACTOR_TICK_LABEL_FONT_SIZE = 0.14; /** * The offset of the formatted value down from the center, relative to the * internal radius. * @type {number} */ goog.ui.Gauge.FACTOR_VALUE_OFFSET = 0.75; /** * The font name for title text. * @type {string} */ goog.ui.Gauge.TITLE_FONT_NAME = 'arial'; /** * The maximal size of a step the needle can move (percent from size of range). * If the needle needs to move more, it will be moved in animated steps, to * show a smooth transition between values. * @type {number} */ goog.ui.Gauge.NEEDLE_MOVE_MAX_STEP = 0.02; /** * Time in miliseconds for animating a move of the value pointer. * @type {number} */ goog.ui.Gauge.NEEDLE_MOVE_TIME = 400; /** * Tolerance factor for how much values can exceed the range (being too * low or too high). The value is presented as a position (percentage). * @type {number} */ goog.ui.Gauge.MAX_EXCEED_POSITION_POSITION = 0.02; /** * The minimal value that can be displayed. * @private * @type {number} */ goog.ui.Gauge.prototype.minValue_ = 0; /** * The maximal value that can be displayed. * @private * @type {number} */ goog.ui.Gauge.prototype.maxValue_ = 100; /** * The number of major tick sections. * @private * @type {number} */ goog.ui.Gauge.prototype.majorTicks_ = 5; /** * The number of minor tick sections in each major tick section. * @private * @type {number} */ goog.ui.Gauge.prototype.minorTicks_ = 2; /** * The current value that needs to be displayed in the gauge. * @private * @type {number} */ goog.ui.Gauge.prototype.value_ = 0; /** * The current value formatted into a String. * @private * @type {?string} */ goog.ui.Gauge.prototype.formattedValue_ = null; /** * The current colors theme. * @private * @type {goog.ui.GaugeTheme?} */ goog.ui.Gauge.prototype.theme_ = null; /** * Title to display above the gauge center. * @private * @type {?string} */ goog.ui.Gauge.prototype.titleTop_ = null; /** * Title to display below the gauge center. * @private * @type {?string} */ goog.ui.Gauge.prototype.titleBottom_ = null; /** * Font to use for drawing titles. * If null (default), computed dynamically with a size relative to the * gauge radius. * @private * @type {goog.graphics.Font?} */ goog.ui.Gauge.prototype.titleFont_ = null; /** * Font to use for drawing the formatted value. * If null (default), computed dynamically with a size relative to the * gauge radius. * @private * @type {goog.graphics.Font?} */ goog.ui.Gauge.prototype.valueFont_ = null; /** * Font to use for drawing tick labels. * If null (default), computed dynamically with a size relative to the * gauge radius. * @private * @type {goog.graphics.Font?} */ goog.ui.Gauge.prototype.tickLabelFont_ = null; /** * The size in angles of the gauge axis area. * @private * @type {number} */ goog.ui.Gauge.prototype.angleSpan_ = 270; /** * The radius for drawing the needle. * Computed on full redraw, and used on every animation step of moving * the needle. * @type {number} * @private */ goog.ui.Gauge.prototype.needleRadius_ = 0; /** * The group elemnt of the needle. Contains all elements that change when the * gauge value changes. * @type {goog.graphics.GroupElement?} * @private */ goog.ui.Gauge.prototype.needleGroup_ = null; /** * The current position (0-1) of the visible needle. * Initially set to null to prevent animation on first opening of the gauge. * @type {?number} * @private */ goog.ui.Gauge.prototype.needleValuePosition_ = null; /** * Text labels to display by major tick marks. * @type {Array?} * @private */ goog.ui.Gauge.prototype.majorTickLabels_ = null; /** * Animation object while needle is being moved (animated). * @type {goog.fx.Animation?} * @private */ goog.ui.Gauge.prototype.animation_ = null; /** * @return {number} The minimum value of the range. */ goog.ui.Gauge.prototype.getMinimum = function() { return this.minValue_; }; /** * Sets the minimum value of the range * @param {number} min The minimum value of the range. */ goog.ui.Gauge.prototype.setMinimum = function(min) { this.minValue_ = min; var element = this.getElement(); if (element) { goog.a11y.aria.setState(element, 'valuemin', min); } }; /** * @return {number} The maximum value of the range. */ goog.ui.Gauge.prototype.getMaximum = function() { return this.maxValue_; }; /** * Sets the maximum number of the range * @param {number} max The maximum value of the range. */ goog.ui.Gauge.prototype.setMaximum = function(max) { this.maxValue_ = max; var element = this.getElement(); if (element) { goog.a11y.aria.setState(element, 'valuemax', max); } }; /** * Sets the current value range displayed by the gauge. * @param {number} value The current value for the gauge. This value * determines the position of the needle of the gauge. * @param {string=} opt_formattedValue The string value to show in the gauge. * If not specified, no string value will be displayed. */ goog.ui.Gauge.prototype.setValue = function(value, opt_formattedValue) { this.value_ = value; this.formattedValue_ = opt_formattedValue || null; this.stopAnimation_(); // Stop the active animation if exists // Compute desired value position (normalize value to range 0-1) var valuePosition = this.valueToRangePosition_(value); if (this.needleValuePosition_ == null) { // No animation on initial display this.needleValuePosition_ = valuePosition; this.drawValue_(); } else { // Animate move this.animation_ = new goog.fx.Animation( [this.needleValuePosition_], [valuePosition], goog.ui.Gauge.NEEDLE_MOVE_TIME, goog.fx.easing.inAndOut); var events = [ goog.fx.Transition.EventType.BEGIN, goog.fx.Animation.EventType.ANIMATE, goog.fx.Transition.EventType.END ]; goog.events.listen(this.animation_, events, this.onAnimate_, false, this); goog.events.listen( this.animation_, goog.fx.Transition.EventType.END, this.onAnimateEnd_, false, this); // Start animation this.animation_.play(false); } var element = this.getElement(); if (element) { goog.a11y.aria.setState(element, 'valuenow', this.value_); } }; /** * Sets the number of major tick sections and minor tick sections. * @param {number} majorUnits The number of major tick sections. * @param {number} minorUnits The number of minor tick sections for each major * tick section. */ goog.ui.Gauge.prototype.setTicks = function(majorUnits, minorUnits) { this.majorTicks_ = Math.max(1, majorUnits); this.minorTicks_ = Math.max(1, minorUnits); this.draw_(); }; /** * Sets the labels of the major ticks. * @param {Array} tickLabels A text label for each major tick value. */ goog.ui.Gauge.prototype.setMajorTickLabels = function(tickLabels) { this.majorTickLabels_ = tickLabels; this.draw_(); }; /** * Sets the top title of the gauge. * The top title is displayed above the center. * @param {string} text The top title text. */ goog.ui.Gauge.prototype.setTitleTop = function(text) { this.titleTop_ = text; this.draw_(); }; /** * Sets the bottom title of the gauge. * The top title is displayed below the center. * @param {string} text The bottom title text. */ goog.ui.Gauge.prototype.setTitleBottom = function(text) { this.titleBottom_ = text; this.draw_(); }; /** * Sets the font for displaying top and bottom titles. * @param {goog.graphics.Font} font The font for titles. */ goog.ui.Gauge.prototype.setTitleFont = function(font) { this.titleFont_ = font; this.draw_(); }; /** * Sets the font for displaying the formatted value. * @param {goog.graphics.Font} font The font for displaying the value. */ goog.ui.Gauge.prototype.setValueFont = function(font) { this.valueFont_ = font; this.drawValue_(); }; /** * Sets the color theme for drawing the gauge. * @param {goog.ui.GaugeTheme} theme The color theme to use. */ goog.ui.Gauge.prototype.setTheme = function(theme) { this.theme_ = theme; this.draw_(); }; /** * Set the background color for a range of values on the gauge. * @param {number} fromValue The lower (start) value of the colored range. * @param {number} toValue The higher (end) value of the colored range. * @param {string} color The color name to paint the range with. For example * 'red', '#ffcc00' or constants like goog.ui.Gauge.RED. */ goog.ui.Gauge.prototype.addBackgroundColor = function( fromValue, toValue, color) { this.rangeColors_.push( new goog.ui.GaugeColoredRange(fromValue, toValue, color)); this.draw_(); }; /** * Creates the DOM representation of the graphics area. * @override */ goog.ui.Gauge.prototype.createDom = function() { this.setElementInternal( this.getDomHelper().createDom( goog.dom.TagName.DIV, goog.getCssName('goog-gauge'), this.graphics_.getElement())); }; /** * Clears the entire graphics area. * @private */ goog.ui.Gauge.prototype.clear_ = function() { this.graphics_.clear(); this.needleGroup_ = null; }; /** * Redraw the entire gauge. * @private */ goog.ui.Gauge.prototype.draw_ = function() { if (!this.isInDocument()) { return; } this.clear_(); var x, y; var size = Math.min(this.width_, this.height_); var r = Math.round(goog.ui.Gauge.FACTOR_RADIUS_FROM_SIZE * size); var cx = this.width_ / 2; var cy = this.height_ / 2; var theme = this.theme_; if (!theme) { // Lazy allocation of default theme, common to all instances theme = goog.ui.Gauge.prototype.theme_ = new goog.ui.GaugeTheme(); } // Draw main circle frame around gauge var graphics = this.graphics_; var stroke = this.theme_.getExternalBorderStroke(); var fill = theme.getExternalBorderFill(cx, cy, r); graphics.drawCircle(cx, cy, r, stroke, fill); r -= stroke.getWidth(); r = Math.round(r * goog.ui.Gauge.FACTOR_MAIN_AREA); stroke = theme.getInternalBorderStroke(); fill = theme.getInternalBorderFill(cx, cy, r); graphics.drawCircle(cx, cy, r, stroke, fill); r -= stroke.getWidth() * 2; // Draw Background with external and internal borders var rBackgroundInternal = r * goog.ui.Gauge.FACTOR_COLOR_RADIUS; for (var i = 0; i < this.rangeColors_.length; i++) { var rangeColor = this.rangeColors_[i]; var fromValue = rangeColor.fromValue; var toValue = rangeColor.toValue; var path = new goog.graphics.Path(); var fromAngle = this.valueToAngle_(fromValue); var toAngle = this.valueToAngle_(toValue); // Move to outer point at "from" angle path.moveTo( cx + goog.math.angleDx(fromAngle, r), cy + goog.math.angleDy(fromAngle, r)); // Arc to outer point at "to" angle path.arcTo(r, r, fromAngle, toAngle - fromAngle); // Line to inner point at "to" angle path.lineTo( cx + goog.math.angleDx(toAngle, rBackgroundInternal), cy + goog.math.angleDy(toAngle, rBackgroundInternal)); // Arc to inner point at "from" angle path.arcTo( rBackgroundInternal, rBackgroundInternal, toAngle, fromAngle - toAngle); path.close(); fill = new goog.graphics.SolidFill(rangeColor.backgroundColor); graphics.drawPath(path, null, fill); } // Draw titles if (this.titleTop_ || this.titleBottom_) { var font = this.titleFont_; if (!font) { // Lazy creation of font var fontSize = Math.round(r * goog.ui.Gauge.FACTOR_TITLE_FONT_SIZE); font = new goog.graphics.Font(fontSize, goog.ui.Gauge.TITLE_FONT_NAME); this.titleFont_ = font; } fill = new goog.graphics.SolidFill(theme.getTitleColor()); if (this.titleTop_) { y = cy - Math.round(r * goog.ui.Gauge.FACTOR_TITLE_OFFSET); graphics.drawTextOnLine( this.titleTop_, 0, y, this.width_, y, 'center', font, null, fill); } if (this.titleBottom_) { y = cy + Math.round(r * goog.ui.Gauge.FACTOR_TITLE_OFFSET); graphics.drawTextOnLine( this.titleBottom_, 0, y, this.width_, y, 'center', font, null, fill); } } // Draw tick marks var majorTicks = this.majorTicks_; var minorTicks = this.minorTicks_; var rMajorTickInternal = r * goog.ui.Gauge.FACTOR_MAJOR_TICKS; var rMinorTickInternal = r * goog.ui.Gauge.FACTOR_MINOR_TICKS; var ticks = majorTicks * minorTicks; var valueRange = this.maxValue_ - this.minValue_; var tickValueSpan = valueRange / ticks; var majorTicksPath = new goog.graphics.Path(); var minorTicksPath = new goog.graphics.Path(); var tickLabelFill = new goog.graphics.SolidFill(theme.getTickLabelColor()); var tickLabelFont = this.tickLabelFont_; if (!tickLabelFont) { tickLabelFont = new goog.graphics.Font( Math.round(r * goog.ui.Gauge.FACTOR_TICK_LABEL_FONT_SIZE), goog.ui.Gauge.TITLE_FONT_NAME); } var tickLabelFontSize = tickLabelFont.size; for (var i = 0; i <= ticks; i++) { var angle = this.valueToAngle_(i * tickValueSpan + this.minValue_); var isMajorTick = i % minorTicks == 0; var rInternal = isMajorTick ? rMajorTickInternal : rMinorTickInternal; var path = isMajorTick ? majorTicksPath : minorTicksPath; x = cx + goog.math.angleDx(angle, rInternal); y = cy + goog.math.angleDy(angle, rInternal); path.moveTo(x, y); x = cx + goog.math.angleDx(angle, r); y = cy + goog.math.angleDy(angle, r); path.lineTo(x, y); // Draw the tick's label for major ticks if (isMajorTick && this.majorTickLabels_) { var tickIndex = Math.floor(i / minorTicks); var label = this.majorTickLabels_[tickIndex]; if (label) { x = cx + goog.math.angleDx(angle, rInternal - tickLabelFontSize / 2); y = cy + goog.math.angleDy(angle, rInternal - tickLabelFontSize / 2); var x1, x2; var align = 'center'; if (angle > 280 || angle < 90) { align = 'right'; x1 = 0; x2 = x; } else if (angle >= 90 && angle < 260) { align = 'left'; x1 = x; x2 = this.width_; } else { // Values around top (angle 260-280) are centered around point var dw = Math.min(x, this.width_ - x); // Nearest side border x1 = x - dw; x2 = x + dw; y += Math.round(tickLabelFontSize / 4); // Movea bit down } graphics.drawTextOnLine( label, x1, y, x2, y, align, tickLabelFont, null, tickLabelFill); } } } stroke = theme.getMinorTickStroke(); graphics.drawPath(minorTicksPath, stroke, null); stroke = theme.getMajorTickStroke(); graphics.drawPath(majorTicksPath, stroke, null); // Draw the needle and the value label. Stop animation when doing // full redraw and jump to the final value position. this.stopAnimation_(); this.needleRadius_ = r; this.drawValue_(); }; /** * Handle animation events while the hand is moving. * @param {goog.fx.AnimationEvent} e The event. * @private */ goog.ui.Gauge.prototype.onAnimate_ = function(e) { this.needleValuePosition_ = e.x; this.drawValue_(); }; /** * Handle animation events when hand move is complete. * @private */ goog.ui.Gauge.prototype.onAnimateEnd_ = function() { this.stopAnimation_(); }; /** * Stop the current animation, if it is active. * @private */ goog.ui.Gauge.prototype.stopAnimation_ = function() { if (this.animation_) { goog.events.removeAll(this.animation_); this.animation_.stop(false); this.animation_ = null; } }; /** * Convert a value to the position in the range. The returned position * is a value between 0 and 1, where 0 indicates the lowest range value, * 1 is the highest range, and any value in between is proportional * to mapping the range to (0-1). * If the value is not within the range, the returned value may be a bit * lower than 0, or a bit higher than 1. This is done so that values out * of range will be displayed just a bit outside of the gauge axis. * @param {number} value The value to convert. * @private * @return {number} The range position. */ goog.ui.Gauge.prototype.valueToRangePosition_ = function(value) { var valueRange = this.maxValue_ - this.minValue_; var valuePct = (value - this.minValue_) / valueRange; // 0 to 1 // If value is out of range, trim it not to be too much out of range valuePct = Math.max(valuePct, -goog.ui.Gauge.MAX_EXCEED_POSITION_POSITION); valuePct = Math.min(valuePct, 1 + goog.ui.Gauge.MAX_EXCEED_POSITION_POSITION); return valuePct; }; /** * Convert a value to an angle based on the value range and angle span * @param {number} value The value. * @return {number} The angle where this value is located on the round * axis, based on the range and angle span. * @private */ goog.ui.Gauge.prototype.valueToAngle_ = function(value) { var valuePct = this.valueToRangePosition_(value); return this.valuePositionToAngle_(valuePct); }; /** * Convert a value-position (percent in the range) to an angle based on * the angle span. A value-position is a value that has been proportinally * adjusted to a value betwwen 0-1, proportionaly to the range. * @param {number} valuePct The value. * @return {number} The angle where this value is located on the round * axis, based on the range and angle span. * @private */ goog.ui.Gauge.prototype.valuePositionToAngle_ = function(valuePct) { var startAngle = goog.math.standardAngle((360 - this.angleSpan_) / 2 + 90); return this.angleSpan_ * valuePct + startAngle; }; /** * Draw the elements that depend on the current value (the needle and * the formatted value). This function is called whenever a value is changed * or when the entire gauge is redrawn. * @private */ goog.ui.Gauge.prototype.drawValue_ = function() { if (!this.isInDocument()) { return; } var r = this.needleRadius_; var graphics = this.graphics_; var theme = this.theme_; var cx = this.width_ / 2; var cy = this.height_ / 2; var angle = this.valuePositionToAngle_( /** @type {number} */ (this.needleValuePosition_)); // Compute the needle path var frontRadius = Math.round(r * goog.ui.Gauge.FACTOR_NEEDLE_FRONT); var backRadius = Math.round(r * goog.ui.Gauge.FACTOR_NEEDLE_BACK); var frontDx = goog.math.angleDx(angle, frontRadius); var frontDy = goog.math.angleDy(angle, frontRadius); var backDx = goog.math.angleDx(angle, backRadius); var backDy = goog.math.angleDy(angle, backRadius); var angleRight = goog.math.standardAngle(angle + 90); var distanceControlPointBase = r * goog.ui.Gauge.FACTOR_NEEDLE_WIDTH; var controlPointMidDx = goog.math.angleDx(angleRight, distanceControlPointBase); var controlPointMidDy = goog.math.angleDy(angleRight, distanceControlPointBase); var path = new goog.graphics.Path(); path.moveTo(cx + frontDx, cy + frontDy); path.curveTo( cx + controlPointMidDx, cy + controlPointMidDy, cx - backDx + (controlPointMidDx / 2), cy - backDy + (controlPointMidDy / 2), cx - backDx, cy - backDy); path.curveTo( cx - backDx - (controlPointMidDx / 2), cy - backDy - (controlPointMidDy / 2), cx - controlPointMidDx, cy - controlPointMidDy, cx + frontDx, cy + frontDy); // Draw the needle hinge var rh = Math.round(r * goog.ui.Gauge.FACTOR_NEEDLE_HINGE); // Clean previous needle var needleGroup = this.needleGroup_; if (needleGroup) { needleGroup.clear(); } else { needleGroup = this.needleGroup_ = graphics.createGroup(); } // Draw current formatted value if provided. if (this.formattedValue_) { var font = this.valueFont_; if (!font) { var fontSize = Math.round(r * goog.ui.Gauge.FACTOR_VALUE_FONT_SIZE); font = new goog.graphics.Font(fontSize, goog.ui.Gauge.TITLE_FONT_NAME); font.bold = true; this.valueFont_ = font; } var fill = new goog.graphics.SolidFill(theme.getValueColor()); var y = cy + Math.round(r * goog.ui.Gauge.FACTOR_VALUE_OFFSET); graphics.drawTextOnLine( this.formattedValue_, 0, y, this.width_, y, 'center', font, null, fill, needleGroup); } // Draw the needle var stroke = theme.getNeedleStroke(); var fill = theme.getNeedleFill(cx, cy, rh); graphics.drawPath(path, stroke, fill, needleGroup); stroke = theme.getHingeStroke(); fill = theme.getHingeFill(cx, cy, rh); graphics.drawCircle(cx, cy, rh, stroke, fill, needleGroup); }; /** * Redraws the entire gauge. * Should be called after theme colors have been changed. */ goog.ui.Gauge.prototype.redraw = function() { this.draw_(); }; /** @override */ goog.ui.Gauge.prototype.enterDocument = function() { goog.ui.Gauge.superClass_.enterDocument.call(this); // set roles and states var el = this.getElement(); goog.asserts.assert(el, 'The DOM element for the gauge cannot be null.'); goog.a11y.aria.setRole(el, 'progressbar'); goog.a11y.aria.setState(el, 'live', 'polite'); goog.a11y.aria.setState(el, 'valuemin', this.minValue_); goog.a11y.aria.setState(el, 'valuemax', this.maxValue_); goog.a11y.aria.setState(el, 'valuenow', this.value_); this.draw_(); }; /** @override */ goog.ui.Gauge.prototype.exitDocument = function() { goog.ui.Gauge.superClass_.exitDocument.call(this); this.stopAnimation_(); }; /** @override */ goog.ui.Gauge.prototype.disposeInternal = function() { this.stopAnimation_(); this.graphics_.dispose(); delete this.graphics_; delete this.needleGroup_; delete this.theme_; delete this.rangeColors_; goog.ui.Gauge.superClass_.disposeInternal.call(this); };