// @info // Polyfill for SVG getPathData() and setPathData() methods. Based on: // - SVGPathSeg polyfill by Philip Rogers (MIT License) // https://github.com/progers/pathseg // - SVGPathNormalizer by Tadahisa Motooka (MIT License) // https://github.com/motooka/SVGPathNormalizer/tree/master/src // - arcToCubicCurves() by Dmitry Baranovskiy (MIT License) // https://github.com/DmitryBaranovskiy/raphael/blob/v2.1.1/raphael.core.js#L1837 // @author // Jarosław Foksa // @license // MIT License if (!SVGPathElement.prototype.getPathData || !SVGPathElement.prototype.setPathData) { (function() { var commandsMap = { "Z":"Z", "M":"M", "L":"L", "C":"C", "Q":"Q", "A":"A", "H":"H", "V":"V", "S":"S", "T":"T", "z":"Z", "m":"m", "l":"l", "c":"c", "q":"q", "a":"a", "h":"h", "v":"v", "s":"s", "t":"t" }; var Source = function(string) { this._string = string; this._currentIndex = 0; this._endIndex = this._string.length; this._prevCommand = null; this._skipOptionalSpaces(); }; var isIE = window.navigator.userAgent.indexOf("MSIE ") !== -1; Source.prototype = { parseSegment: function() { var char = this._string[this._currentIndex]; var command = commandsMap[char] ? commandsMap[char] : null; if (command === null) { // Possibly an implicit command. Not allowed if this is the first command. if (this._prevCommand === null) { return null; } // Check for remaining coordinates in the current command. if ( (char === "+" || char === "-" || char === "." || (char >= "0" && char <= "9")) && this._prevCommand !== "Z" ) { if (this._prevCommand === "M") { command = "L"; } else if (this._prevCommand === "m") { command = "l"; } else { command = this._prevCommand; } } else { command = null; } if (command === null) { return null; } } else { this._currentIndex += 1; } this._prevCommand = command; var values = null; var cmd = command.toUpperCase(); if (cmd === "H" || cmd === "V") { values = [this._parseNumber()]; } else if (cmd === "M" || cmd === "L" || cmd === "T") { values = [this._parseNumber(), this._parseNumber()]; } else if (cmd === "S" || cmd === "Q") { values = [this._parseNumber(), this._parseNumber(), this._parseNumber(), this._parseNumber()]; } else if (cmd === "C") { values = [ this._parseNumber(), this._parseNumber(), this._parseNumber(), this._parseNumber(), this._parseNumber(), this._parseNumber() ]; } else if (cmd === "A") { values = [ this._parseNumber(), this._parseNumber(), this._parseNumber(), this._parseArcFlag(), this._parseArcFlag(), this._parseNumber(), this._parseNumber() ]; } else if (cmd === "Z") { this._skipOptionalSpaces(); values = []; } if (values === null || values.indexOf(null) >= 0) { // Unknown command or known command with invalid values return null; } else { return {type: command, values: values}; } }, hasMoreData: function() { return this._currentIndex < this._endIndex; }, peekSegmentType: function() { var char = this._string[this._currentIndex]; return commandsMap[char] ? commandsMap[char] : null; }, initialCommandIsMoveTo: function() { // If the path is empty it is still valid, so return true. if (!this.hasMoreData()) { return true; } var command = this.peekSegmentType(); // Path must start with moveTo. return command === "M" || command === "m"; }, _isCurrentSpace: function() { var char = this._string[this._currentIndex]; return char <= " " && (char === " " || char === "\n" || char === "\t" || char === "\r" || char === "\f"); }, _skipOptionalSpaces: function() { while (this._currentIndex < this._endIndex && this._isCurrentSpace()) { this._currentIndex += 1; } return this._currentIndex < this._endIndex; }, _skipOptionalSpacesOrDelimiter: function() { if ( this._currentIndex < this._endIndex && !this._isCurrentSpace() && this._string[this._currentIndex] !== "," ) { return false; } if (this._skipOptionalSpaces()) { if (this._currentIndex < this._endIndex && this._string[this._currentIndex] === ",") { this._currentIndex += 1; this._skipOptionalSpaces(); } } return this._currentIndex < this._endIndex; }, // Parse a number from an SVG path. This very closely follows genericParseNumber(...) from // Source/core/svg/SVGParserUtilities.cpp. // Spec: http://www.w3.org/TR/SVG11/single-page.html#paths-PathDataBNF _parseNumber: function() { var exponent = 0; var integer = 0; var frac = 1; var decimal = 0; var sign = 1; var expsign = 1; var startIndex = this._currentIndex; this._skipOptionalSpaces(); // Read the sign. if (this._currentIndex < this._endIndex && this._string[this._currentIndex] === "+") { this._currentIndex += 1; } else if (this._currentIndex < this._endIndex && this._string[this._currentIndex] === "-") { this._currentIndex += 1; sign = -1; } if ( this._currentIndex === this._endIndex || ( (this._string[this._currentIndex] < "0" || this._string[this._currentIndex] > "9") && this._string[this._currentIndex] !== "." ) ) { // The first character of a number must be one of [0-9+-.]. return null; } // Read the integer part, build right-to-left. var startIntPartIndex = this._currentIndex; while ( this._currentIndex < this._endIndex && this._string[this._currentIndex] >= "0" && this._string[this._currentIndex] <= "9" ) { this._currentIndex += 1; // Advance to first non-digit. } if (this._currentIndex !== startIntPartIndex) { var scanIntPartIndex = this._currentIndex - 1; var multiplier = 1; while (scanIntPartIndex >= startIntPartIndex) { integer += multiplier * (this._string[scanIntPartIndex] - "0"); scanIntPartIndex -= 1; multiplier *= 10; } } // Read the decimals. if (this._currentIndex < this._endIndex && this._string[this._currentIndex] === ".") { this._currentIndex += 1; // There must be a least one digit following the . if ( this._currentIndex >= this._endIndex || this._string[this._currentIndex] < "0" || this._string[this._currentIndex] > "9" ) { return null; } while ( this._currentIndex < this._endIndex && this._string[this._currentIndex] >= "0" && this._string[this._currentIndex] <= "9" ) { frac *= 10; decimal += (this._string.charAt(this._currentIndex) - "0") / frac; this._currentIndex += 1; } } // Read the exponent part. if ( this._currentIndex !== startIndex && this._currentIndex + 1 < this._endIndex && (this._string[this._currentIndex] === "e" || this._string[this._currentIndex] === "E") && (this._string[this._currentIndex + 1] !== "x" && this._string[this._currentIndex + 1] !== "m") ) { this._currentIndex += 1; // Read the sign of the exponent. if (this._string[this._currentIndex] === "+") { this._currentIndex += 1; } else if (this._string[this._currentIndex] === "-") { this._currentIndex += 1; expsign = -1; } // There must be an exponent. if ( this._currentIndex >= this._endIndex || this._string[this._currentIndex] < "0" || this._string[this._currentIndex] > "9" ) { return null; } while ( this._currentIndex < this._endIndex && this._string[this._currentIndex] >= "0" && this._string[this._currentIndex] <= "9" ) { exponent *= 10; exponent += (this._string[this._currentIndex] - "0"); this._currentIndex += 1; } } var number = integer + decimal; number *= sign; if (exponent) { number *= Math.pow(10, expsign * exponent); } if (startIndex === this._currentIndex) { return null; } this._skipOptionalSpacesOrDelimiter(); return number; }, _parseArcFlag: function() { if (this._currentIndex >= this._endIndex) { return null; } var flag = null; var flagChar = this._string[this._currentIndex]; this._currentIndex += 1; if (flagChar === "0") { flag = 0; } else if (flagChar === "1") { flag = 1; } else { return null; } this._skipOptionalSpacesOrDelimiter(); return flag; } }; var parsePathDataString = function(string) { if (!string || string.length === 0) return []; var source = new Source(string); var pathData = []; if (source.initialCommandIsMoveTo()) { while (source.hasMoreData()) { var pathSeg = source.parseSegment(); if (pathSeg === null) { break; } else { pathData.push(pathSeg); } } } return pathData; } var setAttribute = SVGPathElement.prototype.setAttribute; var removeAttribute = SVGPathElement.prototype.removeAttribute; var $cachedPathData = window.Symbol ? Symbol() : "__cachedPathData"; var $cachedNormalizedPathData = window.Symbol ? Symbol() : "__cachedNormalizedPathData"; // @info // Get an array of corresponding cubic bezier curve parameters for given arc curve paramters. var arcToCubicCurves = function(x1, y1, x2, y2, r1, r2, angle, largeArcFlag, sweepFlag, _recursive) { var degToRad = function(degrees) { return (Math.PI * degrees) / 180; }; var rotate = function(x, y, angleRad) { var X = x * Math.cos(angleRad) - y * Math.sin(angleRad); var Y = x * Math.sin(angleRad) + y * Math.cos(angleRad); return {x: X, y: Y}; }; var angleRad = degToRad(angle); var params = []; var f1, f2, cx, cy; if (_recursive) { f1 = _recursive[0]; f2 = _recursive[1]; cx = _recursive[2]; cy = _recursive[3]; } else { var p1 = rotate(x1, y1, -angleRad); x1 = p1.x; y1 = p1.y; var p2 = rotate(x2, y2, -angleRad); x2 = p2.x; y2 = p2.y; var x = (x1 - x2) / 2; var y = (y1 - y2) / 2; var h = (x * x) / (r1 * r1) + (y * y) / (r2 * r2); if (h > 1) { h = Math.sqrt(h); r1 = h * r1; r2 = h * r2; } var sign; if (largeArcFlag === sweepFlag) { sign = -1; } else { sign = 1; } var r1Pow = r1 * r1; var r2Pow = r2 * r2; var left = r1Pow * r2Pow - r1Pow * y * y - r2Pow * x * x; var right = r1Pow * y * y + r2Pow * x * x; var k = sign * Math.sqrt(Math.abs(left/right)); cx = k * r1 * y / r2 + (x1 + x2) / 2; cy = k * -r2 * x / r1 + (y1 + y2) / 2; f1 = Math.asin(((y1 - cy) / r2).toFixed(9)); f2 = Math.asin(((y2 - cy) / r2).toFixed(9)); if (x1 < cx) { f1 = Math.PI - f1; } if (x2 < cx) { f2 = Math.PI - f2; } if (f1 < 0) { f1 = Math.PI * 2 + f1; } if (f2 < 0) { f2 = Math.PI * 2 + f2; } if (sweepFlag && f1 > f2) { f1 = f1 - Math.PI * 2; } if (!sweepFlag && f2 > f1) { f2 = f2 - Math.PI * 2; } } var df = f2 - f1; if (Math.abs(df) > (Math.PI * 120 / 180)) { var f2old = f2; var x2old = x2; var y2old = y2; if (sweepFlag && f2 > f1) { f2 = f1 + (Math.PI * 120 / 180) * (1); } else { f2 = f1 + (Math.PI * 120 / 180) * (-1); } x2 = cx + r1 * Math.cos(f2); y2 = cy + r2 * Math.sin(f2); params = arcToCubicCurves(x2, y2, x2old, y2old, r1, r2, angle, 0, sweepFlag, [f2, f2old, cx, cy]); } df = f2 - f1; var c1 = Math.cos(f1); var s1 = Math.sin(f1); var c2 = Math.cos(f2); var s2 = Math.sin(f2); var t = Math.tan(df / 4); var hx = 4 / 3 * r1 * t; var hy = 4 / 3 * r2 * t; var m1 = [x1, y1]; var m2 = [x1 + hx * s1, y1 - hy * c1]; var m3 = [x2 + hx * s2, y2 - hy * c2]; var m4 = [x2, y2]; m2[0] = 2 * m1[0] - m2[0]; m2[1] = 2 * m1[1] - m2[1]; if (_recursive) { return [m2, m3, m4].concat(params); } else { params = [m2, m3, m4].concat(params).join().split(","); var curves = []; var curveParams = []; params.forEach( function(param, i) { if (i % 2) { curveParams.push(rotate(params[i - 1], params[i], angleRad).y); } else { curveParams.push(rotate(params[i], params[i + 1], angleRad).x); } if (curveParams.length === 6) { curves.push(curveParams); curveParams = []; } }); return curves; } }; var clonePathData = function(pathData) { return pathData.map( function(seg) { return {type: seg.type, values: Array.prototype.slice.call(seg.values)} }); }; // @info // Takes any path data, returns path data that consists only from absolute commands. var absolutizePathData = function(pathData) { var absolutizedPathData = []; var currentX = null; var currentY = null; var subpathX = null; var subpathY = null; pathData.forEach( function(seg) { var type = seg.type; if (type === "M") { var x = seg.values[0]; var y = seg.values[1]; absolutizedPathData.push({type: "M", values: [x, y]}); subpathX = x; subpathY = y; currentX = x; currentY = y; } else if (type === "m") { var x = currentX + seg.values[0]; var y = currentY + seg.values[1]; absolutizedPathData.push({type: "M", values: [x, y]}); subpathX = x; subpathY = y; currentX = x; currentY = y; } else if (type === "L") { var x = seg.values[0]; var y = seg.values[1]; absolutizedPathData.push({type: "L", values: [x, y]}); currentX = x; currentY = y; } else if (type === "l") { var x = currentX + seg.values[0]; var y = currentY + seg.values[1]; absolutizedPathData.push({type: "L", values: [x, y]}); currentX = x; currentY = y; } else if (type === "C") { var x1 = seg.values[0]; var y1 = seg.values[1]; var x2 = seg.values[2]; var y2 = seg.values[3]; var x = seg.values[4]; var y = seg.values[5]; absolutizedPathData.push({type: "C", values: [x1, y1, x2, y2, x, y]}); currentX = x; currentY = y; } else if (type === "c") { var x1 = currentX + seg.values[0]; var y1 = currentY + seg.values[1]; var x2 = currentX + seg.values[2]; var y2 = currentY + seg.values[3]; var x = currentX + seg.values[4]; var y = currentY + seg.values[5]; absolutizedPathData.push({type: "C", values: [x1, y1, x2, y2, x, y]}); currentX = x; currentY = y; } else if (type === "Q") { var x1 = seg.values[0]; var y1 = seg.values[1]; var x = seg.values[2]; var y = seg.values[3]; absolutizedPathData.push({type: "Q", values: [x1, y1, x, y]}); currentX = x; currentY = y; } else if (type === "q") { var x1 = currentX + seg.values[0]; var y1 = currentY + seg.values[1]; var x = currentX + seg.values[2]; var y = currentY + seg.values[3]; absolutizedPathData.push({type: "Q", values: [x1, y1, x, y]}); currentX = x; currentY = y; } else if (type === "A") { var x = seg.values[5]; var y = seg.values[6]; absolutizedPathData.push({ type: "A", values: [seg.values[0], seg.values[1], seg.values[2], seg.values[3], seg.values[4], x, y] }); currentX = x; currentY = y; } else if (type === "a") { var x = currentX + seg.values[5]; var y = currentY + seg.values[6]; absolutizedPathData.push({ type: "A", values: [seg.values[0], seg.values[1], seg.values[2], seg.values[3], seg.values[4], x, y] }); currentX = x; currentY = y; } else if (type === "H") { var x = seg.values[0]; absolutizedPathData.push({type: "H", values: [x]}); currentX = x; } else if (type === "h") { var x = currentX + seg.values[0]; absolutizedPathData.push({type: "H", values: [x]}); currentX = x; } else if (type === "V") { var y = seg.values[0]; absolutizedPathData.push({type: "V", values: [y]}); currentY = y; } else if (type === "v") { var y = currentY + seg.values[0]; absolutizedPathData.push({type: "V", values: [y]}); currentY = y; } else if (type === "S") { var x2 = seg.values[0]; var y2 = seg.values[1]; var x = seg.values[2]; var y = seg.values[3]; absolutizedPathData.push({type: "S", values: [x2, y2, x, y]}); currentX = x; currentY = y; } else if (type === "s") { var x2 = currentX + seg.values[0]; var y2 = currentY + seg.values[1]; var x = currentX + seg.values[2]; var y = currentY + seg.values[3]; absolutizedPathData.push({type: "S", values: [x2, y2, x, y]}); currentX = x; currentY = y; } else if (type === "T") { var x = seg.values[0]; var y = seg.values[1] absolutizedPathData.push({type: "T", values: [x, y]}); currentX = x; currentY = y; } else if (type === "t") { var x = currentX + seg.values[0]; var y = currentY + seg.values[1] absolutizedPathData.push({type: "T", values: [x, y]}); currentX = x; currentY = y; } else if (type === "Z" || type === "z") { absolutizedPathData.push({type: "Z", values: []}); currentX = subpathX; currentY = subpathY; } }); return absolutizedPathData; }; // @info // Takes path data that consists only from absolute commands, returns path data that consists only from // "M", "L", "C" and "Z" commands. var reducePathData = function(pathData) { var reducedPathData = []; var lastType = null; var lastControlX = null; var lastControlY = null; var currentX = null; var currentY = null; var subpathX = null; var subpathY = null; pathData.forEach( function(seg) { if (seg.type === "M") { var x = seg.values[0]; var y = seg.values[1]; reducedPathData.push({type: "M", values: [x, y]}); subpathX = x; subpathY = y; currentX = x; currentY = y; } else if (seg.type === "C") { var x1 = seg.values[0]; var y1 = seg.values[1]; var x2 = seg.values[2]; var y2 = seg.values[3]; var x = seg.values[4]; var y = seg.values[5]; reducedPathData.push({type: "C", values: [x1, y1, x2, y2, x, y]}); lastControlX = x2; lastControlY = y2; currentX = x; currentY = y; } else if (seg.type === "L") { var x = seg.values[0]; var y = seg.values[1]; reducedPathData.push({type: "L", values: [x, y]}); currentX = x; currentY = y; } else if (seg.type === "H") { var x = seg.values[0]; reducedPathData.push({type: "L", values: [x, currentY]}); currentX = x; } else if (seg.type === "V") { var y = seg.values[0]; reducedPathData.push({type: "L", values: [currentX, y]}); currentY = y; } else if (seg.type === "S") { var x2 = seg.values[0]; var y2 = seg.values[1]; var x = seg.values[2]; var y = seg.values[3]; var cx1, cy1; if (lastType === "C" || lastType === "S") { cx1 = currentX + (currentX - lastControlX); cy1 = currentY + (currentY - lastControlY); } else { cx1 = currentX; cy1 = currentY; } reducedPathData.push({type: "C", values: [cx1, cy1, x2, y2, x, y]}); lastControlX = x2; lastControlY = y2; currentX = x; currentY = y; } else if (seg.type === "T") { var x = seg.values[0]; var y = seg.values[1]; var x1, y1; if (lastType === "Q" || lastType === "T") { x1 = currentX + (currentX - lastControlX); y1 = currentY + (currentY - lastControlY); } else { x1 = currentX; y1 = currentY; } var cx1 = currentX + 2 * (x1 - currentX) / 3; var cy1 = currentY + 2 * (y1 - currentY) / 3; var cx2 = x + 2 * (x1 - x) / 3; var cy2 = y + 2 * (y1 - y) / 3; reducedPathData.push({type: "C", values: [cx1, cy1, cx2, cy2, x, y]}); lastControlX = x1; lastControlY = y1; currentX = x; currentY = y; } else if (seg.type === "Q") { var x1 = seg.values[0]; var y1 = seg.values[1]; var x = seg.values[2]; var y = seg.values[3]; var cx1 = currentX + 2 * (x1 - currentX) / 3; var cy1 = currentY + 2 * (y1 - currentY) / 3; var cx2 = x + 2 * (x1 - x) / 3; var cy2 = y + 2 * (y1 - y) / 3; reducedPathData.push({type: "C", values: [cx1, cy1, cx2, cy2, x, y]}); lastControlX = x1; lastControlY = y1; currentX = x; currentY = y; } else if (seg.type === "A") { var r1 = seg.values[0]; var r2 = seg.values[1]; var angle = seg.values[2]; var largeArcFlag = seg.values[3]; var sweepFlag = seg.values[4]; var x = seg.values[5]; var y = seg.values[6]; if (r1 === 0 || r2 === 0) { reducedPathData.push({type: "C", values: [currentX, currentY, x, y, x, y]}); currentX = x; currentY = y; } else { if (currentX !== x || currentY !== y) { var curves = arcToCubicCurves(currentX, currentY, x, y, r1, r2, angle, largeArcFlag, sweepFlag); curves.forEach( function(curve) { reducedPathData.push({type: "C", values: curve}); currentX = x; currentY = y; }); } } } else if (seg.type === "Z") { reducedPathData.push(seg); currentX = subpathX; currentY = subpathY; } lastType = seg.type; }); return reducedPathData; }; SVGPathElement.prototype.setAttribute = function(name, value) { if (name === "d") { this[$cachedPathData] = null; this[$cachedNormalizedPathData] = null; } setAttribute.call(this, name, value); }; SVGPathElement.prototype.removeAttribute = function(name, value) { if (name === "d") { this[$cachedPathData] = null; this[$cachedNormalizedPathData] = null; } removeAttribute.call(this, name); }; SVGPathElement.prototype.getPathData = function(options) { if (options && options.normalize) { if (this[$cachedNormalizedPathData]) { return clonePathData(this[$cachedNormalizedPathData]); } else { var pathData; if (this[$cachedPathData]) { pathData = clonePathData(this[$cachedPathData]); } else { pathData = parsePathDataString(this.getAttribute("d") || ""); this[$cachedPathData] = clonePathData(pathData); } var normalizedPathData = reducePathData(absolutizePathData(pathData)); this[$cachedNormalizedPathData] = clonePathData(normalizedPathData); return normalizedPathData; } } else { if (this[$cachedPathData]) { return clonePathData(this[$cachedPathData]); } else { var pathData = parsePathDataString(this.getAttribute("d") || ""); this[$cachedPathData] = clonePathData(pathData); return pathData; } } }; SVGPathElement.prototype.setPathData = function(pathData) { if (pathData.length === 0) { if (isIE) { // @bugfix https://github.com/mbostock/d3/issues/1737 this.setAttribute("d", ""); } else { this.removeAttribute("d"); } } else { var d = ""; for (var i = 0, l = pathData.length; i < l; i += 1) { var seg = pathData[i]; if (i > 0) { d += " "; } d += seg.type; if (seg.values && seg.values.length > 0) { d += " " + seg.values.join(" "); } } this.setAttribute("d", d); } }; SVGRectElement.prototype.getPathData = function(options) { var x = this.x.baseVal.value; var y = this.y.baseVal.value; var width = this.width.baseVal.value; var height = this.height.baseVal.value; var rx = this.hasAttribute("rx") ? this.rx.baseVal.value : this.ry.baseVal.value; var ry = this.hasAttribute("ry") ? this.ry.baseVal.value : this.rx.baseVal.value; if (rx > width / 2) { rx = width / 2; } if (ry > height / 2) { ry = height / 2; } var pathData = [ {type: "M", values: [x+rx, y]}, {type: "H", values: [x+width-rx]}, {type: "A", values: [rx, ry, 0, 0, 1, x+width, y+ry]}, {type: "V", values: [y+height-ry]}, {type: "A", values: [rx, ry, 0, 0, 1, x+width-rx, y+height]}, {type: "H", values: [x+rx]}, {type: "A", values: [rx, ry, 0, 0, 1, x, y+height-ry]}, {type: "V", values: [y+ry]}, {type: "A", values: [rx, ry, 0, 0, 1, x+rx, y]}, {type: "Z", values: []} ]; // Get rid of redundant "A" segs when either rx or ry is 0 pathData = pathData.filter(function(s) { return s.type === "A" && (s.values[0] === 0 || s.values[1] === 0) ? false : true; }); if (options && options.normalize === true) { pathData = reducePathData(pathData); } return pathData; }; SVGCircleElement.prototype.getPathData = function(options) { var cx = this.cx.baseVal.value; var cy = this.cy.baseVal.value; var r = this.r.baseVal.value; var pathData = [ { type: "M", values: [cx + r, cy] }, { type: "A", values: [r, r, 0, 0, 1, cx, cy+r] }, { type: "A", values: [r, r, 0, 0, 1, cx-r, cy] }, { type: "A", values: [r, r, 0, 0, 1, cx, cy-r] }, { type: "A", values: [r, r, 0, 0, 1, cx+r, cy] }, { type: "Z", values: [] } ]; if (options && options.normalize === true) { pathData = reducePathData(pathData); } return pathData; }; SVGEllipseElement.prototype.getPathData = function(options) { var cx = this.cx.baseVal.value; var cy = this.cy.baseVal.value; var rx = this.rx.baseVal.value; var ry = this.ry.baseVal.value; var pathData = [ { type: "M", values: [cx + rx, cy] }, { type: "A", values: [rx, ry, 0, 0, 1, cx, cy+ry] }, { type: "A", values: [rx, ry, 0, 0, 1, cx-rx, cy] }, { type: "A", values: [rx, ry, 0, 0, 1, cx, cy-ry] }, { type: "A", values: [rx, ry, 0, 0, 1, cx+rx, cy] }, { type: "Z", values: [] } ]; if (options && options.normalize === true) { pathData = reducePathData(pathData); } return pathData; }; SVGLineElement.prototype.getPathData = function() { return [ { type: "M", values: [this.x1.baseVal.value, this.y1.baseVal.value] }, { type: "L", values: [this.x2.baseVal.value, this.y2.baseVal.value] } ]; }; SVGPolylineElement.prototype.getPathData = function() { var pathData = []; for (var i = 0; i < this.points.numberOfItems; i += 1) { var point = this.points.getItem(i); pathData.push({ type: (i === 0 ? "M" : "L"), values: [point.x, point.y] }); } return pathData; }; SVGPolygonElement.prototype.getPathData = function() { var pathData = []; for (var i = 0; i < this.points.numberOfItems; i += 1) { var point = this.points.getItem(i); pathData.push({ type: (i === 0 ? "M" : "L"), values: [point.x, point.y] }); } pathData.push({ type: "Z", values: [] }); return pathData; }; })(); }