@svgedit/svgcanvas 7.1.4
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/blur-event.js +156 -0
- package/clear.js +43 -0
- package/coords.js +298 -0
- package/copy-elem.js +45 -0
- package/dataStorage.js +28 -0
- package/dist/svgcanvas.js +515 -0
- package/dist/svgcanvas.js.map +1 -0
- package/draw.js +1064 -0
- package/elem-get-set.js +1077 -0
- package/event.js +1388 -0
- package/history.js +619 -0
- package/historyrecording.js +161 -0
- package/json.js +110 -0
- package/layer.js +228 -0
- package/math.js +221 -0
- package/namespaces.js +40 -0
- package/package.json +54 -0
- package/paint.js +88 -0
- package/paste-elem.js +127 -0
- package/path-actions.js +1237 -0
- package/path-method.js +1012 -0
- package/path.js +781 -0
- package/recalculate.js +794 -0
- package/rollup.config.js +40 -0
- package/sanitize.js +252 -0
- package/select.js +543 -0
- package/selected-elem.js +1297 -0
- package/selection.js +482 -0
- package/svg-exec.js +1289 -0
- package/svgcanvas.js +1347 -0
- package/svgroot.js +36 -0
- package/text-actions.js +530 -0
- package/touch.js +51 -0
- package/undo.js +279 -0
- package/utilities.js +1214 -0
package/math.js
ADDED
|
@@ -0,0 +1,221 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Mathematical utilities.
|
|
3
|
+
* @module math
|
|
4
|
+
* @license MIT
|
|
5
|
+
*
|
|
6
|
+
* @copyright 2010 Alexis Deveria, 2010 Jeff Schiller
|
|
7
|
+
*/
|
|
8
|
+
|
|
9
|
+
/**
|
|
10
|
+
* @typedef {PlainObject} module:math.AngleCoord45
|
|
11
|
+
* @property {Float} x - The angle-snapped x value
|
|
12
|
+
* @property {Float} y - The angle-snapped y value
|
|
13
|
+
* @property {Integer} a - The angle at which to snap
|
|
14
|
+
*/
|
|
15
|
+
|
|
16
|
+
/**
|
|
17
|
+
* @typedef {PlainObject} module:math.XYObject
|
|
18
|
+
* @property {Float} x
|
|
19
|
+
* @property {Float} y
|
|
20
|
+
*/
|
|
21
|
+
|
|
22
|
+
import { NS } from './namespaces.js'
|
|
23
|
+
|
|
24
|
+
// Constants
|
|
25
|
+
const NEAR_ZERO = 1e-14
|
|
26
|
+
|
|
27
|
+
// Throw away SVGSVGElement used for creating matrices/transforms.
|
|
28
|
+
const svg = document.createElementNS(NS.SVG, 'svg')
|
|
29
|
+
|
|
30
|
+
/**
|
|
31
|
+
* A (hopefully) quicker function to transform a point by a matrix
|
|
32
|
+
* (this function avoids any DOM calls and just does the math).
|
|
33
|
+
* @function module:math.transformPoint
|
|
34
|
+
* @param {Float} x - Float representing the x coordinate
|
|
35
|
+
* @param {Float} y - Float representing the y coordinate
|
|
36
|
+
* @param {SVGMatrix} m - Matrix object to transform the point with
|
|
37
|
+
* @returns {module:math.XYObject} An x, y object representing the transformed point
|
|
38
|
+
*/
|
|
39
|
+
export const transformPoint = function (x, y, m) {
|
|
40
|
+
return { x: m.a * x + m.c * y + m.e, y: m.b * x + m.d * y + m.f }
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
/**
|
|
44
|
+
* Helper function to check if the matrix performs no actual transform
|
|
45
|
+
* (i.e. exists for identity purposes).
|
|
46
|
+
* @function module:math.isIdentity
|
|
47
|
+
* @param {SVGMatrix} m - The matrix object to check
|
|
48
|
+
* @returns {boolean} Indicates whether or not the matrix is 1,0,0,1,0,0
|
|
49
|
+
*/
|
|
50
|
+
export const isIdentity = function (m) {
|
|
51
|
+
return (m.a === 1 && m.b === 0 && m.c === 0 && m.d === 1 && m.e === 0 && m.f === 0)
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
/**
|
|
55
|
+
* This function tries to return a `SVGMatrix` that is the multiplication `m1 * m2`.
|
|
56
|
+
* We also round to zero when it's near zero.
|
|
57
|
+
* @function module:math.matrixMultiply
|
|
58
|
+
* @param {...SVGMatrix} args - Matrix objects to multiply
|
|
59
|
+
* @returns {SVGMatrix} The matrix object resulting from the calculation
|
|
60
|
+
*/
|
|
61
|
+
export const matrixMultiply = function (...args) {
|
|
62
|
+
const m = args.reduceRight((prev, m1) => {
|
|
63
|
+
return m1.multiply(prev)
|
|
64
|
+
})
|
|
65
|
+
|
|
66
|
+
if (Math.abs(m.a) < NEAR_ZERO) { m.a = 0 }
|
|
67
|
+
if (Math.abs(m.b) < NEAR_ZERO) { m.b = 0 }
|
|
68
|
+
if (Math.abs(m.c) < NEAR_ZERO) { m.c = 0 }
|
|
69
|
+
if (Math.abs(m.d) < NEAR_ZERO) { m.d = 0 }
|
|
70
|
+
if (Math.abs(m.e) < NEAR_ZERO) { m.e = 0 }
|
|
71
|
+
if (Math.abs(m.f) < NEAR_ZERO) { m.f = 0 }
|
|
72
|
+
|
|
73
|
+
return m
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
/**
|
|
77
|
+
* See if the given transformlist includes a non-indentity matrix transform.
|
|
78
|
+
* @function module:math.hasMatrixTransform
|
|
79
|
+
* @param {SVGTransformList} [tlist] - The transformlist to check
|
|
80
|
+
* @returns {boolean} Whether or not a matrix transform was found
|
|
81
|
+
*/
|
|
82
|
+
export const hasMatrixTransform = function (tlist) {
|
|
83
|
+
if (!tlist) { return false }
|
|
84
|
+
let num = tlist.numberOfItems
|
|
85
|
+
while (num--) {
|
|
86
|
+
const xform = tlist.getItem(num)
|
|
87
|
+
if (xform.type === 1 && !isIdentity(xform.matrix)) { return true }
|
|
88
|
+
}
|
|
89
|
+
return false
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
/**
|
|
93
|
+
* @typedef {PlainObject} module:math.TransformedBox An object with the following values
|
|
94
|
+
* @property {module:math.XYObject} tl - The top left coordinate
|
|
95
|
+
* @property {module:math.XYObject} tr - The top right coordinate
|
|
96
|
+
* @property {module:math.XYObject} bl - The bottom left coordinate
|
|
97
|
+
* @property {module:math.XYObject} br - The bottom right coordinate
|
|
98
|
+
* @property {PlainObject} aabox - Object with the following values:
|
|
99
|
+
* @property {Float} aabox.x - Float with the axis-aligned x coordinate
|
|
100
|
+
* @property {Float} aabox.y - Float with the axis-aligned y coordinate
|
|
101
|
+
* @property {Float} aabox.width - Float with the axis-aligned width coordinate
|
|
102
|
+
* @property {Float} aabox.height - Float with the axis-aligned height coordinate
|
|
103
|
+
*/
|
|
104
|
+
|
|
105
|
+
/**
|
|
106
|
+
* Transforms a rectangle based on the given matrix.
|
|
107
|
+
* @function module:math.transformBox
|
|
108
|
+
* @param {Float} l - Float with the box's left coordinate
|
|
109
|
+
* @param {Float} t - Float with the box's top coordinate
|
|
110
|
+
* @param {Float} w - Float with the box width
|
|
111
|
+
* @param {Float} h - Float with the box height
|
|
112
|
+
* @param {SVGMatrix} m - Matrix object to transform the box by
|
|
113
|
+
* @returns {module:math.TransformedBox}
|
|
114
|
+
*/
|
|
115
|
+
export const transformBox = function (l, t, w, h, m) {
|
|
116
|
+
const tl = transformPoint(l, t, m)
|
|
117
|
+
const tr = transformPoint((l + w), t, m)
|
|
118
|
+
const bl = transformPoint(l, (t + h), m)
|
|
119
|
+
const br = transformPoint((l + w), (t + h), m)
|
|
120
|
+
|
|
121
|
+
const minx = Math.min(tl.x, tr.x, bl.x, br.x)
|
|
122
|
+
const maxx = Math.max(tl.x, tr.x, bl.x, br.x)
|
|
123
|
+
const miny = Math.min(tl.y, tr.y, bl.y, br.y)
|
|
124
|
+
const maxy = Math.max(tl.y, tr.y, bl.y, br.y)
|
|
125
|
+
|
|
126
|
+
return {
|
|
127
|
+
tl,
|
|
128
|
+
tr,
|
|
129
|
+
bl,
|
|
130
|
+
br,
|
|
131
|
+
aabox: {
|
|
132
|
+
x: minx,
|
|
133
|
+
y: miny,
|
|
134
|
+
width: (maxx - minx),
|
|
135
|
+
height: (maxy - miny)
|
|
136
|
+
}
|
|
137
|
+
}
|
|
138
|
+
}
|
|
139
|
+
|
|
140
|
+
/**
|
|
141
|
+
* This returns a single matrix Transform for a given Transform List
|
|
142
|
+
* (this is the equivalent of `SVGTransformList.consolidate()` but unlike
|
|
143
|
+
* that method, this one does not modify the actual `SVGTransformList`).
|
|
144
|
+
* This function is very liberal with its `min`, `max` arguments.
|
|
145
|
+
* @function module:math.transformListToTransform
|
|
146
|
+
* @param {SVGTransformList} tlist - The transformlist object
|
|
147
|
+
* @param {Integer} [min=0] - Optional integer indicating start transform position
|
|
148
|
+
* @param {Integer} [max] - Optional integer indicating end transform position;
|
|
149
|
+
* defaults to one less than the tlist's `numberOfItems`
|
|
150
|
+
* @returns {SVGTransform} A single matrix transform object
|
|
151
|
+
*/
|
|
152
|
+
export const transformListToTransform = function (tlist, min, max) {
|
|
153
|
+
if (!tlist) {
|
|
154
|
+
// Or should tlist = null have been prevented before this?
|
|
155
|
+
return svg.createSVGTransformFromMatrix(svg.createSVGMatrix())
|
|
156
|
+
}
|
|
157
|
+
min = min || 0
|
|
158
|
+
max = max || (tlist.numberOfItems - 1)
|
|
159
|
+
min = Number.parseInt(min)
|
|
160
|
+
max = Number.parseInt(max)
|
|
161
|
+
if (min > max) { const temp = max; max = min; min = temp }
|
|
162
|
+
let m = svg.createSVGMatrix()
|
|
163
|
+
for (let i = min; i <= max; ++i) {
|
|
164
|
+
// if our indices are out of range, just use a harmless identity matrix
|
|
165
|
+
const mtom = (i >= 0 && i < tlist.numberOfItems
|
|
166
|
+
? tlist.getItem(i).matrix
|
|
167
|
+
: svg.createSVGMatrix())
|
|
168
|
+
m = matrixMultiply(m, mtom)
|
|
169
|
+
}
|
|
170
|
+
return svg.createSVGTransformFromMatrix(m)
|
|
171
|
+
}
|
|
172
|
+
|
|
173
|
+
/**
|
|
174
|
+
* Get the matrix object for a given element.
|
|
175
|
+
* @function module:math.getMatrix
|
|
176
|
+
* @param {Element} elem - The DOM element to check
|
|
177
|
+
* @returns {SVGMatrix} The matrix object associated with the element's transformlist
|
|
178
|
+
*/
|
|
179
|
+
export const getMatrix = (elem) => {
|
|
180
|
+
const tlist = elem.transform.baseVal
|
|
181
|
+
return transformListToTransform(tlist).matrix
|
|
182
|
+
}
|
|
183
|
+
|
|
184
|
+
/**
|
|
185
|
+
* Returns a 45 degree angle coordinate associated with the two given
|
|
186
|
+
* coordinates.
|
|
187
|
+
* @function module:math.snapToAngle
|
|
188
|
+
* @param {Integer} x1 - First coordinate's x value
|
|
189
|
+
* @param {Integer} y1 - First coordinate's y value
|
|
190
|
+
* @param {Integer} x2 - Second coordinate's x value
|
|
191
|
+
* @param {Integer} y2 - Second coordinate's y value
|
|
192
|
+
* @returns {module:math.AngleCoord45}
|
|
193
|
+
*/
|
|
194
|
+
export const snapToAngle = (x1, y1, x2, y2) => {
|
|
195
|
+
const snap = Math.PI / 4 // 45 degrees
|
|
196
|
+
const dx = x2 - x1
|
|
197
|
+
const dy = y2 - y1
|
|
198
|
+
const angle = Math.atan2(dy, dx)
|
|
199
|
+
const dist = Math.sqrt(dx * dx + dy * dy)
|
|
200
|
+
const snapangle = Math.round(angle / snap) * snap
|
|
201
|
+
|
|
202
|
+
return {
|
|
203
|
+
x: x1 + dist * Math.cos(snapangle),
|
|
204
|
+
y: y1 + dist * Math.sin(snapangle),
|
|
205
|
+
a: snapangle
|
|
206
|
+
}
|
|
207
|
+
}
|
|
208
|
+
|
|
209
|
+
/**
|
|
210
|
+
* Check if two rectangles (BBoxes objects) intersect each other.
|
|
211
|
+
* @function module:math.rectsIntersect
|
|
212
|
+
* @param {SVGRect} r1 - The first BBox-like object
|
|
213
|
+
* @param {SVGRect} r2 - The second BBox-like object
|
|
214
|
+
* @returns {boolean} True if rectangles intersect
|
|
215
|
+
*/
|
|
216
|
+
export const rectsIntersect = (r1, r2) => {
|
|
217
|
+
return r2.x < (r1.x + r1.width) &&
|
|
218
|
+
(r2.x + r2.width) > r1.x &&
|
|
219
|
+
r2.y < (r1.y + r1.height) &&
|
|
220
|
+
(r2.y + r2.height) > r1.y
|
|
221
|
+
}
|
package/namespaces.js
ADDED
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Namespaces or tools therefor.
|
|
3
|
+
* @module namespaces
|
|
4
|
+
* @license MIT
|
|
5
|
+
*/
|
|
6
|
+
|
|
7
|
+
/**
|
|
8
|
+
* Common namepaces constants in alpha order.
|
|
9
|
+
* @enum {string}
|
|
10
|
+
* @type {PlainObject}
|
|
11
|
+
* @memberof module:namespaces
|
|
12
|
+
*/
|
|
13
|
+
export const NS = {
|
|
14
|
+
HTML: 'http://www.w3.org/1999/xhtml',
|
|
15
|
+
MATH: 'http://www.w3.org/1998/Math/MathML',
|
|
16
|
+
SE: 'http://svg-edit.googlecode.com',
|
|
17
|
+
SVG: 'http://www.w3.org/2000/svg',
|
|
18
|
+
XLINK: 'http://www.w3.org/1999/xlink',
|
|
19
|
+
OI: 'http://www.optimistik.fr/namespace/svg/OIdata',
|
|
20
|
+
XML: 'http://www.w3.org/XML/1998/namespace',
|
|
21
|
+
XMLNS: 'http://www.w3.org/2000/xmlns/' // see http://www.w3.org/TR/REC-xml-names/#xmlReserved
|
|
22
|
+
// SODIPODI: 'http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd',
|
|
23
|
+
// INKSCAPE: 'http://www.inkscape.org/namespaces/inkscape',
|
|
24
|
+
// RDF: 'http://www.w3.org/1999/02/22-rdf-syntax-ns#',
|
|
25
|
+
// OSB: 'http://www.openswatchbook.org/uri/2009/osb',
|
|
26
|
+
// CC: 'http://creativecommons.org/ns#',
|
|
27
|
+
// DC: 'http://purl.org/dc/elements/1.1/'
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
/**
|
|
31
|
+
* @function module:namespaces.getReverseNS
|
|
32
|
+
* @returns {string} The NS with key values switched and lowercase
|
|
33
|
+
*/
|
|
34
|
+
export const getReverseNS = function () {
|
|
35
|
+
const reverseNS = {}
|
|
36
|
+
Object.entries(NS).forEach(([name, URI]) => {
|
|
37
|
+
reverseNS[URI] = name.toLowerCase()
|
|
38
|
+
})
|
|
39
|
+
return reverseNS
|
|
40
|
+
}
|
package/package.json
ADDED
|
@@ -0,0 +1,54 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@svgedit/svgcanvas",
|
|
3
|
+
"version": "7.1.4",
|
|
4
|
+
"description": "SVG Canvas",
|
|
5
|
+
"main": "dist/svgcanvas.js",
|
|
6
|
+
"author": "Narendra Sisodiya",
|
|
7
|
+
"publishConfig": {
|
|
8
|
+
"access": "public"
|
|
9
|
+
},
|
|
10
|
+
"bugs": {
|
|
11
|
+
"url": "https://github.com/SVG-Edit/svgedit/issues"
|
|
12
|
+
},
|
|
13
|
+
"repository": {
|
|
14
|
+
"type": "git",
|
|
15
|
+
"url": "git+https://github.com/SVG-Edit/svgedit.git"
|
|
16
|
+
},
|
|
17
|
+
"homepage": "https://github.com/SVG-Edit/svgedit#readme",
|
|
18
|
+
"contributors": [
|
|
19
|
+
"Pavol Rusnak",
|
|
20
|
+
"Jeff Schiller",
|
|
21
|
+
"Vidar Hokstad",
|
|
22
|
+
"Alexis Deveria",
|
|
23
|
+
"Brett Zamir",
|
|
24
|
+
"Fabien Jacq",
|
|
25
|
+
"OptimistikSAS"
|
|
26
|
+
],
|
|
27
|
+
"keywords": [
|
|
28
|
+
"svg-editor",
|
|
29
|
+
"javascript",
|
|
30
|
+
"svg-edit",
|
|
31
|
+
"svg",
|
|
32
|
+
"svgcanvas"
|
|
33
|
+
],
|
|
34
|
+
"license": "MIT",
|
|
35
|
+
"browserslist": [
|
|
36
|
+
"defaults",
|
|
37
|
+
"not IE 11",
|
|
38
|
+
"not OperaMini all"
|
|
39
|
+
],
|
|
40
|
+
"standard": {
|
|
41
|
+
"ignore": [],
|
|
42
|
+
"globals": [
|
|
43
|
+
"cy",
|
|
44
|
+
"assert"
|
|
45
|
+
],
|
|
46
|
+
"env": [
|
|
47
|
+
"mocha",
|
|
48
|
+
"browser"
|
|
49
|
+
]
|
|
50
|
+
},
|
|
51
|
+
"scripts": {
|
|
52
|
+
"build": "rollup -c"
|
|
53
|
+
}
|
|
54
|
+
}
|
package/paint.js
ADDED
|
@@ -0,0 +1,88 @@
|
|
|
1
|
+
/**
|
|
2
|
+
*
|
|
3
|
+
*/
|
|
4
|
+
export default class Paint {
|
|
5
|
+
/**
|
|
6
|
+
* @param {module:jGraduate.jGraduatePaintOptions} [opt]
|
|
7
|
+
*/
|
|
8
|
+
constructor (opt) {
|
|
9
|
+
const options = opt || {}
|
|
10
|
+
this.alpha = isNaN(options.alpha) ? 100 : options.alpha
|
|
11
|
+
// copy paint object
|
|
12
|
+
if (options.copy) {
|
|
13
|
+
/**
|
|
14
|
+
* @name module:jGraduate~Paint#type
|
|
15
|
+
* @type {"none"|"solidColor"|"linearGradient"|"radialGradient"}
|
|
16
|
+
*/
|
|
17
|
+
this.type = options.copy.type
|
|
18
|
+
/**
|
|
19
|
+
* Represents opacity (0-100).
|
|
20
|
+
* @name module:jGraduate~Paint#alpha
|
|
21
|
+
* @type {Float}
|
|
22
|
+
*/
|
|
23
|
+
this.alpha = options.copy.alpha
|
|
24
|
+
/**
|
|
25
|
+
* Represents #RRGGBB hex of color.
|
|
26
|
+
* @name module:jGraduate~Paint#solidColor
|
|
27
|
+
* @type {string}
|
|
28
|
+
*/
|
|
29
|
+
this.solidColor = null
|
|
30
|
+
/**
|
|
31
|
+
* @name module:jGraduate~Paint#linearGradient
|
|
32
|
+
* @type {SVGLinearGradientElement}
|
|
33
|
+
*/
|
|
34
|
+
this.linearGradient = null
|
|
35
|
+
/**
|
|
36
|
+
* @name module:jGraduate~Paint#radialGradient
|
|
37
|
+
* @type {SVGRadialGradientElement}
|
|
38
|
+
*/
|
|
39
|
+
this.radialGradient = null
|
|
40
|
+
|
|
41
|
+
switch (this.type) {
|
|
42
|
+
case 'none':
|
|
43
|
+
break
|
|
44
|
+
case 'solidColor':
|
|
45
|
+
this.solidColor = options.copy.solidColor
|
|
46
|
+
break
|
|
47
|
+
case 'linearGradient':
|
|
48
|
+
this.linearGradient = options.copy.linearGradient.cloneNode(true)
|
|
49
|
+
break
|
|
50
|
+
case 'radialGradient':
|
|
51
|
+
this.radialGradient = options.copy.radialGradient.cloneNode(true)
|
|
52
|
+
break
|
|
53
|
+
}
|
|
54
|
+
// create linear gradient paint
|
|
55
|
+
} else if (options.linearGradient) {
|
|
56
|
+
this.type = 'linearGradient'
|
|
57
|
+
this.solidColor = null
|
|
58
|
+
this.radialGradient = null
|
|
59
|
+
if (options.linearGradient.hasAttribute('xlink:href')) {
|
|
60
|
+
const xhref = document.getElementById(options.linearGradient.getAttribute('xlink:href').substr(1))
|
|
61
|
+
this.linearGradient = xhref.cloneNode(true)
|
|
62
|
+
} else {
|
|
63
|
+
this.linearGradient = options.linearGradient.cloneNode(true)
|
|
64
|
+
}
|
|
65
|
+
// create linear gradient paint
|
|
66
|
+
} else if (options.radialGradient) {
|
|
67
|
+
this.type = 'radialGradient'
|
|
68
|
+
this.solidColor = null
|
|
69
|
+
this.linearGradient = null
|
|
70
|
+
if (options.radialGradient.hasAttribute('xlink:href')) {
|
|
71
|
+
const xhref = document.getElementById(options.radialGradient.getAttribute('xlink:href').substr(1))
|
|
72
|
+
this.radialGradient = xhref.cloneNode(true)
|
|
73
|
+
} else {
|
|
74
|
+
this.radialGradient = options.radialGradient.cloneNode(true)
|
|
75
|
+
}
|
|
76
|
+
// create solid color paint
|
|
77
|
+
} else if (options.solidColor) {
|
|
78
|
+
this.type = 'solidColor'
|
|
79
|
+
this.solidColor = options.solidColor
|
|
80
|
+
// create empty paint
|
|
81
|
+
} else {
|
|
82
|
+
this.type = 'none'
|
|
83
|
+
this.solidColor = null
|
|
84
|
+
this.linearGradient = null
|
|
85
|
+
this.radialGradient = null
|
|
86
|
+
}
|
|
87
|
+
}
|
|
88
|
+
}
|
package/paste-elem.js
ADDED
|
@@ -0,0 +1,127 @@
|
|
|
1
|
+
import {
|
|
2
|
+
getStrokedBBoxDefaultVisible
|
|
3
|
+
} from './utilities.js'
|
|
4
|
+
import * as hstry from './history.js'
|
|
5
|
+
|
|
6
|
+
const {
|
|
7
|
+
InsertElementCommand, BatchCommand
|
|
8
|
+
} = hstry
|
|
9
|
+
|
|
10
|
+
let svgCanvas = null
|
|
11
|
+
|
|
12
|
+
/**
|
|
13
|
+
* @function module:paste-elem.init
|
|
14
|
+
* @param {module:paste-elem.pasteContext} pasteContext
|
|
15
|
+
* @returns {void}
|
|
16
|
+
*/
|
|
17
|
+
export const init = (canvas) => {
|
|
18
|
+
svgCanvas = canvas
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
/**
|
|
22
|
+
* @function module:svgcanvas.SvgCanvas#pasteElements
|
|
23
|
+
* @param {"in_place"|"point"|void} type
|
|
24
|
+
* @param {Integer|void} x Expected if type is "point"
|
|
25
|
+
* @param {Integer|void} y Expected if type is "point"
|
|
26
|
+
* @fires module:svgcanvas.SvgCanvas#event:changed
|
|
27
|
+
* @fires module:svgcanvas.SvgCanvas#event:ext_IDsUpdated
|
|
28
|
+
* @returns {void}
|
|
29
|
+
*/
|
|
30
|
+
export const pasteElementsMethod = function (type, x, y) {
|
|
31
|
+
let clipb = JSON.parse(sessionStorage.getItem(svgCanvas.getClipboardID()))
|
|
32
|
+
if (!clipb) return
|
|
33
|
+
let len = clipb.length
|
|
34
|
+
if (!len) return
|
|
35
|
+
|
|
36
|
+
const pasted = []
|
|
37
|
+
const batchCmd = new BatchCommand('Paste elements')
|
|
38
|
+
// const drawing = getCurrentDrawing();
|
|
39
|
+
/**
|
|
40
|
+
* @typedef {PlainObject<string, string>} module:svgcanvas.ChangedIDs
|
|
41
|
+
*/
|
|
42
|
+
/**
|
|
43
|
+
* @type {module:svgcanvas.ChangedIDs}
|
|
44
|
+
*/
|
|
45
|
+
const changedIDs = {}
|
|
46
|
+
|
|
47
|
+
// Recursively replace IDs and record the changes
|
|
48
|
+
/**
|
|
49
|
+
*
|
|
50
|
+
* @param {module:svgcanvas.SVGAsJSON} elem
|
|
51
|
+
* @returns {void}
|
|
52
|
+
*/
|
|
53
|
+
function checkIDs (elem) {
|
|
54
|
+
if (elem.attr?.id) {
|
|
55
|
+
changedIDs[elem.attr.id] = svgCanvas.getNextId()
|
|
56
|
+
elem.attr.id = changedIDs[elem.attr.id]
|
|
57
|
+
}
|
|
58
|
+
if (elem.children) elem.children.forEach((child) => checkIDs(child))
|
|
59
|
+
}
|
|
60
|
+
clipb.forEach((elem) => checkIDs(elem))
|
|
61
|
+
|
|
62
|
+
// Give extensions like the connector extension a chance to reflect new IDs and remove invalid elements
|
|
63
|
+
/**
|
|
64
|
+
* Triggered when `pasteElements` is called from a paste action (context menu or key).
|
|
65
|
+
* @event module:svgcanvas.SvgCanvas#event:ext_IDsUpdated
|
|
66
|
+
* @type {PlainObject}
|
|
67
|
+
* @property {module:svgcanvas.SVGAsJSON[]} elems
|
|
68
|
+
* @property {module:svgcanvas.ChangedIDs} changes Maps past ID (on attribute) to current ID
|
|
69
|
+
*/
|
|
70
|
+
svgCanvas.runExtensions(
|
|
71
|
+
'IDsUpdated',
|
|
72
|
+
/** @type {module:svgcanvas.SvgCanvas#event:ext_IDsUpdated} */
|
|
73
|
+
{ elems: clipb, changes: changedIDs },
|
|
74
|
+
true
|
|
75
|
+
).forEach(function (extChanges) {
|
|
76
|
+
if (!extChanges || !('remove' in extChanges)) return
|
|
77
|
+
|
|
78
|
+
extChanges.remove.forEach(function (removeID) {
|
|
79
|
+
clipb = clipb.filter(function (clipBoardItem) {
|
|
80
|
+
return clipBoardItem.attr.id !== removeID
|
|
81
|
+
})
|
|
82
|
+
})
|
|
83
|
+
})
|
|
84
|
+
|
|
85
|
+
// Move elements to lastClickPoint
|
|
86
|
+
while (len--) {
|
|
87
|
+
const elem = clipb[len]
|
|
88
|
+
if (!elem) { continue }
|
|
89
|
+
|
|
90
|
+
const copy = svgCanvas.addSVGElementsFromJson(elem)
|
|
91
|
+
pasted.push(copy)
|
|
92
|
+
batchCmd.addSubCommand(new InsertElementCommand(copy))
|
|
93
|
+
|
|
94
|
+
svgCanvas.restoreRefElements(copy)
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
svgCanvas.selectOnly(pasted)
|
|
98
|
+
|
|
99
|
+
if (type !== 'in_place') {
|
|
100
|
+
let ctrX; let ctrY
|
|
101
|
+
|
|
102
|
+
if (!type) {
|
|
103
|
+
ctrX = svgCanvas.getLastClickPoint('x')
|
|
104
|
+
ctrY = svgCanvas.getLastClickPoint('y')
|
|
105
|
+
} else if (type === 'point') {
|
|
106
|
+
ctrX = x
|
|
107
|
+
ctrY = y
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
const bbox = getStrokedBBoxDefaultVisible(pasted)
|
|
111
|
+
const cx = ctrX - (bbox.x + bbox.width / 2)
|
|
112
|
+
const cy = ctrY - (bbox.y + bbox.height / 2)
|
|
113
|
+
const dx = []
|
|
114
|
+
const dy = []
|
|
115
|
+
|
|
116
|
+
pasted.forEach(function (_item) {
|
|
117
|
+
dx.push(cx)
|
|
118
|
+
dy.push(cy)
|
|
119
|
+
})
|
|
120
|
+
|
|
121
|
+
const cmd = svgCanvas.moveSelectedElements(dx, dy, false)
|
|
122
|
+
if (cmd) batchCmd.addSubCommand(cmd)
|
|
123
|
+
}
|
|
124
|
+
|
|
125
|
+
svgCanvas.addCommandToHistory(batchCmd)
|
|
126
|
+
svgCanvas.call('changed', pasted)
|
|
127
|
+
}
|