Path: blob/trunk/third_party/closure/goog/graphics/vmlgraphics.js
2868 views
// Copyright 2007 The Closure Library Authors. All Rights Reserved.1//2// Licensed under the Apache License, Version 2.0 (the "License");3// you may not use this file except in compliance with the License.4// You may obtain a copy of the License at5//6// http://www.apache.org/licenses/LICENSE-2.07//8// Unless required by applicable law or agreed to in writing, software9// distributed under the License is distributed on an "AS-IS" BASIS,10// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.11// See the License for the specific language governing permissions and12// limitations under the License.131415/**16* @fileoverview VmlGraphics sub class that uses VML to draw the graphics.17* @author [email protected] (Erik Arvidsson)18*/192021goog.provide('goog.graphics.VmlGraphics');222324goog.require('goog.array');25goog.require('goog.dom.TagName');26goog.require('goog.dom.safe');27goog.require('goog.events');28goog.require('goog.events.EventHandler');29goog.require('goog.events.EventType');30goog.require('goog.graphics.AbstractGraphics');31goog.require('goog.graphics.Font');32goog.require('goog.graphics.LinearGradient');33goog.require('goog.graphics.Path');34goog.require('goog.graphics.SolidFill');35goog.require('goog.graphics.VmlEllipseElement');36goog.require('goog.graphics.VmlGroupElement');37goog.require('goog.graphics.VmlImageElement');38goog.require('goog.graphics.VmlPathElement');39goog.require('goog.graphics.VmlRectElement');40goog.require('goog.graphics.VmlTextElement');41goog.require('goog.html.uncheckedconversions');42goog.require('goog.math');43goog.require('goog.math.Size');44goog.require('goog.reflect');45goog.require('goog.string');46goog.require('goog.string.Const');47goog.require('goog.style');48goog.require('goog.userAgent');49505152/**53* A Graphics implementation for drawing using VML.54* @param {string|number} width The (non-zero) width in pixels. Strings55* expressing percentages of parent with (e.g. '80%') are also accepted.56* @param {string|number} height The (non-zero) height in pixels. Strings57* expressing percentages of parent with (e.g. '80%') are also accepted.58* @param {?number=} opt_coordWidth The coordinate width - if59* omitted or null, defaults to same as width.60* @param {?number=} opt_coordHeight The coordinate height - if61* omitted or null, defaults to same as height.62* @param {goog.dom.DomHelper=} opt_domHelper The DOM helper object for the63* document we want to render in.64* @constructor65* @extends {goog.graphics.AbstractGraphics}66* @deprecated goog.graphics is deprecated. It existed to abstract over browser67* differences before the canvas tag was widely supported. See68* http://en.wikipedia.org/wiki/Canvas_element for details.69* @final70*/71goog.graphics.VmlGraphics = function(72width, height, opt_coordWidth, opt_coordHeight, opt_domHelper) {73goog.graphics.AbstractGraphics.call(74this, width, height, opt_coordWidth, opt_coordHeight, opt_domHelper);75this.handler_ = new goog.events.EventHandler(this);76this.registerDisposable(this.handler_);77};78goog.inherits(goog.graphics.VmlGraphics, goog.graphics.AbstractGraphics);798081/**82* The prefix to use for VML elements83* @private84* @type {string}85*/86goog.graphics.VmlGraphics.VML_PREFIX_ = 'g_vml_';878889/**90* The VML namespace URN91* @private92* @type {string}93*/94goog.graphics.VmlGraphics.VML_NS_ = 'urn:schemas-microsoft-com:vml';959697/**98* The VML behavior URL.99* @private100* @type {string}101*/102goog.graphics.VmlGraphics.VML_IMPORT_ = '#default#VML';103104105/**106* Whether the document is using IE8 standards mode, and therefore needs hacks.107* @private108* @type {boolean}109*/110goog.graphics.VmlGraphics.IE8_MODE_ = goog.global.document &&111goog.global.document.documentMode && goog.global.document.documentMode >= 8;112113114/**115* The coordinate multiplier to allow sub-pixel rendering116* @type {number}117*/118goog.graphics.VmlGraphics.COORD_MULTIPLIER = 100;119120121/**122* Converts the given size to a css size. If it is a percentage, leaves it123* alone. Otherwise assumes px.124*125* @param {number|string} size The size to use.126* @return {string} The position adjusted for COORD_MULTIPLIER.127*/128goog.graphics.VmlGraphics.toCssSize = function(size) {129return goog.isString(size) && goog.string.endsWith(size, '%') ?130size :131parseFloat(size.toString()) + 'px';132};133134135/**136* Multiplies positioning coordinates by COORD_MULTIPLIER to allow sub-pixel137* coordinates. Also adds a half pixel offset to match SVG.138*139* This function is internal for the VML supporting classes, and140* should not be used externally.141*142* @param {number|string} number A position in pixels.143* @return {number} The position adjusted for COORD_MULTIPLIER.144*/145goog.graphics.VmlGraphics.toPosCoord = function(number) {146return Math.round(147(parseFloat(number.toString()) - 0.5) *148goog.graphics.VmlGraphics.COORD_MULTIPLIER);149};150151152/**153* Add a "px" suffix to a number of pixels, and multiplies all coordinates by154* COORD_MULTIPLIER to allow sub-pixel coordinates.155*156* This function is internal for the VML supporting classes, and157* should not be used externally.158*159* @param {number|string} number A position in pixels.160* @return {string} The position with suffix 'px'.161*/162goog.graphics.VmlGraphics.toPosPx = function(number) {163return goog.graphics.VmlGraphics.toPosCoord(number) + 'px';164};165166167/**168* Multiplies the width or height coordinate by COORD_MULTIPLIER to allow169* sub-pixel coordinates.170*171* This function is internal for the VML supporting classes, and172* should not be used externally.173*174* @param {string|number} number A size in units.175* @return {number} The size multiplied by the correct factor.176*/177goog.graphics.VmlGraphics.toSizeCoord = function(number) {178return Math.round(179parseFloat(number.toString()) *180goog.graphics.VmlGraphics.COORD_MULTIPLIER);181};182183184/**185* Add a "px" suffix to a number of pixels, and multiplies all coordinates by186* COORD_MULTIPLIER to allow sub-pixel coordinates.187*188* This function is internal for the VML supporting classes, and189* should not be used externally.190*191* @param {number|string} number A size in pixels.192* @return {string} The size with suffix 'px'.193*/194goog.graphics.VmlGraphics.toSizePx = function(number) {195return goog.graphics.VmlGraphics.toSizeCoord(number) + 'px';196};197198199/**200* Sets an attribute on the given VML element, in the way best suited to the201* current version of IE. Should only be used in the goog.graphics package.202* @param {Element} element The element to set an attribute203* on.204* @param {string} name The name of the attribute to set.205* @param {string} value The value to set it to.206*/207goog.graphics.VmlGraphics.setAttribute = function(element, name, value) {208if (goog.graphics.VmlGraphics.IE8_MODE_) {209element[name] = value;210} else {211element.setAttribute(name, value);212}213};214215216/**217* Event handler.218* @type {goog.events.EventHandler}219* @private220*/221goog.graphics.VmlGraphics.prototype.handler_;222223224/**225* Creates a VML element. Used internally and by different VML classes.226* @param {string} tagName The type of element to create.227* @return {!Element} The created element.228*/229goog.graphics.VmlGraphics.prototype.createVmlElement = function(tagName) {230var element = this.dom_.createElement(231goog.graphics.VmlGraphics.VML_PREFIX_ + ':' + tagName);232element.id = goog.string.createUniqueString();233return element;234};235236237/**238* Returns the VML element with the given id that is a child of this graphics239* object.240* Should be considered package private, and not used externally.241* @param {string} id The element id to find.242* @return {Element} The element with the given id, or null if none is found.243*/244goog.graphics.VmlGraphics.prototype.getVmlElement = function(id) {245return this.dom_.getElement(id);246};247248249/**250* Resets the graphics so they will display properly on IE8. Noop in older251* versions.252* @private253*/254goog.graphics.VmlGraphics.prototype.updateGraphics_ = function() {255if (goog.graphics.VmlGraphics.IE8_MODE_ && this.isInDocument()) {256// There's a risk of mXSS here, as the browser is not guaranteed to257// return the HTML that was originally written, when innerHTML is read.258// However, given that this a deprecated API and affects only IE, it seems259// an acceptable risk.260var html = goog.html.uncheckedconversions261.safeHtmlFromStringKnownToSatisfyTypeContract(262goog.string.Const.from('Assign innerHTML to itself'),263this.getElement().innerHTML);264goog.dom.safe.setInnerHtml(265/** @type {!Element} */ (this.getElement()), html);266}267};268269270/**271* Appends an element.272*273* @param {goog.graphics.Element} element The element wrapper.274* @param {goog.graphics.GroupElement=} opt_group The group wrapper element275* to append to. If not specified, appends to the main canvas.276* @private277*/278goog.graphics.VmlGraphics.prototype.append_ = function(element, opt_group) {279var parent = opt_group || this.canvasElement;280parent.getElement().appendChild(element.getElement());281this.updateGraphics_();282};283284285/**286* Sets the fill for the given element.287* @param {goog.graphics.StrokeAndFillElement} element The element wrapper.288* @param {goog.graphics.Fill?} fill The fill object.289* @override290*/291goog.graphics.VmlGraphics.prototype.setElementFill = function(element, fill) {292var vmlElement = element.getElement();293goog.graphics.VmlGraphics.removeFill_(vmlElement);294if (fill instanceof goog.graphics.SolidFill) {295// NOTE(arv): VML does not understand 'transparent' so hard code support296// for it.297if (fill.getColor() == 'transparent') {298vmlElement.filled = false;299} else if (fill.getOpacity() != 1) {300vmlElement.filled = true;301// Set opacity (number 0-1 is translated to percent)302var fillNode = this.createVmlElement('fill');303fillNode.opacity = Math.round(fill.getOpacity() * 100) + '%';304fillNode.color = fill.getColor();305vmlElement.appendChild(fillNode);306} else {307vmlElement.filled = true;308vmlElement.fillcolor = fill.getColor();309}310} else if (fill instanceof goog.graphics.LinearGradient) {311vmlElement.filled = true;312// Add a 'fill' element313var gradient = this.createVmlElement('fill');314gradient.color = fill.getColor1();315gradient.color2 = fill.getColor2();316if (goog.isNumber(fill.getOpacity1())) {317gradient.opacity = fill.getOpacity1();318}319if (goog.isNumber(fill.getOpacity2())) {320gradient.opacity2 = fill.getOpacity2();321}322var angle =323goog.math.angle(fill.getX1(), fill.getY1(), fill.getX2(), fill.getY2());324// Our angles start from 0 to the right, and grow clockwise.325// MSIE starts from 0 to top, and grows anti-clockwise.326angle = Math.round(goog.math.standardAngle(270 - angle));327gradient.angle = angle;328gradient.type = 'gradient';329vmlElement.appendChild(gradient);330} else {331vmlElement.filled = false;332}333this.updateGraphics_();334};335336337/**338* Sets the stroke for the given element.339* @param {goog.graphics.StrokeAndFillElement} element The element wrapper.340* @param {goog.graphics.Stroke?} stroke The stroke object.341* @override342*/343goog.graphics.VmlGraphics.prototype.setElementStroke = function(344element, stroke) {345var vmlElement = element.getElement();346if (stroke) {347vmlElement.stroked = true;348349var width = stroke.getWidth();350if (goog.isString(width) && width.indexOf('px') == -1) {351width = parseFloat(width);352} else {353width = width * this.getPixelScaleX();354}355356var strokeElement = vmlElement.getElementsByTagName('stroke')[0];357if (!strokeElement) {358strokeElement = strokeElement || this.createVmlElement('stroke');359vmlElement.appendChild(strokeElement);360}361strokeElement.opacity = stroke.getOpacity();362strokeElement.weight = width + 'px';363strokeElement.color = stroke.getColor();364} else {365vmlElement.stroked = false;366}367this.updateGraphics_();368};369370371/**372* Set the translation and rotation of an element.373*374* If a more general affine transform is needed than this provides375* (e.g. skew and scale) then use setElementAffineTransform.376* @param {goog.graphics.Element} element The element wrapper.377* @param {number} x The x coordinate of the translation transform.378* @param {number} y The y coordinate of the translation transform.379* @param {number} angle The angle of the rotation transform.380* @param {number} centerX The horizontal center of the rotation transform.381* @param {number} centerY The vertical center of the rotation transform.382* @override383*/384goog.graphics.VmlGraphics.prototype.setElementTransform = function(385element, x, y, angle, centerX, centerY) {386var el = element.getElement();387388el.style.left = goog.graphics.VmlGraphics.toPosPx(x);389el.style.top = goog.graphics.VmlGraphics.toPosPx(y);390if (angle || el.rotation) {391el.rotation = angle;392el.coordsize = goog.graphics.VmlGraphics.toSizeCoord(centerX * 2) + ' ' +393goog.graphics.VmlGraphics.toSizeCoord(centerY * 2);394}395};396397398/**399* Set the transformation of an element.400* @param {!goog.graphics.Element} element The element wrapper.401* @param {!goog.graphics.AffineTransform} affineTransform The402* transformation applied to this element.403* @override404*/405goog.graphics.VmlGraphics.prototype.setElementAffineTransform = function(406element, affineTransform) {407var t = affineTransform;408var vmlElement = element.getElement();409goog.graphics.VmlGraphics.removeSkew_(vmlElement);410var skewNode = this.createVmlElement('skew');411skewNode.on = 'true';412// Move the transform origin to 0px,0px of the graphics.413// In VML, 0,0 means the center of the element, -0.5,-0.5 left top conner of414// it.415skewNode.origin =416(-vmlElement.style.pixelLeft / vmlElement.style.pixelWidth - 0.5) + ',' +417(-vmlElement.style.pixelTop / vmlElement.style.pixelHeight - 0.5);418skewNode.offset = t.getTranslateX().toFixed(1) + 'px,' +419t.getTranslateY().toFixed(1) + 'px';420skewNode.matrix = [421t.getScaleX().toFixed(6), t.getShearX().toFixed(6),422t.getShearY().toFixed(6), t.getScaleY().toFixed(6), 0, 0423].join(',');424vmlElement.appendChild(skewNode);425this.updateGraphics_();426};427428429/**430* Removes the skew information from a dom element.431* @param {Element} element DOM element.432* @private433*/434goog.graphics.VmlGraphics.removeSkew_ = function(element) {435goog.array.forEach(element.childNodes, function(child) {436if (child.tagName == 'skew') {437element.removeChild(child);438}439});440};441442443/**444* Removes the fill information from a dom element.445* @param {Element} element DOM element.446* @private447*/448goog.graphics.VmlGraphics.removeFill_ = function(element) {449element.fillcolor = '';450goog.array.forEach(element.childNodes, function(child) {451if (child.tagName == 'fill') {452element.removeChild(child);453}454});455};456457458/**459* Set top, left, width and height for an element.460* This function is internal for the VML supporting classes, and461* should not be used externally.462*463* @param {Element} element DOM element.464* @param {number} left Left ccordinate in pixels.465* @param {number} top Top ccordinate in pixels.466* @param {number} width Width in pixels.467* @param {number} height Height in pixels.468*/469goog.graphics.VmlGraphics.setPositionAndSize = function(470element, left, top, width, height) {471var style = element.style;472style.position = 'absolute';473style.left = goog.graphics.VmlGraphics.toPosPx(left);474style.top = goog.graphics.VmlGraphics.toPosPx(top);475style.width = goog.graphics.VmlGraphics.toSizePx(width);476style.height = goog.graphics.VmlGraphics.toSizePx(height);477478if (element.tagName == 'shape') {479element.coordsize = goog.graphics.VmlGraphics.toSizeCoord(width) + ' ' +480goog.graphics.VmlGraphics.toSizeCoord(height);481}482};483484485/**486* Creates an element spanning the surface.487*488* @param {string} type The type of element to create.489* @return {!Element} The created, positioned, and sized element.490* @private491*/492goog.graphics.VmlGraphics.prototype.createFullSizeElement_ = function(type) {493var element = this.createVmlElement(type);494var size = this.getCoordSize();495goog.graphics.VmlGraphics.setPositionAndSize(496element, 0, 0, size.width, size.height);497return element;498};499500501/**502* IE magic - if this "no-op" logic is not here, the 'if' statement in createDom503* will fail intermittently. The logic is used to prevent the JsCompiler from504* stripping this piece of code, which it quite reasonably thinks is doing505* nothing. Put it in try-catch block to prevent "Unspecified Error" when506* this statement is executed in a defer JS in IE.507* More info here:508* http://www.mail-archive.com/[email protected]/msg01838.html509*/510if (goog.userAgent.IE) {511try {512goog.reflect.sinkValue(document.namespaces);513} catch (e) {514}515}516517518/**519* Creates the DOM representation of the graphics area.520* @override521*/522goog.graphics.VmlGraphics.prototype.createDom = function() {523var doc = this.dom_.getDocument();524525// Add the namespace.526if (!doc.namespaces[goog.graphics.VmlGraphics.VML_PREFIX_]) {527if (goog.graphics.VmlGraphics.IE8_MODE_) {528doc.namespaces.add(529goog.graphics.VmlGraphics.VML_PREFIX_,530goog.graphics.VmlGraphics.VML_NS_,531goog.graphics.VmlGraphics.VML_IMPORT_);532} else {533doc.namespaces.add(534goog.graphics.VmlGraphics.VML_PREFIX_,535goog.graphics.VmlGraphics.VML_NS_);536}537538// We assume that we only need to add the CSS if the namespace was not539// present540var ss = doc.createStyleSheet();541ss.cssText = goog.graphics.VmlGraphics.VML_PREFIX_ + '\\:*' +542'{behavior:url(#default#VML)}';543}544545// Outer a DIV with overflow hidden for clipping.546// All inner elements are absolutely positioned on-top of this div.547var pixelWidth = this.width;548var pixelHeight = this.height;549var divElement = this.dom_.createDom(goog.dom.TagName.DIV, {550'style': 'overflow:hidden;position:relative;width:' +551goog.graphics.VmlGraphics.toCssSize(pixelWidth) + ';height:' +552goog.graphics.VmlGraphics.toCssSize(pixelHeight)553});554555this.setElementInternal(divElement);556557var group = this.createVmlElement('group');558var style = group.style;559560style.position = 'absolute';561style.left = style.top = '0';562style.width = this.width;563style.height = this.height;564if (this.coordWidth) {565group.coordsize = goog.graphics.VmlGraphics.toSizeCoord(this.coordWidth) +566' ' +567goog.graphics.VmlGraphics.toSizeCoord(568/** @type {number} */ (this.coordHeight));569} else {570group.coordsize = goog.graphics.VmlGraphics.toSizeCoord(pixelWidth) + ' ' +571goog.graphics.VmlGraphics.toSizeCoord(pixelHeight);572}573574if (goog.isDef(this.coordLeft)) {575group.coordorigin = goog.graphics.VmlGraphics.toSizeCoord(this.coordLeft) +576' ' + goog.graphics.VmlGraphics.toSizeCoord(this.coordTop);577} else {578group.coordorigin = '0 0';579}580divElement.appendChild(group);581582this.canvasElement = new goog.graphics.VmlGroupElement(group, this);583584goog.events.listen(585divElement, goog.events.EventType.RESIZE,586goog.bind(this.handleContainerResize_, this));587};588589590/**591* Changes the canvas element size to match the container element size.592* @private593*/594goog.graphics.VmlGraphics.prototype.handleContainerResize_ = function() {595var size = goog.style.getSize(this.getElement());596var style = this.canvasElement.getElement().style;597598if (size.width) {599style.width = size.width + 'px';600style.height = size.height + 'px';601} else {602var current = this.getElement();603while (current && current.currentStyle &&604current.currentStyle.display != 'none') {605current = current.parentNode;606}607if (current && current.currentStyle) {608this.handler_.listen(609current, 'propertychange', this.handleContainerResize_);610}611}612613this.dispatchEvent(goog.events.EventType.RESIZE);614};615616617/**618* Handle property changes on hidden ancestors.619* @param {goog.events.BrowserEvent} e The browser event.620* @private621*/622goog.graphics.VmlGraphics.prototype.handlePropertyChange_ = function(e) {623var prop = e.getBrowserEvent().propertyName;624if (prop == 'display' || prop == 'className') {625this.handler_.unlisten(626/** @type {Element} */ (e.target), 'propertychange',627this.handlePropertyChange_);628this.handleContainerResize_();629}630};631632633/**634* Changes the coordinate system position.635* @param {number} left The coordinate system left bound.636* @param {number} top The coordinate system top bound.637* @override638*/639goog.graphics.VmlGraphics.prototype.setCoordOrigin = function(left, top) {640this.coordLeft = left;641this.coordTop = top;642643this.canvasElement.getElement().coordorigin =644goog.graphics.VmlGraphics.toSizeCoord(this.coordLeft) + ' ' +645goog.graphics.VmlGraphics.toSizeCoord(this.coordTop);646};647648649/**650* Changes the coordinate size.651* @param {number} coordWidth The coordinate width.652* @param {number} coordHeight The coordinate height.653* @override654*/655goog.graphics.VmlGraphics.prototype.setCoordSize = function(656coordWidth, coordHeight) {657goog.graphics.VmlGraphics.superClass_.setCoordSize.apply(this, arguments);658659this.canvasElement.getElement().coordsize =660goog.graphics.VmlGraphics.toSizeCoord(coordWidth) + ' ' +661goog.graphics.VmlGraphics.toSizeCoord(coordHeight);662};663664665/**666* Change the size of the canvas.667* @param {number} pixelWidth The width in pixels.668* @param {number} pixelHeight The height in pixels.669* @override670*/671goog.graphics.VmlGraphics.prototype.setSize = function(672pixelWidth, pixelHeight) {673goog.style.setSize(this.getElement(), pixelWidth, pixelHeight);674};675676677/**678* @return {!goog.math.Size} Returns the number of pixels spanned by the679* surface.680* @override681*/682goog.graphics.VmlGraphics.prototype.getPixelSize = function() {683var el = this.getElement();684// The following relies on the fact that the size can never be 0.685return new goog.math.Size(686el.style.pixelWidth || el.offsetWidth || 1,687el.style.pixelHeight || el.offsetHeight || 1);688};689690691/**692* Remove all drawing elements from the graphics.693* @override694*/695goog.graphics.VmlGraphics.prototype.clear = function() {696this.canvasElement.clear();697};698699700/**701* Draw an ellipse.702*703* @param {number} cx Center X coordinate.704* @param {number} cy Center Y coordinate.705* @param {number} rx Radius length for the x-axis.706* @param {number} ry Radius length for the y-axis.707* @param {goog.graphics.Stroke?} stroke Stroke object describing the708* stroke.709* @param {goog.graphics.Fill?} fill Fill object describing the fill.710* @param {goog.graphics.GroupElement=} opt_group The group wrapper element711* to append to. If not specified, appends to the main canvas.712*713* @return {!goog.graphics.EllipseElement} The newly created element.714* @override715*/716goog.graphics.VmlGraphics.prototype.drawEllipse = function(717cx, cy, rx, ry, stroke, fill, opt_group) {718var element = this.createVmlElement('oval');719goog.graphics.VmlGraphics.setPositionAndSize(720element, cx - rx, cy - ry, rx * 2, ry * 2);721var wrapper = new goog.graphics.VmlEllipseElement(722element, this, cx, cy, rx, ry, stroke, fill);723this.append_(wrapper, opt_group);724return wrapper;725};726727728/**729* Draw a rectangle.730*731* @param {number} x X coordinate (left).732* @param {number} y Y coordinate (top).733* @param {number} width Width of rectangle.734* @param {number} height Height of rectangle.735* @param {goog.graphics.Stroke?} stroke Stroke object describing the736* stroke.737* @param {goog.graphics.Fill?} fill Fill object describing the fill.738* @param {goog.graphics.GroupElement=} opt_group The group wrapper element739* to append to. If not specified, appends to the main canvas.740*741* @return {!goog.graphics.RectElement} The newly created element.742* @override743*/744goog.graphics.VmlGraphics.prototype.drawRect = function(745x, y, width, height, stroke, fill, opt_group) {746var element = this.createVmlElement('rect');747goog.graphics.VmlGraphics.setPositionAndSize(element, x, y, width, height);748var wrapper = new goog.graphics.VmlRectElement(element, this, stroke, fill);749this.append_(wrapper, opt_group);750return wrapper;751};752753754/**755* Draw an image.756*757* @param {number} x X coordinate (left).758* @param {number} y Y coordinate (top).759* @param {number} width Width of image.760* @param {number} height Height of image.761* @param {string} src Source of the image.762* @param {goog.graphics.GroupElement=} opt_group The group wrapper element763* to append to. If not specified, appends to the main canvas.764*765* @return {!goog.graphics.ImageElement} The newly created element.766*/767goog.graphics.VmlGraphics.prototype.drawImage = function(768x, y, width, height, src, opt_group) {769var element = this.createVmlElement('image');770goog.graphics.VmlGraphics.setPositionAndSize(element, x, y, width, height);771goog.graphics.VmlGraphics.setAttribute(element, 'src', src);772var wrapper = new goog.graphics.VmlImageElement(element, this);773this.append_(wrapper, opt_group);774return wrapper;775};776777778/**779* Draw a text string vertically centered on a given line.780*781* @param {string} text The text to draw.782* @param {number} x1 X coordinate of start of line.783* @param {number} y1 Y coordinate of start of line.784* @param {number} x2 X coordinate of end of line.785* @param {number} y2 Y coordinate of end of line.786* @param {?string} align Horizontal alignment: left (default), center, right.787* @param {goog.graphics.Font} font Font describing the font properties.788* @param {goog.graphics.Stroke?} stroke Stroke object describing the stroke.789* @param {goog.graphics.Fill?} fill Fill object describing the fill.790* @param {goog.graphics.GroupElement=} opt_group The group wrapper element791* to append to. If not specified, appends to the main canvas.792*793* @return {!goog.graphics.TextElement} The newly created element.794* @override795*/796goog.graphics.VmlGraphics.prototype.drawTextOnLine = function(797text, x1, y1, x2, y2, align, font, stroke, fill, opt_group) {798var shape = this.createFullSizeElement_('shape');799800var pathElement = this.createVmlElement('path');801var path = 'M' + goog.graphics.VmlGraphics.toPosCoord(x1) + ',' +802goog.graphics.VmlGraphics.toPosCoord(y1) + 'L' +803goog.graphics.VmlGraphics.toPosCoord(x2) + ',' +804goog.graphics.VmlGraphics.toPosCoord(y2) + 'E';805goog.graphics.VmlGraphics.setAttribute(pathElement, 'v', path);806goog.graphics.VmlGraphics.setAttribute(pathElement, 'textpathok', 'true');807808var textPathElement = this.createVmlElement('textpath');809textPathElement.setAttribute('on', 'true');810var style = textPathElement.style;811style.fontSize = font.size * this.getPixelScaleX();812style.fontFamily = font.family;813if (align != null) {814style['v-text-align'] = align;815}816if (font.bold) {817style.fontWeight = 'bold';818}819if (font.italic) {820style.fontStyle = 'italic';821}822goog.graphics.VmlGraphics.setAttribute(textPathElement, 'string', text);823824shape.appendChild(pathElement);825shape.appendChild(textPathElement);826var wrapper = new goog.graphics.VmlTextElement(shape, this, stroke, fill);827this.append_(wrapper, opt_group);828return wrapper;829};830831832/**833* Draw a path.834*835* @param {!goog.graphics.Path} path The path object to draw.836* @param {goog.graphics.Stroke?} stroke Stroke object describing the stroke.837* @param {goog.graphics.Fill?} fill Fill object describing the fill.838* @param {goog.graphics.GroupElement=} opt_group The group wrapper element839* to append to. If not specified, appends to the main canvas.840*841* @return {!goog.graphics.PathElement} The newly created element.842* @override843*/844goog.graphics.VmlGraphics.prototype.drawPath = function(845path, stroke, fill, opt_group) {846var element = this.createFullSizeElement_('shape');847goog.graphics.VmlGraphics.setAttribute(848element, 'path', goog.graphics.VmlGraphics.getVmlPath(path));849850var wrapper = new goog.graphics.VmlPathElement(element, this, stroke, fill);851this.append_(wrapper, opt_group);852return wrapper;853};854855856/**857* Returns a string representation of a logical path suitable for use in858* a VML element.859*860* @param {goog.graphics.Path} path The logical path.861* @return {string} The VML path representation.862* @suppress {deprecated} goog.graphics is deprecated.863*/864goog.graphics.VmlGraphics.getVmlPath = function(path) {865var list = [];866path.forEachSegment(function(segment, args) {867switch (segment) {868case goog.graphics.Path.Segment.MOVETO:869list.push('m');870Array.prototype.push.apply(871list, goog.array.map(args, goog.graphics.VmlGraphics.toSizeCoord));872break;873case goog.graphics.Path.Segment.LINETO:874list.push('l');875Array.prototype.push.apply(876list, goog.array.map(args, goog.graphics.VmlGraphics.toSizeCoord));877break;878case goog.graphics.Path.Segment.CURVETO:879list.push('c');880Array.prototype.push.apply(881list, goog.array.map(args, goog.graphics.VmlGraphics.toSizeCoord));882break;883case goog.graphics.Path.Segment.CLOSE:884list.push('x');885break;886case goog.graphics.Path.Segment.ARCTO:887var toAngle = args[2] + args[3];888var cx = goog.graphics.VmlGraphics.toSizeCoord(889args[4] - goog.math.angleDx(toAngle, args[0]));890var cy = goog.graphics.VmlGraphics.toSizeCoord(891args[5] - goog.math.angleDy(toAngle, args[1]));892var rx = goog.graphics.VmlGraphics.toSizeCoord(args[0]);893var ry = goog.graphics.VmlGraphics.toSizeCoord(args[1]);894// VML angles are in fd units (see http://www.w3.org/TR/NOTE-VML) and895// are positive counter-clockwise.896var fromAngle = Math.round(args[2] * -65536);897var extent = Math.round(args[3] * -65536);898list.push('ae', cx, cy, rx, ry, fromAngle, extent);899break;900}901});902return list.join(' ');903};904905906/**907* Create an empty group of drawing elements.908*909* @param {goog.graphics.GroupElement=} opt_group The group wrapper element910* to append to. If not specified, appends to the main canvas.911*912* @return {!goog.graphics.GroupElement} The newly created group.913* @override914*/915goog.graphics.VmlGraphics.prototype.createGroup = function(opt_group) {916var element = this.createFullSizeElement_('group');917var parent = opt_group || this.canvasElement;918parent.getElement().appendChild(element);919return new goog.graphics.VmlGroupElement(element, this);920};921922923/**924* Measure and return the width (in pixels) of a given text string.925* Text measurement is needed to make sure a text can fit in the allocated926* area. The way text length is measured is by writing it into a div that is927* after the visible area, measure the div width, and immediately erase the928* written value.929*930* @param {string} text The text string to measure.931* @param {goog.graphics.Font} font The font object describing the font style.932*933* @return {number} The width in pixels of the text strings.934* @override935*/936goog.graphics.VmlGraphics.prototype.getTextWidth = function(text, font) {937// TODO(arv): Implement938return 0;939};940941942/** @override */943goog.graphics.VmlGraphics.prototype.enterDocument = function() {944goog.graphics.VmlGraphics.superClass_.enterDocument.call(this);945this.handleContainerResize_();946this.updateGraphics_();947};948949950/**951* Disposes of the component by removing event handlers, detacing DOM nodes from952* the document body, and removing references to them.953* @override954* @protected955*/956goog.graphics.VmlGraphics.prototype.disposeInternal = function() {957this.canvasElement = null;958goog.graphics.VmlGraphics.superClass_.disposeInternal.call(this);959};960961962