@svgedit/svgcanvas 7.2.7 → 7.4.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/common/browser.js +104 -37
- package/common/logger.js +151 -0
- package/common/util.js +96 -155
- package/core/blur-event.js +106 -42
- package/core/clear.js +13 -3
- package/core/coords.js +214 -90
- package/core/copy-elem.js +27 -13
- package/core/dataStorage.js +84 -21
- package/core/draw.js +80 -40
- package/core/elem-get-set.js +247 -115
- package/core/event.js +171 -29
- package/core/history.js +105 -50
- package/core/historyrecording.js +4 -2
- package/core/json.js +54 -12
- package/core/layer.js +11 -17
- package/core/math.js +102 -23
- package/core/namespaces.js +5 -5
- package/core/paint.js +95 -24
- package/core/paste-elem.js +58 -19
- package/core/path-actions.js +812 -791
- package/core/path-method.js +236 -37
- package/core/path.js +45 -10
- package/core/recalculate.js +410 -15
- package/core/sanitize.js +51 -18
- package/core/select.js +44 -20
- package/core/selected-elem.js +159 -38
- package/core/selection.js +16 -6
- package/core/svg-exec.js +99 -27
- package/core/svgroot.js +1 -1
- package/core/text-actions.js +363 -307
- package/core/undo.js +47 -15
- package/core/units.js +8 -6
- package/core/utilities.js +282 -170
- package/dist/svgcanvas.js +40021 -55172
- package/dist/svgcanvas.js.map +1 -1
- package/package.json +55 -54
- package/publish.md +1 -6
- package/svgcanvas.d.ts +225 -0
- package/svgcanvas.js +11 -9
- package/vite.config.mjs +20 -0
- package/rollup.config.mjs +0 -38
package/core/elem-get-set.js
CHANGED
|
@@ -122,25 +122,36 @@ const setGroupTitleMethod = (val) => {
|
|
|
122
122
|
const selectedElements = svgCanvas.getSelectedElements()
|
|
123
123
|
const dataStorage = svgCanvas.getDataStorage()
|
|
124
124
|
let elem = selectedElements[0]
|
|
125
|
+
if (!elem) { return }
|
|
125
126
|
if (dataStorage.has(elem, 'gsvg')) {
|
|
126
127
|
elem = dataStorage.get(elem, 'gsvg')
|
|
128
|
+
} else if (dataStorage.has(elem, 'symbol')) {
|
|
129
|
+
elem = dataStorage.get(elem, 'symbol')
|
|
127
130
|
}
|
|
128
|
-
|
|
129
|
-
const ts = elem.querySelectorAll('title')
|
|
131
|
+
if (!elem) { return }
|
|
130
132
|
|
|
131
133
|
const batchCmd = new BatchCommand('Set Label')
|
|
132
134
|
|
|
133
|
-
let title
|
|
135
|
+
let title = null
|
|
136
|
+
for (const child of elem.childNodes) {
|
|
137
|
+
if (child.nodeName === 'title') {
|
|
138
|
+
title = child
|
|
139
|
+
break
|
|
140
|
+
}
|
|
141
|
+
}
|
|
142
|
+
|
|
134
143
|
if (val.length === 0) {
|
|
144
|
+
if (!title) { return }
|
|
135
145
|
// Remove title element
|
|
136
|
-
const
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
} else if (
|
|
146
|
+
const { nextSibling } = title
|
|
147
|
+
title.remove()
|
|
148
|
+
batchCmd.addSubCommand(new RemoveElementCommand(title, nextSibling, elem))
|
|
149
|
+
} else if (title) {
|
|
140
150
|
// Change title contents
|
|
141
|
-
|
|
142
|
-
|
|
151
|
+
const oldText = title.textContent
|
|
152
|
+
if (oldText === val) { return }
|
|
143
153
|
title.textContent = val
|
|
154
|
+
batchCmd.addSubCommand(new ChangeElementCommand(title, { '#text': oldText }))
|
|
144
155
|
} else {
|
|
145
156
|
// Add title element
|
|
146
157
|
title = svgCanvas.getDOMDocument().createElementNS(NS.SVG, 'title')
|
|
@@ -149,7 +160,9 @@ const setGroupTitleMethod = (val) => {
|
|
|
149
160
|
batchCmd.addSubCommand(new InsertElementCommand(title))
|
|
150
161
|
}
|
|
151
162
|
|
|
152
|
-
|
|
163
|
+
if (!batchCmd.isEmpty()) {
|
|
164
|
+
svgCanvas.addCommandToHistory(batchCmd)
|
|
165
|
+
}
|
|
153
166
|
}
|
|
154
167
|
|
|
155
168
|
/**
|
|
@@ -160,33 +173,44 @@ const setGroupTitleMethod = (val) => {
|
|
|
160
173
|
* @returns {void}
|
|
161
174
|
*/
|
|
162
175
|
const setDocumentTitleMethod = (newTitle) => {
|
|
163
|
-
const {
|
|
164
|
-
|
|
165
|
-
|
|
176
|
+
const {
|
|
177
|
+
InsertElementCommand, RemoveElementCommand,
|
|
178
|
+
ChangeElementCommand, BatchCommand
|
|
179
|
+
} = svgCanvas.history
|
|
180
|
+
const svgContent = svgCanvas.getSvgContent()
|
|
166
181
|
|
|
167
182
|
const batchCmd = new BatchCommand('Change Image Title')
|
|
168
183
|
|
|
169
|
-
|
|
184
|
+
/** @type {Element|null} */
|
|
185
|
+
let docTitle = null
|
|
186
|
+
for (const child of svgContent.childNodes) {
|
|
170
187
|
if (child.nodeName === 'title') {
|
|
171
188
|
docTitle = child
|
|
172
|
-
oldTitle = docTitle.textContent
|
|
173
189
|
break
|
|
174
190
|
}
|
|
175
191
|
}
|
|
192
|
+
|
|
176
193
|
if (!docTitle) {
|
|
194
|
+
if (!newTitle.length) { return }
|
|
177
195
|
docTitle = svgCanvas.getDOMDocument().createElementNS(NS.SVG, 'title')
|
|
178
|
-
svgCanvas.getSvgContent().insertBefore(docTitle, svgCanvas.getSvgContent().firstChild)
|
|
179
|
-
// svgContent.firstChild.before(docTitle); // Ok to replace above with this?
|
|
180
|
-
}
|
|
181
|
-
|
|
182
|
-
if (newTitle.length) {
|
|
183
196
|
docTitle.textContent = newTitle
|
|
197
|
+
svgContent.insertBefore(docTitle, svgContent.firstChild)
|
|
198
|
+
batchCmd.addSubCommand(new InsertElementCommand(docTitle))
|
|
199
|
+
} else if (newTitle.length) {
|
|
200
|
+
const oldTitle = docTitle.textContent
|
|
201
|
+
if (oldTitle === newTitle) { return }
|
|
202
|
+
docTitle.textContent = newTitle
|
|
203
|
+
batchCmd.addSubCommand(new ChangeElementCommand(docTitle, { '#text': oldTitle }))
|
|
184
204
|
} else {
|
|
185
205
|
// No title given, so element is not necessary
|
|
206
|
+
const { nextSibling } = docTitle
|
|
186
207
|
docTitle.remove()
|
|
208
|
+
batchCmd.addSubCommand(new RemoveElementCommand(docTitle, nextSibling, svgContent))
|
|
209
|
+
}
|
|
210
|
+
|
|
211
|
+
if (!batchCmd.isEmpty()) {
|
|
212
|
+
svgCanvas.addCommandToHistory(batchCmd)
|
|
187
213
|
}
|
|
188
|
-
batchCmd.addSubCommand(new ChangeElementCommand(docTitle, { '#text': oldTitle }))
|
|
189
|
-
svgCanvas.addCommandToHistory(batchCmd)
|
|
190
214
|
}
|
|
191
215
|
|
|
192
216
|
/**
|
|
@@ -201,7 +225,6 @@ const setDocumentTitleMethod = (newTitle) => {
|
|
|
201
225
|
*/
|
|
202
226
|
const setResolutionMethod = (x, y) => {
|
|
203
227
|
const { ChangeElementCommand, BatchCommand } = svgCanvas.history
|
|
204
|
-
const zoom = svgCanvas.getZoom()
|
|
205
228
|
const res = svgCanvas.getResolution()
|
|
206
229
|
const { w, h } = res
|
|
207
230
|
let batchCmd
|
|
@@ -220,8 +243,10 @@ const setResolutionMethod = (x, y) => {
|
|
|
220
243
|
dy.push(bbox.y * -1)
|
|
221
244
|
})
|
|
222
245
|
|
|
223
|
-
const cmd = svgCanvas.moveSelectedElements(dx, dy,
|
|
224
|
-
|
|
246
|
+
const cmd = svgCanvas.moveSelectedElements(dx, dy, false)
|
|
247
|
+
if (cmd) {
|
|
248
|
+
batchCmd.addSubCommand(cmd)
|
|
249
|
+
}
|
|
225
250
|
svgCanvas.clearSelection()
|
|
226
251
|
|
|
227
252
|
x = Math.round(bbox.width)
|
|
@@ -230,26 +255,25 @@ const setResolutionMethod = (x, y) => {
|
|
|
230
255
|
return false
|
|
231
256
|
}
|
|
232
257
|
}
|
|
233
|
-
|
|
258
|
+
const newW = convertToNum('width', x)
|
|
259
|
+
const newH = convertToNum('height', y)
|
|
260
|
+
if (newW !== w || newH !== h) {
|
|
234
261
|
if (!batchCmd) {
|
|
235
262
|
batchCmd = new BatchCommand('Change Image Dimensions')
|
|
236
263
|
}
|
|
264
|
+
const svgContent = svgCanvas.getSvgContent()
|
|
265
|
+
const oldViewBox = svgContent.getAttribute('viewBox')
|
|
237
266
|
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
svgCanvas.getSvgContent().setAttribute('width', x)
|
|
242
|
-
svgCanvas.getSvgContent().setAttribute('height', y)
|
|
243
|
-
|
|
244
|
-
svgCanvas.contentW = x
|
|
245
|
-
svgCanvas.contentH = y
|
|
246
|
-
batchCmd.addSubCommand(new ChangeElementCommand(svgCanvas.getSvgContent(), { width: w, height: h }))
|
|
267
|
+
svgContent.setAttribute('width', newW)
|
|
268
|
+
svgContent.setAttribute('height', newH)
|
|
247
269
|
|
|
248
|
-
svgCanvas.
|
|
249
|
-
|
|
270
|
+
svgCanvas.contentW = newW
|
|
271
|
+
svgCanvas.contentH = newH
|
|
272
|
+
svgContent.setAttribute('viewBox', [0, 0, newW, newH].join(' '))
|
|
273
|
+
batchCmd.addSubCommand(new ChangeElementCommand(svgContent, { width: w, height: h, viewBox: oldViewBox }))
|
|
250
274
|
|
|
251
275
|
svgCanvas.addCommandToHistory(batchCmd)
|
|
252
|
-
svgCanvas.call('changed', [
|
|
276
|
+
svgCanvas.call('changed', [svgContent])
|
|
253
277
|
}
|
|
254
278
|
return true
|
|
255
279
|
}
|
|
@@ -286,20 +310,36 @@ const setBBoxZoomMethod = (val, editorW, editorH) => {
|
|
|
286
310
|
let spacer = 0.85
|
|
287
311
|
let bb
|
|
288
312
|
const calcZoom = (bb) => {
|
|
289
|
-
if (!bb) { return
|
|
313
|
+
if (!bb) { return undefined }
|
|
314
|
+
if (!Number.isFinite(editorW) || !Number.isFinite(editorH) || editorW <= 0 || editorH <= 0) {
|
|
315
|
+
return undefined
|
|
316
|
+
}
|
|
317
|
+
if (!Number.isFinite(bb.width) || !Number.isFinite(bb.height) || bb.width <= 0 || bb.height <= 0) {
|
|
318
|
+
return undefined
|
|
319
|
+
}
|
|
290
320
|
const wZoom = Math.round((editorW / bb.width) * 100 * spacer) / 100
|
|
291
321
|
const hZoom = Math.round((editorH / bb.height) * 100 * spacer) / 100
|
|
292
322
|
const zoom = Math.min(wZoom, hZoom)
|
|
323
|
+
if (!Number.isFinite(zoom) || zoom <= 0) {
|
|
324
|
+
return undefined
|
|
325
|
+
}
|
|
293
326
|
svgCanvas.setZoom(zoom)
|
|
294
327
|
return { zoom, bbox: bb }
|
|
295
328
|
}
|
|
296
329
|
|
|
297
|
-
if (typeof val === 'object') {
|
|
330
|
+
if (val && typeof val === 'object') {
|
|
298
331
|
bb = val
|
|
299
332
|
if (bb.width === 0 || bb.height === 0) {
|
|
300
|
-
|
|
301
|
-
|
|
302
|
-
|
|
333
|
+
let newzoom = zoom
|
|
334
|
+
if (Number.isFinite(bb.zoom) && bb.zoom > 0) {
|
|
335
|
+
newzoom = bb.zoom
|
|
336
|
+
} else if (Number.isFinite(bb.factor) && bb.factor > 0) {
|
|
337
|
+
newzoom = zoom * bb.factor
|
|
338
|
+
}
|
|
339
|
+
if (Number.isFinite(newzoom) && newzoom > 0) {
|
|
340
|
+
svgCanvas.setZoom(newzoom)
|
|
341
|
+
}
|
|
342
|
+
return { zoom: newzoom, bbox: bb }
|
|
303
343
|
}
|
|
304
344
|
return calcZoom(bb)
|
|
305
345
|
}
|
|
@@ -307,12 +347,7 @@ const setBBoxZoomMethod = (val, editorW, editorH) => {
|
|
|
307
347
|
switch (val) {
|
|
308
348
|
case 'selection': {
|
|
309
349
|
if (!selectedElements[0]) { return undefined }
|
|
310
|
-
const selectedElems = selectedElements.
|
|
311
|
-
if (n) {
|
|
312
|
-
return n
|
|
313
|
-
}
|
|
314
|
-
return undefined
|
|
315
|
-
})
|
|
350
|
+
const selectedElems = selectedElements.filter(Boolean)
|
|
316
351
|
bb = getStrokedBBoxDefaultVisible(selectedElems)
|
|
317
352
|
break
|
|
318
353
|
} case 'canvas': {
|
|
@@ -340,13 +375,22 @@ const setBBoxZoomMethod = (val, editorW, editorH) => {
|
|
|
340
375
|
* @returns {void}
|
|
341
376
|
*/
|
|
342
377
|
const setZoomMethod = (zoomLevel) => {
|
|
378
|
+
if (!Number.isFinite(zoomLevel) || zoomLevel <= 0) {
|
|
379
|
+
return
|
|
380
|
+
}
|
|
343
381
|
const selectedElements = svgCanvas.getSelectedElements()
|
|
344
382
|
const res = svgCanvas.getResolution()
|
|
345
|
-
|
|
383
|
+
const w = res.w / zoomLevel
|
|
384
|
+
const h = res.h / zoomLevel
|
|
385
|
+
if (!Number.isFinite(w) || !Number.isFinite(h) || w <= 0 || h <= 0) {
|
|
386
|
+
return
|
|
387
|
+
}
|
|
388
|
+
svgCanvas.getSvgContent().setAttribute('viewBox', `0 0 ${w} ${h}`)
|
|
346
389
|
svgCanvas.setZoom(zoomLevel)
|
|
347
390
|
selectedElements.forEach((elem) => {
|
|
348
391
|
if (!elem) { return }
|
|
349
|
-
svgCanvas.selectorManager.requestSelector(elem)
|
|
392
|
+
const selector = svgCanvas.selectorManager.requestSelector(elem)
|
|
393
|
+
selector && selector.resize()
|
|
350
394
|
})
|
|
351
395
|
svgCanvas.pathActions.zoomChange()
|
|
352
396
|
svgCanvas.runExtensions('zoomChanged', zoomLevel)
|
|
@@ -364,7 +408,7 @@ const setZoomMethod = (zoomLevel) => {
|
|
|
364
408
|
const setColorMethod = (type, val, preventUndo) => {
|
|
365
409
|
const selectedElements = svgCanvas.getSelectedElements()
|
|
366
410
|
svgCanvas.setCurShape(type, val)
|
|
367
|
-
svgCanvas.setCurProperties(type
|
|
411
|
+
svgCanvas.setCurProperties(`${type}_paint`, { type: 'solidColor' })
|
|
368
412
|
const elems = []
|
|
369
413
|
/**
|
|
370
414
|
*
|
|
@@ -408,10 +452,11 @@ const setColorMethod = (type, val, preventUndo) => {
|
|
|
408
452
|
* @returns {void}
|
|
409
453
|
*/
|
|
410
454
|
const setGradientMethod = (type) => {
|
|
411
|
-
if (!svgCanvas.getCurProperties(type
|
|
412
|
-
svgCanvas.getCurProperties(type
|
|
455
|
+
if (!svgCanvas.getCurProperties(`${type}_paint`) ||
|
|
456
|
+
svgCanvas.getCurProperties(`${type}_paint`).type === 'solidColor') { return }
|
|
413
457
|
const canvas = svgCanvas
|
|
414
458
|
let grad = canvas[type + 'Grad']
|
|
459
|
+
if (!grad) { return }
|
|
415
460
|
// find out if there is a duplicate gradient already in the defs
|
|
416
461
|
const duplicateGrad = findDuplicateGradient(grad)
|
|
417
462
|
const defs = findDefs()
|
|
@@ -425,7 +470,7 @@ const setGradientMethod = (type) => {
|
|
|
425
470
|
} else { // use existing gradient
|
|
426
471
|
grad = duplicateGrad
|
|
427
472
|
}
|
|
428
|
-
svgCanvas.setColor(type,
|
|
473
|
+
svgCanvas.setColor(type, `url(#${grad.id})`)
|
|
429
474
|
}
|
|
430
475
|
|
|
431
476
|
/**
|
|
@@ -435,12 +480,21 @@ const setGradientMethod = (type) => {
|
|
|
435
480
|
* @returns {SVGGradientElement} The existing gradient if found, `null` if not
|
|
436
481
|
*/
|
|
437
482
|
const findDuplicateGradient = (grad) => {
|
|
483
|
+
if (!grad) {
|
|
484
|
+
return null
|
|
485
|
+
}
|
|
486
|
+
if (!['linearGradient', 'radialGradient'].includes(grad.tagName)) {
|
|
487
|
+
return null
|
|
488
|
+
}
|
|
438
489
|
const defs = findDefs()
|
|
439
490
|
const existingGrads = defs.querySelectorAll('linearGradient, radialGradient')
|
|
440
491
|
let i = existingGrads.length
|
|
441
492
|
const radAttrs = ['r', 'cx', 'cy', 'fx', 'fy']
|
|
442
493
|
while (i--) {
|
|
443
494
|
const og = existingGrads[i]
|
|
495
|
+
if (og.tagName !== grad.tagName) {
|
|
496
|
+
continue
|
|
497
|
+
}
|
|
444
498
|
if (grad.tagName === 'linearGradient') {
|
|
445
499
|
if (grad.getAttribute('x1') !== og.getAttribute('x1') ||
|
|
446
500
|
grad.getAttribute('y1') !== og.getAttribute('y1') ||
|
|
@@ -514,10 +568,10 @@ const setPaintMethod = (type, paint) => {
|
|
|
514
568
|
svgCanvas.setPaintOpacity(type, p.alpha / 100, true)
|
|
515
569
|
|
|
516
570
|
// now set the current paint object
|
|
517
|
-
svgCanvas.setCurProperties(type
|
|
571
|
+
svgCanvas.setCurProperties(`${type}_paint`, p)
|
|
518
572
|
switch (p.type) {
|
|
519
573
|
case 'solidColor':
|
|
520
|
-
svgCanvas.setColor(type, p.solidColor !== 'none' ?
|
|
574
|
+
svgCanvas.setColor(type, p.solidColor !== 'none' ? `#${p.solidColor}` : 'none')
|
|
521
575
|
break
|
|
522
576
|
case 'linearGradient':
|
|
523
577
|
case 'radialGradient':
|
|
@@ -601,14 +655,31 @@ const setStrokeAttrMethod = (attr, val) => {
|
|
|
601
655
|
}
|
|
602
656
|
}
|
|
603
657
|
|
|
658
|
+
const getSelectedTextElements = () => {
|
|
659
|
+
return svgCanvas.getSelectedElements().filter(el => el?.tagName === 'text')
|
|
660
|
+
}
|
|
661
|
+
|
|
662
|
+
const getChangedTextElements = (textElements, attr, newValue) => {
|
|
663
|
+
const normalizedValue = String(newValue)
|
|
664
|
+
return textElements.filter((elem) => {
|
|
665
|
+
const oldValue = attr === '#text' ? elem.textContent : elem.getAttribute(attr)
|
|
666
|
+
return (oldValue || '') !== normalizedValue
|
|
667
|
+
})
|
|
668
|
+
}
|
|
669
|
+
|
|
670
|
+
const notifyTextChange = (textElements) => {
|
|
671
|
+
if (textElements.length > 0) {
|
|
672
|
+
svgCanvas.call('changed', textElements)
|
|
673
|
+
}
|
|
674
|
+
}
|
|
675
|
+
|
|
604
676
|
/**
|
|
605
677
|
* Check if all selected text elements are in bold.
|
|
606
678
|
* @function module:svgcanvas.SvgCanvas#getBold
|
|
607
679
|
* @returns {boolean} `true` if all selected elements are bold, `false` otherwise.
|
|
608
680
|
*/
|
|
609
681
|
const getBoldMethod = () => {
|
|
610
|
-
const
|
|
611
|
-
const textElements = selectedElements.filter(el => el?.tagName === 'text')
|
|
682
|
+
const textElements = getSelectedTextElements()
|
|
612
683
|
return textElements.every(el => el.getAttribute('font-weight') === 'bold')
|
|
613
684
|
}
|
|
614
685
|
|
|
@@ -619,12 +690,16 @@ const getBoldMethod = () => {
|
|
|
619
690
|
* @returns {void}
|
|
620
691
|
*/
|
|
621
692
|
const setBoldMethod = (b) => {
|
|
622
|
-
const
|
|
623
|
-
const
|
|
624
|
-
|
|
693
|
+
const textElements = getSelectedTextElements()
|
|
694
|
+
const value = b ? 'bold' : 'normal'
|
|
695
|
+
const changedTextElements = getChangedTextElements(textElements, 'font-weight', value)
|
|
696
|
+
if (changedTextElements.length > 0) {
|
|
697
|
+
svgCanvas.changeSelectedAttribute('font-weight', value, changedTextElements)
|
|
698
|
+
}
|
|
625
699
|
if (!textElements.some(el => el.textContent)) {
|
|
626
700
|
svgCanvas.textActions.setCursor()
|
|
627
701
|
}
|
|
702
|
+
notifyTextChange(changedTextElements)
|
|
628
703
|
}
|
|
629
704
|
|
|
630
705
|
/**
|
|
@@ -632,8 +707,7 @@ const setBoldMethod = (b) => {
|
|
|
632
707
|
* @returns {boolean} Indicates whether or not elements have the text decoration value
|
|
633
708
|
*/
|
|
634
709
|
const hasTextDecorationMethod = (value) => {
|
|
635
|
-
const
|
|
636
|
-
const textElements = selectedElements.filter(el => el?.tagName === 'text')
|
|
710
|
+
const textElements = getSelectedTextElements()
|
|
637
711
|
return textElements.every(el => (el.getAttribute('text-decoration') || '').includes(value))
|
|
638
712
|
}
|
|
639
713
|
|
|
@@ -644,8 +718,7 @@ const hasTextDecorationMethod = (value) => {
|
|
|
644
718
|
*/
|
|
645
719
|
const addTextDecorationMethod = (value) => {
|
|
646
720
|
const { ChangeElementCommand, BatchCommand } = svgCanvas.history
|
|
647
|
-
const
|
|
648
|
-
const textElements = selectedElements.filter(el => el?.tagName === 'text')
|
|
721
|
+
const textElements = getSelectedTextElements()
|
|
649
722
|
|
|
650
723
|
const batchCmd = new BatchCommand()
|
|
651
724
|
textElements.forEach(elem => {
|
|
@@ -653,7 +726,7 @@ const addTextDecorationMethod = (value) => {
|
|
|
653
726
|
// Add the new text decoration value if it did not exist
|
|
654
727
|
if (!oldValue.includes(value)) {
|
|
655
728
|
batchCmd.addSubCommand(new ChangeElementCommand(elem, { 'text-decoration': oldValue }))
|
|
656
|
-
svgCanvas.changeSelectedAttributeNoUndo('text-decoration',
|
|
729
|
+
svgCanvas.changeSelectedAttributeNoUndo('text-decoration', `${oldValue} ${value}`.trim(), [elem])
|
|
657
730
|
}
|
|
658
731
|
})
|
|
659
732
|
if (!batchCmd.isEmpty()) {
|
|
@@ -672,8 +745,7 @@ const addTextDecorationMethod = (value) => {
|
|
|
672
745
|
*/
|
|
673
746
|
const removeTextDecorationMethod = (value) => {
|
|
674
747
|
const { ChangeElementCommand, BatchCommand } = svgCanvas.history
|
|
675
|
-
const
|
|
676
|
-
const textElements = selectedElements.filter(el => el?.tagName === 'text')
|
|
748
|
+
const textElements = getSelectedTextElements()
|
|
677
749
|
|
|
678
750
|
const batchCmd = new BatchCommand()
|
|
679
751
|
textElements.forEach(elem => {
|
|
@@ -696,8 +768,7 @@ const removeTextDecorationMethod = (value) => {
|
|
|
696
768
|
* @returns {boolean} `true` if all selected elements are in italics, `false` otherwise.
|
|
697
769
|
*/
|
|
698
770
|
const getItalicMethod = () => {
|
|
699
|
-
const
|
|
700
|
-
const textElements = selectedElements.filter(el => el?.tagName === 'text')
|
|
771
|
+
const textElements = getSelectedTextElements()
|
|
701
772
|
return textElements.every(el => el.getAttribute('font-style') === 'italic')
|
|
702
773
|
}
|
|
703
774
|
|
|
@@ -708,12 +779,16 @@ const getItalicMethod = () => {
|
|
|
708
779
|
* @returns {void}
|
|
709
780
|
*/
|
|
710
781
|
const setItalicMethod = (i) => {
|
|
711
|
-
const
|
|
712
|
-
const
|
|
713
|
-
|
|
782
|
+
const textElements = getSelectedTextElements()
|
|
783
|
+
const value = i ? 'italic' : 'normal'
|
|
784
|
+
const changedTextElements = getChangedTextElements(textElements, 'font-style', value)
|
|
785
|
+
if (changedTextElements.length > 0) {
|
|
786
|
+
svgCanvas.changeSelectedAttribute('font-style', value, changedTextElements)
|
|
787
|
+
}
|
|
714
788
|
if (!textElements.some(el => el.textContent)) {
|
|
715
789
|
svgCanvas.textActions.setCursor()
|
|
716
790
|
}
|
|
791
|
+
notifyTextChange(changedTextElements)
|
|
717
792
|
}
|
|
718
793
|
|
|
719
794
|
/**
|
|
@@ -722,9 +797,12 @@ const setItalicMethod = (i) => {
|
|
|
722
797
|
* @returns {void}
|
|
723
798
|
*/
|
|
724
799
|
const setTextAnchorMethod = (value) => {
|
|
725
|
-
const
|
|
726
|
-
const
|
|
727
|
-
|
|
800
|
+
const textElements = getSelectedTextElements()
|
|
801
|
+
const changedTextElements = getChangedTextElements(textElements, 'text-anchor', value)
|
|
802
|
+
if (changedTextElements.length > 0) {
|
|
803
|
+
svgCanvas.changeSelectedAttribute('text-anchor', value, changedTextElements)
|
|
804
|
+
}
|
|
805
|
+
notifyTextChange(changedTextElements)
|
|
728
806
|
}
|
|
729
807
|
|
|
730
808
|
/**
|
|
@@ -733,12 +811,15 @@ const setTextAnchorMethod = (value) => {
|
|
|
733
811
|
* @returns {void}
|
|
734
812
|
*/
|
|
735
813
|
const setLetterSpacingMethod = (value) => {
|
|
736
|
-
const
|
|
737
|
-
const
|
|
738
|
-
|
|
814
|
+
const textElements = getSelectedTextElements()
|
|
815
|
+
const changedTextElements = getChangedTextElements(textElements, 'letter-spacing', value)
|
|
816
|
+
if (changedTextElements.length > 0) {
|
|
817
|
+
svgCanvas.changeSelectedAttribute('letter-spacing', value, changedTextElements)
|
|
818
|
+
}
|
|
739
819
|
if (!textElements.some(el => el.textContent)) {
|
|
740
820
|
svgCanvas.textActions.setCursor()
|
|
741
821
|
}
|
|
822
|
+
notifyTextChange(changedTextElements)
|
|
742
823
|
}
|
|
743
824
|
|
|
744
825
|
/**
|
|
@@ -747,12 +828,15 @@ const setLetterSpacingMethod = (value) => {
|
|
|
747
828
|
* @returns {void}
|
|
748
829
|
*/
|
|
749
830
|
const setWordSpacingMethod = (value) => {
|
|
750
|
-
const
|
|
751
|
-
const
|
|
752
|
-
|
|
831
|
+
const textElements = getSelectedTextElements()
|
|
832
|
+
const changedTextElements = getChangedTextElements(textElements, 'word-spacing', value)
|
|
833
|
+
if (changedTextElements.length > 0) {
|
|
834
|
+
svgCanvas.changeSelectedAttribute('word-spacing', value, changedTextElements)
|
|
835
|
+
}
|
|
753
836
|
if (!textElements.some(el => el.textContent)) {
|
|
754
837
|
svgCanvas.textActions.setCursor()
|
|
755
838
|
}
|
|
839
|
+
notifyTextChange(changedTextElements)
|
|
756
840
|
}
|
|
757
841
|
|
|
758
842
|
/**
|
|
@@ -761,12 +845,15 @@ const setWordSpacingMethod = (value) => {
|
|
|
761
845
|
* @returns {void}
|
|
762
846
|
*/
|
|
763
847
|
const setTextLengthMethod = (value) => {
|
|
764
|
-
const
|
|
765
|
-
const
|
|
766
|
-
|
|
848
|
+
const textElements = getSelectedTextElements()
|
|
849
|
+
const changedTextElements = getChangedTextElements(textElements, 'textLength', value)
|
|
850
|
+
if (changedTextElements.length > 0) {
|
|
851
|
+
svgCanvas.changeSelectedAttribute('textLength', value, changedTextElements)
|
|
852
|
+
}
|
|
767
853
|
if (!textElements.some(el => el.textContent)) {
|
|
768
854
|
svgCanvas.textActions.setCursor()
|
|
769
855
|
}
|
|
856
|
+
notifyTextChange(changedTextElements)
|
|
770
857
|
}
|
|
771
858
|
|
|
772
859
|
/**
|
|
@@ -775,12 +862,15 @@ const setTextLengthMethod = (value) => {
|
|
|
775
862
|
* @returns {void}
|
|
776
863
|
*/
|
|
777
864
|
const setLengthAdjustMethod = (value) => {
|
|
778
|
-
const
|
|
779
|
-
const
|
|
780
|
-
|
|
865
|
+
const textElements = getSelectedTextElements()
|
|
866
|
+
const changedTextElements = getChangedTextElements(textElements, 'lengthAdjust', value)
|
|
867
|
+
if (changedTextElements.length > 0) {
|
|
868
|
+
svgCanvas.changeSelectedAttribute('lengthAdjust', value, changedTextElements)
|
|
869
|
+
}
|
|
781
870
|
if (!textElements.some(el => el.textContent)) {
|
|
782
871
|
svgCanvas.textActions.setCursor()
|
|
783
872
|
}
|
|
873
|
+
notifyTextChange(changedTextElements)
|
|
784
874
|
}
|
|
785
875
|
|
|
786
876
|
/**
|
|
@@ -798,13 +888,16 @@ const getFontFamilyMethod = () => {
|
|
|
798
888
|
* @returns {void}
|
|
799
889
|
*/
|
|
800
890
|
const setFontFamilyMethod = (val) => {
|
|
801
|
-
const
|
|
802
|
-
const
|
|
891
|
+
const textElements = getSelectedTextElements()
|
|
892
|
+
const changedTextElements = getChangedTextElements(textElements, 'font-family', val)
|
|
803
893
|
svgCanvas.setCurText('font_family', val)
|
|
804
|
-
|
|
894
|
+
if (changedTextElements.length > 0) {
|
|
895
|
+
svgCanvas.changeSelectedAttribute('font-family', val, changedTextElements)
|
|
896
|
+
}
|
|
805
897
|
if (!textElements.some(el => el.textContent)) {
|
|
806
898
|
svgCanvas.textActions.setCursor()
|
|
807
899
|
}
|
|
900
|
+
notifyTextChange(changedTextElements)
|
|
808
901
|
}
|
|
809
902
|
|
|
810
903
|
/**
|
|
@@ -814,8 +907,13 @@ const setFontFamilyMethod = (val) => {
|
|
|
814
907
|
* @returns {void}
|
|
815
908
|
*/
|
|
816
909
|
const setFontColorMethod = (val) => {
|
|
910
|
+
const textElements = getSelectedTextElements()
|
|
911
|
+
const changedTextElements = getChangedTextElements(textElements, 'fill', val)
|
|
817
912
|
svgCanvas.setCurText('fill', val)
|
|
818
|
-
|
|
913
|
+
if (changedTextElements.length > 0) {
|
|
914
|
+
svgCanvas.changeSelectedAttribute('fill', val, changedTextElements)
|
|
915
|
+
}
|
|
916
|
+
notifyTextChange(changedTextElements)
|
|
819
917
|
}
|
|
820
918
|
|
|
821
919
|
/**
|
|
@@ -841,12 +939,16 @@ const getFontSizeMethod = () => {
|
|
|
841
939
|
* @returns {void}
|
|
842
940
|
*/
|
|
843
941
|
const setFontSizeMethod = (val) => {
|
|
844
|
-
const
|
|
942
|
+
const textElements = getSelectedTextElements()
|
|
943
|
+
const changedTextElements = getChangedTextElements(textElements, 'font-size', val)
|
|
845
944
|
svgCanvas.setCurText('font_size', val)
|
|
846
|
-
|
|
847
|
-
|
|
945
|
+
if (changedTextElements.length > 0) {
|
|
946
|
+
svgCanvas.changeSelectedAttribute('font-size', val, changedTextElements)
|
|
947
|
+
}
|
|
948
|
+
if (!textElements.some(el => el.textContent)) {
|
|
848
949
|
svgCanvas.textActions.setCursor()
|
|
849
950
|
}
|
|
951
|
+
notifyTextChange(changedTextElements)
|
|
850
952
|
}
|
|
851
953
|
|
|
852
954
|
/**
|
|
@@ -892,32 +994,48 @@ const setImageURLMethod = (val) => {
|
|
|
892
994
|
const setsize = (!attrs.width || !attrs.height)
|
|
893
995
|
|
|
894
996
|
const curHref = getHref(elem)
|
|
997
|
+
const hrefChanged = curHref !== val
|
|
895
998
|
|
|
896
999
|
// Do nothing if no URL change or size change
|
|
897
|
-
if (
|
|
1000
|
+
if (!hrefChanged && !setsize) {
|
|
898
1001
|
return
|
|
899
1002
|
}
|
|
900
1003
|
|
|
901
1004
|
const batchCmd = new BatchCommand('Change Image URL')
|
|
902
1005
|
|
|
903
|
-
|
|
904
|
-
|
|
905
|
-
|
|
906
|
-
|
|
1006
|
+
if (hrefChanged) {
|
|
1007
|
+
setHref(elem, val)
|
|
1008
|
+
batchCmd.addSubCommand(new ChangeElementCommand(elem, {
|
|
1009
|
+
'#href': curHref
|
|
1010
|
+
}))
|
|
1011
|
+
}
|
|
1012
|
+
|
|
1013
|
+
let finalized = false
|
|
1014
|
+
const finalize = () => {
|
|
1015
|
+
if (finalized) { return }
|
|
1016
|
+
finalized = true
|
|
1017
|
+
if (batchCmd.isEmpty()) { return }
|
|
1018
|
+
svgCanvas.addCommandToHistory(batchCmd)
|
|
1019
|
+
svgCanvas.call('changed', [elem])
|
|
1020
|
+
}
|
|
1021
|
+
|
|
907
1022
|
const img = new Image()
|
|
908
|
-
img.onload =
|
|
1023
|
+
img.onload = () => {
|
|
909
1024
|
const changes = {
|
|
910
1025
|
width: elem.getAttribute('width'),
|
|
911
1026
|
height: elem.getAttribute('height')
|
|
912
1027
|
}
|
|
913
|
-
elem.setAttribute('width',
|
|
914
|
-
elem.setAttribute('height',
|
|
1028
|
+
elem.setAttribute('width', img.width)
|
|
1029
|
+
elem.setAttribute('height', img.height)
|
|
915
1030
|
|
|
916
|
-
svgCanvas.selectorManager.requestSelector(elem)
|
|
1031
|
+
const selector = svgCanvas.selectorManager.requestSelector(elem)
|
|
1032
|
+
selector && selector.resize()
|
|
917
1033
|
|
|
918
1034
|
batchCmd.addSubCommand(new ChangeElementCommand(elem, changes))
|
|
919
|
-
|
|
920
|
-
|
|
1035
|
+
finalize()
|
|
1036
|
+
}
|
|
1037
|
+
img.onerror = () => {
|
|
1038
|
+
finalize()
|
|
921
1039
|
}
|
|
922
1040
|
img.src = val
|
|
923
1041
|
}
|
|
@@ -969,15 +1087,27 @@ const setRectRadiusMethod = (val) => {
|
|
|
969
1087
|
const { ChangeElementCommand } = svgCanvas.history
|
|
970
1088
|
const selectedElements = svgCanvas.getSelectedElements()
|
|
971
1089
|
const selected = selectedElements[0]
|
|
972
|
-
if (selected?.tagName
|
|
973
|
-
|
|
974
|
-
|
|
975
|
-
|
|
976
|
-
|
|
977
|
-
svgCanvas.addCommandToHistory(new ChangeElementCommand(selected, { rx: r, ry: r }, 'Radius'))
|
|
978
|
-
svgCanvas.call('changed', [selected])
|
|
979
|
-
}
|
|
1090
|
+
if (selected?.tagName !== 'rect') { return }
|
|
1091
|
+
|
|
1092
|
+
const radius = Number(val)
|
|
1093
|
+
if (!Number.isFinite(radius) || radius < 0) {
|
|
1094
|
+
return
|
|
980
1095
|
}
|
|
1096
|
+
|
|
1097
|
+
const oldRx = selected.getAttribute('rx')
|
|
1098
|
+
const oldRy = selected.getAttribute('ry')
|
|
1099
|
+
const currentRx = Number(oldRx)
|
|
1100
|
+
const currentRy = Number(oldRy)
|
|
1101
|
+
const hasCurrentRx = oldRx !== null && Number.isFinite(currentRx)
|
|
1102
|
+
const hasCurrentRy = oldRy !== null && Number.isFinite(currentRy)
|
|
1103
|
+
const already = (radius === 0 && oldRx === null && oldRy === null) ||
|
|
1104
|
+
(hasCurrentRx && hasCurrentRy && currentRx === radius && currentRy === radius)
|
|
1105
|
+
if (already) { return }
|
|
1106
|
+
|
|
1107
|
+
selected.setAttribute('rx', radius)
|
|
1108
|
+
selected.setAttribute('ry', radius)
|
|
1109
|
+
svgCanvas.addCommandToHistory(new ChangeElementCommand(selected, { rx: oldRx, ry: oldRy }, 'Radius'))
|
|
1110
|
+
svgCanvas.call('changed', [selected])
|
|
981
1111
|
}
|
|
982
1112
|
|
|
983
1113
|
/**
|
|
@@ -1021,7 +1151,9 @@ const setSegTypeMethod = (newType) => {
|
|
|
1021
1151
|
*/
|
|
1022
1152
|
const setBackgroundMethod = (color, url) => {
|
|
1023
1153
|
const bg = getElement('canvasBackground')
|
|
1154
|
+
if (!bg) { return }
|
|
1024
1155
|
const border = bg.querySelector('rect')
|
|
1156
|
+
if (!border) { return }
|
|
1025
1157
|
let bgImg = getElement('background_image')
|
|
1026
1158
|
let bgPattern = getElement('background_pattern')
|
|
1027
1159
|
border.setAttribute('fill', color === 'chessboard' ? '#fff' : color)
|