| // 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 SvgGraphics sub class that uses SVG to draw the graphics. |
| * @author arv@google.com (Erik Arvidsson) |
| */ |
| |
| goog.provide('goog.graphics.SvgGraphics'); |
| |
| goog.require('goog.Timer'); |
| goog.require('goog.dom'); |
| goog.require('goog.events.EventHandler'); |
| goog.require('goog.events.EventType'); |
| goog.require('goog.graphics.AbstractGraphics'); |
| goog.require('goog.graphics.LinearGradient'); |
| goog.require('goog.graphics.Path'); |
| goog.require('goog.graphics.SolidFill'); |
| goog.require('goog.graphics.Stroke'); |
| goog.require('goog.graphics.SvgEllipseElement'); |
| goog.require('goog.graphics.SvgGroupElement'); |
| goog.require('goog.graphics.SvgImageElement'); |
| goog.require('goog.graphics.SvgPathElement'); |
| goog.require('goog.graphics.SvgRectElement'); |
| goog.require('goog.graphics.SvgTextElement'); |
| goog.require('goog.math'); |
| goog.require('goog.math.Size'); |
| goog.require('goog.style'); |
| goog.require('goog.userAgent'); |
| |
| |
| |
| /** |
| * A Graphics implementation for drawing using SVG. |
| * @param {string|number} width The width in pixels. Strings |
| * expressing percentages of parent with (e.g. '80%') are also accepted. |
| * @param {string|number} height The height in pixels. Strings |
| * expressing percentages of parent with (e.g. '80%') are also accepted. |
| * @param {?number=} opt_coordWidth The coordinate width - if |
| * omitted or null, defaults to same as width. |
| * @param {?number=} opt_coordHeight The coordinate height - if |
| * omitted or null, defaults to same as height. |
| * @param {goog.dom.DomHelper=} opt_domHelper The DOM helper object for the |
| * document we want to render in. |
| * @constructor |
| * @extends {goog.graphics.AbstractGraphics} |
| * @deprecated goog.graphics is deprecated. It existed to abstract over browser |
| * differences before the canvas tag was widely supported. See |
| * http://en.wikipedia.org/wiki/Canvas_element for details. |
| * @final |
| */ |
| goog.graphics.SvgGraphics = function(width, height, |
| opt_coordWidth, opt_coordHeight, |
| opt_domHelper) { |
| goog.graphics.AbstractGraphics.call(this, width, height, |
| opt_coordWidth, opt_coordHeight, |
| opt_domHelper); |
| |
| /** |
| * Map from def key to id of def root element. |
| * Defs are global "defines" of svg that are used to share common attributes, |
| * for example gradients. |
| * @type {Object} |
| * @private |
| */ |
| this.defs_ = {}; |
| |
| /** |
| * Whether to manually implement viewBox by using a coordinate transform. |
| * As of 1/11/08 this is necessary for Safari 3 but not for the nightly |
| * WebKit build. Apply to webkit versions < 526. 525 is the |
| * last version used by Safari 3.1. |
| * @type {boolean} |
| * @private |
| */ |
| this.useManualViewbox_ = goog.userAgent.WEBKIT && |
| !goog.userAgent.isVersionOrHigher(526); |
| |
| /** |
| * Event handler. |
| * @type {goog.events.EventHandler<!goog.graphics.SvgGraphics>} |
| * @private |
| */ |
| this.handler_ = new goog.events.EventHandler(this); |
| }; |
| goog.inherits(goog.graphics.SvgGraphics, goog.graphics.AbstractGraphics); |
| |
| |
| /** |
| * The SVG namespace URN |
| * @private |
| * @type {string} |
| */ |
| goog.graphics.SvgGraphics.SVG_NS_ = 'http://www.w3.org/2000/svg'; |
| |
| |
| /** |
| * The name prefix for def entries |
| * @private |
| * @type {string} |
| */ |
| goog.graphics.SvgGraphics.DEF_ID_PREFIX_ = '_svgdef_'; |
| |
| |
| /** |
| * The next available unique identifier for a def entry. |
| * This is a static variable, so that when multiple graphics are used in one |
| * document, the same def id can not be re-defined by another SvgGraphics. |
| * @type {number} |
| * @private |
| */ |
| goog.graphics.SvgGraphics.nextDefId_ = 0; |
| |
| |
| /** |
| * Svg element for definitions for other elements, e.g. linear gradients. |
| * @type {Element} |
| * @private |
| */ |
| goog.graphics.SvgGraphics.prototype.defsElement_; |
| |
| |
| /** |
| * Creates an SVG element. Used internally and by different SVG classes. |
| * @param {string} tagName The type of element to create. |
| * @param {Object=} opt_attributes Map of name-value pairs for attributes. |
| * @return {!Element} The created element. |
| * @private |
| */ |
| goog.graphics.SvgGraphics.prototype.createSvgElement_ = function(tagName, |
| opt_attributes) { |
| var element = this.dom_.getDocument().createElementNS( |
| goog.graphics.SvgGraphics.SVG_NS_, tagName); |
| |
| if (opt_attributes) { |
| this.setElementAttributes(element, opt_attributes); |
| } |
| |
| return element; |
| }; |
| |
| |
| /** |
| * Sets properties to an SVG element. Used internally and by different |
| * SVG elements. |
| * @param {Element} element The svg element. |
| * @param {Object} attributes Map of name-value pairs for attributes. |
| */ |
| goog.graphics.SvgGraphics.prototype.setElementAttributes = function(element, |
| attributes) { |
| for (var key in attributes) { |
| element.setAttribute(key, attributes[key]); |
| } |
| }; |
| |
| |
| /** |
| * Appends an element. |
| * |
| * @param {goog.graphics.Element} element The element wrapper. |
| * @param {goog.graphics.GroupElement=} opt_group The group wrapper element |
| * to append to. If not specified, appends to the main canvas. |
| * @private |
| */ |
| goog.graphics.SvgGraphics.prototype.append_ = function(element, opt_group) { |
| var parent = opt_group || this.canvasElement; |
| parent.getElement().appendChild(element.getElement()); |
| }; |
| |
| |
| /** |
| * Sets the fill of the given element. |
| * @param {goog.graphics.StrokeAndFillElement} element The element wrapper. |
| * @param {goog.graphics.Fill?} fill The fill object. |
| * @override |
| */ |
| goog.graphics.SvgGraphics.prototype.setElementFill = function(element, fill) { |
| var svgElement = element.getElement(); |
| if (fill instanceof goog.graphics.SolidFill) { |
| svgElement.setAttribute('fill', fill.getColor()); |
| svgElement.setAttribute('fill-opacity', fill.getOpacity()); |
| } else if (fill instanceof goog.graphics.LinearGradient) { |
| // create a def key which is just a concat of all the relevant fields |
| var defKey = 'lg-' + |
| fill.getX1() + '-' + fill.getY1() + '-' + |
| fill.getX2() + '-' + fill.getY2() + '-' + |
| fill.getColor1() + '-' + fill.getColor2(); |
| // It seems that the SVG version accepts opacity where the VML does not |
| |
| var id = this.getDef(defKey); |
| |
| if (!id) { // No def for this yet, create it |
| // Create the gradient def entry (only linear gradient are supported) |
| var gradient = this.createSvgElement_('linearGradient', { |
| 'x1': fill.getX1(), |
| 'y1': fill.getY1(), |
| 'x2': fill.getX2(), |
| 'y2': fill.getY2(), |
| 'gradientUnits': 'userSpaceOnUse' |
| }); |
| |
| var gstyle = 'stop-color:' + fill.getColor1(); |
| if (goog.isNumber(fill.getOpacity1())) { |
| gstyle += ';stop-opacity:' + fill.getOpacity1(); |
| } |
| var stop1 = this.createSvgElement_( |
| 'stop', {'offset': '0%', 'style': gstyle}); |
| gradient.appendChild(stop1); |
| |
| // LinearGradients don't have opacity in VML so implement that before |
| // enabling the following code. |
| // if (fill.getOpacity() != null) { |
| // gstyles += 'opacity:' + fill.getOpacity() + ';' |
| // } |
| gstyle = 'stop-color:' + fill.getColor2(); |
| if (goog.isNumber(fill.getOpacity2())) { |
| gstyle += ';stop-opacity:' + fill.getOpacity2(); |
| } |
| var stop2 = this.createSvgElement_( |
| 'stop', {'offset': '100%', 'style': gstyle}); |
| gradient.appendChild(stop2); |
| |
| // LinearGradients don't have opacity in VML so implement that before |
| // enabling the following code. |
| // if (fill.getOpacity() != null) { |
| // gstyles += 'opacity:' + fill.getOpacity() + ';' |
| // } |
| |
| id = this.addDef(defKey, gradient); |
| } |
| |
| // Link element to linearGradient definition |
| svgElement.setAttribute('fill', 'url(#' + id + ')'); |
| } else { |
| svgElement.setAttribute('fill', 'none'); |
| } |
| }; |
| |
| |
| /** |
| * Sets the stroke of the given element. |
| * @param {goog.graphics.StrokeAndFillElement} element The element wrapper. |
| * @param {goog.graphics.Stroke?} stroke The stroke object. |
| * @override |
| */ |
| goog.graphics.SvgGraphics.prototype.setElementStroke = function(element, |
| stroke) { |
| var svgElement = element.getElement(); |
| if (stroke) { |
| svgElement.setAttribute('stroke', stroke.getColor()); |
| svgElement.setAttribute('stroke-opacity', stroke.getOpacity()); |
| |
| var width = stroke.getWidth(); |
| if (goog.isString(width) && width.indexOf('px') != -1) { |
| svgElement.setAttribute('stroke-width', |
| parseFloat(width) / this.getPixelScaleX()); |
| } else { |
| svgElement.setAttribute('stroke-width', width); |
| } |
| } else { |
| svgElement.setAttribute('stroke', 'none'); |
| } |
| }; |
| |
| |
| /** |
| * Set the translation and rotation of an element. |
| * |
| * If a more general affine transform is needed than this provides |
| * (e.g. skew and scale) then use setElementAffineTransform. |
| * @param {goog.graphics.Element} element The element wrapper. |
| * @param {number} x The x coordinate of the translation transform. |
| * @param {number} y The y coordinate of the translation transform. |
| * @param {number} angle The angle of the rotation transform. |
| * @param {number} centerX The horizontal center of the rotation transform. |
| * @param {number} centerY The vertical center of the rotation transform. |
| * @override |
| */ |
| goog.graphics.SvgGraphics.prototype.setElementTransform = function(element, x, |
| y, angle, centerX, centerY) { |
| element.getElement().setAttribute('transform', 'translate(' + x + ',' + y + |
| ') rotate(' + angle + ' ' + centerX + ' ' + centerY + ')'); |
| }; |
| |
| |
| /** |
| * Set the transformation of an element. |
| * @param {goog.graphics.Element} element The element wrapper. |
| * @param {!goog.graphics.AffineTransform} affineTransform The |
| * transformation applied to this element. |
| * @override |
| */ |
| goog.graphics.SvgGraphics.prototype.setElementAffineTransform = function( |
| element, affineTransform) { |
| var t = affineTransform; |
| var substr = [t.getScaleX(), t.getShearY(), t.getShearX(), t.getScaleY(), |
| t.getTranslateX(), t.getTranslateY()].join(','); |
| element.getElement().setAttribute('transform', 'matrix(' + substr + ')'); |
| }; |
| |
| |
| /** |
| * Creates the DOM representation of the graphics area. |
| * @override |
| */ |
| goog.graphics.SvgGraphics.prototype.createDom = function() { |
| // Set up the standard attributes. |
| var attributes = { |
| 'width': this.width, |
| 'height': this.height, |
| 'overflow': 'hidden' |
| }; |
| |
| var svgElement = this.createSvgElement_('svg', attributes); |
| |
| var groupElement = this.createSvgElement_('g'); |
| |
| this.defsElement_ = this.createSvgElement_('defs'); |
| this.canvasElement = new goog.graphics.SvgGroupElement(groupElement, this); |
| |
| svgElement.appendChild(this.defsElement_); |
| svgElement.appendChild(groupElement); |
| |
| // Use the svgElement as the root element. |
| this.setElementInternal(svgElement); |
| |
| // Set up the coordinate system. |
| this.setViewBox_(); |
| }; |
| |
| |
| /** |
| * Changes the coordinate system position. |
| * @param {number} left The coordinate system left bound. |
| * @param {number} top The coordinate system top bound. |
| * @override |
| */ |
| goog.graphics.SvgGraphics.prototype.setCoordOrigin = function(left, top) { |
| this.coordLeft = left; |
| this.coordTop = top; |
| |
| this.setViewBox_(); |
| }; |
| |
| |
| /** |
| * Changes the coordinate size. |
| * @param {number} coordWidth The coordinate width. |
| * @param {number} coordHeight The coordinate height. |
| * @override |
| */ |
| goog.graphics.SvgGraphics.prototype.setCoordSize = function(coordWidth, |
| coordHeight) { |
| goog.graphics.SvgGraphics.superClass_.setCoordSize.apply( |
| this, arguments); |
| this.setViewBox_(); |
| }; |
| |
| |
| /** |
| * @return {string} The view box string. |
| * @private |
| */ |
| goog.graphics.SvgGraphics.prototype.getViewBox_ = function() { |
| return this.coordLeft + ' ' + this.coordTop + ' ' + |
| (this.coordWidth ? this.coordWidth + ' ' + this.coordHeight : ''); |
| }; |
| |
| |
| /** |
| * Sets up the view box. |
| * @private |
| */ |
| goog.graphics.SvgGraphics.prototype.setViewBox_ = function() { |
| if (this.coordWidth || this.coordLeft || this.coordTop) { |
| this.getElement().setAttribute('preserveAspectRatio', 'none'); |
| if (this.useManualViewbox_) { |
| this.updateManualViewBox_(); |
| } else { |
| this.getElement().setAttribute('viewBox', this.getViewBox_()); |
| } |
| } |
| }; |
| |
| |
| /** |
| * Updates the transform of the root element to fake a viewBox. Should only |
| * be called when useManualViewbox_ is set. |
| * @private |
| */ |
| goog.graphics.SvgGraphics.prototype.updateManualViewBox_ = function() { |
| if (!this.isInDocument() || |
| !(this.coordWidth || this.coordLeft || !this.coordTop)) { |
| return; |
| } |
| |
| var size = this.getPixelSize(); |
| if (size.width == 0) { |
| // In Safari, invisible SVG is sometimes shown. Explicitly hide it. |
| this.getElement().style.visibility = 'hidden'; |
| return; |
| } |
| |
| this.getElement().style.visibility = ''; |
| |
| var offsetX = - this.coordLeft; |
| var offsetY = - this.coordTop; |
| var scaleX = size.width / this.coordWidth; |
| var scaleY = size.height / this.coordHeight; |
| |
| this.canvasElement.getElement().setAttribute('transform', |
| 'scale(' + scaleX + ' ' + scaleY + ') ' + |
| 'translate(' + offsetX + ' ' + offsetY + ')'); |
| }; |
| |
| |
| /** |
| * Change the size of the canvas. |
| * @param {number} pixelWidth The width in pixels. |
| * @param {number} pixelHeight The height in pixels. |
| * @override |
| */ |
| goog.graphics.SvgGraphics.prototype.setSize = function(pixelWidth, |
| pixelHeight) { |
| goog.style.setSize(this.getElement(), pixelWidth, pixelHeight); |
| }; |
| |
| |
| /** @override */ |
| goog.graphics.SvgGraphics.prototype.getPixelSize = function() { |
| if (!goog.userAgent.GECKO) { |
| return this.isInDocument() ? |
| goog.style.getSize(this.getElement()) : |
| goog.graphics.SvgGraphics.base(this, 'getPixelSize'); |
| } |
| |
| // In Gecko, goog.style.getSize does not work for SVG elements. We have to |
| // compute the size manually if it is percentage based. |
| var width = this.width; |
| var height = this.height; |
| var computeWidth = goog.isString(width) && width.indexOf('%') != -1; |
| var computeHeight = goog.isString(height) && height.indexOf('%') != -1; |
| |
| if (!this.isInDocument() && (computeWidth || computeHeight)) { |
| return null; |
| } |
| |
| var parent; |
| var parentSize; |
| |
| if (computeWidth) { |
| parent = /** @type {Element} */ (this.getElement().parentNode); |
| parentSize = goog.style.getSize(parent); |
| width = parseFloat(/** @type {string} */ (width)) * parentSize.width / 100; |
| } |
| |
| if (computeHeight) { |
| parent = parent || /** @type {Element} */ (this.getElement().parentNode); |
| parentSize = parentSize || goog.style.getSize(parent); |
| height = parseFloat(/** @type {string} */ (height)) * parentSize.height / |
| 100; |
| } |
| |
| return new goog.math.Size(/** @type {number} */ (width), |
| /** @type {number} */ (height)); |
| }; |
| |
| |
| /** |
| * Remove all drawing elements from the graphics. |
| * @override |
| */ |
| goog.graphics.SvgGraphics.prototype.clear = function() { |
| this.canvasElement.clear(); |
| goog.dom.removeChildren(this.defsElement_); |
| this.defs_ = {}; |
| }; |
| |
| |
| /** |
| * Draw an ellipse. |
| * |
| * @param {number} cx Center X coordinate. |
| * @param {number} cy Center Y coordinate. |
| * @param {number} rx Radius length for the x-axis. |
| * @param {number} ry Radius length for the y-axis. |
| * @param {goog.graphics.Stroke?} stroke Stroke object describing the |
| * stroke. |
| * @param {goog.graphics.Fill?} fill Fill object describing the fill. |
| * @param {goog.graphics.GroupElement=} opt_group The group wrapper element |
| * to append to. If not specified, appends to the main canvas. |
| * |
| * @return {!goog.graphics.EllipseElement} The newly created element. |
| * @override |
| */ |
| goog.graphics.SvgGraphics.prototype.drawEllipse = function( |
| cx, cy, rx, ry, stroke, fill, opt_group) { |
| var element = this.createSvgElement_('ellipse', |
| {'cx': cx, 'cy': cy, 'rx': rx, 'ry': ry}); |
| var wrapper = new goog.graphics.SvgEllipseElement(element, this, stroke, |
| fill); |
| this.append_(wrapper, opt_group); |
| return wrapper; |
| }; |
| |
| |
| /** |
| * Draw a rectangle. |
| * |
| * @param {number} x X coordinate (left). |
| * @param {number} y Y coordinate (top). |
| * @param {number} width Width of rectangle. |
| * @param {number} height Height of rectangle. |
| * @param {goog.graphics.Stroke?} stroke Stroke object describing the |
| * stroke. |
| * @param {goog.graphics.Fill?} fill Fill object describing the fill. |
| * @param {goog.graphics.GroupElement=} opt_group The group wrapper element |
| * to append to. If not specified, appends to the main canvas. |
| * |
| * @return {!goog.graphics.RectElement} The newly created element. |
| * @override |
| */ |
| goog.graphics.SvgGraphics.prototype.drawRect = function(x, y, width, height, |
| stroke, fill, opt_group) { |
| var element = this.createSvgElement_('rect', |
| {'x': x, 'y': y, 'width': width, 'height': height}); |
| var wrapper = new goog.graphics.SvgRectElement(element, this, stroke, fill); |
| this.append_(wrapper, opt_group); |
| return wrapper; |
| }; |
| |
| |
| /** |
| * Draw an image. |
| * |
| * @param {number} x X coordinate (left). |
| * @param {number} y Y coordinate (top). |
| * @param {number} width Width of the image. |
| * @param {number} height Height of the image. |
| * @param {string} src The source fo the image. |
| * @param {goog.graphics.GroupElement=} opt_group The group wrapper element |
| * to append to. If not specified, appends to the main canvas. |
| * |
| * @return {!goog.graphics.ImageElement} The newly created image wrapped in a |
| * rectangle element. |
| */ |
| goog.graphics.SvgGraphics.prototype.drawImage = function(x, y, width, height, |
| src, opt_group) { |
| var element = this.createSvgElement_('image', { |
| 'x': x, |
| 'y': y, |
| 'width': width, |
| 'height': height, |
| 'image-rendering': 'optimizeQuality', |
| 'preserveAspectRatio': 'none' |
| }); |
| element.setAttributeNS('http://www.w3.org/1999/xlink', 'href', src); |
| var wrapper = new goog.graphics.SvgImageElement(element, this); |
| this.append_(wrapper, opt_group); |
| return wrapper; |
| }; |
| |
| |
| /** |
| * Draw a text string vertically centered on a given line. |
| * |
| * @param {string} text The text to draw. |
| * @param {number} x1 X coordinate of start of line. |
| * @param {number} y1 Y coordinate of start of line. |
| * @param {number} x2 X coordinate of end of line. |
| * @param {number} y2 Y coordinate of end of line. |
| * @param {string} align Horizontal alignment: left (default), center, right. |
| * @param {goog.graphics.Font} font Font describing the font properties. |
| * @param {goog.graphics.Stroke?} stroke Stroke object describing the |
| * stroke. |
| * @param {goog.graphics.Fill?} fill Fill object describing the fill. |
| * @param {goog.graphics.GroupElement=} opt_group The group wrapper element |
| * to append to. If not specified, appends to the main canvas. |
| * |
| * @return {!goog.graphics.TextElement} The newly created element. |
| * @override |
| */ |
| goog.graphics.SvgGraphics.prototype.drawTextOnLine = function( |
| text, x1, y1, x2, y2, align, font, stroke, fill, opt_group) { |
| var angle = Math.round(goog.math.angle(x1, y1, x2, y2)); |
| var dx = x2 - x1; |
| var dy = y2 - y1; |
| var lineLength = Math.round(Math.sqrt(dx * dx + dy * dy)); // Length of line |
| |
| // SVG baseline is on the glyph's base line. We estimate it as 85% of the |
| // font height. This is just a rough estimate, but do not have a better way. |
| var fontSize = font.size; |
| var attributes = {'font-family': font.family, 'font-size': fontSize}; |
| var baseline = Math.round(fontSize * 0.85); |
| var textY = Math.round(y1 - (fontSize / 2) + baseline); |
| var textX = x1; |
| if (align == 'center') { |
| textX += Math.round(lineLength / 2); |
| attributes['text-anchor'] = 'middle'; |
| } else if (align == 'right') { |
| textX += lineLength; |
| attributes['text-anchor'] = 'end'; |
| } |
| attributes['x'] = textX; |
| attributes['y'] = textY; |
| if (font.bold) { |
| attributes['font-weight'] = 'bold'; |
| } |
| if (font.italic) { |
| attributes['font-style'] = 'italic'; |
| } |
| if (angle != 0) { |
| attributes['transform'] = 'rotate(' + angle + ' ' + x1 + ' ' + y1 + ')'; |
| } |
| |
| var element = this.createSvgElement_('text', attributes); |
| element.appendChild(this.dom_.getDocument().createTextNode(text)); |
| |
| // Bypass a Firefox-Mac bug where text fill is ignored. If text has no stroke, |
| // set a stroke, otherwise the text will not be visible. |
| if (stroke == null && goog.userAgent.GECKO && goog.userAgent.MAC) { |
| var color = 'black'; |
| // For solid fills, use the fill color |
| if (fill instanceof goog.graphics.SolidFill) { |
| color = fill.getColor(); |
| } |
| stroke = new goog.graphics.Stroke(1, color); |
| } |
| |
| var wrapper = new goog.graphics.SvgTextElement(element, this, stroke, fill); |
| this.append_(wrapper, opt_group); |
| return wrapper; |
| }; |
| |
| |
| /** |
| * Draw a path. |
| * |
| * @param {!goog.graphics.Path} path The path object to draw. |
| * @param {goog.graphics.Stroke?} stroke Stroke object describing the |
| * stroke. |
| * @param {goog.graphics.Fill?} fill Fill object describing the fill. |
| * @param {goog.graphics.GroupElement=} opt_group The group wrapper element |
| * to append to. If not specified, appends to the main canvas. |
| * |
| * @return {!goog.graphics.PathElement} The newly created element. |
| * @override |
| */ |
| goog.graphics.SvgGraphics.prototype.drawPath = function( |
| path, stroke, fill, opt_group) { |
| |
| var element = this.createSvgElement_('path', |
| {'d': goog.graphics.SvgGraphics.getSvgPath(path)}); |
| var wrapper = new goog.graphics.SvgPathElement(element, this, stroke, fill); |
| this.append_(wrapper, opt_group); |
| return wrapper; |
| }; |
| |
| |
| /** |
| * Returns a string representation of a logical path suitable for use in |
| * an SVG element. |
| * |
| * @param {goog.graphics.Path} path The logical path. |
| * @return {string} The SVG path representation. |
| * @suppress {deprecated} goog.graphics is deprecated. |
| */ |
| goog.graphics.SvgGraphics.getSvgPath = function(path) { |
| var list = []; |
| path.forEachSegment(function(segment, args) { |
| switch (segment) { |
| case goog.graphics.Path.Segment.MOVETO: |
| list.push('M'); |
| Array.prototype.push.apply(list, args); |
| break; |
| case goog.graphics.Path.Segment.LINETO: |
| list.push('L'); |
| Array.prototype.push.apply(list, args); |
| break; |
| case goog.graphics.Path.Segment.CURVETO: |
| list.push('C'); |
| Array.prototype.push.apply(list, args); |
| break; |
| case goog.graphics.Path.Segment.ARCTO: |
| var extent = args[3]; |
| list.push('A', args[0], args[1], |
| 0, Math.abs(extent) > 180 ? 1 : 0, extent > 0 ? 1 : 0, |
| args[4], args[5]); |
| break; |
| case goog.graphics.Path.Segment.CLOSE: |
| list.push('Z'); |
| break; |
| } |
| }); |
| return list.join(' '); |
| }; |
| |
| |
| /** |
| * Create an empty group of drawing elements. |
| * |
| * @param {goog.graphics.GroupElement=} opt_group The group wrapper element |
| * to append to. If not specified, appends to the main canvas. |
| * |
| * @return {!goog.graphics.GroupElement} The newly created group. |
| * @override |
| */ |
| goog.graphics.SvgGraphics.prototype.createGroup = function(opt_group) { |
| var element = this.createSvgElement_('g'); |
| var parent = opt_group || this.canvasElement; |
| parent.getElement().appendChild(element); |
| return new goog.graphics.SvgGroupElement(element, this); |
| }; |
| |
| |
| /** |
| * Measure and return the width (in pixels) of a given text string. |
| * Text measurement is needed to make sure a text can fit in the allocated area. |
| * The way text length is measured is by writing it into a div that is after |
| * the visible area, measure the div width, and immediatly erase the written |
| * value. |
| * |
| * @param {string} text The text string to measure. |
| * @param {goog.graphics.Font} font The font object describing the font style. |
| * @override |
| */ |
| goog.graphics.SvgGraphics.prototype.getTextWidth = function(text, font) { |
| // TODO(user) Implement |
| }; |
| |
| |
| /** |
| * Adds a defintion of an element to the global definitions. |
| * @param {string} defKey This is a key that should be unique in a way that |
| * if two definitions are equal the should have the same key. |
| * @param {Element} defElement DOM element to add as a definition. It must |
| * have an id attribute set. |
| * @return {string} The assigned id of the defElement. |
| */ |
| goog.graphics.SvgGraphics.prototype.addDef = function(defKey, defElement) { |
| if (defKey in this.defs_) { |
| return this.defs_[defKey]; |
| } |
| var id = goog.graphics.SvgGraphics.DEF_ID_PREFIX_ + |
| goog.graphics.SvgGraphics.nextDefId_++; |
| defElement.setAttribute('id', id); |
| this.defs_[defKey] = id; |
| |
| // Add the def defElement of the defs list. |
| var defs = this.defsElement_; |
| defs.appendChild(defElement); |
| return id; |
| }; |
| |
| |
| /** |
| * Returns the id of a definition element. |
| * @param {string} defKey This is a key that should be unique in a way that |
| * if two definitions are equal the should have the same key. |
| * @return {?string} The id of the found definition element or null if |
| * not found. |
| */ |
| goog.graphics.SvgGraphics.prototype.getDef = function(defKey) { |
| return defKey in this.defs_ ? this.defs_[defKey] : null; |
| }; |
| |
| |
| /** |
| * Removes a definition of an elemnt from the global definitions. |
| * @param {string} defKey This is a key that should be unique in a way that |
| * if two definitions are equal they should have the same key. |
| */ |
| goog.graphics.SvgGraphics.prototype.removeDef = function(defKey) { |
| var id = this.getDef(defKey); |
| if (id) { |
| var element = this.dom_.getElement(id); |
| this.defsElement_.removeChild(element); |
| delete this.defs_[defKey]; |
| } |
| }; |
| |
| |
| /** @override */ |
| goog.graphics.SvgGraphics.prototype.enterDocument = function() { |
| var oldPixelSize = this.getPixelSize(); |
| goog.graphics.SvgGraphics.superClass_.enterDocument.call(this); |
| |
| // Dispatch a resize if this is the first time the size value is accurate. |
| if (!oldPixelSize) { |
| this.dispatchEvent(goog.events.EventType.RESIZE); |
| } |
| |
| |
| // For percentage based heights, listen for changes to size. |
| if (this.useManualViewbox_) { |
| var width = this.width; |
| var height = this.height; |
| |
| if (typeof width == 'string' && width.indexOf('%') != -1 && |
| typeof height == 'string' && height.indexOf('%') != -1) { |
| // SVG elements don't behave well with respect to size events, so we |
| // resort to polling. |
| this.handler_.listen(goog.graphics.SvgGraphics.getResizeCheckTimer_(), |
| goog.Timer.TICK, this.updateManualViewBox_); |
| } |
| |
| this.updateManualViewBox_(); |
| } |
| }; |
| |
| |
| /** @override */ |
| goog.graphics.SvgGraphics.prototype.exitDocument = function() { |
| goog.graphics.SvgGraphics.superClass_.exitDocument.call(this); |
| |
| // Stop polling. |
| if (this.useManualViewbox_) { |
| this.handler_.unlisten(goog.graphics.SvgGraphics.getResizeCheckTimer_(), |
| goog.Timer.TICK, this.updateManualViewBox_); |
| } |
| }; |
| |
| |
| /** |
| * Disposes of the component by removing event handlers, detacing DOM nodes from |
| * the document body, and removing references to them. |
| * @override |
| * @protected |
| */ |
| goog.graphics.SvgGraphics.prototype.disposeInternal = function() { |
| delete this.defs_; |
| delete this.defsElement_; |
| delete this.canvasElement; |
| this.handler_.dispose(); |
| delete this.handler_; |
| goog.graphics.SvgGraphics.superClass_.disposeInternal.call(this); |
| }; |
| |
| |
| /** |
| * The centralized resize checking timer. |
| * @type {goog.Timer|undefined} |
| * @private |
| */ |
| goog.graphics.SvgGraphics.resizeCheckTimer_; |
| |
| |
| /** |
| * @return {goog.Timer} The centralized timer object used for interval timing. |
| * @private |
| */ |
| goog.graphics.SvgGraphics.getResizeCheckTimer_ = function() { |
| if (!goog.graphics.SvgGraphics.resizeCheckTimer_) { |
| goog.graphics.SvgGraphics.resizeCheckTimer_ = new goog.Timer(400); |
| goog.graphics.SvgGraphics.resizeCheckTimer_.start(); |
| } |
| |
| return /** @type {goog.Timer} */ ( |
| goog.graphics.SvgGraphics.resizeCheckTimer_); |
| }; |
| |
| |
| /** @override */ |
| goog.graphics.SvgGraphics.prototype.isDomClonable = function() { |
| return true; |
| }; |