@svgedit/svgcanvas 7.2.0 → 7.2.2
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/CHANGES.md +8 -0
- package/core/coords.js +3 -3
- package/core/elem-get-set.js +80 -90
- package/core/event.js +5 -5
- package/core/math.js +90 -52
- package/core/path-actions.js +2 -2
- package/core/path.js +2 -2
- package/core/recalculate.js +8 -9
- package/core/select.js +3 -3
- package/core/selected-elem.js +6 -5
- package/core/selection.js +3 -2
- package/core/svg-exec.js +29 -39
- package/core/undo.js +5 -5
- package/core/utilities.js +449 -354
- package/dist/svgcanvas.js +4924 -6331
- package/dist/svgcanvas.js.map +1 -1
- package/package.json +1 -1
- package/publish.md +6 -0
- package/rollup.config.mjs +3 -2
package/core/utilities.js
CHANGED
|
@@ -7,14 +7,18 @@
|
|
|
7
7
|
*/
|
|
8
8
|
|
|
9
9
|
import { NS } from './namespaces.js'
|
|
10
|
-
import { setUnitAttr, getTypeMap } from './units.js'
|
|
10
|
+
import { setUnitAttr, getTypeMap, shortFloat } from './units.js'
|
|
11
11
|
import {
|
|
12
|
-
hasMatrixTransform,
|
|
12
|
+
hasMatrixTransform,
|
|
13
|
+
transformListToTransform,
|
|
14
|
+
transformBox,
|
|
15
|
+
getTransformList
|
|
13
16
|
} from './math.js'
|
|
14
17
|
import { getClosest, mergeDeep } from '../common/util.js'
|
|
15
18
|
|
|
16
19
|
// Much faster than running getBBox() every time
|
|
17
|
-
const visElems =
|
|
20
|
+
const visElems =
|
|
21
|
+
'a,circle,ellipse,foreignObject,g,image,line,path,polygon,polyline,rect,svg,text,tspan,use,clipPath'
|
|
18
22
|
const visElemsArr = visElems.split(',')
|
|
19
23
|
// const hidElems = 'defs,desc,feGaussianBlur,filter,linearGradient,marker,mask,metadata,pattern,radialGradient,stop,switch,symbol,title,textPath';
|
|
20
24
|
|
|
@@ -22,15 +26,15 @@ let svgCanvas = null
|
|
|
22
26
|
let svgroot_ = null
|
|
23
27
|
|
|
24
28
|
/**
|
|
25
|
-
* Object with the following keys/values.
|
|
26
|
-
* @typedef {PlainObject} module:utilities.SVGElementJSON
|
|
27
|
-
* @property {string} element - Tag name of the SVG element to create
|
|
28
|
-
* @property {PlainObject<string, string>} attr - Has key-value attributes to assign to the new element.
|
|
29
|
-
* An `id` should be set so that {@link module:utilities.EditorContext#addSVGElementsFromJson} can later re-identify the element for modification or replacement.
|
|
30
|
-
* @property {boolean} [curStyles=false] - Indicates whether current style attributes should be applied first
|
|
31
|
-
* @property {module:utilities.SVGElementJSON[]} [children] - Data objects to be added recursively as children
|
|
32
|
-
* @property {string} [namespace="http://www.w3.org/2000/svg"] - Indicate a (non-SVG) namespace
|
|
33
|
-
*/
|
|
29
|
+
* Object with the following keys/values.
|
|
30
|
+
* @typedef {PlainObject} module:utilities.SVGElementJSON
|
|
31
|
+
* @property {string} element - Tag name of the SVG element to create
|
|
32
|
+
* @property {PlainObject<string, string>} attr - Has key-value attributes to assign to the new element.
|
|
33
|
+
* An `id` should be set so that {@link module:utilities.EditorContext#addSVGElementsFromJson} can later re-identify the element for modification or replacement.
|
|
34
|
+
* @property {boolean} [curStyles=false] - Indicates whether current style attributes should be applied first
|
|
35
|
+
* @property {module:utilities.SVGElementJSON[]} [children] - Data objects to be added recursively as children
|
|
36
|
+
* @property {string} [namespace="http://www.w3.org/2000/svg"] - Indicate a (non-SVG) namespace
|
|
37
|
+
*/
|
|
34
38
|
|
|
35
39
|
/**
|
|
36
40
|
* An object that creates SVG elements for the canvas.
|
|
@@ -49,38 +53,38 @@ let svgroot_ = null
|
|
|
49
53
|
* @function module:utilities.EditorContext#addSVGElementsFromJson
|
|
50
54
|
* @param {module:utilities.SVGElementJSON} data
|
|
51
55
|
* @returns {Element} The new element
|
|
52
|
-
*/
|
|
56
|
+
*/
|
|
53
57
|
/**
|
|
54
58
|
* @function module:utilities.EditorContext#getSelectedElements
|
|
55
59
|
* @returns {Element[]} the array with selected DOM elements
|
|
56
|
-
*/
|
|
60
|
+
*/
|
|
57
61
|
/**
|
|
58
62
|
* @function module:utilities.EditorContext#getDOMDocument
|
|
59
63
|
* @returns {HTMLDocument}
|
|
60
|
-
*/
|
|
64
|
+
*/
|
|
61
65
|
/**
|
|
62
66
|
* @function module:utilities.EditorContext#getDOMContainer
|
|
63
67
|
* @returns {HTMLElement}
|
|
64
|
-
*/
|
|
68
|
+
*/
|
|
65
69
|
/**
|
|
66
70
|
* @function module:utilities.EditorContext#getSvgRoot
|
|
67
71
|
* @returns {SVGSVGElement}
|
|
68
|
-
*/
|
|
72
|
+
*/
|
|
69
73
|
/**
|
|
70
74
|
* @function module:utilities.EditorContext#getBaseUnit
|
|
71
75
|
* @returns {string}
|
|
72
|
-
*/
|
|
76
|
+
*/
|
|
73
77
|
/**
|
|
74
78
|
* @function module:utilities.EditorContext#getSnappingStep
|
|
75
79
|
* @returns {Float|string}
|
|
76
|
-
*/
|
|
80
|
+
*/
|
|
77
81
|
|
|
78
82
|
/**
|
|
79
|
-
* @function module:utilities.init
|
|
80
|
-
* @param {module:utilities.EditorContext} canvas
|
|
81
|
-
* @returns {void}
|
|
82
|
-
*/
|
|
83
|
-
export const init =
|
|
83
|
+
* @function module:utilities.init
|
|
84
|
+
* @param {module:utilities.EditorContext} canvas
|
|
85
|
+
* @returns {void}
|
|
86
|
+
*/
|
|
87
|
+
export const init = canvas => {
|
|
84
88
|
svgCanvas = canvas
|
|
85
89
|
svgroot_ = canvas.getSvgRoot()
|
|
86
90
|
}
|
|
@@ -92,19 +96,19 @@ export const init = (canvas) => {
|
|
|
92
96
|
* @returns {string} The string with entity declarations in the internal subset removed
|
|
93
97
|
* @todo This might be needed in other places `parseFromString` is used even without LGTM flagging
|
|
94
98
|
*/
|
|
95
|
-
export const dropXMLInternalSubset =
|
|
99
|
+
export const dropXMLInternalSubset = str => {
|
|
96
100
|
return str.replace(/(<!DOCTYPE\s+\w*\s*\[).*(\?]>)/, '$1$2')
|
|
97
101
|
// return str.replace(/(?<doctypeOpen><!DOCTYPE\s+\w*\s*\[).*(?<doctypeClose>\?\]>)/, '$<doctypeOpen>$<doctypeClose>');
|
|
98
102
|
}
|
|
99
103
|
|
|
100
104
|
/**
|
|
101
|
-
* Converts characters in a string to XML-friendly entities.
|
|
102
|
-
* @function module:utilities.toXml
|
|
103
|
-
* @example `&` becomes `&`
|
|
104
|
-
* @param {string} str - The string to be converted
|
|
105
|
-
* @returns {string} The converted string
|
|
106
|
-
*/
|
|
107
|
-
export const toXml =
|
|
105
|
+
* Converts characters in a string to XML-friendly entities.
|
|
106
|
+
* @function module:utilities.toXml
|
|
107
|
+
* @example `&` becomes `&`
|
|
108
|
+
* @param {string} str - The string to be converted
|
|
109
|
+
* @returns {string} The converted string
|
|
110
|
+
*/
|
|
111
|
+
export const toXml = str => {
|
|
108
112
|
// ' is ok in XML, but not HTML
|
|
109
113
|
// > does not normally need escaping, though it can if within a CDATA expression (and preceded by "]]")
|
|
110
114
|
return str
|
|
@@ -123,11 +127,11 @@ export const toXml = (str) => {
|
|
|
123
127
|
// also precalculate the size of the array needed.
|
|
124
128
|
|
|
125
129
|
/**
|
|
126
|
-
* Converts a string to base64.
|
|
127
|
-
* @function module:utilities.encode64
|
|
128
|
-
* @param {string} input
|
|
129
|
-
* @returns {string} Base64 output
|
|
130
|
-
*/
|
|
130
|
+
* Converts a string to base64.
|
|
131
|
+
* @function module:utilities.encode64
|
|
132
|
+
* @param {string} input
|
|
133
|
+
* @returns {string} Base64 output
|
|
134
|
+
*/
|
|
131
135
|
export function encode64 (input) {
|
|
132
136
|
// base64 strings are 4/3 larger than the original string
|
|
133
137
|
input = encodeUTF8(input) // convert non-ASCII characters
|
|
@@ -135,11 +139,11 @@ export function encode64 (input) {
|
|
|
135
139
|
}
|
|
136
140
|
|
|
137
141
|
/**
|
|
138
|
-
* Converts a string from base64.
|
|
139
|
-
* @function module:utilities.decode64
|
|
140
|
-
* @param {string} input Base64-encoded input
|
|
141
|
-
* @returns {string} Decoded output
|
|
142
|
-
*/
|
|
142
|
+
* Converts a string from base64.
|
|
143
|
+
* @function module:utilities.decode64
|
|
144
|
+
* @param {string} input Base64-encoded input
|
|
145
|
+
* @returns {string} Decoded output
|
|
146
|
+
*/
|
|
143
147
|
export function decode64 (input) {
|
|
144
148
|
return decodeUTF8(window.atob(input))
|
|
145
149
|
}
|
|
@@ -155,28 +159,28 @@ export function hashCode (word) {
|
|
|
155
159
|
if (word.length === 0) return hash
|
|
156
160
|
for (let i = 0; i < word.length; i++) {
|
|
157
161
|
chr = word.charCodeAt(i)
|
|
158
|
-
hash = (
|
|
162
|
+
hash = (hash << 5) - hash + chr
|
|
159
163
|
hash |= 0 // Convert to 32bit integer
|
|
160
164
|
}
|
|
161
165
|
return hash
|
|
162
166
|
}
|
|
163
167
|
|
|
164
168
|
/**
|
|
165
|
-
* @function module:utilities.decodeUTF8
|
|
166
|
-
* @param {string} argString
|
|
167
|
-
* @returns {string}
|
|
168
|
-
*/
|
|
169
|
+
* @function module:utilities.decodeUTF8
|
|
170
|
+
* @param {string} argString
|
|
171
|
+
* @returns {string}
|
|
172
|
+
*/
|
|
169
173
|
export function decodeUTF8 (argString) {
|
|
170
174
|
return decodeURIComponent(escape(argString))
|
|
171
175
|
}
|
|
172
176
|
|
|
173
177
|
// codedread:does not seem to work with webkit-based browsers on OSX // Brettz9: please test again as function upgraded
|
|
174
178
|
/**
|
|
175
|
-
* @function module:utilities.encodeUTF8
|
|
176
|
-
* @param {string} argString
|
|
177
|
-
* @returns {string}
|
|
178
|
-
*/
|
|
179
|
-
export const encodeUTF8 =
|
|
179
|
+
* @function module:utilities.encodeUTF8
|
|
180
|
+
* @param {string} argString
|
|
181
|
+
* @returns {string}
|
|
182
|
+
*/
|
|
183
|
+
export const encodeUTF8 = argString => {
|
|
180
184
|
return unescape(encodeURIComponent(argString))
|
|
181
185
|
}
|
|
182
186
|
|
|
@@ -186,8 +190,13 @@ export const encodeUTF8 = (argString) => {
|
|
|
186
190
|
* @param {string} dataurl
|
|
187
191
|
* @returns {string} object URL or empty string
|
|
188
192
|
*/
|
|
189
|
-
export const dataURLToObjectURL =
|
|
190
|
-
if (
|
|
193
|
+
export const dataURLToObjectURL = dataurl => {
|
|
194
|
+
if (
|
|
195
|
+
typeof Uint8Array === 'undefined' ||
|
|
196
|
+
typeof Blob === 'undefined' ||
|
|
197
|
+
typeof URL === 'undefined' ||
|
|
198
|
+
!URL.createObjectURL
|
|
199
|
+
) {
|
|
191
200
|
return ''
|
|
192
201
|
}
|
|
193
202
|
const arr = dataurl.split(',')
|
|
@@ -213,7 +222,7 @@ export const dataURLToObjectURL = (dataurl) => {
|
|
|
213
222
|
* @param {Blob} blob A Blob object or File object
|
|
214
223
|
* @returns {string} object URL or empty string
|
|
215
224
|
*/
|
|
216
|
-
export const createObjectURL =
|
|
225
|
+
export const createObjectURL = blob => {
|
|
217
226
|
if (!blob || typeof URL === 'undefined' || !URL.createObjectURL) {
|
|
218
227
|
return ''
|
|
219
228
|
}
|
|
@@ -227,39 +236,43 @@ export const blankPageObjectURL = (() => {
|
|
|
227
236
|
if (typeof Blob === 'undefined') {
|
|
228
237
|
return ''
|
|
229
238
|
}
|
|
230
|
-
const blob = new Blob(
|
|
239
|
+
const blob = new Blob(
|
|
240
|
+
['<html><head><title>SVG-edit</title></head><body> </body></html>'],
|
|
241
|
+
{ type: 'text/html' }
|
|
242
|
+
)
|
|
231
243
|
return createObjectURL(blob)
|
|
232
244
|
})()
|
|
233
245
|
|
|
234
246
|
/**
|
|
235
|
-
* Converts a string to use XML references (for non-ASCII).
|
|
236
|
-
* @function module:utilities.convertToXMLReferences
|
|
237
|
-
* @param {string} input
|
|
238
|
-
* @returns {string} Decimal numeric character references
|
|
239
|
-
*/
|
|
240
|
-
export const convertToXMLReferences =
|
|
241
|
-
let output = ''
|
|
242
|
-
[...input].forEach(
|
|
247
|
+
* Converts a string to use XML references (for non-ASCII).
|
|
248
|
+
* @function module:utilities.convertToXMLReferences
|
|
249
|
+
* @param {string} input
|
|
250
|
+
* @returns {string} Decimal numeric character references
|
|
251
|
+
*/
|
|
252
|
+
export const convertToXMLReferences = input => {
|
|
253
|
+
let output = ''
|
|
254
|
+
;[...input].forEach(ch => {
|
|
243
255
|
const c = ch.charCodeAt()
|
|
244
|
-
output +=
|
|
256
|
+
output += c <= 127 ? ch : `&#${c};`
|
|
245
257
|
})
|
|
246
258
|
return output
|
|
247
259
|
}
|
|
248
260
|
|
|
249
261
|
/**
|
|
250
|
-
* Cross-browser compatible method of converting a string to an XML tree.
|
|
251
|
-
* Found this function [here]{@link http://groups.google.com/group/jquery-dev/browse_thread/thread/c6d11387c580a77f}.
|
|
252
|
-
* @function module:utilities.text2xml
|
|
253
|
-
* @param {string} sXML
|
|
254
|
-
* @throws {Error}
|
|
255
|
-
* @returns {XMLDocument}
|
|
256
|
-
*/
|
|
257
|
-
export const text2xml =
|
|
262
|
+
* Cross-browser compatible method of converting a string to an XML tree.
|
|
263
|
+
* Found this function [here]{@link http://groups.google.com/group/jquery-dev/browse_thread/thread/c6d11387c580a77f}.
|
|
264
|
+
* @function module:utilities.text2xml
|
|
265
|
+
* @param {string} sXML
|
|
266
|
+
* @throws {Error}
|
|
267
|
+
* @returns {XMLDocument}
|
|
268
|
+
*/
|
|
269
|
+
export const text2xml = sXML => {
|
|
258
270
|
if (sXML.includes('<svg:svg')) {
|
|
259
271
|
sXML = sXML.replace(/<(\/?)svg:/g, '<$1').replace('xmlns:svg', 'xmlns')
|
|
260
272
|
}
|
|
261
273
|
|
|
262
|
-
let out
|
|
274
|
+
let out
|
|
275
|
+
let dXML
|
|
263
276
|
try {
|
|
264
277
|
dXML = new DOMParser()
|
|
265
278
|
dXML.async = false
|
|
@@ -268,41 +281,43 @@ export const text2xml = (sXML) => {
|
|
|
268
281
|
}
|
|
269
282
|
try {
|
|
270
283
|
out = dXML.parseFromString(sXML, 'text/xml')
|
|
271
|
-
} catch (e2) {
|
|
284
|
+
} catch (e2) {
|
|
285
|
+
throw new Error('Error parsing XML string')
|
|
286
|
+
}
|
|
272
287
|
return out
|
|
273
288
|
}
|
|
274
289
|
|
|
275
290
|
/**
|
|
276
|
-
* @typedef {PlainObject} module:utilities.BBoxObject (like `DOMRect`)
|
|
277
|
-
* @property {Float} x
|
|
278
|
-
* @property {Float} y
|
|
279
|
-
* @property {Float} width
|
|
280
|
-
* @property {Float} height
|
|
281
|
-
*/
|
|
291
|
+
* @typedef {PlainObject} module:utilities.BBoxObject (like `DOMRect`)
|
|
292
|
+
* @property {Float} x
|
|
293
|
+
* @property {Float} y
|
|
294
|
+
* @property {Float} width
|
|
295
|
+
* @property {Float} height
|
|
296
|
+
*/
|
|
282
297
|
|
|
283
298
|
/**
|
|
284
|
-
* Converts a `SVGRect` into an object.
|
|
285
|
-
* @function module:utilities.bboxToObj
|
|
286
|
-
* @param {SVGRect} bbox - a SVGRect
|
|
287
|
-
* @returns {module:utilities.BBoxObject} An object with properties names x, y, width, height.
|
|
288
|
-
*/
|
|
299
|
+
* Converts a `SVGRect` into an object.
|
|
300
|
+
* @function module:utilities.bboxToObj
|
|
301
|
+
* @param {SVGRect} bbox - a SVGRect
|
|
302
|
+
* @returns {module:utilities.BBoxObject} An object with properties names x, y, width, height.
|
|
303
|
+
*/
|
|
289
304
|
export const bboxToObj = ({ x, y, width, height }) => {
|
|
290
305
|
return { x, y, width, height }
|
|
291
306
|
}
|
|
292
307
|
|
|
293
308
|
/**
|
|
294
|
-
* @callback module:utilities.TreeWalker
|
|
295
|
-
* @param {Element} elem - DOM element being traversed
|
|
296
|
-
* @returns {void}
|
|
297
|
-
*/
|
|
309
|
+
* @callback module:utilities.TreeWalker
|
|
310
|
+
* @param {Element} elem - DOM element being traversed
|
|
311
|
+
* @returns {void}
|
|
312
|
+
*/
|
|
298
313
|
|
|
299
314
|
/**
|
|
300
|
-
* Walks the tree and executes the callback on each element in a top-down fashion.
|
|
301
|
-
* @function module:utilities.walkTree
|
|
302
|
-
* @param {Element} elem - DOM element to traverse
|
|
303
|
-
* @param {module:utilities.TreeWalker} cbFn - Callback function to run on each element
|
|
304
|
-
* @returns {void}
|
|
305
|
-
*/
|
|
315
|
+
* Walks the tree and executes the callback on each element in a top-down fashion.
|
|
316
|
+
* @function module:utilities.walkTree
|
|
317
|
+
* @param {Element} elem - DOM element to traverse
|
|
318
|
+
* @param {module:utilities.TreeWalker} cbFn - Callback function to run on each element
|
|
319
|
+
* @returns {void}
|
|
320
|
+
*/
|
|
306
321
|
export const walkTree = (elem, cbFn) => {
|
|
307
322
|
if (elem?.nodeType === 1) {
|
|
308
323
|
cbFn(elem)
|
|
@@ -314,13 +329,13 @@ export const walkTree = (elem, cbFn) => {
|
|
|
314
329
|
}
|
|
315
330
|
|
|
316
331
|
/**
|
|
317
|
-
* Walks the tree and executes the callback on each element in a depth-first fashion.
|
|
318
|
-
* @function module:utilities.walkTreePost
|
|
319
|
-
* @todo Shouldn't this be calling walkTreePost?
|
|
320
|
-
* @param {Element} elem - DOM element to traverse
|
|
321
|
-
* @param {module:utilities.TreeWalker} cbFn - Callback function to run on each element
|
|
322
|
-
* @returns {void}
|
|
323
|
-
*/
|
|
332
|
+
* Walks the tree and executes the callback on each element in a depth-first fashion.
|
|
333
|
+
* @function module:utilities.walkTreePost
|
|
334
|
+
* @todo Shouldn't this be calling walkTreePost?
|
|
335
|
+
* @param {Element} elem - DOM element to traverse
|
|
336
|
+
* @param {module:utilities.TreeWalker} cbFn - Callback function to run on each element
|
|
337
|
+
* @returns {void}
|
|
338
|
+
*/
|
|
324
339
|
export const walkTreePost = (elem, cbFn) => {
|
|
325
340
|
if (elem?.nodeType === 1) {
|
|
326
341
|
let i = elem.childNodes.length
|
|
@@ -332,15 +347,15 @@ export const walkTreePost = (elem, cbFn) => {
|
|
|
332
347
|
}
|
|
333
348
|
|
|
334
349
|
/**
|
|
335
|
-
* Extracts the URL from the `url(...)` syntax of some attributes.
|
|
336
|
-
* Three variants:
|
|
337
|
-
* - `<circle fill="url(someFile.svg#foo)" />`
|
|
338
|
-
* - `<circle fill="url('someFile.svg#foo')" />`
|
|
339
|
-
* - `<circle fill='url("someFile.svg#foo")' />`
|
|
340
|
-
* @function module:utilities.getUrlFromAttr
|
|
341
|
-
* @param {string} attrVal The attribute value as a string
|
|
342
|
-
* @returns {string} String with just the URL, like "someFile.svg#foo"
|
|
343
|
-
*/
|
|
350
|
+
* Extracts the URL from the `url(...)` syntax of some attributes.
|
|
351
|
+
* Three variants:
|
|
352
|
+
* - `<circle fill="url(someFile.svg#foo)" />`
|
|
353
|
+
* - `<circle fill="url('someFile.svg#foo')" />`
|
|
354
|
+
* - `<circle fill='url("someFile.svg#foo")' />`
|
|
355
|
+
* @function module:utilities.getUrlFromAttr
|
|
356
|
+
* @param {string} attrVal The attribute value as a string
|
|
357
|
+
* @returns {string} String with just the URL, like "someFile.svg#foo"
|
|
358
|
+
*/
|
|
344
359
|
export const getUrlFromAttr = function (attrVal) {
|
|
345
360
|
if (attrVal) {
|
|
346
361
|
// url('#somegrad')
|
|
@@ -359,29 +374,29 @@ export const getUrlFromAttr = function (attrVal) {
|
|
|
359
374
|
}
|
|
360
375
|
|
|
361
376
|
/**
|
|
362
|
-
* @function module:utilities.getHref
|
|
363
|
-
* @param {Element} elem
|
|
364
|
-
* @returns {string} The given element's `xlink:href` value
|
|
365
|
-
*/
|
|
377
|
+
* @function module:utilities.getHref
|
|
378
|
+
* @param {Element} elem
|
|
379
|
+
* @returns {string} The given element's `xlink:href` value
|
|
380
|
+
*/
|
|
366
381
|
export let getHref = function (elem) {
|
|
367
382
|
return elem.getAttributeNS(NS.XLINK, 'href')
|
|
368
383
|
}
|
|
369
384
|
|
|
370
385
|
/**
|
|
371
|
-
* Sets the given element's `xlink:href` value.
|
|
372
|
-
* @function module:utilities.setHref
|
|
373
|
-
* @param {Element} elem
|
|
374
|
-
* @param {string} val
|
|
375
|
-
* @returns {void}
|
|
376
|
-
*/
|
|
386
|
+
* Sets the given element's `xlink:href` value.
|
|
387
|
+
* @function module:utilities.setHref
|
|
388
|
+
* @param {Element} elem
|
|
389
|
+
* @param {string} val
|
|
390
|
+
* @returns {void}
|
|
391
|
+
*/
|
|
377
392
|
export let setHref = function (elem, val) {
|
|
378
393
|
elem.setAttributeNS(NS.XLINK, 'xlink:href', val)
|
|
379
394
|
}
|
|
380
395
|
|
|
381
396
|
/**
|
|
382
|
-
* @function module:utilities.findDefs
|
|
383
|
-
* @returns {SVGDefsElement} The document's `<defs>` element, creating it first if necessary
|
|
384
|
-
*/
|
|
397
|
+
* @function module:utilities.findDefs
|
|
398
|
+
* @returns {SVGDefsElement} The document's `<defs>` element, creating it first if necessary
|
|
399
|
+
*/
|
|
385
400
|
export const findDefs = function () {
|
|
386
401
|
const svgElement = svgCanvas.getSvgContent()
|
|
387
402
|
let defs = svgElement.getElementsByTagNameNS(NS.SVG, 'defs')
|
|
@@ -403,12 +418,12 @@ export const findDefs = function () {
|
|
|
403
418
|
// TODO(codedread): Consider moving the next to functions to bbox.js
|
|
404
419
|
|
|
405
420
|
/**
|
|
406
|
-
* Get correct BBox for a path in Webkit.
|
|
407
|
-
* Converted from code found [here]{@link http://blog.hackers-cafe.net/2009/06/how-to-calculate-bezier-curves-bounding.html}.
|
|
408
|
-
* @function module:utilities.getPathBBox
|
|
409
|
-
* @param {SVGPathElement} path - The path DOM element to get the BBox for
|
|
410
|
-
* @returns {module:utilities.BBoxObject} A BBox-like object
|
|
411
|
-
*/
|
|
421
|
+
* Get correct BBox for a path in Webkit.
|
|
422
|
+
* Converted from code found [here]{@link http://blog.hackers-cafe.net/2009/06/how-to-calculate-bezier-curves-bounding.html}.
|
|
423
|
+
* @function module:utilities.getPathBBox
|
|
424
|
+
* @param {SVGPathElement} path - The path DOM element to get the BBox for
|
|
425
|
+
* @returns {module:utilities.BBoxObject} A BBox-like object
|
|
426
|
+
*/
|
|
412
427
|
export const getPathBBox = function (path) {
|
|
413
428
|
const seglist = path.pathSegList
|
|
414
429
|
const tot = seglist.numberOfItems
|
|
@@ -419,17 +434,23 @@ export const getPathBBox = function (path) {
|
|
|
419
434
|
|
|
420
435
|
const getCalc = function (j, P1, P2, P3) {
|
|
421
436
|
return function (t) {
|
|
422
|
-
return
|
|
423
|
-
|
|
437
|
+
return (
|
|
438
|
+
1 -
|
|
439
|
+
t ** 3 * P0[j] +
|
|
440
|
+
3 * 1 -
|
|
441
|
+
t ** 2 * t * P1[j] +
|
|
424
442
|
3 * (1 - t) * t ** 2 * P2[j] +
|
|
425
443
|
t ** 3 * P3[j]
|
|
444
|
+
)
|
|
426
445
|
}
|
|
427
446
|
}
|
|
428
447
|
|
|
429
448
|
for (let i = 0; i < tot; i++) {
|
|
430
449
|
const seg = seglist.getItem(i)
|
|
431
450
|
|
|
432
|
-
if (seg.x === undefined) {
|
|
451
|
+
if (seg.x === undefined) {
|
|
452
|
+
continue
|
|
453
|
+
}
|
|
433
454
|
|
|
434
455
|
// Add actual points to limits
|
|
435
456
|
bounds[0].push(P0[0])
|
|
@@ -448,7 +469,9 @@ export const getPathBBox = function (path) {
|
|
|
448
469
|
const c = 3 * P1[j] - 3 * P0[j]
|
|
449
470
|
|
|
450
471
|
if (a === 0) {
|
|
451
|
-
if (b === 0) {
|
|
472
|
+
if (b === 0) {
|
|
473
|
+
continue
|
|
474
|
+
}
|
|
452
475
|
const t = -c / b
|
|
453
476
|
if (t > 0 && t < 1) {
|
|
454
477
|
bounds[j].push(calc(t))
|
|
@@ -456,11 +479,17 @@ export const getPathBBox = function (path) {
|
|
|
456
479
|
continue
|
|
457
480
|
}
|
|
458
481
|
const b2ac = b ** 2 - 4 * c * a
|
|
459
|
-
if (b2ac < 0) {
|
|
482
|
+
if (b2ac < 0) {
|
|
483
|
+
continue
|
|
484
|
+
}
|
|
460
485
|
const t1 = (-b + Math.sqrt(b2ac)) / (2 * a)
|
|
461
|
-
if (t1 > 0 && t1 < 1) {
|
|
486
|
+
if (t1 > 0 && t1 < 1) {
|
|
487
|
+
bounds[j].push(calc(t1))
|
|
488
|
+
}
|
|
462
489
|
const t2 = (-b - Math.sqrt(b2ac)) / (2 * a)
|
|
463
|
-
if (t2 > 0 && t2 < 1) {
|
|
490
|
+
if (t2 > 0 && t2 < 1) {
|
|
491
|
+
bounds[j].push(calc(t2))
|
|
492
|
+
}
|
|
464
493
|
}
|
|
465
494
|
P0 = P3
|
|
466
495
|
} else {
|
|
@@ -482,15 +511,17 @@ export const getPathBBox = function (path) {
|
|
|
482
511
|
}
|
|
483
512
|
|
|
484
513
|
/**
|
|
485
|
-
* Get the given/selected element's bounding box object, convert it to be more
|
|
486
|
-
* usable when necessary.
|
|
487
|
-
* @function module:utilities.getBBox
|
|
488
|
-
* @param {Element} elem - Optional DOM element to get the BBox for
|
|
489
|
-
* @returns {module:utilities.BBoxObject} Bounding box object
|
|
490
|
-
*/
|
|
514
|
+
* Get the given/selected element's bounding box object, convert it to be more
|
|
515
|
+
* usable when necessary.
|
|
516
|
+
* @function module:utilities.getBBox
|
|
517
|
+
* @param {Element} elem - Optional DOM element to get the BBox for
|
|
518
|
+
* @returns {module:utilities.BBoxObject} Bounding box object
|
|
519
|
+
*/
|
|
491
520
|
export const getBBox = function (elem) {
|
|
492
521
|
const selected = elem || svgCanvas.getSelectedElements()[0]
|
|
493
|
-
if (elem.nodeType !== 1) {
|
|
522
|
+
if (elem.nodeType !== 1) {
|
|
523
|
+
return null
|
|
524
|
+
}
|
|
494
525
|
const elname = selected.nodeName
|
|
495
526
|
|
|
496
527
|
let ret = null
|
|
@@ -512,7 +543,6 @@ export const getBBox = function (elem) {
|
|
|
512
543
|
}
|
|
513
544
|
break
|
|
514
545
|
default:
|
|
515
|
-
|
|
516
546
|
if (elname === 'use') {
|
|
517
547
|
ret = selected.getBBox() // , true);
|
|
518
548
|
} else if (visElemsArr.includes(elname)) {
|
|
@@ -520,8 +550,8 @@ export const getBBox = function (elem) {
|
|
|
520
550
|
try {
|
|
521
551
|
ret = selected.getBBox()
|
|
522
552
|
} catch (err) {
|
|
523
|
-
|
|
524
|
-
|
|
553
|
+
// tspan (and textPath apparently) have no `getBBox` in Firefox: https://bugzilla.mozilla.org/show_bug.cgi?id=937268
|
|
554
|
+
// Re: Chrome returning bbox for containing text element, see: https://bugs.chromium.org/p/chromium/issues/detail?id=349835
|
|
525
555
|
const extent = selected.getExtentOfChar(0) // pos+dimensions of the first glyph
|
|
526
556
|
const width = selected.getComputedTextLength() // width of the tspan
|
|
527
557
|
ret = {
|
|
@@ -532,7 +562,7 @@ export const getBBox = function (elem) {
|
|
|
532
562
|
}
|
|
533
563
|
}
|
|
534
564
|
} else {
|
|
535
|
-
|
|
565
|
+
// Check if element is child of a foreignObject
|
|
536
566
|
const fo = getClosest(selected.parentNode, 'foreignObject')
|
|
537
567
|
if (fo.length && fo[0].getBBox) {
|
|
538
568
|
ret = fo[0].getBBox()
|
|
@@ -549,26 +579,26 @@ export const getBBox = function (elem) {
|
|
|
549
579
|
}
|
|
550
580
|
|
|
551
581
|
/**
|
|
552
|
-
* @typedef {GenericArray} module:utilities.PathSegmentArray
|
|
553
|
-
* @property {Integer} length 2
|
|
554
|
-
* @property {"M"|"L"|"C"|"Z"} 0
|
|
555
|
-
* @property {Float[]} 1
|
|
556
|
-
*/
|
|
582
|
+
* @typedef {GenericArray} module:utilities.PathSegmentArray
|
|
583
|
+
* @property {Integer} length 2
|
|
584
|
+
* @property {"M"|"L"|"C"|"Z"} 0
|
|
585
|
+
* @property {Float[]} 1
|
|
586
|
+
*/
|
|
557
587
|
|
|
558
588
|
/**
|
|
559
|
-
* Create a path 'd' attribute from path segments.
|
|
560
|
-
* Each segment is an array of the form: `[singleChar, [x,y, x,y, ...]]`
|
|
561
|
-
* @function module:utilities.getPathDFromSegments
|
|
562
|
-
* @param {module:utilities.PathSegmentArray[]} pathSegments - An array of path segments to be converted
|
|
563
|
-
* @returns {string} The converted path d attribute.
|
|
564
|
-
*/
|
|
589
|
+
* Create a path 'd' attribute from path segments.
|
|
590
|
+
* Each segment is an array of the form: `[singleChar, [x,y, x,y, ...]]`
|
|
591
|
+
* @function module:utilities.getPathDFromSegments
|
|
592
|
+
* @param {module:utilities.PathSegmentArray[]} pathSegments - An array of path segments to be converted
|
|
593
|
+
* @returns {string} The converted path d attribute.
|
|
594
|
+
*/
|
|
565
595
|
export const getPathDFromSegments = function (pathSegments) {
|
|
566
596
|
let d = ''
|
|
567
597
|
|
|
568
598
|
pathSegments.forEach(function ([singleChar, pts], _j) {
|
|
569
599
|
d += singleChar
|
|
570
600
|
for (let i = 0; i < pts.length; i += 2) {
|
|
571
|
-
d +=
|
|
601
|
+
d += pts[i] + ',' + pts[i + 1] + ' '
|
|
572
602
|
}
|
|
573
603
|
})
|
|
574
604
|
|
|
@@ -576,15 +606,17 @@ export const getPathDFromSegments = function (pathSegments) {
|
|
|
576
606
|
}
|
|
577
607
|
|
|
578
608
|
/**
|
|
579
|
-
* Make a path 'd' attribute from a simple SVG element shape.
|
|
580
|
-
* @function module:utilities.getPathDFromElement
|
|
581
|
-
* @param {Element} elem - The element to be converted
|
|
582
|
-
* @returns {string} The path d attribute or `undefined` if the element type is unknown.
|
|
583
|
-
*/
|
|
609
|
+
* Make a path 'd' attribute from a simple SVG element shape.
|
|
610
|
+
* @function module:utilities.getPathDFromElement
|
|
611
|
+
* @param {Element} elem - The element to be converted
|
|
612
|
+
* @returns {string} The path d attribute or `undefined` if the element type is unknown.
|
|
613
|
+
*/
|
|
584
614
|
export const getPathDFromElement = function (elem) {
|
|
585
615
|
// Possibly the cubed root of 6, but 1.81 works best
|
|
586
616
|
let num = 1.81
|
|
587
|
-
let d
|
|
617
|
+
let d
|
|
618
|
+
let rx
|
|
619
|
+
let ry
|
|
588
620
|
switch (elem.tagName) {
|
|
589
621
|
case 'ellipse':
|
|
590
622
|
case 'circle': {
|
|
@@ -597,24 +629,26 @@ export const getPathDFromElement = function (elem) {
|
|
|
597
629
|
rx = ry
|
|
598
630
|
}
|
|
599
631
|
d = getPathDFromSegments([
|
|
600
|
-
['M', [
|
|
601
|
-
['C', [
|
|
602
|
-
['C', [
|
|
603
|
-
['C', [
|
|
604
|
-
['C', [
|
|
632
|
+
['M', [cx - rx, cy]],
|
|
633
|
+
['C', [cx - rx, cy - ry / num, cx - rx / num, cy - ry, cx, cy - ry]],
|
|
634
|
+
['C', [cx + rx / num, cy - ry, cx + rx, cy - ry / num, cx + rx, cy]],
|
|
635
|
+
['C', [cx + rx, cy + ry / num, cx + rx / num, cy + ry, cx, cy + ry]],
|
|
636
|
+
['C', [cx - rx / num, cy + ry, cx - rx, cy + ry / num, cx - rx, cy]],
|
|
605
637
|
['Z', []]
|
|
606
638
|
])
|
|
607
639
|
break
|
|
608
|
-
}
|
|
640
|
+
}
|
|
641
|
+
case 'path':
|
|
609
642
|
d = elem.getAttribute('d')
|
|
610
643
|
break
|
|
611
|
-
case 'line':
|
|
612
|
-
|
|
613
|
-
|
|
614
|
-
|
|
615
|
-
|
|
616
|
-
|
|
617
|
-
|
|
644
|
+
case 'line':
|
|
645
|
+
{
|
|
646
|
+
const x1 = elem.getAttribute('x1')
|
|
647
|
+
const y1 = elem.getAttribute('y1')
|
|
648
|
+
const x2 = elem.getAttribute('x2')
|
|
649
|
+
const y2 = elem.getAttribute('y2')
|
|
650
|
+
d = 'M' + x1 + ',' + y1 + 'L' + x2 + ',' + y2
|
|
651
|
+
}
|
|
618
652
|
break
|
|
619
653
|
case 'polyline':
|
|
620
654
|
d = 'M' + elem.getAttribute('points')
|
|
@@ -647,14 +681,25 @@ export const getPathDFromElement = function (elem) {
|
|
|
647
681
|
['L', [x + w - rx, y]],
|
|
648
682
|
['C', [x + w - rx / num, y, x + w, y + ry / num, x + w, y + ry]],
|
|
649
683
|
['L', [x + w, y + h - ry]],
|
|
650
|
-
[
|
|
684
|
+
[
|
|
685
|
+
'C',
|
|
686
|
+
[
|
|
687
|
+
x + w,
|
|
688
|
+
y + h - ry / num,
|
|
689
|
+
x + w - rx / num,
|
|
690
|
+
y + h,
|
|
691
|
+
x + w - rx,
|
|
692
|
+
y + h
|
|
693
|
+
]
|
|
694
|
+
],
|
|
651
695
|
['L', [x + rx, y + h]],
|
|
652
696
|
['C', [x + rx / num, y + h, x, y + h - ry / num, x, y + h - ry]],
|
|
653
697
|
['L', [x, y + ry]],
|
|
654
698
|
['Z', []]
|
|
655
699
|
])
|
|
656
700
|
break
|
|
657
|
-
}
|
|
701
|
+
}
|
|
702
|
+
default:
|
|
658
703
|
break
|
|
659
704
|
}
|
|
660
705
|
|
|
@@ -662,33 +707,39 @@ export const getPathDFromElement = function (elem) {
|
|
|
662
707
|
}
|
|
663
708
|
|
|
664
709
|
/**
|
|
665
|
-
* Get a set of attributes from an element that is useful for convertToPath.
|
|
666
|
-
* @function module:utilities.getExtraAttributesForConvertToPath
|
|
667
|
-
* @param {Element} elem - The element to be probed
|
|
668
|
-
* @returns {PlainObject<"marker-start"|"marker-end"|"marker-mid"|"filter"|"clip-path", string>} An object with attributes.
|
|
669
|
-
*/
|
|
710
|
+
* Get a set of attributes from an element that is useful for convertToPath.
|
|
711
|
+
* @function module:utilities.getExtraAttributesForConvertToPath
|
|
712
|
+
* @param {Element} elem - The element to be probed
|
|
713
|
+
* @returns {PlainObject<"marker-start"|"marker-end"|"marker-mid"|"filter"|"clip-path", string>} An object with attributes.
|
|
714
|
+
*/
|
|
670
715
|
export const getExtraAttributesForConvertToPath = function (elem) {
|
|
671
|
-
const attrs = {}
|
|
716
|
+
const attrs = {}
|
|
672
717
|
// TODO: make this list global so that we can properly maintain it
|
|
673
718
|
// TODO: what about @transform, @clip-rule, @fill-rule, etc?
|
|
674
|
-
['marker-start', 'marker-end', 'marker-mid', 'filter', 'clip-path'].forEach(
|
|
675
|
-
|
|
676
|
-
|
|
677
|
-
|
|
719
|
+
;['marker-start', 'marker-end', 'marker-mid', 'filter', 'clip-path'].forEach(
|
|
720
|
+
function (item) {
|
|
721
|
+
const a = elem.getAttribute(item)
|
|
722
|
+
if (a) {
|
|
723
|
+
attrs[item] = a
|
|
724
|
+
}
|
|
678
725
|
}
|
|
679
|
-
|
|
726
|
+
)
|
|
680
727
|
return attrs
|
|
681
728
|
}
|
|
682
729
|
|
|
683
730
|
/**
|
|
684
|
-
* Get the BBox of an element-as-path.
|
|
685
|
-
* @function module:utilities.getBBoxOfElementAsPath
|
|
686
|
-
* @param {Element} elem - The DOM element to be probed
|
|
687
|
-
* @param {module:utilities.EditorContext#addSVGElementsFromJson} addSVGElementsFromJson - Function to add the path element to the current layer. See canvas.addSVGElementsFromJson
|
|
688
|
-
* @param {module:path.pathActions} pathActions - If a transform exists, `pathActions.resetOrientation()` is used. See: canvas.pathActions.
|
|
689
|
-
* @returns {DOMRect|false} The resulting path's bounding box object.
|
|
690
|
-
*/
|
|
691
|
-
export const getBBoxOfElementAsPath = function (
|
|
731
|
+
* Get the BBox of an element-as-path.
|
|
732
|
+
* @function module:utilities.getBBoxOfElementAsPath
|
|
733
|
+
* @param {Element} elem - The DOM element to be probed
|
|
734
|
+
* @param {module:utilities.EditorContext#addSVGElementsFromJson} addSVGElementsFromJson - Function to add the path element to the current layer. See canvas.addSVGElementsFromJson
|
|
735
|
+
* @param {module:path.pathActions} pathActions - If a transform exists, `pathActions.resetOrientation()` is used. See: canvas.pathActions.
|
|
736
|
+
* @returns {DOMRect|false} The resulting path's bounding box object.
|
|
737
|
+
*/
|
|
738
|
+
export const getBBoxOfElementAsPath = function (
|
|
739
|
+
elem,
|
|
740
|
+
addSVGElementsFromJson,
|
|
741
|
+
pathActions
|
|
742
|
+
) {
|
|
692
743
|
const path = addSVGElementsFromJson({
|
|
693
744
|
element: 'path',
|
|
694
745
|
attr: getExtraAttributesForConvertToPath(elem)
|
|
@@ -726,18 +777,18 @@ export const getBBoxOfElementAsPath = function (elem, addSVGElementsFromJson, pa
|
|
|
726
777
|
}
|
|
727
778
|
|
|
728
779
|
/**
|
|
729
|
-
* Convert selected element to a path.
|
|
730
|
-
* @function module:utilities.convertToPath
|
|
731
|
-
* @param {Element} elem - The DOM element to be converted
|
|
732
|
-
* @param {module:utilities.SVGElementJSON} attrs - Apply attributes to new path. see canvas.convertToPath
|
|
733
|
-
* @param {module:utilities.EditorContext#addSVGElementsFromJson} addSVGElementsFromJson - Function to add the path element to the current layer. See canvas.addSVGElementsFromJson
|
|
734
|
-
* @param {module:path.pathActions} pathActions - If a transform exists, pathActions.resetOrientation() is used. See: canvas.pathActions.
|
|
735
|
-
* @param {module:draw.DrawCanvasInit#clearSelection|module:path.EditorContext#clearSelection} clearSelection - see [canvas.clearSelection]{@link module:svgcanvas.SvgCanvas#clearSelection}
|
|
736
|
-
* @param {module:path.EditorContext#addToSelection} addToSelection - see [canvas.addToSelection]{@link module:svgcanvas.SvgCanvas#addToSelection}
|
|
737
|
-
* @param {module:history} hstry - see history module
|
|
738
|
-
* @param {module:path.EditorContext#addCommandToHistory|module:draw.DrawCanvasInit#addCommandToHistory} addCommandToHistory - see [canvas.addCommandToHistory]{@link module:svgcanvas~addCommandToHistory}
|
|
739
|
-
* @returns {SVGPathElement|null} The converted path element or null if the DOM element was not recognized.
|
|
740
|
-
*/
|
|
780
|
+
* Convert selected element to a path.
|
|
781
|
+
* @function module:utilities.convertToPath
|
|
782
|
+
* @param {Element} elem - The DOM element to be converted
|
|
783
|
+
* @param {module:utilities.SVGElementJSON} attrs - Apply attributes to new path. see canvas.convertToPath
|
|
784
|
+
* @param {module:utilities.EditorContext#addSVGElementsFromJson} addSVGElementsFromJson - Function to add the path element to the current layer. See canvas.addSVGElementsFromJson
|
|
785
|
+
* @param {module:path.pathActions} pathActions - If a transform exists, pathActions.resetOrientation() is used. See: canvas.pathActions.
|
|
786
|
+
* @param {module:draw.DrawCanvasInit#clearSelection|module:path.EditorContext#clearSelection} clearSelection - see [canvas.clearSelection]{@link module:svgcanvas.SvgCanvas#clearSelection}
|
|
787
|
+
* @param {module:path.EditorContext#addToSelection} addToSelection - see [canvas.addToSelection]{@link module:svgcanvas.SvgCanvas#addToSelection}
|
|
788
|
+
* @param {module:history} hstry - see history module
|
|
789
|
+
* @param {module:path.EditorContext#addCommandToHistory|module:draw.DrawCanvasInit#addCommandToHistory} addCommandToHistory - see [canvas.addCommandToHistory]{@link module:svgcanvas~addCommandToHistory}
|
|
790
|
+
* @returns {SVGPathElement|null} The converted path element or null if the DOM element was not recognized.
|
|
791
|
+
*/
|
|
741
792
|
export const convertToPath = (elem, attrs, svgCanvas) => {
|
|
742
793
|
const batchCmd = new svgCanvas.history.BatchCommand('Convert element to Path')
|
|
743
794
|
|
|
@@ -770,14 +821,20 @@ export const convertToPath = (elem, attrs, svgCanvas) => {
|
|
|
770
821
|
|
|
771
822
|
// Reorient if it has a matrix
|
|
772
823
|
if (eltrans) {
|
|
773
|
-
const tlist = path
|
|
824
|
+
const tlist = getTransformList(path)
|
|
774
825
|
if (hasMatrixTransform(tlist)) {
|
|
775
826
|
svgCanvas.pathActions.resetOrientation(path)
|
|
776
827
|
}
|
|
777
828
|
}
|
|
778
829
|
|
|
779
830
|
const { nextSibling } = elem
|
|
780
|
-
batchCmd.addSubCommand(
|
|
831
|
+
batchCmd.addSubCommand(
|
|
832
|
+
new svgCanvas.history.RemoveElementCommand(
|
|
833
|
+
elem,
|
|
834
|
+
nextSibling,
|
|
835
|
+
elem.parentNode
|
|
836
|
+
)
|
|
837
|
+
)
|
|
781
838
|
svgCanvas.clearSelection()
|
|
782
839
|
elem.remove() // We need to remove this element otherwise the nextSibling of 'path' won't be null and an exception will be thrown after subsequent undo and redos.
|
|
783
840
|
|
|
@@ -796,25 +853,25 @@ export const convertToPath = (elem, attrs, svgCanvas) => {
|
|
|
796
853
|
}
|
|
797
854
|
|
|
798
855
|
/**
|
|
799
|
-
* Can the bbox be optimized over the native getBBox? The optimized bbox is the same as the native getBBox when
|
|
800
|
-
* the rotation angle is a multiple of 90 degrees and there are no complex transforms.
|
|
801
|
-
* Getting an optimized bbox can be dramatically slower, so we want to make sure it's worth it.
|
|
802
|
-
*
|
|
803
|
-
* The best example for this is a circle rotate 45 degrees. The circle doesn't get wider or taller when rotated
|
|
804
|
-
* about it's center.
|
|
805
|
-
*
|
|
806
|
-
* The standard, unoptimized technique gets the native bbox of the circle, rotates the box 45 degrees, uses
|
|
807
|
-
* that width and height, and applies any transforms to get the final bbox. This means the calculated bbox
|
|
808
|
-
* is much wider than the original circle. If the angle had been 0, 90, 180, etc. both techniques render the
|
|
809
|
-
* same bbox.
|
|
810
|
-
*
|
|
811
|
-
* The optimization is not needed if the rotation is a multiple 90 degrees. The default technique is to call
|
|
812
|
-
* getBBox then apply the angle and any transforms.
|
|
813
|
-
*
|
|
814
|
-
* @param {Float} angle - The rotation angle in degrees
|
|
815
|
-
* @param {boolean} hasAMatrixTransform - True if there is a matrix transform
|
|
816
|
-
* @returns {boolean} True if the bbox can be optimized.
|
|
817
|
-
*/
|
|
856
|
+
* Can the bbox be optimized over the native getBBox? The optimized bbox is the same as the native getBBox when
|
|
857
|
+
* the rotation angle is a multiple of 90 degrees and there are no complex transforms.
|
|
858
|
+
* Getting an optimized bbox can be dramatically slower, so we want to make sure it's worth it.
|
|
859
|
+
*
|
|
860
|
+
* The best example for this is a circle rotate 45 degrees. The circle doesn't get wider or taller when rotated
|
|
861
|
+
* about it's center.
|
|
862
|
+
*
|
|
863
|
+
* The standard, unoptimized technique gets the native bbox of the circle, rotates the box 45 degrees, uses
|
|
864
|
+
* that width and height, and applies any transforms to get the final bbox. This means the calculated bbox
|
|
865
|
+
* is much wider than the original circle. If the angle had been 0, 90, 180, etc. both techniques render the
|
|
866
|
+
* same bbox.
|
|
867
|
+
*
|
|
868
|
+
* The optimization is not needed if the rotation is a multiple 90 degrees. The default technique is to call
|
|
869
|
+
* getBBox then apply the angle and any transforms.
|
|
870
|
+
*
|
|
871
|
+
* @param {Float} angle - The rotation angle in degrees
|
|
872
|
+
* @param {boolean} hasAMatrixTransform - True if there is a matrix transform
|
|
873
|
+
* @returns {boolean} True if the bbox can be optimized.
|
|
874
|
+
*/
|
|
818
875
|
function bBoxCanBeOptimizedOverNativeGetBBox (angle, hasAMatrixTransform) {
|
|
819
876
|
const angleModulo90 = angle % 90
|
|
820
877
|
const closeTo90 = angleModulo90 < -89.99 || angleModulo90 > 89.99
|
|
@@ -823,14 +880,18 @@ function bBoxCanBeOptimizedOverNativeGetBBox (angle, hasAMatrixTransform) {
|
|
|
823
880
|
}
|
|
824
881
|
|
|
825
882
|
/**
|
|
826
|
-
* Get bounding box that includes any transforms.
|
|
827
|
-
* @function module:utilities.getBBoxWithTransform
|
|
828
|
-
* @param {Element} elem - The DOM element to be converted
|
|
829
|
-
* @param {module:utilities.EditorContext#addSVGElementsFromJson} addSVGElementsFromJson - Function to add the path element to the current layer. See canvas.addSVGElementsFromJson
|
|
830
|
-
* @param {module:path.pathActions} pathActions - If a transform exists, pathActions.resetOrientation() is used. See: canvas.pathActions.
|
|
831
|
-
* @returns {module:utilities.BBoxObject|module:math.TransformedBox|DOMRect} A single bounding box object
|
|
832
|
-
*/
|
|
833
|
-
export const getBBoxWithTransform = function (
|
|
883
|
+
* Get bounding box that includes any transforms.
|
|
884
|
+
* @function module:utilities.getBBoxWithTransform
|
|
885
|
+
* @param {Element} elem - The DOM element to be converted
|
|
886
|
+
* @param {module:utilities.EditorContext#addSVGElementsFromJson} addSVGElementsFromJson - Function to add the path element to the current layer. See canvas.addSVGElementsFromJson
|
|
887
|
+
* @param {module:path.pathActions} pathActions - If a transform exists, pathActions.resetOrientation() is used. See: canvas.pathActions.
|
|
888
|
+
* @returns {module:utilities.BBoxObject|module:math.TransformedBox|DOMRect} A single bounding box object
|
|
889
|
+
*/
|
|
890
|
+
export const getBBoxWithTransform = function (
|
|
891
|
+
elem,
|
|
892
|
+
addSVGElementsFromJson,
|
|
893
|
+
pathActions
|
|
894
|
+
) {
|
|
834
895
|
// TODO: Fix issue with rotated groups. Currently they work
|
|
835
896
|
// fine in FF, but not in other browsers (same problem mentioned
|
|
836
897
|
// in Issue 339 comment #2).
|
|
@@ -841,7 +902,7 @@ export const getBBoxWithTransform = function (elem, addSVGElementsFromJson, path
|
|
|
841
902
|
return null
|
|
842
903
|
}
|
|
843
904
|
|
|
844
|
-
const tlist = elem
|
|
905
|
+
const tlist = getTransformList(elem)
|
|
845
906
|
const angle = getRotationAngleFromTransformList(tlist)
|
|
846
907
|
const hasMatrixXForm = hasMatrixTransform(tlist)
|
|
847
908
|
|
|
@@ -852,14 +913,22 @@ export const getBBoxWithTransform = function (elem, addSVGElementsFromJson, path
|
|
|
852
913
|
// TODO: why ellipse and not circle
|
|
853
914
|
const elemNames = ['ellipse', 'path', 'line', 'polyline', 'polygon']
|
|
854
915
|
if (elemNames.includes(elem.tagName)) {
|
|
855
|
-
goodBb = getBBoxOfElementAsPath(
|
|
916
|
+
goodBb = getBBoxOfElementAsPath(
|
|
917
|
+
elem,
|
|
918
|
+
addSVGElementsFromJson,
|
|
919
|
+
pathActions
|
|
920
|
+
)
|
|
856
921
|
bb = goodBb
|
|
857
922
|
} else if (elem.tagName === 'rect') {
|
|
858
923
|
// Look for radius
|
|
859
924
|
const rx = Number(elem.getAttribute('rx'))
|
|
860
925
|
const ry = Number(elem.getAttribute('ry'))
|
|
861
926
|
if (rx || ry) {
|
|
862
|
-
goodBb = getBBoxOfElementAsPath(
|
|
927
|
+
goodBb = getBBoxOfElementAsPath(
|
|
928
|
+
elem,
|
|
929
|
+
addSVGElementsFromJson,
|
|
930
|
+
pathActions
|
|
931
|
+
)
|
|
863
932
|
bb = goodBb
|
|
864
933
|
}
|
|
865
934
|
}
|
|
@@ -879,9 +948,9 @@ export const getBBoxWithTransform = function (elem, addSVGElementsFromJson, path
|
|
|
879
948
|
* @todo This is problematic with large stroke-width and, for example, a single
|
|
880
949
|
* horizontal line. The calculated BBox extends way beyond left and right sides.
|
|
881
950
|
*/
|
|
882
|
-
const getStrokeOffsetForBBox =
|
|
951
|
+
const getStrokeOffsetForBBox = elem => {
|
|
883
952
|
const sw = elem.getAttribute('stroke-width')
|
|
884
|
-
return
|
|
953
|
+
return !isNaN(sw) && elem.getAttribute('stroke') !== 'none' ? sw / 2 : 0
|
|
885
954
|
}
|
|
886
955
|
|
|
887
956
|
/**
|
|
@@ -893,25 +962,33 @@ const getStrokeOffsetForBBox = (elem) => {
|
|
|
893
962
|
*/
|
|
894
963
|
|
|
895
964
|
/**
|
|
896
|
-
* Get the bounding box for one or more stroked and/or transformed elements.
|
|
897
|
-
* @function module:utilities.getStrokedBBox
|
|
898
|
-
* @param {Element[]} elems - Array with DOM elements to check
|
|
899
|
-
* @param {module:utilities.EditorContext#addSVGElementsFromJson} addSVGElementsFromJson - Function to add the path element to the current layer. See canvas.addSVGElementsFromJson
|
|
900
|
-
* @param {module:path.pathActions} pathActions - If a transform exists, pathActions.resetOrientation() is used. See: canvas.pathActions.
|
|
901
|
-
* @returns {module:utilities.BBoxObject|module:math.TransformedBox|DOMRect} A single bounding box object
|
|
902
|
-
*/
|
|
965
|
+
* Get the bounding box for one or more stroked and/or transformed elements.
|
|
966
|
+
* @function module:utilities.getStrokedBBox
|
|
967
|
+
* @param {Element[]} elems - Array with DOM elements to check
|
|
968
|
+
* @param {module:utilities.EditorContext#addSVGElementsFromJson} addSVGElementsFromJson - Function to add the path element to the current layer. See canvas.addSVGElementsFromJson
|
|
969
|
+
* @param {module:path.pathActions} pathActions - If a transform exists, pathActions.resetOrientation() is used. See: canvas.pathActions.
|
|
970
|
+
* @returns {module:utilities.BBoxObject|module:math.TransformedBox|DOMRect} A single bounding box object
|
|
971
|
+
*/
|
|
903
972
|
export const getStrokedBBox = (elems, addSVGElementsFromJson, pathActions) => {
|
|
904
|
-
if (!elems || !elems.length) {
|
|
973
|
+
if (!elems || !elems.length) {
|
|
974
|
+
return false
|
|
975
|
+
}
|
|
905
976
|
|
|
906
977
|
let fullBb
|
|
907
|
-
elems.forEach(
|
|
908
|
-
if (fullBb) {
|
|
909
|
-
|
|
978
|
+
elems.forEach(elem => {
|
|
979
|
+
if (fullBb) {
|
|
980
|
+
return
|
|
981
|
+
}
|
|
982
|
+
if (!elem.parentNode) {
|
|
983
|
+
return
|
|
984
|
+
}
|
|
910
985
|
fullBb = getBBoxWithTransform(elem, addSVGElementsFromJson, pathActions)
|
|
911
986
|
})
|
|
912
987
|
|
|
913
988
|
// This shouldn't ever happen...
|
|
914
|
-
if (!fullBb) {
|
|
989
|
+
if (!fullBb) {
|
|
990
|
+
return null
|
|
991
|
+
}
|
|
915
992
|
|
|
916
993
|
// fullBb doesn't include the stoke, so this does no good!
|
|
917
994
|
// if (elems.length == 1) return fullBb;
|
|
@@ -929,8 +1006,12 @@ export const getStrokedBBox = (elems, addSVGElementsFromJson, pathActions) => {
|
|
|
929
1006
|
maxX += offset
|
|
930
1007
|
maxY += offset
|
|
931
1008
|
} else {
|
|
932
|
-
elems.forEach(
|
|
933
|
-
const curBb = getBBoxWithTransform(
|
|
1009
|
+
elems.forEach(elem => {
|
|
1010
|
+
const curBb = getBBoxWithTransform(
|
|
1011
|
+
elem,
|
|
1012
|
+
addSVGElementsFromJson,
|
|
1013
|
+
pathActions
|
|
1014
|
+
)
|
|
934
1015
|
if (curBb) {
|
|
935
1016
|
const offset = getStrokeOffsetForBBox(elem)
|
|
936
1017
|
minX = Math.min(minX, curBb.x - offset)
|
|
@@ -944,28 +1025,33 @@ export const getStrokedBBox = (elems, addSVGElementsFromJson, pathActions) => {
|
|
|
944
1025
|
})
|
|
945
1026
|
}
|
|
946
1027
|
|
|
947
|
-
fullBb.x = minX
|
|
948
|
-
fullBb.y = minY
|
|
949
|
-
fullBb.width = maxX - minX
|
|
950
|
-
fullBb.height = maxY - minY
|
|
1028
|
+
fullBb.x = shortFloat(minX)
|
|
1029
|
+
fullBb.y = shortFloat(minY)
|
|
1030
|
+
fullBb.width = shortFloat(maxX - minX)
|
|
1031
|
+
fullBb.height = shortFloat(maxY - minY)
|
|
951
1032
|
return fullBb
|
|
952
1033
|
}
|
|
953
1034
|
|
|
954
1035
|
/**
|
|
955
|
-
* Get all elements that have a BBox (excludes `<defs>`, `<title>`, etc).
|
|
956
|
-
* Note that 0-opacity, off-screen etc elements are still considered "visible"
|
|
957
|
-
* for this function.
|
|
958
|
-
* @function module:utilities.getVisibleElements
|
|
959
|
-
* @param {Element} parentElement - The parent DOM element to search within
|
|
960
|
-
* @returns {Element[]} All "visible" elements.
|
|
961
|
-
*/
|
|
962
|
-
export const getVisibleElements =
|
|
1036
|
+
* Get all elements that have a BBox (excludes `<defs>`, `<title>`, etc).
|
|
1037
|
+
* Note that 0-opacity, off-screen etc elements are still considered "visible"
|
|
1038
|
+
* for this function.
|
|
1039
|
+
* @function module:utilities.getVisibleElements
|
|
1040
|
+
* @param {Element} parentElement - The parent DOM element to search within
|
|
1041
|
+
* @returns {Element[]} All "visible" elements.
|
|
1042
|
+
*/
|
|
1043
|
+
export const getVisibleElements = parentElement => {
|
|
963
1044
|
if (!parentElement) {
|
|
964
1045
|
const svgContent = svgCanvas.getSvgContent()
|
|
965
1046
|
for (let i = 0; i < svgContent.children.length; i++) {
|
|
966
1047
|
if (svgContent.children[i].getBBox) {
|
|
967
1048
|
const bbox = svgContent.children[i].getBBox()
|
|
968
|
-
if (
|
|
1049
|
+
if (
|
|
1050
|
+
bbox.width !== 0 &&
|
|
1051
|
+
bbox.height !== 0 &&
|
|
1052
|
+
bbox.width !== 0 &&
|
|
1053
|
+
bbox.height !== 0
|
|
1054
|
+
) {
|
|
969
1055
|
parentElement = svgContent.children[i]
|
|
970
1056
|
break
|
|
971
1057
|
}
|
|
@@ -977,7 +1063,7 @@ export const getVisibleElements = (parentElement) => {
|
|
|
977
1063
|
if (parentElement) {
|
|
978
1064
|
const children = parentElement.children
|
|
979
1065
|
// eslint-disable-next-line array-callback-return
|
|
980
|
-
Array.from(children,
|
|
1066
|
+
Array.from(children, elem => {
|
|
981
1067
|
if (elem.getBBox) {
|
|
982
1068
|
contentElems.push(elem)
|
|
983
1069
|
}
|
|
@@ -987,13 +1073,15 @@ export const getVisibleElements = (parentElement) => {
|
|
|
987
1073
|
}
|
|
988
1074
|
|
|
989
1075
|
/**
|
|
990
|
-
* Get the bounding box for one or more stroked and/or transformed elements.
|
|
991
|
-
* @function module:utilities.getStrokedBBoxDefaultVisible
|
|
992
|
-
* @param {Element[]} elems - Array with DOM elements to check
|
|
993
|
-
* @returns {module:utilities.BBoxObject} A single bounding box object
|
|
994
|
-
*/
|
|
995
|
-
export const getStrokedBBoxDefaultVisible =
|
|
996
|
-
if (!elems) {
|
|
1076
|
+
* Get the bounding box for one or more stroked and/or transformed elements.
|
|
1077
|
+
* @function module:utilities.getStrokedBBoxDefaultVisible
|
|
1078
|
+
* @param {Element[]} elems - Array with DOM elements to check
|
|
1079
|
+
* @returns {module:utilities.BBoxObject} A single bounding box object
|
|
1080
|
+
*/
|
|
1081
|
+
export const getStrokedBBoxDefaultVisible = elems => {
|
|
1082
|
+
if (!elems) {
|
|
1083
|
+
elems = getVisibleElements()
|
|
1084
|
+
}
|
|
997
1085
|
return getStrokedBBox(
|
|
998
1086
|
elems,
|
|
999
1087
|
svgCanvas.addSVGElementsFromJson,
|
|
@@ -1002,53 +1090,55 @@ export const getStrokedBBoxDefaultVisible = (elems) => {
|
|
|
1002
1090
|
}
|
|
1003
1091
|
|
|
1004
1092
|
/**
|
|
1005
|
-
* Get the rotation angle of the given transform list.
|
|
1006
|
-
* @function module:utilities.getRotationAngleFromTransformList
|
|
1007
|
-
* @param {SVGTransformList} tlist - List of transforms
|
|
1008
|
-
* @param {boolean} toRad - When true returns the value in radians rather than degrees
|
|
1009
|
-
* @returns {Float} The angle in degrees or radians
|
|
1010
|
-
*/
|
|
1093
|
+
* Get the rotation angle of the given transform list.
|
|
1094
|
+
* @function module:utilities.getRotationAngleFromTransformList
|
|
1095
|
+
* @param {SVGTransformList} tlist - List of transforms
|
|
1096
|
+
* @param {boolean} toRad - When true returns the value in radians rather than degrees
|
|
1097
|
+
* @returns {Float} The angle in degrees or radians
|
|
1098
|
+
*/
|
|
1011
1099
|
export const getRotationAngleFromTransformList = (tlist, toRad) => {
|
|
1012
|
-
if (!tlist) {
|
|
1100
|
+
if (!tlist) {
|
|
1101
|
+
return 0
|
|
1102
|
+
} // <svg> element have no tlist
|
|
1013
1103
|
for (let i = 0; i < tlist.numberOfItems; ++i) {
|
|
1014
1104
|
const xform = tlist.getItem(i)
|
|
1015
1105
|
if (xform.type === 4) {
|
|
1016
|
-
return toRad ? xform.angle * Math.PI / 180.0 : xform.angle
|
|
1106
|
+
return toRad ? (xform.angle * Math.PI) / 180.0 : xform.angle
|
|
1017
1107
|
}
|
|
1018
1108
|
}
|
|
1019
1109
|
return 0.0
|
|
1020
1110
|
}
|
|
1021
1111
|
|
|
1022
1112
|
/**
|
|
1023
|
-
* Get the rotation angle of the given/selected DOM element.
|
|
1024
|
-
* @function module:utilities.getRotationAngle
|
|
1025
|
-
* @param {Element} [elem] - DOM element to get the angle for. Default to first of selected elements.
|
|
1026
|
-
* @param {boolean} [toRad=false] - When true returns the value in radians rather than degrees
|
|
1027
|
-
* @returns {Float} The angle in degrees or radians
|
|
1028
|
-
*/
|
|
1113
|
+
* Get the rotation angle of the given/selected DOM element.
|
|
1114
|
+
* @function module:utilities.getRotationAngle
|
|
1115
|
+
* @param {Element} [elem] - DOM element to get the angle for. Default to first of selected elements.
|
|
1116
|
+
* @param {boolean} [toRad=false] - When true returns the value in radians rather than degrees
|
|
1117
|
+
* @returns {Float} The angle in degrees or radians
|
|
1118
|
+
*/
|
|
1029
1119
|
export let getRotationAngle = (elem, toRad) => {
|
|
1030
1120
|
const selected = elem || svgCanvas.getSelectedElements()[0]
|
|
1031
1121
|
// find the rotation transform (if any) and set it
|
|
1032
|
-
const tlist = selected
|
|
1122
|
+
const tlist = getTransformList(selected)
|
|
1033
1123
|
return getRotationAngleFromTransformList(tlist, toRad)
|
|
1034
1124
|
}
|
|
1035
1125
|
|
|
1036
1126
|
/**
|
|
1037
|
-
* Get the reference element associated with the given attribute value.
|
|
1038
|
-
* @function module:utilities.getRefElem
|
|
1039
|
-
* @param {string} attrVal - The attribute value as a string
|
|
1040
|
-
* @returns {Element} Reference element
|
|
1041
|
-
*/
|
|
1042
|
-
export const getRefElem =
|
|
1127
|
+
* Get the reference element associated with the given attribute value.
|
|
1128
|
+
* @function module:utilities.getRefElem
|
|
1129
|
+
* @param {string} attrVal - The attribute value as a string
|
|
1130
|
+
* @returns {Element} Reference element
|
|
1131
|
+
*/
|
|
1132
|
+
export const getRefElem = attrVal => {
|
|
1043
1133
|
return getElement(getUrlFromAttr(attrVal).substr(1))
|
|
1044
1134
|
}
|
|
1045
1135
|
/**
|
|
1046
|
-
* Get the reference element associated with the given attribute value.
|
|
1047
|
-
* @function module:utilities.getFeGaussianBlur
|
|
1048
|
-
* @param {any} Element
|
|
1049
|
-
* @returns {any} Reference element
|
|
1050
|
-
*/
|
|
1051
|
-
export const getFeGaussianBlur =
|
|
1136
|
+
* Get the reference element associated with the given attribute value.
|
|
1137
|
+
* @function module:utilities.getFeGaussianBlur
|
|
1138
|
+
* @param {any} Element
|
|
1139
|
+
* @returns {any} Reference element
|
|
1140
|
+
*/
|
|
1141
|
+
export const getFeGaussianBlur = ele => {
|
|
1052
1142
|
if (ele?.firstChild?.tagName === 'feGaussianBlur') {
|
|
1053
1143
|
return ele.firstChild
|
|
1054
1144
|
} else {
|
|
@@ -1064,30 +1154,33 @@ export const getFeGaussianBlur = (ele) => {
|
|
|
1064
1154
|
}
|
|
1065
1155
|
|
|
1066
1156
|
/**
|
|
1067
|
-
* Get a DOM element by ID within the SVG root element.
|
|
1068
|
-
* @function module:utilities.getElement
|
|
1069
|
-
* @param {string} id - String with the element's new ID
|
|
1070
|
-
* @returns {?Element}
|
|
1071
|
-
*/
|
|
1072
|
-
export const getElement =
|
|
1157
|
+
* Get a DOM element by ID within the SVG root element.
|
|
1158
|
+
* @function module:utilities.getElement
|
|
1159
|
+
* @param {string} id - String with the element's new ID
|
|
1160
|
+
* @returns {?Element}
|
|
1161
|
+
*/
|
|
1162
|
+
export const getElement = id => {
|
|
1073
1163
|
// querySelector lookup
|
|
1074
1164
|
return svgroot_.querySelector('#' + id)
|
|
1075
1165
|
}
|
|
1076
1166
|
|
|
1077
1167
|
/**
|
|
1078
|
-
* Assigns multiple attributes to an element.
|
|
1079
|
-
* @function module:utilities.assignAttributes
|
|
1080
|
-
* @param {Element} elem - DOM element to apply new attribute values to
|
|
1081
|
-
* @param {PlainObject<string, string>} attrs - Object with attribute keys/values
|
|
1082
|
-
* @param {Integer} [suspendLength] - Milliseconds to suspend redraw
|
|
1083
|
-
* @param {boolean} [unitCheck=false] - Boolean to indicate the need to use units.setUnitAttr
|
|
1084
|
-
* @returns {void}
|
|
1085
|
-
*/
|
|
1168
|
+
* Assigns multiple attributes to an element.
|
|
1169
|
+
* @function module:utilities.assignAttributes
|
|
1170
|
+
* @param {Element} elem - DOM element to apply new attribute values to
|
|
1171
|
+
* @param {PlainObject<string, string>} attrs - Object with attribute keys/values
|
|
1172
|
+
* @param {Integer} [suspendLength] - Milliseconds to suspend redraw
|
|
1173
|
+
* @param {boolean} [unitCheck=false] - Boolean to indicate the need to use units.setUnitAttr
|
|
1174
|
+
* @returns {void}
|
|
1175
|
+
*/
|
|
1086
1176
|
export const assignAttributes = (elem, attrs, suspendLength, unitCheck) => {
|
|
1087
1177
|
for (const [key, value] of Object.entries(attrs)) {
|
|
1088
|
-
const ns =
|
|
1089
|
-
|
|
1090
|
-
|
|
1178
|
+
const ns =
|
|
1179
|
+
key.substr(0, 4) === 'xml:'
|
|
1180
|
+
? NS.XML
|
|
1181
|
+
: key.substr(0, 6) === 'xlink:'
|
|
1182
|
+
? NS.XLINK
|
|
1183
|
+
: null
|
|
1091
1184
|
if (value === undefined) {
|
|
1092
1185
|
if (ns) {
|
|
1093
1186
|
elem.removeAttributeNS(ns, key)
|
|
@@ -1107,12 +1200,12 @@ export const assignAttributes = (elem, attrs, suspendLength, unitCheck) => {
|
|
|
1107
1200
|
}
|
|
1108
1201
|
|
|
1109
1202
|
/**
|
|
1110
|
-
* Remove unneeded (default) attributes, making resulting SVG smaller.
|
|
1111
|
-
* @function module:utilities.cleanupElement
|
|
1112
|
-
* @param {Element} element - DOM element to clean up
|
|
1113
|
-
* @returns {void}
|
|
1114
|
-
*/
|
|
1115
|
-
export const cleanupElement =
|
|
1203
|
+
* Remove unneeded (default) attributes, making resulting SVG smaller.
|
|
1204
|
+
* @function module:utilities.cleanupElement
|
|
1205
|
+
* @param {Element} element - DOM element to clean up
|
|
1206
|
+
* @returns {void}
|
|
1207
|
+
*/
|
|
1208
|
+
export const cleanupElement = element => {
|
|
1116
1209
|
const defaults = {
|
|
1117
1210
|
'fill-opacity': 1,
|
|
1118
1211
|
'stop-opacity': 1,
|
|
@@ -1141,12 +1234,12 @@ export const cleanupElement = (element) => {
|
|
|
1141
1234
|
}
|
|
1142
1235
|
|
|
1143
1236
|
/**
|
|
1144
|
-
* Round value to for snapping.
|
|
1145
|
-
* @function module:utilities.snapToGrid
|
|
1146
|
-
* @param {Float} value
|
|
1147
|
-
* @returns {Integer}
|
|
1148
|
-
*/
|
|
1149
|
-
export const snapToGrid =
|
|
1237
|
+
* Round value to for snapping.
|
|
1238
|
+
* @function module:utilities.snapToGrid
|
|
1239
|
+
* @param {Float} value
|
|
1240
|
+
* @returns {Integer}
|
|
1241
|
+
*/
|
|
1242
|
+
export const snapToGrid = value => {
|
|
1150
1243
|
const unit = svgCanvas.getBaseUnit()
|
|
1151
1244
|
let stepSize = svgCanvas.getSnappingStep()
|
|
1152
1245
|
if (unit !== 'px') {
|
|
@@ -1162,8 +1255,8 @@ export const snapToGrid = (value) => {
|
|
|
1162
1255
|
* @param {Element} img - The DOM element to prevent the click on
|
|
1163
1256
|
* @returns {void}
|
|
1164
1257
|
*/
|
|
1165
|
-
export const preventClickDefault =
|
|
1166
|
-
$click(img,
|
|
1258
|
+
export const preventClickDefault = img => {
|
|
1259
|
+
$click(img, e => {
|
|
1167
1260
|
e.preventDefault()
|
|
1168
1261
|
})
|
|
1169
1262
|
}
|
|
@@ -1178,28 +1271,30 @@ export const preventClickDefault = (img) => {
|
|
|
1178
1271
|
* @param {any} val
|
|
1179
1272
|
* @returns {boolean}
|
|
1180
1273
|
*/
|
|
1181
|
-
export const isNullish =
|
|
1274
|
+
export const isNullish = val => {
|
|
1182
1275
|
return val === null || val === undefined
|
|
1183
1276
|
}
|
|
1184
1277
|
|
|
1185
1278
|
/**
|
|
1186
|
-
* Overwrite methods for unit testing.
|
|
1187
|
-
* @function module:utilities.mock
|
|
1188
|
-
* @param {PlainObject} mockMethods
|
|
1189
|
-
* @param {module:utilities.getHref} mockMethods.getHref
|
|
1190
|
-
* @param {module:utilities.setHref} mockMethods.setHref
|
|
1191
|
-
* @param {module:utilities.getRotationAngle} mockMethods.getRotationAngle
|
|
1192
|
-
* @returns {void}
|
|
1193
|
-
*/
|
|
1279
|
+
* Overwrite methods for unit testing.
|
|
1280
|
+
* @function module:utilities.mock
|
|
1281
|
+
* @param {PlainObject} mockMethods
|
|
1282
|
+
* @param {module:utilities.getHref} mockMethods.getHref
|
|
1283
|
+
* @param {module:utilities.setHref} mockMethods.setHref
|
|
1284
|
+
* @param {module:utilities.getRotationAngle} mockMethods.getRotationAngle
|
|
1285
|
+
* @returns {void}
|
|
1286
|
+
*/
|
|
1194
1287
|
export const mock = ({
|
|
1195
|
-
getHref: getHrefUser,
|
|
1288
|
+
getHref: getHrefUser,
|
|
1289
|
+
setHref: setHrefUser,
|
|
1290
|
+
getRotationAngle: getRotationAngleUser
|
|
1196
1291
|
}) => {
|
|
1197
1292
|
getHref = getHrefUser
|
|
1198
1293
|
setHref = setHrefUser
|
|
1199
1294
|
getRotationAngle = getRotationAngleUser
|
|
1200
1295
|
}
|
|
1201
1296
|
|
|
1202
|
-
export const stringToHTML =
|
|
1297
|
+
export const stringToHTML = str => {
|
|
1203
1298
|
const parser = new DOMParser()
|
|
1204
1299
|
const doc = parser.parseFromString(str, 'text/html')
|
|
1205
1300
|
return doc.body.firstChild
|
|
@@ -1215,9 +1310,9 @@ export const insertChildAtIndex = (parent, child, index = 0) => {
|
|
|
1215
1310
|
}
|
|
1216
1311
|
|
|
1217
1312
|
// shortcuts to common DOM functions
|
|
1218
|
-
export const $id =
|
|
1219
|
-
export const $qq =
|
|
1220
|
-
export const $qa =
|
|
1313
|
+
export const $id = id => document.getElementById(id)
|
|
1314
|
+
export const $qq = sel => document.querySelector(sel)
|
|
1315
|
+
export const $qa = sel => [...document.querySelectorAll(sel)]
|
|
1221
1316
|
export const $click = (element, handler) => {
|
|
1222
1317
|
element.addEventListener('click', handler)
|
|
1223
1318
|
element.addEventListener('touchend', handler)
|