blob: 59db4a2ad4439a9f5cbc2794cd83182da78b57d4 [file] [log] [blame]
// 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;
};