From 7e3f9e037da2aab40d0cc635312da60614126f31 Mon Sep 17 00:00:00 2001 From: Philip Rogers Date: Wed, 4 Nov 2015 19:25:30 -0800 Subject: [PATCH 1/2] Add pathseg.js, a polyfill for the SVGPathSeg API The SVGPathSeg API is being removed from the spec [1] and is being removed in Chromium 47 [2]. I implemented a drop-in polyfill[3] so svg-edit users are not broken as browsers migrate away from the path seg api. This patch simply imports the upstream pathseg.js and updates all dependencies. With this change all tests pass in Chrome 46 (with the path seg api), Chrome 47 (without the path seg api), and there are no changes to tests in Safari 9.01 or Firefox 43. I also manually tested svg-edit while developing the polyfill and could not find any broken features. [1] https://lists.w3.org/Archives/Public/www-svg/2015Jun/0044.html [2] https://groups.google.com/a/chromium.org/d/msg/blink-dev/EDC3cBg9mCU/OvElJgOWCgAJ [3] https://github.com/progers/pathseg --- Makefile | 1 + editor/coords.js | 9 +- editor/pathseg.js | 820 +++++++++++++++++++++++++++++++++++++ editor/recalculate.js | 15 +- editor/sanitize.js | 5 +- editor/select.js | 7 +- editor/svg-editor.html | 1 + editor/svgcanvas.js | 25 +- editor/svgutils.js | 7 +- test/coords_test.html | 2 + test/recalculate_test.html | 1 + test/sanitize_test.html | 1 + test/select_test.html | 1 + test/svgutils_test.html | 3 +- test/test1.html | 1 + 15 files changed, 867 insertions(+), 32 deletions(-) create mode 100644 editor/pathseg.js diff --git a/Makefile b/Makefile index 9773329b..46a6b016 100644 --- a/Makefile +++ b/Makefile @@ -11,6 +11,7 @@ JS_FILES=\ svgedit.js \ jquery-svg.js \ contextmenu/jquery.contextmenu.js \ + pathseg.js \ browser.js \ svgtransformlist.js \ math.js \ diff --git a/editor/coords.js b/editor/coords.js index 7105aeef..7495ee36 100644 --- a/editor/coords.js +++ b/editor/coords.js @@ -10,10 +10,11 @@ // Dependencies: // 1) jquery.js // 2) math.js -// 3) browser.js -// 4) svgutils.js -// 5) units.js -// 6) svgtransformlist.js +// 3) pathseg.js +// 4) browser.js +// 5) svgutils.js +// 6) units.js +// 7) svgtransformlist.js var svgedit = svgedit || {}; diff --git a/editor/pathseg.js b/editor/pathseg.js new file mode 100644 index 00000000..3d5f71a5 --- /dev/null +++ b/editor/pathseg.js @@ -0,0 +1,820 @@ +// SVGPathSeg API polyfill +// +// This is a drop-in replacement for the SVGPathSeg and SVGPathSegList APIs that were removed from +// SVG2 (https://lists.w3.org/Archives/Public/www-svg/2015Jun/0044.html), including the latest spec +// changes which were implemented in Firefox 43 and Chrome 46. +// +// Example API usage: +// var path = document.createElementNS("http://www.w3.org/2000/svg", "path"); +// var moveToSeg = path.createSVGPathSegMovetoRel(10, 10); +// var lineToSeg = path.createSVGPathSegLinetoRel(100, 100); +// path.pathSegList.appendItem(moveToSeg); +// path.pathSegList.appendItem(lineToSeg); +// console.log(path.getAttribute('d')); // m 10 10 l 100 100 +// moveToSeg.x += 200; +// moveToSeg.y += 200; +// console.log(path.getAttribute('d')); // m 210 210 l 100 100 + +(function() { "use strict"; + if (!window.SVGPathSeg) { + // Spec: http://www.w3.org/TR/SVG11/single-page.html#paths-InterfaceSVGPathSeg + window.SVGPathSeg = function(type, typeAsLetter, owningPathSegList) { + this.pathSegType = type; + this.pathSegTypeAsLetter = typeAsLetter; + this._owningPathSegList = owningPathSegList; + } + + SVGPathSeg.PATHSEG_UNKNOWN = 0; + SVGPathSeg.PATHSEG_CLOSEPATH = 1; + SVGPathSeg.PATHSEG_MOVETO_ABS = 2; + SVGPathSeg.PATHSEG_MOVETO_REL = 3; + SVGPathSeg.PATHSEG_LINETO_ABS = 4; + SVGPathSeg.PATHSEG_LINETO_REL = 5; + SVGPathSeg.PATHSEG_CURVETO_CUBIC_ABS = 6; + SVGPathSeg.PATHSEG_CURVETO_CUBIC_REL = 7; + SVGPathSeg.PATHSEG_CURVETO_QUADRATIC_ABS = 8; + SVGPathSeg.PATHSEG_CURVETO_QUADRATIC_REL = 9; + SVGPathSeg.PATHSEG_ARC_ABS = 10; + SVGPathSeg.PATHSEG_ARC_REL = 11; + SVGPathSeg.PATHSEG_LINETO_HORIZONTAL_ABS = 12; + SVGPathSeg.PATHSEG_LINETO_HORIZONTAL_REL = 13; + SVGPathSeg.PATHSEG_LINETO_VERTICAL_ABS = 14; + SVGPathSeg.PATHSEG_LINETO_VERTICAL_REL = 15; + SVGPathSeg.PATHSEG_CURVETO_CUBIC_SMOOTH_ABS = 16; + SVGPathSeg.PATHSEG_CURVETO_CUBIC_SMOOTH_REL = 17; + SVGPathSeg.PATHSEG_CURVETO_QUADRATIC_SMOOTH_ABS = 18; + SVGPathSeg.PATHSEG_CURVETO_QUADRATIC_SMOOTH_REL = 19; + + // Notify owning PathSegList on any changes so they can be synchronized back to the path element. + SVGPathSeg.prototype._segmentChanged = function() { + if (this._owningPathSegList) + this._owningPathSegList.segmentChanged(this); + } + + window.SVGPathSegClosePath = function(owningPathSegList) { + SVGPathSeg.call(this, SVGPathSeg.PATHSEG_CLOSEPATH, "z", owningPathSegList); + } + SVGPathSegClosePath.prototype = Object.create(SVGPathSeg.prototype); + SVGPathSegClosePath.prototype.toString = function() { return "[object SVGPathSegClosePath]"; } + SVGPathSegClosePath.prototype._asPathString = function() { return this.pathSegTypeAsLetter; } + SVGPathSegClosePath.prototype.clone = function() { return new SVGPathSegClosePath(undefined); } + + window.SVGPathSegMovetoAbs = function(owningPathSegList, x, y) { + SVGPathSeg.call(this, SVGPathSeg.PATHSEG_MOVETO_ABS, "M", owningPathSegList); + this._x = x; + this._y = y; + } + SVGPathSegMovetoAbs.prototype = Object.create(SVGPathSeg.prototype); + SVGPathSegMovetoAbs.prototype.toString = function() { return "[object SVGPathSegMovetoAbs]"; } + SVGPathSegMovetoAbs.prototype._asPathString = function() { return this.pathSegTypeAsLetter + " " + this._x + " " + this._y; } + SVGPathSegMovetoAbs.prototype.clone = function() { return new SVGPathSegMovetoAbs(undefined, this._x, this._y); } + Object.defineProperty(SVGPathSegMovetoAbs.prototype, "x", { get: function() { return this._x; }, set: function(x) { this._x = x; this._segmentChanged(); }, enumerable: true }); + Object.defineProperty(SVGPathSegMovetoAbs.prototype, "y", { get: function() { return this._y; }, set: function(y) { this._y = y; this._segmentChanged(); }, enumerable: true }); + + window.SVGPathSegMovetoRel = function(owningPathSegList, x, y) { + SVGPathSeg.call(this, SVGPathSeg.PATHSEG_MOVETO_REL, "m", owningPathSegList); + this._x = x; + this._y = y; + } + SVGPathSegMovetoRel.prototype = Object.create(SVGPathSeg.prototype); + SVGPathSegMovetoRel.prototype.toString = function() { return "[object SVGPathSegMovetoRel]"; } + SVGPathSegMovetoRel.prototype._asPathString = function() { return this.pathSegTypeAsLetter + " " + this._x + " " + this._y; } + SVGPathSegMovetoRel.prototype.clone = function() { return new SVGPathSegMovetoRel(undefined, this._x, this._y); } + Object.defineProperty(SVGPathSegMovetoRel.prototype, "x", { get: function() { return this._x; }, set: function(x) { this._x = x; this._segmentChanged(); }, enumerable: true }); + Object.defineProperty(SVGPathSegMovetoRel.prototype, "y", { get: function() { return this._y; }, set: function(y) { this._y = y; this._segmentChanged(); }, enumerable: true }); + + window.SVGPathSegLinetoAbs = function(owningPathSegList, x, y) { + SVGPathSeg.call(this, SVGPathSeg.PATHSEG_LINETO_ABS, "L", owningPathSegList); + this._x = x; + this._y = y; + } + SVGPathSegLinetoAbs.prototype = Object.create(SVGPathSeg.prototype); + SVGPathSegLinetoAbs.prototype.toString = function() { return "[object SVGPathSegLinetoAbs]"; } + SVGPathSegLinetoAbs.prototype._asPathString = function() { return this.pathSegTypeAsLetter + " " + this._x + " " + this._y; } + SVGPathSegLinetoAbs.prototype.clone = function() { return new SVGPathSegLinetoAbs(undefined, this._x, this._y); } + Object.defineProperty(SVGPathSegLinetoAbs.prototype, "x", { get: function() { return this._x; }, set: function(x) { this._x = x; this._segmentChanged(); }, enumerable: true }); + Object.defineProperty(SVGPathSegLinetoAbs.prototype, "y", { get: function() { return this._y; }, set: function(y) { this._y = y; this._segmentChanged(); }, enumerable: true }); + + window.SVGPathSegLinetoRel = function(owningPathSegList, x, y) { + SVGPathSeg.call(this, SVGPathSeg.PATHSEG_LINETO_REL, "l", owningPathSegList); + this._x = x; + this._y = y; + } + SVGPathSegLinetoRel.prototype = Object.create(SVGPathSeg.prototype); + SVGPathSegLinetoRel.prototype.toString = function() { return "[object SVGPathSegLinetoRel]"; } + SVGPathSegLinetoRel.prototype._asPathString = function() { return this.pathSegTypeAsLetter + " " + this._x + " " + this._y; } + SVGPathSegLinetoRel.prototype.clone = function() { return new SVGPathSegLinetoRel(undefined, this._x, this._y); } + Object.defineProperty(SVGPathSegLinetoRel.prototype, "x", { get: function() { return this._x; }, set: function(x) { this._x = x; this._segmentChanged(); }, enumerable: true }); + Object.defineProperty(SVGPathSegLinetoRel.prototype, "y", { get: function() { return this._y; }, set: function(y) { this._y = y; this._segmentChanged(); }, enumerable: true }); + + window.SVGPathSegCurvetoCubicAbs = function(owningPathSegList, x, y, x1, y1, x2, y2) { + SVGPathSeg.call(this, SVGPathSeg.PATHSEG_CURVETO_CUBIC_ABS, "C", owningPathSegList); + this._x = x; + this._y = y; + this._x1 = x1; + this._y1 = y1; + this._x2 = x2; + this._y2 = y2; + } + SVGPathSegCurvetoCubicAbs.prototype = Object.create(SVGPathSeg.prototype); + SVGPathSegCurvetoCubicAbs.prototype.toString = function() { return "[object SVGPathSegCurvetoCubicAbs]"; } + SVGPathSegCurvetoCubicAbs.prototype._asPathString = function() { return this.pathSegTypeAsLetter + " " + this._x1 + " " + this._y1 + " " + this._x2 + " " + this._y2 + " " + this._x + " " + this._y; } + SVGPathSegCurvetoCubicAbs.prototype.clone = function() { return new SVGPathSegCurvetoCubicAbs(undefined, this._x, this._y, this._x1, this._y1, this._x2, this._y2); } + Object.defineProperty(SVGPathSegCurvetoCubicAbs.prototype, "x", { get: function() { return this._x; }, set: function(x) { this._x = x; this._segmentChanged(); }, enumerable: true }); + Object.defineProperty(SVGPathSegCurvetoCubicAbs.prototype, "y", { get: function() { return this._y; }, set: function(y) { this._y = y; this._segmentChanged(); }, enumerable: true }); + Object.defineProperty(SVGPathSegCurvetoCubicAbs.prototype, "x1", { get: function() { return this._x1; }, set: function(x1) { this._x1 = x1; this._segmentChanged(); }, enumerable: true }); + Object.defineProperty(SVGPathSegCurvetoCubicAbs.prototype, "y1", { get: function() { return this._y1; }, set: function(y1) { this._y1 = y1; this._segmentChanged(); }, enumerable: true }); + Object.defineProperty(SVGPathSegCurvetoCubicAbs.prototype, "x2", { get: function() { return this._x2; }, set: function(x2) { this._x2 = x2; this._segmentChanged(); }, enumerable: true }); + Object.defineProperty(SVGPathSegCurvetoCubicAbs.prototype, "y2", { get: function() { return this._y2; }, set: function(y2) { this._y2 = y2; this._segmentChanged(); }, enumerable: true }); + + window.SVGPathSegCurvetoCubicRel = function(owningPathSegList, x, y, x1, y1, x2, y2) { + SVGPathSeg.call(this, SVGPathSeg.PATHSEG_CURVETO_CUBIC_REL, "c", owningPathSegList); + this._x = x; + this._y = y; + this._x1 = x1; + this._y1 = y1; + this._x2 = x2; + this._y2 = y2; + } + SVGPathSegCurvetoCubicRel.prototype = Object.create(SVGPathSeg.prototype); + SVGPathSegCurvetoCubicRel.prototype.toString = function() { return "[object SVGPathSegCurvetoCubicRel]"; } + SVGPathSegCurvetoCubicRel.prototype._asPathString = function() { return this.pathSegTypeAsLetter + " " + this._x1 + " " + this._y1 + " " + this._x2 + " " + this._y2 + " " + this._x + " " + this._y; } + SVGPathSegCurvetoCubicRel.prototype.clone = function() { return new SVGPathSegCurvetoCubicRel(undefined, this._x, this._y, this._x1, this._y1, this._x2, this._y2); } + Object.defineProperty(SVGPathSegCurvetoCubicRel.prototype, "x", { get: function() { return this._x; }, set: function(x) { this._x = x; this._segmentChanged(); }, enumerable: true }); + Object.defineProperty(SVGPathSegCurvetoCubicRel.prototype, "y", { get: function() { return this._y; }, set: function(y) { this._y = y; this._segmentChanged(); }, enumerable: true }); + Object.defineProperty(SVGPathSegCurvetoCubicRel.prototype, "x1", { get: function() { return this._x1; }, set: function(x1) { this._x1 = x1; this._segmentChanged(); }, enumerable: true }); + Object.defineProperty(SVGPathSegCurvetoCubicRel.prototype, "y1", { get: function() { return this._y1; }, set: function(y1) { this._y1 = y1; this._segmentChanged(); }, enumerable: true }); + Object.defineProperty(SVGPathSegCurvetoCubicRel.prototype, "x2", { get: function() { return this._x2; }, set: function(x2) { this._x2 = x2; this._segmentChanged(); }, enumerable: true }); + Object.defineProperty(SVGPathSegCurvetoCubicRel.prototype, "y2", { get: function() { return this._y2; }, set: function(y2) { this._y2 = y2; this._segmentChanged(); }, enumerable: true }); + + window.SVGPathSegCurvetoQuadraticAbs = function(owningPathSegList, x, y, x1, y1) { + SVGPathSeg.call(this, SVGPathSeg.PATHSEG_CURVETO_QUADRATIC_ABS, "Q", owningPathSegList); + this._x = x; + this._y = y; + this._x1 = x1; + this._y1 = y1; + } + SVGPathSegCurvetoQuadraticAbs.prototype = Object.create(SVGPathSeg.prototype); + SVGPathSegCurvetoQuadraticAbs.prototype.toString = function() { return "[object SVGPathSegCurvetoQuadraticAbs]"; } + SVGPathSegCurvetoQuadraticAbs.prototype._asPathString = function() { return this.pathSegTypeAsLetter + " " + this._x1 + " " + this._y1 + " " + this._x + " " + this._y; } + SVGPathSegCurvetoQuadraticAbs.prototype.clone = function() { return new SVGPathSegCurvetoQuadraticAbs(undefined, this._x, this._y, this._x1, this._y1); } + Object.defineProperty(SVGPathSegCurvetoQuadraticAbs.prototype, "x", { get: function() { return this._x; }, set: function(x) { this._x = x; this._segmentChanged(); }, enumerable: true }); + Object.defineProperty(SVGPathSegCurvetoQuadraticAbs.prototype, "y", { get: function() { return this._y; }, set: function(y) { this._y = y; this._segmentChanged(); }, enumerable: true }); + Object.defineProperty(SVGPathSegCurvetoQuadraticAbs.prototype, "x1", { get: function() { return this._x1; }, set: function(x1) { this._x1 = x1; this._segmentChanged(); }, enumerable: true }); + Object.defineProperty(SVGPathSegCurvetoQuadraticAbs.prototype, "y1", { get: function() { return this._y1; }, set: function(y1) { this._y1 = y1; this._segmentChanged(); }, enumerable: true }); + + window.SVGPathSegCurvetoQuadraticRel = function(owningPathSegList, x, y, x1, y1) { + SVGPathSeg.call(this, SVGPathSeg.PATHSEG_CURVETO_QUADRATIC_REL, "q", owningPathSegList); + this._x = x; + this._y = y; + this._x1 = x1; + this._y1 = y1; + } + SVGPathSegCurvetoQuadraticRel.prototype = Object.create(SVGPathSeg.prototype); + SVGPathSegCurvetoQuadraticRel.prototype.toString = function() { return "[object SVGPathSegCurvetoQuadraticRel]"; } + SVGPathSegCurvetoQuadraticRel.prototype._asPathString = function() { return this.pathSegTypeAsLetter + " " + this._x1 + " " + this._y1 + " " + this._x + " " + this._y; } + SVGPathSegCurvetoQuadraticRel.prototype.clone = function() { return new SVGPathSegCurvetoQuadraticRel(undefined, this._x, this._y, this._x1, this._y1); } + Object.defineProperty(SVGPathSegCurvetoQuadraticRel.prototype, "x", { get: function() { return this._x; }, set: function(x) { this._x = x; this._segmentChanged(); }, enumerable: true }); + Object.defineProperty(SVGPathSegCurvetoQuadraticRel.prototype, "y", { get: function() { return this._y; }, set: function(y) { this._y = y; this._segmentChanged(); }, enumerable: true }); + Object.defineProperty(SVGPathSegCurvetoQuadraticRel.prototype, "x1", { get: function() { return this._x1; }, set: function(x1) { this._x1 = x1; this._segmentChanged(); }, enumerable: true }); + Object.defineProperty(SVGPathSegCurvetoQuadraticRel.prototype, "y1", { get: function() { return this._y1; }, set: function(y1) { this._y1 = y1; this._segmentChanged(); }, enumerable: true }); + + window.SVGPathSegArcAbs = function(owningPathSegList, x, y, r1, r2, angle, largeArcFlag, sweepFlag) { + SVGPathSeg.call(this, SVGPathSeg.PATHSEG_ARC_ABS, "A", owningPathSegList); + this._x = x; + this._y = y; + this._r1 = r1; + this._r2 = r2; + this._angle = angle; + this._largeArcFlag = largeArcFlag; + this._sweepFlag = sweepFlag; + } + SVGPathSegArcAbs.prototype = Object.create(SVGPathSeg.prototype); + SVGPathSegArcAbs.prototype.toString = function() { return "[object SVGPathSegArcAbs]"; } + SVGPathSegArcAbs.prototype._asPathString = function() { return this.pathSegTypeAsLetter + " " + this._r1 + " " + this._r2 + " " + this._angle + " " + (this._largeArcFlag ? "1" : "0") + " " + (this._sweepFlag ? "1" : "0") + " " + this._x + " " + this._y; } + SVGPathSegArcAbs.prototype.clone = function() { return new SVGPathSegArcAbs(undefined, this._x, this._y, this._r1, this._r2, this._angle, this._largeArcFlag, this._sweepFlag); } + Object.defineProperty(SVGPathSegArcAbs.prototype, "x", { get: function() { return this._x; }, set: function(x) { this._x = x; this._segmentChanged(); }, enumerable: true }); + Object.defineProperty(SVGPathSegArcAbs.prototype, "y", { get: function() { return this._y; }, set: function(y) { this._y = y; this._segmentChanged(); }, enumerable: true }); + Object.defineProperty(SVGPathSegArcAbs.prototype, "r1", { get: function() { return this._r1; }, set: function(r1) { this._r1 = r1; this._segmentChanged(); }, enumerable: true }); + Object.defineProperty(SVGPathSegArcAbs.prototype, "r2", { get: function() { return this._r2; }, set: function(r2) { this._r2 = r2; this._segmentChanged(); }, enumerable: true }); + Object.defineProperty(SVGPathSegArcAbs.prototype, "angle", { get: function() { return this._angle; }, set: function(angle) { this._angle = angle; this._segmentChanged(); }, enumerable: true }); + Object.defineProperty(SVGPathSegArcAbs.prototype, "largeArcFlag", { get: function() { return this._largeArcFlag; }, set: function(largeArcFlag) { this._largeArcFlag = largeArcFlag; this._segmentChanged(); }, enumerable: true }); + Object.defineProperty(SVGPathSegArcAbs.prototype, "sweepFlag", { get: function() { return this._sweepFlag; }, set: function(sweepFlag) { this._sweepFlag = sweepFlag; this._segmentChanged(); }, enumerable: true }); + + window.SVGPathSegArcRel = function(owningPathSegList, x, y, r1, r2, angle, largeArcFlag, sweepFlag) { + SVGPathSeg.call(this, SVGPathSeg.PATHSEG_ARC_REL, "a", owningPathSegList); + this._x = x; + this._y = y; + this._r1 = r1; + this._r2 = r2; + this._angle = angle; + this._largeArcFlag = largeArcFlag; + this._sweepFlag = sweepFlag; + } + SVGPathSegArcRel.prototype = Object.create(SVGPathSeg.prototype); + SVGPathSegArcRel.prototype.toString = function() { return "[object SVGPathSegArcRel]"; } + SVGPathSegArcRel.prototype._asPathString = function() { return this.pathSegTypeAsLetter + " " + this._r1 + " " + this._r2 + " " + this._angle + " " + (this._largeArcFlag ? "1" : "0") + " " + (this._sweepFlag ? "1" : "0") + " " + this._x + " " + this._y; } + SVGPathSegArcRel.prototype.clone = function() { return new SVGPathSegArcRel(undefined, this._x, this._y, this._r1, this._r2, this._angle, this._largeArcFlag, this._sweepFlag); } + Object.defineProperty(SVGPathSegArcRel.prototype, "x", { get: function() { return this._x; }, set: function(x) { this._x = x; this._segmentChanged(); }, enumerable: true }); + Object.defineProperty(SVGPathSegArcRel.prototype, "y", { get: function() { return this._y; }, set: function(y) { this._y = y; this._segmentChanged(); }, enumerable: true }); + Object.defineProperty(SVGPathSegArcRel.prototype, "r1", { get: function() { return this._r1; }, set: function(r1) { this._r1 = r1; this._segmentChanged(); }, enumerable: true }); + Object.defineProperty(SVGPathSegArcRel.prototype, "r2", { get: function() { return this._r2; }, set: function(r2) { this._r2 = r2; this._segmentChanged(); }, enumerable: true }); + Object.defineProperty(SVGPathSegArcRel.prototype, "angle", { get: function() { return this._angle; }, set: function(angle) { this._angle = angle; this._segmentChanged(); }, enumerable: true }); + Object.defineProperty(SVGPathSegArcRel.prototype, "largeArcFlag", { get: function() { return this._largeArcFlag; }, set: function(largeArcFlag) { this._largeArcFlag = largeArcFlag; this._segmentChanged(); }, enumerable: true }); + Object.defineProperty(SVGPathSegArcRel.prototype, "sweepFlag", { get: function() { return this._sweepFlag; }, set: function(sweepFlag) { this._sweepFlag = sweepFlag; this._segmentChanged(); }, enumerable: true }); + + window.SVGPathSegLinetoHorizontalAbs = function(owningPathSegList, x) { + SVGPathSeg.call(this, SVGPathSeg.PATHSEG_LINETO_HORIZONTAL_ABS, "H", owningPathSegList); + this._x = x; + } + SVGPathSegLinetoHorizontalAbs.prototype = Object.create(SVGPathSeg.prototype); + SVGPathSegLinetoHorizontalAbs.prototype.toString = function() { return "[object SVGPathSegLinetoHorizontalAbs]"; } + SVGPathSegLinetoHorizontalAbs.prototype._asPathString = function() { return this.pathSegTypeAsLetter + " " + this._x; } + SVGPathSegLinetoHorizontalAbs.prototype.clone = function() { return new SVGPathSegLinetoHorizontalAbs(undefined, this._x); } + Object.defineProperty(SVGPathSegLinetoHorizontalAbs.prototype, "x", { get: function() { return this._x; }, set: function(x) { this._x = x; this._segmentChanged(); }, enumerable: true }); + + window.SVGPathSegLinetoHorizontalRel = function(owningPathSegList, x) { + SVGPathSeg.call(this, SVGPathSeg.PATHSEG_LINETO_HORIZONTAL_REL, "h", owningPathSegList); + this._x = x; + } + SVGPathSegLinetoHorizontalRel.prototype = Object.create(SVGPathSeg.prototype); + SVGPathSegLinetoHorizontalRel.prototype.toString = function() { return "[object SVGPathSegLinetoHorizontalRel]"; } + SVGPathSegLinetoHorizontalRel.prototype._asPathString = function() { return this.pathSegTypeAsLetter + " " + this._x; } + SVGPathSegLinetoHorizontalRel.prototype.clone = function() { return new SVGPathSegLinetoHorizontalRel(undefined, this._x); } + Object.defineProperty(SVGPathSegLinetoHorizontalRel.prototype, "x", { get: function() { return this._x; }, set: function(x) { this._x = x; this._segmentChanged(); }, enumerable: true }); + + window.SVGPathSegLinetoVerticalAbs = function(owningPathSegList, y) { + SVGPathSeg.call(this, SVGPathSeg.PATHSEG_LINETO_VERTICAL_ABS, "V", owningPathSegList); + this._y = y; + } + SVGPathSegLinetoVerticalAbs.prototype = Object.create(SVGPathSeg.prototype); + SVGPathSegLinetoVerticalAbs.prototype.toString = function() { return "[object SVGPathSegLinetoVerticalAbs]"; } + SVGPathSegLinetoVerticalAbs.prototype._asPathString = function() { return this.pathSegTypeAsLetter + " " + this._y; } + SVGPathSegLinetoVerticalAbs.prototype.clone = function() { return new SVGPathSegLinetoVerticalAbs(undefined, this._y); } + Object.defineProperty(SVGPathSegLinetoVerticalAbs.prototype, "y", { get: function() { return this._y; }, set: function(y) { this._y = y; this._segmentChanged(); }, enumerable: true }); + + window.SVGPathSegLinetoVerticalRel = function(owningPathSegList, y) { + SVGPathSeg.call(this, SVGPathSeg.PATHSEG_LINETO_VERTICAL_REL, "v", owningPathSegList); + this._y = y; + } + SVGPathSegLinetoVerticalRel.prototype = Object.create(SVGPathSeg.prototype); + SVGPathSegLinetoVerticalRel.prototype.toString = function() { return "[object SVGPathSegLinetoVerticalRel]"; } + SVGPathSegLinetoVerticalRel.prototype._asPathString = function() { return this.pathSegTypeAsLetter + " " + this._y; } + SVGPathSegLinetoVerticalRel.prototype.clone = function() { return new SVGPathSegLinetoVerticalRel(undefined, this._y); } + Object.defineProperty(SVGPathSegLinetoVerticalRel.prototype, "y", { get: function() { return this._y; }, set: function(y) { this._y = y; this._segmentChanged(); }, enumerable: true }); + + window.SVGPathSegCurvetoCubicSmoothAbs = function(owningPathSegList, x, y, x2, y2) { + SVGPathSeg.call(this, SVGPathSeg.PATHSEG_CURVETO_CUBIC_SMOOTH_ABS, "S", owningPathSegList); + this._x = x; + this._y = y; + this._x2 = x2; + this._y2 = y2; + } + SVGPathSegCurvetoCubicSmoothAbs.prototype = Object.create(SVGPathSeg.prototype); + SVGPathSegCurvetoCubicSmoothAbs.prototype.toString = function() { return "[object SVGPathSegCurvetoCubicSmoothAbs]"; } + SVGPathSegCurvetoCubicSmoothAbs.prototype._asPathString = function() { return this.pathSegTypeAsLetter + " " + this._x2 + " " + this._y2 + " " + this._x + " " + this._y; } + SVGPathSegCurvetoCubicSmoothAbs.prototype.clone = function() { return new SVGPathSegCurvetoCubicSmoothAbs(undefined, this._x, this._y, this._x2, this._y2); } + Object.defineProperty(SVGPathSegCurvetoCubicSmoothAbs.prototype, "x", { get: function() { return this._x; }, set: function(x) { this._x = x; this._segmentChanged(); }, enumerable: true }); + Object.defineProperty(SVGPathSegCurvetoCubicSmoothAbs.prototype, "y", { get: function() { return this._y; }, set: function(y) { this._y = y; this._segmentChanged(); }, enumerable: true }); + Object.defineProperty(SVGPathSegCurvetoCubicSmoothAbs.prototype, "x2", { get: function() { return this._x2; }, set: function(x2) { this._x2 = x2; this._segmentChanged(); }, enumerable: true }); + Object.defineProperty(SVGPathSegCurvetoCubicSmoothAbs.prototype, "y2", { get: function() { return this._y2; }, set: function(y2) { this._y2 = y2; this._segmentChanged(); }, enumerable: true }); + + window.SVGPathSegCurvetoCubicSmoothRel = function(owningPathSegList, x, y, x2, y2) { + SVGPathSeg.call(this, SVGPathSeg.PATHSEG_CURVETO_CUBIC_SMOOTH_REL, "s", owningPathSegList); + this._x = x; + this._y = y; + this._x2 = x2; + this._y2 = y2; + } + SVGPathSegCurvetoCubicSmoothRel.prototype = Object.create(SVGPathSeg.prototype); + SVGPathSegCurvetoCubicSmoothRel.prototype.toString = function() { return "[object SVGPathSegCurvetoCubicSmoothRel]"; } + SVGPathSegCurvetoCubicSmoothRel.prototype._asPathString = function() { return this.pathSegTypeAsLetter + " " + this._x2 + " " + this._y2 + " " + this._x + " " + this._y; } + SVGPathSegCurvetoCubicSmoothRel.prototype.clone = function() { return new SVGPathSegCurvetoCubicSmoothRel(undefined, this._x, this._y, this._x2, this._y2); } + Object.defineProperty(SVGPathSegCurvetoCubicSmoothRel.prototype, "x", { get: function() { return this._x; }, set: function(x) { this._x = x; this._segmentChanged(); }, enumerable: true }); + Object.defineProperty(SVGPathSegCurvetoCubicSmoothRel.prototype, "y", { get: function() { return this._y; }, set: function(y) { this._y = y; this._segmentChanged(); }, enumerable: true }); + Object.defineProperty(SVGPathSegCurvetoCubicSmoothRel.prototype, "x2", { get: function() { return this._x2; }, set: function(x2) { this._x2 = x2; this._segmentChanged(); }, enumerable: true }); + Object.defineProperty(SVGPathSegCurvetoCubicSmoothRel.prototype, "y2", { get: function() { return this._y2; }, set: function(y2) { this._y2 = y2; this._segmentChanged(); }, enumerable: true }); + + window.SVGPathSegCurvetoQuadraticSmoothAbs = function(owningPathSegList, x, y) { + SVGPathSeg.call(this, SVGPathSeg.PATHSEG_CURVETO_QUADRATIC_SMOOTH_ABS, "T", owningPathSegList); + this._x = x; + this._y = y; + } + SVGPathSegCurvetoQuadraticSmoothAbs.prototype = Object.create(SVGPathSeg.prototype); + SVGPathSegCurvetoQuadraticSmoothAbs.prototype.toString = function() { return "[object SVGPathSegCurvetoQuadraticSmoothAbs]"; } + SVGPathSegCurvetoQuadraticSmoothAbs.prototype._asPathString = function() { return this.pathSegTypeAsLetter + " " + this._x + " " + this._y; } + SVGPathSegCurvetoQuadraticSmoothAbs.prototype.clone = function() { return new SVGPathSegCurvetoQuadraticSmoothAbs(undefined, this._x, this._y); } + Object.defineProperty(SVGPathSegCurvetoQuadraticSmoothAbs.prototype, "x", { get: function() { return this._x; }, set: function(x) { this._x = x; this._segmentChanged(); }, enumerable: true }); + Object.defineProperty(SVGPathSegCurvetoQuadraticSmoothAbs.prototype, "y", { get: function() { return this._y; }, set: function(y) { this._y = y; this._segmentChanged(); }, enumerable: true }); + + window.SVGPathSegCurvetoQuadraticSmoothRel = function(owningPathSegList, x, y) { + SVGPathSeg.call(this, SVGPathSeg.PATHSEG_CURVETO_QUADRATIC_SMOOTH_REL, "t", owningPathSegList); + this._x = x; + this._y = y; + } + SVGPathSegCurvetoQuadraticSmoothRel.prototype = Object.create(SVGPathSeg.prototype); + SVGPathSegCurvetoQuadraticSmoothRel.prototype.toString = function() { return "[object SVGPathSegCurvetoQuadraticSmoothRel]"; } + SVGPathSegCurvetoQuadraticSmoothRel.prototype._asPathString = function() { return this.pathSegTypeAsLetter + " " + this._x + " " + this._y; } + SVGPathSegCurvetoQuadraticSmoothRel.prototype.clone = function() { return new SVGPathSegCurvetoQuadraticSmoothRel(undefined, this._x, this._y); } + Object.defineProperty(SVGPathSegCurvetoQuadraticSmoothRel.prototype, "x", { get: function() { return this._x; }, set: function(x) { this._x = x; this._segmentChanged(); }, enumerable: true }); + Object.defineProperty(SVGPathSegCurvetoQuadraticSmoothRel.prototype, "y", { get: function() { return this._y; }, set: function(y) { this._y = y; this._segmentChanged(); }, enumerable: true }); + + // Add createSVGPathSeg* functions to SVGPathElement. + // Spec: http://www.w3.org/TR/SVG11/single-page.html#paths-InterfaceSVGPathElement. + SVGPathElement.prototype.createSVGPathSegClosePath = function() { return new SVGPathSegClosePath(undefined); } + SVGPathElement.prototype.createSVGPathSegMovetoAbs = function(x, y) { return new SVGPathSegMovetoAbs(undefined, x, y); } + SVGPathElement.prototype.createSVGPathSegMovetoRel = function(x, y) { return new SVGPathSegMovetoRel(undefined, x, y); } + SVGPathElement.prototype.createSVGPathSegLinetoAbs = function(x, y) { return new SVGPathSegLinetoAbs(undefined, x, y); } + SVGPathElement.prototype.createSVGPathSegLinetoRel = function(x, y) { return new SVGPathSegLinetoRel(undefined, x, y); } + SVGPathElement.prototype.createSVGPathSegCurvetoCubicAbs = function(x, y, x1, y1, x2, y2) { return new SVGPathSegCurvetoCubicAbs(undefined, x, y, x1, y1, x2, y2); } + SVGPathElement.prototype.createSVGPathSegCurvetoCubicRel = function(x, y, x1, y1, x2, y2) { return new SVGPathSegCurvetoCubicRel(undefined, x, y, x1, y1, x2, y2); } + SVGPathElement.prototype.createSVGPathSegCurvetoQuadraticAbs = function(x, y, x1, y1) { return new SVGPathSegCurvetoQuadraticAbs(undefined, x, y, x1, y1); } + SVGPathElement.prototype.createSVGPathSegCurvetoQuadraticRel = function(x, y, x1, y1) { return new SVGPathSegCurvetoQuadraticRel(undefined, x, y, x1, y1); } + SVGPathElement.prototype.createSVGPathSegArcAbs = function(x, y, r1, r2, angle, largeArcFlag, sweepFlag) { return new SVGPathSegArcAbs(undefined, x, y, r1, r2, angle, largeArcFlag, sweepFlag); } + SVGPathElement.prototype.createSVGPathSegArcRel = function(x, y, r1, r2, angle, largeArcFlag, sweepFlag) { return new SVGPathSegArcRel(undefined, x, y, r1, r2, angle, largeArcFlag, sweepFlag); } + SVGPathElement.prototype.createSVGPathSegLinetoHorizontalAbs = function(x) { return new SVGPathSegLinetoHorizontalAbs(undefined, x); } + SVGPathElement.prototype.createSVGPathSegLinetoHorizontalRel = function(x) { return new SVGPathSegLinetoHorizontalRel(undefined, x); } + SVGPathElement.prototype.createSVGPathSegLinetoVerticalAbs = function(y) { return new SVGPathSegLinetoVerticalAbs(undefined, y); } + SVGPathElement.prototype.createSVGPathSegLinetoVerticalRel = function(y) { return new SVGPathSegLinetoVerticalRel(undefined, y); } + SVGPathElement.prototype.createSVGPathSegCurvetoCubicSmoothAbs = function(x, y, x2, y2) { return new SVGPathSegCurvetoCubicSmoothAbs(undefined, x, y, x2, y2); } + SVGPathElement.prototype.createSVGPathSegCurvetoCubicSmoothRel = function(x, y, x2, y2) { return new SVGPathSegCurvetoCubicSmoothRel(undefined, x, y, x2, y2); } + SVGPathElement.prototype.createSVGPathSegCurvetoQuadraticSmoothAbs = function(x, y) { return new SVGPathSegCurvetoQuadraticSmoothAbs(undefined, x, y); } + SVGPathElement.prototype.createSVGPathSegCurvetoQuadraticSmoothRel = function(x, y) { return new SVGPathSegCurvetoQuadraticSmoothRel(undefined, x, y); } + } + + if (!window.SVGPathSegList) { + // Spec: http://www.w3.org/TR/SVG11/single-page.html#paths-InterfaceSVGPathSegList + window.SVGPathSegList = function(pathElement) { + this._pathElement = pathElement; + this._list = this._parsePath(this._pathElement.getAttribute("d")); + + // Use a MutationObserver to catch changes to the path's "d" attribute. + this._mutationObserverConfig = { "attributes": true, "attributeFilter": ["d"] }; + this._pathElementMutationObserver = new MutationObserver(this._updateListFromPathMutations); + this._pathElementMutationObserver.observe(this._pathElement, this._mutationObserverConfig); + } + + Object.defineProperty(SVGPathSegList.prototype, "numberOfItems", { + get: function() { + this._checkPathSynchronizedToList(); + return this._list.length; + } + }); + + // Process any pending mutations to the path element and update the list as needed. + // This should be the first call of all public functions and is needed because + // MutationObservers are not synchronous so we can have pending asynchronous mutations. + SVGPathSegList.prototype._checkPathSynchronizedToList = function() { + this._updateListFromPathMutations(this._pathElementMutationObserver.takeRecords()); + } + + SVGPathSegList.prototype._updateListFromPathMutations = function(mutationRecords) { + if (!this._pathElement) + return; + var hasPathMutations = false; + mutationRecords.forEach(function(record) { + if (record.attributeName == "d") + hasPathMutations = true; + }); + if (hasPathMutations) + this._list = this._parsePath(this._pathElement.getAttribute("d")); + } + + // Serialize the list and update the path's 'd' attribute. + SVGPathSegList.prototype._writeListToPath = function() { + this._pathElementMutationObserver.disconnect(); + this._pathElement.setAttribute("d", SVGPathSegList._pathSegArrayAsString(this._list)); + this._pathElementMutationObserver.observe(this._pathElement, this._mutationObserverConfig); + } + + // When a path segment changes the list needs to be synchronized back to the path element. + SVGPathSegList.prototype.segmentChanged = function(pathSeg) { + this._writeListToPath(); + } + + SVGPathSegList.prototype.clear = function() { + this._checkPathSynchronizedToList(); + + this._list.forEach(function(pathSeg) { + pathSeg._owningPathSegList = null; + }); + this._list = []; + this._writeListToPath(); + } + + SVGPathSegList.prototype.initialize = function(newItem) { + this._checkPathSynchronizedToList(); + + this._list = [newItem]; + newItem._owningPathSegList = this; + this._writeListToPath(); + return newItem; + } + + SVGPathSegList.prototype._checkValidIndex = function(index) { + if (isNaN(index) || index < 0 || index >= this.numberOfItems) + throw "INDEX_SIZE_ERR"; + } + + SVGPathSegList.prototype.getItem = function(index) { + this._checkPathSynchronizedToList(); + + this._checkValidIndex(index); + return this._list[index]; + } + + SVGPathSegList.prototype.insertItemBefore = function(newItem, index) { + this._checkPathSynchronizedToList(); + + // Spec: If the index is greater than or equal to numberOfItems, then the new item is appended to the end of the list. + if (index > this.numberOfItems) + index = this.numberOfItems; + if (newItem._owningPathSegList) { + // SVG2 spec says to make a copy. + newItem = newItem.clone(); + } + this._list.splice(index, 0, newItem); + newItem._owningPathSegList = this; + this._writeListToPath(); + return newItem; + } + + SVGPathSegList.prototype.replaceItem = function(newItem, index) { + this._checkPathSynchronizedToList(); + + if (newItem._owningPathSegList) { + // SVG2 spec says to make a copy. + newItem = newItem.clone(); + } + this._checkValidIndex(index); + this._list[index] = newItem; + newItem._owningPathSegList = this; + this._writeListToPath(); + return newItem; + } + + SVGPathSegList.prototype.removeItem = function(index) { + this._checkPathSynchronizedToList(); + + this._checkValidIndex(index); + var item = this._list[index]; + this._list.splice(index, 1); + this._writeListToPath(); + return item; + } + + SVGPathSegList.prototype.appendItem = function(newItem) { + this._checkPathSynchronizedToList(); + + if (newItem._owningPathSegList) { + // SVG2 spec says to make a copy. + newItem = newItem.clone(); + } + this._list.push(newItem); + newItem._owningPathSegList = this; + // TODO: Optimize this to just append to the existing attribute. + this._writeListToPath(); + return newItem; + } + + SVGPathSegList._pathSegArrayAsString = function(pathSegArray) { + var string = ""; + var first = true; + pathSegArray.forEach(function(pathSeg) { + if (first) { + first = false; + string += pathSeg._asPathString(); + } else { + string += " " + pathSeg._asPathString(); + } + }); + return string; + } + + // This closely follows SVGPathParser::parsePath from Source/core/svg/SVGPathParser.cpp. + SVGPathSegList.prototype._parsePath = function(string) { + if (!string || string.length == 0) + return []; + + var owningPathSegList = this; + + var Builder = function() { + this.pathSegList = []; + } + + Builder.prototype.appendSegment = function(pathSeg) { + this.pathSegList.push(pathSeg); + } + + var Source = function(string) { + this._string = string; + this._currentIndex = 0; + this._endIndex = this._string.length; + this._previousCommand = SVGPathSeg.PATHSEG_UNKNOWN; + + this._skipOptionalSpaces(); + } + + Source.prototype._isCurrentSpace = function() { + var character = this._string[this._currentIndex]; + return character <= " " && (character == " " || character == "\n" || character == "\t" || character == "\r" || character == "\f"); + } + + Source.prototype._skipOptionalSpaces = function() { + while (this._currentIndex < this._endIndex && this._isCurrentSpace()) + this._currentIndex++; + return this._currentIndex < this._endIndex; + } + + Source.prototype._skipOptionalSpacesOrDelimiter = function() { + if (this._currentIndex < this._endIndex && !this._isCurrentSpace() && this._string.charAt(this._currentIndex) != ",") + return false; + if (this._skipOptionalSpaces()) { + if (this._currentIndex < this._endIndex && this._string.charAt(this._currentIndex) == ",") { + this._currentIndex++; + this._skipOptionalSpaces(); + } + } + return this._currentIndex < this._endIndex; + } + + Source.prototype.hasMoreData = function() { + return this._currentIndex < this._endIndex; + } + + Source.prototype.peekSegmentType = function() { + var lookahead = this._string[this._currentIndex]; + return this._pathSegTypeFromChar(lookahead); + } + + Source.prototype._pathSegTypeFromChar = function(lookahead) { + switch (lookahead) { + case "Z": + case "z": + return SVGPathSeg.PATHSEG_CLOSEPATH; + case "M": + return SVGPathSeg.PATHSEG_MOVETO_ABS; + case "m": + return SVGPathSeg.PATHSEG_MOVETO_REL; + case "L": + return SVGPathSeg.PATHSEG_LINETO_ABS; + case "l": + return SVGPathSeg.PATHSEG_LINETO_REL; + case "C": + return SVGPathSeg.PATHSEG_CURVETO_CUBIC_ABS; + case "c": + return SVGPathSeg.PATHSEG_CURVETO_CUBIC_REL; + case "Q": + return SVGPathSeg.PATHSEG_CURVETO_QUADRATIC_ABS; + case "q": + return SVGPathSeg.PATHSEG_CURVETO_QUADRATIC_REL; + case "A": + return SVGPathSeg.PATHSEG_ARC_ABS; + case "a": + return SVGPathSeg.PATHSEG_ARC_REL; + case "H": + return SVGPathSeg.PATHSEG_LINETO_HORIZONTAL_ABS; + case "h": + return SVGPathSeg.PATHSEG_LINETO_HORIZONTAL_REL; + case "V": + return SVGPathSeg.PATHSEG_LINETO_VERTICAL_ABS; + case "v": + return SVGPathSeg.PATHSEG_LINETO_VERTICAL_REL; + case "S": + return SVGPathSeg.PATHSEG_CURVETO_CUBIC_SMOOTH_ABS; + case "s": + return SVGPathSeg.PATHSEG_CURVETO_CUBIC_SMOOTH_REL; + case "T": + return SVGPathSeg.PATHSEG_CURVETO_QUADRATIC_SMOOTH_ABS; + case "t": + return SVGPathSeg.PATHSEG_CURVETO_QUADRATIC_SMOOTH_REL; + default: + return SVGPathSeg.PATHSEG_UNKNOWN; + } + } + + Source.prototype._nextCommandHelper = function(lookahead, previousCommand) { + // Check for remaining coordinates in the current command. + if ((lookahead == "+" || lookahead == "-" || lookahead == "." || (lookahead >= "0" && lookahead <= "9")) && previousCommand != SVGPathSeg.PATHSEG_CLOSEPATH) { + if (previousCommand == SVGPathSeg.PATHSEG_MOVETO_ABS) + return SVGPathSeg.PATHSEG_LINETO_ABS; + if (previousCommand == SVGPathSeg.PATHSEG_MOVETO_REL) + return SVGPathSeg.PATHSEG_LINETO_REL; + return previousCommand; + } + return SVGPathSeg.PATHSEG_UNKNOWN; + } + + Source.prototype.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 == SVGPathSeg.PATHSEG_MOVETO_ABS || command == SVGPathSeg.PATHSEG_MOVETO_REL; + } + + // 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 + Source.prototype._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.charAt(this._currentIndex) == "+") + this._currentIndex++; + else if (this._currentIndex < this._endIndex && this._string.charAt(this._currentIndex) == "-") { + this._currentIndex++; + sign = -1; + } + + if (this._currentIndex == this._endIndex || ((this._string.charAt(this._currentIndex) < "0" || this._string.charAt(this._currentIndex) > "9") && this._string.charAt(this._currentIndex) != ".")) + // The first character of a number must be one of [0-9+-.]. + return undefined; + + // Read the integer part, build right-to-left. + var startIntPartIndex = this._currentIndex; + while (this._currentIndex < this._endIndex && this._string.charAt(this._currentIndex) >= "0" && this._string.charAt(this._currentIndex) <= "9") + this._currentIndex++; // Advance to first non-digit. + + if (this._currentIndex != startIntPartIndex) { + var scanIntPartIndex = this._currentIndex - 1; + var multiplier = 1; + while (scanIntPartIndex >= startIntPartIndex) { + integer += multiplier * (this._string.charAt(scanIntPartIndex--) - "0"); + multiplier *= 10; + } + } + + // Read the decimals. + if (this._currentIndex < this._endIndex && this._string.charAt(this._currentIndex) == ".") { + this._currentIndex++; + + // There must be a least one digit following the . + if (this._currentIndex >= this._endIndex || this._string.charAt(this._currentIndex) < "0" || this._string.charAt(this._currentIndex) > "9") + return undefined; + while (this._currentIndex < this._endIndex && this._string.charAt(this._currentIndex) >= "0" && this._string.charAt(this._currentIndex) <= "9") + decimal += (this._string.charAt(this._currentIndex++) - "0") * (frac *= 0.1); + } + + // Read the exponent part. + if (this._currentIndex != startIndex && this._currentIndex + 1 < this._endIndex && (this._string.charAt(this._currentIndex) == "e" || this._string.charAt(this._currentIndex) == "E") && (this._string.charAt(this._currentIndex + 1) != "x" && this._string.charAt(this._currentIndex + 1) != "m")) { + this._currentIndex++; + + // Read the sign of the exponent. + if (this._string.charAt(this._currentIndex) == "+") { + this._currentIndex++; + } else if (this._string.charAt(this._currentIndex) == "-") { + this._currentIndex++; + expsign = -1; + } + + // There must be an exponent. + if (this._currentIndex >= this._endIndex || this._string.charAt(this._currentIndex) < "0" || this._string.charAt(this._currentIndex) > "9") + return undefined; + + while (this._currentIndex < this._endIndex && this._string.charAt(this._currentIndex) >= "0" && this._string.charAt(this._currentIndex) <= "9") { + exponent *= 10; + exponent += (this._string.charAt(this._currentIndex) - "0"); + this._currentIndex++; + } + } + + var number = integer + decimal; + number *= sign; + + if (exponent) + number *= Math.pow(10, expsign * exponent); + + if (startIndex == this._currentIndex) + return undefined; + + this._skipOptionalSpacesOrDelimiter(); + + return number; + } + + Source.prototype._parseArcFlag = function() { + if (this._currentIndex >= this._endIndex) + return undefined; + var flag = false; + var flagChar = this._string.charAt(this._currentIndex++); + if (flagChar == "0") + flag = false; + else if (flagChar == "1") + flag = true; + else + return undefined; + + this._skipOptionalSpacesOrDelimiter(); + return flag; + } + + Source.prototype.parseSegment = function() { + var lookahead = this._string[this._currentIndex]; + var command = this._pathSegTypeFromChar(lookahead); + if (command == SVGPathSeg.PATHSEG_UNKNOWN) { + // Possibly an implicit command. Not allowed if this is the first command. + if (this._previousCommand == SVGPathSeg.PATHSEG_UNKNOWN) + return null; + command = this._nextCommandHelper(lookahead, this._previousCommand); + if (command == SVGPathSeg.PATHSEG_UNKNOWN) + return null; + } else { + this._currentIndex++; + } + + this._previousCommand = command; + + switch (command) { + case SVGPathSeg.PATHSEG_MOVETO_REL: + return new SVGPathSegMovetoRel(owningPathSegList, this._parseNumber(), this._parseNumber()); + case SVGPathSeg.PATHSEG_MOVETO_ABS: + return new SVGPathSegMovetoAbs(owningPathSegList, this._parseNumber(), this._parseNumber()); + case SVGPathSeg.PATHSEG_LINETO_REL: + return new SVGPathSegLinetoRel(owningPathSegList, this._parseNumber(), this._parseNumber()); + case SVGPathSeg.PATHSEG_LINETO_ABS: + return new SVGPathSegLinetoAbs(owningPathSegList, this._parseNumber(), this._parseNumber()); + case SVGPathSeg.PATHSEG_LINETO_HORIZONTAL_REL: + return new SVGPathSegLinetoHorizontalRel(owningPathSegList, this._parseNumber()); + case SVGPathSeg.PATHSEG_LINETO_HORIZONTAL_ABS: + return new SVGPathSegLinetoHorizontalAbs(owningPathSegList, this._parseNumber()); + case SVGPathSeg.PATHSEG_LINETO_VERTICAL_REL: + return new SVGPathSegLinetoVerticalRel(owningPathSegList, this._parseNumber()); + case SVGPathSeg.PATHSEG_LINETO_VERTICAL_ABS: + return new SVGPathSegLinetoVerticalAbs(owningPathSegList, this._parseNumber()); + case SVGPathSeg.PATHSEG_CLOSEPATH: + this._skipOptionalSpaces(); + return new SVGPathSegClosePath(owningPathSegList); + case SVGPathSeg.PATHSEG_CURVETO_CUBIC_REL: + var points = {x1: this._parseNumber(), y1: this._parseNumber(), x2: this._parseNumber(), y2: this._parseNumber(), x: this._parseNumber(), y: this._parseNumber()}; + return new SVGPathSegCurvetoCubicRel(owningPathSegList, points.x, points.y, points.x1, points.y1, points.x2, points.y2); + case SVGPathSeg.PATHSEG_CURVETO_CUBIC_ABS: + var points = {x1: this._parseNumber(), y1: this._parseNumber(), x2: this._parseNumber(), y2: this._parseNumber(), x: this._parseNumber(), y: this._parseNumber()}; + return new SVGPathSegCurvetoCubicAbs(owningPathSegList, points.x, points.y, points.x1, points.y1, points.x2, points.y2); + case SVGPathSeg.PATHSEG_CURVETO_CUBIC_SMOOTH_REL: + var points = {x2: this._parseNumber(), y2: this._parseNumber(), x: this._parseNumber(), y: this._parseNumber()}; + return new SVGPathSegCurvetoCubicSmoothRel(owningPathSegList, points.x, points.y, points.x2, points.y2); + case SVGPathSeg.PATHSEG_CURVETO_CUBIC_SMOOTH_ABS: + var points = {x2: this._parseNumber(), y2: this._parseNumber(), x: this._parseNumber(), y: this._parseNumber()}; + return new SVGPathSegCurvetoCubicSmoothAbs(owningPathSegList, points.x, points.y, points.x2, points.y2); + case SVGPathSeg.PATHSEG_CURVETO_QUADRATIC_REL: + var points = {x1: this._parseNumber(), y1: this._parseNumber(), x: this._parseNumber(), y: this._parseNumber()}; + return new SVGPathSegCurvetoQuadraticRel(owningPathSegList, points.x, points.y, points.x1, points.y1); + case SVGPathSeg.PATHSEG_CURVETO_QUADRATIC_ABS: + var points = {x1: this._parseNumber(), y1: this._parseNumber(), x: this._parseNumber(), y: this._parseNumber()}; + return new SVGPathSegCurvetoQuadraticAbs(owningPathSegList, points.x, points.y, points.x1, points.y1); + case SVGPathSeg.PATHSEG_CURVETO_QUADRATIC_SMOOTH_REL: + return new SVGPathSegCurvetoQuadraticSmoothRel(owningPathSegList, this._parseNumber(), this._parseNumber()); + case SVGPathSeg.PATHSEG_CURVETO_QUADRATIC_SMOOTH_ABS: + return new SVGPathSegCurvetoQuadraticSmoothAbs(owningPathSegList, this._parseNumber(), this._parseNumber()); + case SVGPathSeg.PATHSEG_ARC_REL: + var points = {x1: this._parseNumber(), y1: this._parseNumber(), arcAngle: this._parseNumber(), arcLarge: this._parseArcFlag(), arcSweep: this._parseArcFlag(), x: this._parseNumber(), y: this._parseNumber()}; + return new SVGPathSegArcRel(owningPathSegList, points.x, points.y, points.x1, points.y1, points.arcAngle, points.arcLarge, points.arcSweep); + case SVGPathSeg.PATHSEG_ARC_ABS: + var points = {x1: this._parseNumber(), y1: this._parseNumber(), arcAngle: this._parseNumber(), arcLarge: this._parseArcFlag(), arcSweep: this._parseArcFlag(), x: this._parseNumber(), y: this._parseNumber()}; + return new SVGPathSegArcAbs(owningPathSegList, points.x, points.y, points.x1, points.y1, points.arcAngle, points.arcLarge, points.arcSweep); + default: + throw "Unknown path seg type." + } + } + + var builder = new Builder(); + var source = new Source(string); + + if (!source.initialCommandIsMoveTo()) + return []; + while (source.hasMoreData()) { + var pathSeg = source.parseSegment(); + if (!pathSeg) + return []; + builder.appendSegment(pathSeg); + } + + return builder.pathSegList; + } + + // Add the pathSegList accessors to SVGPathElement. + // Spec: http://www.w3.org/TR/SVG11/single-page.html#paths-InterfaceSVGAnimatedPathData + Object.defineProperty(SVGPathElement.prototype, "pathSegList", { + get: function() { + if (!this._pathSegList) + this._pathSegList = new SVGPathSegList(this); + return this._pathSegList; + }, + enumerable: true + }); + // FIXME: The following are not implemented and simply return SVGPathElement.pathSegList. + Object.defineProperty(SVGPathElement.prototype, "normalizedPathSegList", { get: function() { return this.pathSegList; }, enumerable: true }); + Object.defineProperty(SVGPathElement.prototype, "animatedPathSegList", { get: function() { return this.pathSegList; }, enumerable: true }); + Object.defineProperty(SVGPathElement.prototype, "animatedNormalizedPathSegList", { get: function() { return this.pathSegList; }, enumerable: true }); + } +}()); \ No newline at end of file diff --git a/editor/recalculate.js b/editor/recalculate.js index 3585147c..6ae2046d 100644 --- a/editor/recalculate.js +++ b/editor/recalculate.js @@ -11,13 +11,14 @@ // 1) jquery // 2) jquery-svg.js // 3) svgedit.js -// 4) browser.js -// 5) math.js -// 6) history.js -// 7) units.js -// 8) svgtransformlist.js -// 9) svgutils.js -// 10) coords.js +// 4) pathseg.js +// 5) browser.js +// 6) math.js +// 7) history.js +// 8) units.js +// 9) svgtransformlist.js +// 10) svgutils.js +// 11) coords.js var svgedit = svgedit || {}; diff --git a/editor/sanitize.js b/editor/sanitize.js index f289f50a..a6d6f9f6 100644 --- a/editor/sanitize.js +++ b/editor/sanitize.js @@ -11,8 +11,9 @@ // Dependencies: // 1) jQuery -// 2) browser.js -// 3) svgutils.js +// 2) pathseg.js +// 3) browser.js +// 4) svgutils.js (function() {'use strict'; diff --git a/editor/select.js b/editor/select.js index 587d3198..caab5c24 100644 --- a/editor/select.js +++ b/editor/select.js @@ -11,9 +11,10 @@ // Dependencies: // 1) jQuery -// 2) browser.js -// 3) math.js -// 4) svgutils.js +// 2) pathseg.js +// 3) browser.js +// 4) math.js +// 5) svgutils.js (function() {'use strict'; diff --git a/editor/svg-editor.html b/editor/svg-editor.html index 99087e3c..51f3d7d3 100644 --- a/editor/svg-editor.html +++ b/editor/svg-editor.html @@ -31,6 +31,7 @@ + diff --git a/editor/svgcanvas.js b/editor/svgcanvas.js index 773ba265..ee65f040 100644 --- a/editor/svgcanvas.js +++ b/editor/svgcanvas.js @@ -13,18 +13,19 @@ // Dependencies: // 1) jQuery -// 2) browser.js -// 3) svgtransformlist.js -// 4) math.js -// 5) units.js -// 6) svgutils.js -// 7) sanitize.js -// 8) history.js -// 9) select.js -// 10) draw.js -// 11) path.js -// 12) coords.js -// 13) recalculate.js +// 2) pathseg.js +// 3) browser.js +// 4) svgtransformlist.js +// 5) math.js +// 6) units.js +// 7) svgutils.js +// 8) sanitize.js +// 9) history.js +// 10) select.js +// 11) draw.js +// 12) path.js +// 13) coords.js +// 14) recalculate.js (function () { diff --git a/editor/svgutils.js b/editor/svgutils.js index 9e7a1f75..bcca3a62 100644 --- a/editor/svgutils.js +++ b/editor/svgutils.js @@ -11,9 +11,10 @@ // Dependencies: // 1) jQuery -// 2) browser.js -// 3) svgtransformlist.js -// 4) units.js +// 2) pathseg.js +// 3) browser.js +// 4) svgtransformlist.js +// 5) units.js (function(undef) {'use strict'; diff --git a/test/coords_test.html b/test/coords_test.html index c19034fd..ea612439 100644 --- a/test/coords_test.html +++ b/test/coords_test.html @@ -7,11 +7,13 @@ + + + diff --git a/test/sanitize_test.html b/test/sanitize_test.html index 540fa906..3804cafb 100644 --- a/test/sanitize_test.html +++ b/test/sanitize_test.html @@ -6,6 +6,7 @@ + diff --git a/test/select_test.html b/test/select_test.html index d3a97d04..4de0cfb2 100644 --- a/test/select_test.html +++ b/test/select_test.html @@ -6,6 +6,7 @@ + diff --git a/test/svgutils_test.html b/test/svgutils_test.html index 3890f5f2..d81e8aa9 100644 --- a/test/svgutils_test.html +++ b/test/svgutils_test.html @@ -6,7 +6,8 @@ - + + diff --git a/test/test1.html b/test/test1.html index 5a83720a..47c8b40c 100644 --- a/test/test1.html +++ b/test/test1.html @@ -9,6 +9,7 @@ + From c5dadfbe7773660d9fcd7f632461d1e03937ab25 Mon Sep 17 00:00:00 2001 From: Philip Rogers Date: Fri, 6 Nov 2015 21:08:23 -0800 Subject: [PATCH 2/2] Include upstream fix for shape editing: https://github.com/progers/pathseg/commit/11f7e5a958aac953bc9bd49ac4f0d91e1d108841 --- editor/pathseg.js | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/editor/pathseg.js b/editor/pathseg.js index 3d5f71a5..14d19fa8 100644 --- a/editor/pathseg.js +++ b/editor/pathseg.js @@ -1,4 +1,5 @@ // SVGPathSeg API polyfill +// https://github.com/progers/pathseg // // This is a drop-in replacement for the SVGPathSeg and SVGPathSegList APIs that were removed from // SVG2 (https://lists.w3.org/Archives/Public/www-svg/2015Jun/0044.html), including the latest spec @@ -350,7 +351,7 @@ // Use a MutationObserver to catch changes to the path's "d" attribute. this._mutationObserverConfig = { "attributes": true, "attributeFilter": ["d"] }; - this._pathElementMutationObserver = new MutationObserver(this._updateListFromPathMutations); + this._pathElementMutationObserver = new MutationObserver(this._updateListFromPathMutations.bind(this)); this._pathElementMutationObserver.observe(this._pathElement, this._mutationObserverConfig); }