@svgedit/svgcanvas 7.1.4

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/svgcanvas.js ADDED
@@ -0,0 +1,1347 @@
1
+ /**
2
+ * Numerous tools for working with the editor's "canvas".
3
+ * @module svgcanvas
4
+ *
5
+ * @license MIT
6
+ *
7
+ * @copyright 2010 Alexis Deveria, 2010 Pavol Rusnak, 2010 Jeff Schiller, 2021 OptimistikSAS
8
+ *
9
+ */
10
+ import 'pathseg' // SVGPathSeg Polyfill (see https://github.com/progers/pathseg)
11
+
12
+ import Paint from './paint.js'
13
+ import * as pathModule from './path.js'
14
+ import * as history from './history.js'
15
+ import * as draw from './draw.js'
16
+ import { init as pasteInit, pasteElementsMethod } from './paste-elem.js'
17
+ import { init as touchInit } from './touch.js'
18
+ import { svgRootElement } from './svgroot.js'
19
+ import {
20
+ init as undoInit,
21
+ changeSelectedAttributeNoUndoMethod,
22
+ changeSelectedAttributeMethod
23
+ } from './undo.js'
24
+ import { init as selectionInit } from './selection.js'
25
+ import { init as textActionsInit, textActionsMethod } from './text-actions.js'
26
+ import { init as eventInit } from './event.js'
27
+ import {
28
+ init as jsonInit,
29
+ getJsonFromSvgElements,
30
+ addSVGElementsFromJson
31
+ } from './json.js'
32
+ import * as elemGetSet from './elem-get-set.js'
33
+ import { init as selectedElemInit } from './selected-elem.js'
34
+ import {
35
+ init as blurInit,
36
+ setBlurNoUndo,
37
+ setBlurOffsets,
38
+ setBlur
39
+ } from './blur-event.js'
40
+ import { sanitizeSvg } from './sanitize.js'
41
+ import { getReverseNS, NS } from './namespaces.js'
42
+ import {
43
+ assignAttributes,
44
+ cleanupElement,
45
+ getElement,
46
+ getUrlFromAttr,
47
+ findDefs,
48
+ getHref,
49
+ setHref,
50
+ getRefElem,
51
+ getRotationAngle,
52
+ getBBoxOfElementAsPath,
53
+ convertToPath,
54
+ encode64,
55
+ decode64,
56
+ getVisibleElements,
57
+ init as utilsInit,
58
+ getBBox as utilsGetBBox,
59
+ getStrokedBBoxDefaultVisible,
60
+ blankPageObjectURL,
61
+ $id,
62
+ $qa,
63
+ $qq,
64
+ $click,
65
+ getFeGaussianBlur,
66
+ stringToHTML,
67
+ insertChildAtIndex
68
+ } from './utilities.js'
69
+ import {
70
+ matrixMultiply,
71
+ hasMatrixTransform,
72
+ transformListToTransform
73
+ } from './math.js'
74
+ import { convertToNum, init as unitsInit, getTypeMap } from '../../src/common/units.js'
75
+ import { init as svgInit } from './svg-exec.js'
76
+ import { remapElement, init as coordsInit } from './coords.js'
77
+ import {
78
+ recalculateDimensions,
79
+ init as recalculateInit
80
+ } from './recalculate.js'
81
+ import { getSelectorManager, Selector, init as selectInit } from './select.js'
82
+ import { clearSvgContentElementInit, init as clearInit } from './clear.js'
83
+ import {
84
+ getClosest,
85
+ getParents,
86
+ mergeDeep
87
+ } from '../../src/common/util.js'
88
+
89
+ import dataStorage from './dataStorage.js'
90
+
91
+ const visElems =
92
+ 'a,circle,ellipse,foreignObject,g,image,line,path,polygon,polyline,rect,svg,text,tspan,use'
93
+ const refAttrs = [
94
+ 'clip-path',
95
+ 'fill',
96
+ 'filter',
97
+ 'marker-end',
98
+ 'marker-mid',
99
+ 'marker-start',
100
+ 'mask',
101
+ 'stroke'
102
+ ]
103
+
104
+ const THRESHOLD_DIST = 0.8
105
+ const STEP_COUNT = 10
106
+ const CLIPBOARD_ID = 'svgedit_clipboard'
107
+
108
+ /**
109
+ * The main SvgCanvas class that manages all SVG-related functions.
110
+ * @memberof module:svgcanvas
111
+ *
112
+ */
113
+ class SvgCanvas {
114
+ /**
115
+ * @param {HTMLElement} container - The container HTML element that should hold the SVG root element
116
+ * @param {module:SVGeditor.configObj.curConfig} config - An object that contains configuration data
117
+ */
118
+ constructor (container, config) {
119
+ // imported function made available as methods
120
+ this.initializeSvgCanvasMethods()
121
+ const { pathActions } = pathModule
122
+
123
+ // initialize class variables
124
+ this.saveOptions = { round_digits: 2 } // Object with save options
125
+ this.importIds = {} // Object with IDs for imported files, to see if one was already added
126
+ this.extensions = {} // Object to contain all included extensions
127
+ this.removedElements = {} // Map of deleted reference elements
128
+ this.started = false // Boolean indicating whether or not a draw action has been this.started
129
+ this.startTransform = null // String with an element's initial transform attribute value
130
+ this.currentMode = 'select' // String indicating the current editor mode
131
+ this.currentResizeMode = 'none' // String with the current direction in which an element is being resized
132
+ this.justSelected = null // The DOM element that was just selected
133
+ this.rubberBox = null // DOM element for selection rectangle drawn by the user
134
+ this.curBBoxes = [] // Array of current BBoxes, used in getIntersectionList().
135
+ this.lastClickPoint = null // Canvas point for the most recent right click
136
+ this.events = {} // Object to contain editor event names and callback functions
137
+ this.rootSctm = null // Root Current Transformation Matrix in user units
138
+ this.drawnPath = null
139
+ this.freehand = {
140
+ // Mouse events
141
+ minx: null,
142
+ miny: null,
143
+ maxx: null,
144
+ maxy: null
145
+ }
146
+ this.dAttr = null
147
+ this.startX = null
148
+ this.startY = null
149
+ this.rStartX = null
150
+ this.rStartY = null
151
+ this.initBbox = {}
152
+ this.sumDistance = 0
153
+ this.controllPoint2 = { x: 0, y: 0 }
154
+ this.controllPoint1 = { x: 0, y: 0 }
155
+ this.start = { x: 0, y: 0 }
156
+ this.end = { x: 0, y: 0 }
157
+ this.bSpline = { x: 0, y: 0 }
158
+ this.nextPos = { x: 0, y: 0 }
159
+ this.idprefix = 'svg_' // Prefix string for element IDs
160
+ this.encodableImages = {}
161
+
162
+ this.curConfig = {
163
+ // Default configuration options
164
+ show_outside_canvas: true,
165
+ selectNew: true,
166
+ dimensions: [640, 480]
167
+ }
168
+ // Update config with new one if given
169
+ if (config) {
170
+ this.curConfig = SvgCanvas.mergeDeep(this.curConfig, config)
171
+ }
172
+ this.lastGoodImgUrl = `${this.curConfig.imgPath}/logo.svg` // String with image URL of last loadable image
173
+ const { dimensions } = this.curConfig // Array with width/height of canvas
174
+
175
+ // "document" element associated with the container (same as window.document using default svg-editor.js)
176
+ // NOTE: This is not actually a SVG document, but an HTML document.
177
+ this.svgdoc = window.document
178
+ this.container = container
179
+ // This is a container for the document being edited, not the document itself.
180
+ this.svgroot = svgRootElement(this.svgdoc, dimensions)
181
+ container.append(this.svgroot)
182
+ // The actual element that represents the final output SVG element.
183
+ this.svgContent = this.svgdoc.createElementNS(NS.SVG, 'svg')
184
+ touchInit(this)
185
+ clearInit(this)
186
+ this.clearSvgContentElement()
187
+ // Current `draw.Drawing` object.
188
+ this.current_drawing_ = new draw.Drawing(this.svgContent, this.idprefix)
189
+ // Float displaying the current zoom level (1 = 100%, .5 = 50%, etc.).
190
+ this.zoom = 1
191
+
192
+ // pointer to current group (for in-group editing)
193
+ this.currentGroup = null
194
+
195
+ // Object containing data for the currently selected styles
196
+ const allProperties = {
197
+ shape: {
198
+ fill:
199
+ (this.curConfig.initFill.color === 'none' ? '' : '#') +
200
+ this.curConfig.initFill.color,
201
+ fill_paint: null,
202
+ fill_opacity: this.curConfig.initFill.opacity,
203
+ stroke: '#' + this.curConfig.initStroke.color,
204
+ stroke_paint: null,
205
+ stroke_opacity: this.curConfig.initStroke.opacity,
206
+ stroke_width: this.curConfig.initStroke.width,
207
+ stroke_dasharray: 'none',
208
+ stroke_linejoin: 'miter',
209
+ stroke_linecap: 'butt',
210
+ opacity: this.curConfig.initOpacity
211
+ }
212
+ }
213
+ allProperties.text = SvgCanvas.mergeDeep({}, allProperties.shape)
214
+ allProperties.text = SvgCanvas.mergeDeep(allProperties.text, {
215
+ fill: '#000000',
216
+ stroke_width: this.curConfig.text?.stroke_width,
217
+ font_size: this.curConfig.text?.font_size,
218
+ font_family: this.curConfig.text?.font_family
219
+ })
220
+ this.curText = allProperties.text // Current text style properties
221
+
222
+ // Current shape style properties
223
+ this.curShape = allProperties.shape
224
+ this.curProperties = this.curShape // Current general properties
225
+
226
+ // Array with all the currently selected elements
227
+ // default size of 1 until it needs to grow bigger
228
+ this.selectedElements = []
229
+
230
+ jsonInit(this)
231
+ unitsInit(this)
232
+ utilsInit(this)
233
+ coordsInit(this)
234
+ recalculateInit(this)
235
+ selectInit(this)
236
+ undoInit(this)
237
+ selectionInit(this)
238
+
239
+ this.nsMap = getReverseNS()
240
+ this.selectorManager = getSelectorManager()
241
+
242
+ this.pathActions = pathActions
243
+ pathModule.init(this)
244
+ // Interface strings, usually for title elements
245
+ this.uiStrings = {}
246
+
247
+ // Animation element to change the opacity of any newly created element
248
+ this.opacAni = document.createElementNS(NS.SVG, 'animate')
249
+ this.opacAni.setAttribute('attributeName', 'opacity')
250
+ this.opacAni.setAttribute('begin', 'indefinite')
251
+ this.opacAni.setAttribute('dur', 1)
252
+ this.opacAni.setAttribute('fill', 'freeze')
253
+ this.svgroot.appendChild(this.opacAni)
254
+
255
+ eventInit(this)
256
+ textActionsInit(this)
257
+ svgInit(this)
258
+ draw.init(this)
259
+ elemGetSet.init(this)
260
+
261
+ // prevent links from being followed in the canvas
262
+ const handleLinkInCanvas = e => {
263
+ e.preventDefault()
264
+ return false
265
+ }
266
+ container.addEventListener('mousedown', this.mouseDownEvent)
267
+ container.addEventListener('mousemove', this.mouseMoveEvent)
268
+ $click(container, handleLinkInCanvas)
269
+ container.addEventListener('dblclick', this.dblClickEvent)
270
+ container.addEventListener('mouseup', this.mouseUpEvent)
271
+ container.addEventListener('mouseleave', this.mouseOutEvent)
272
+ container.addEventListener('mousewheel', this.DOMMouseScrollEvent)
273
+ container.addEventListener('DOMMouseScroll', this.DOMMouseScrollEvent)
274
+
275
+ // Alias function
276
+ this.linkControlPoints = pathActions.linkControlPoints
277
+ this.curCommand = null
278
+ this.filter = null
279
+ this.filterHidden = false
280
+
281
+ blurInit(this)
282
+ selectedElemInit(this)
283
+
284
+ /**
285
+ * Transfers sessionStorage from one tab to another.
286
+ * @param {!Event} ev Storage event.
287
+ * @returns {void}
288
+ */
289
+ const storageChange = ev => {
290
+ if (!ev.newValue) return // This is a call from removeItem.
291
+ if (ev.key === CLIPBOARD_ID + '_startup') {
292
+ // Another tab asked for our sessionStorage.
293
+ localStorage.removeItem(CLIPBOARD_ID + '_startup')
294
+ this.flashStorage()
295
+ } else if (ev.key === CLIPBOARD_ID) {
296
+ // Another tab sent data.
297
+ sessionStorage.setItem(CLIPBOARD_ID, ev.newValue)
298
+ }
299
+ }
300
+
301
+ // Listen for changes to localStorage.
302
+ window.addEventListener('storage', storageChange, false)
303
+ // Ask other tabs for sessionStorage (this is ONLY to trigger event).
304
+ localStorage.setItem(CLIPBOARD_ID + '_startup', Math.random())
305
+
306
+ pasteInit(this)
307
+
308
+ this.contentW = this.getResolution().w
309
+ this.contentH = this.getResolution().h
310
+ this.clear()
311
+ } // End constructor
312
+
313
+ getSvgOption () {
314
+ return this.saveOptions
315
+ }
316
+
317
+ setSvgOption (key, value) {
318
+ this.saveOptions[key] = value
319
+ }
320
+
321
+ getSelectedElements () {
322
+ return this.selectedElements
323
+ }
324
+
325
+ setSelectedElements (key, value) {
326
+ this.selectedElements[key] = value
327
+ }
328
+
329
+ setEmptySelectedElements () {
330
+ this.selectedElements = []
331
+ }
332
+
333
+ getSvgRoot () {
334
+ return this.svgroot
335
+ }
336
+
337
+ getDOMDocument () {
338
+ return this.svgdoc
339
+ }
340
+
341
+ getDOMContainer () {
342
+ return this.container
343
+ }
344
+
345
+ getCurConfig () {
346
+ return this.curConfig
347
+ }
348
+
349
+ setIdPrefix (p) {
350
+ this.idprefix = p
351
+ }
352
+
353
+ getCurrentDrawing () {
354
+ return this.current_drawing_
355
+ }
356
+
357
+ getCurShape () {
358
+ return this.curShape
359
+ }
360
+
361
+ getCurrentGroup () {
362
+ return this.currentGroup
363
+ }
364
+
365
+ getBaseUnit () {
366
+ return this.curConfig.baseUnit
367
+ }
368
+
369
+ getHeight () {
370
+ return this.svgContent.getAttribute('height') / this.zoom
371
+ }
372
+
373
+ getWidth () {
374
+ return this.svgContent.getAttribute('width') / this.zoom
375
+ }
376
+
377
+ getRoundDigits () {
378
+ return this.saveOptions.round_digits
379
+ }
380
+
381
+ getSnappingStep () {
382
+ return this.curConfig.snappingStep
383
+ }
384
+
385
+ getGridSnapping () {
386
+ return this.curConfig.gridSnapping
387
+ }
388
+
389
+ getStartTransform () {
390
+ return this.startTransform
391
+ }
392
+
393
+ setStartTransform (transform) {
394
+ this.startTransform = transform
395
+ }
396
+
397
+ getZoom () {
398
+ return this.zoom
399
+ }
400
+
401
+ round (val) {
402
+ return Number.parseInt(val * this.zoom) / this.zoom
403
+ }
404
+
405
+ createSVGElement (jsonMap) {
406
+ return this.addSVGElementsFromJson(jsonMap)
407
+ }
408
+
409
+ getContainer () {
410
+ return this.container
411
+ }
412
+
413
+ setStarted (s) {
414
+ this.started = s
415
+ }
416
+
417
+ getRubberBox () {
418
+ return this.rubberBox
419
+ }
420
+
421
+ setRubberBox (rb) {
422
+ this.rubberBox = rb
423
+ return this.rubberBox
424
+ }
425
+
426
+ addPtsToSelection ({ closedSubpath, grips }) {
427
+ // TODO: Correct this:
428
+ this.pathActions.canDeleteNodes = true
429
+ this.pathActions.closed_subpath = closedSubpath
430
+ this.call('pointsAdded', { closedSubpath, grips })
431
+ this.call('selected', grips)
432
+ }
433
+
434
+ /**
435
+ * @param {PlainObject} changes
436
+ * @param {ChangeElementCommand} changes.cmd
437
+ * @param {SVGPathElement} changes.elem
438
+ * @fires module:svgcanvas.SvgCanvas#event:changed
439
+ * @returns {void}
440
+ */
441
+ endChanges ({ cmd, elem }) {
442
+ this.addCommandToHistory(cmd)
443
+ this.call('changed', [elem])
444
+ }
445
+
446
+ getCurrentMode () {
447
+ return this.currentMode
448
+ }
449
+
450
+ setCurrentMode (cm) {
451
+ this.currentMode = cm
452
+ return this.currentMode
453
+ }
454
+
455
+ getDrawnPath () {
456
+ return this.drawnPath
457
+ }
458
+
459
+ setDrawnPath (dp) {
460
+ this.drawnPath = dp
461
+ return this.drawnPath
462
+ }
463
+
464
+ setCurrentGroup (cg) {
465
+ this.currentGroup = cg
466
+ }
467
+
468
+ changeSvgContent () {
469
+ this.call('changed', [this.svgContent])
470
+ }
471
+
472
+ getStarted () {
473
+ return this.started
474
+ }
475
+
476
+ getCanvas () {
477
+ return this
478
+ }
479
+
480
+ getrootSctm () {
481
+ return this.rootSctm
482
+ }
483
+
484
+ getStartX () {
485
+ return this.startX
486
+ }
487
+
488
+ setStartX (value) {
489
+ this.startX = value
490
+ }
491
+
492
+ getStartY () {
493
+ return this.startY
494
+ }
495
+
496
+ setStartY (value) {
497
+ this.startY = value
498
+ }
499
+
500
+ getRStartX () {
501
+ return this.rStartX
502
+ }
503
+
504
+ getRStartY () {
505
+ return this.rStartY
506
+ }
507
+
508
+ getInitBbox () {
509
+ return this.initBbox
510
+ }
511
+
512
+ getCurrentResizeMode () {
513
+ return this.currentResizeMode
514
+ }
515
+
516
+ getJustSelected () {
517
+ return this.justSelected
518
+ }
519
+
520
+ getOpacAni () {
521
+ return this.opacAni
522
+ }
523
+
524
+ getParameter () {
525
+ return this.parameter
526
+ }
527
+
528
+ getNextParameter () {
529
+ return this.nextParameter
530
+ }
531
+
532
+ getStepCount () {
533
+ return STEP_COUNT
534
+ }
535
+
536
+ getThreSholdDist () {
537
+ return THRESHOLD_DIST
538
+ }
539
+
540
+ getSumDistance () {
541
+ return this.sumDistance
542
+ }
543
+
544
+ getStart (key) {
545
+ return this.start[key]
546
+ }
547
+
548
+ getEnd (key) {
549
+ return this.end[key]
550
+ }
551
+
552
+ getbSpline (key) {
553
+ return this.bSpline[key]
554
+ }
555
+
556
+ getNextPos (key) {
557
+ return this.nextPos[key]
558
+ }
559
+
560
+ getControllPoint1 (key) {
561
+ return this.controllPoint1[key]
562
+ }
563
+
564
+ getControllPoint2 (key) {
565
+ return this.controllPoint2[key]
566
+ }
567
+
568
+ getFreehand (key) {
569
+ return this.freehand[key]
570
+ }
571
+
572
+ getDrawing () {
573
+ return this.getCurrentDrawing()
574
+ }
575
+
576
+ getDAttr () {
577
+ return this.dAttr
578
+ }
579
+
580
+ getLastGoodImgUrl () {
581
+ return this.lastGoodImgUrl
582
+ }
583
+
584
+ getCurText (key) {
585
+ return this.curText[key]
586
+ }
587
+
588
+ setDAttr (value) {
589
+ this.dAttr = value
590
+ }
591
+
592
+ setEnd (key, value) {
593
+ this.end[key] = value
594
+ }
595
+
596
+ setControllPoint1 (key, value) {
597
+ this.controllPoint1[key] = value
598
+ }
599
+
600
+ setControllPoint2 (key, value) {
601
+ this.controllPoint2[key] = value
602
+ }
603
+
604
+ setJustSelected (value) {
605
+ this.justSelected = value
606
+ }
607
+
608
+ setParameter (value) {
609
+ this.parameter = value
610
+ }
611
+
612
+ setStart (value) {
613
+ this.start = value
614
+ }
615
+
616
+ setRStartX (value) {
617
+ this.rStartX = value
618
+ }
619
+
620
+ setRStartY (value) {
621
+ this.rStartY = value
622
+ }
623
+
624
+ setSumDistance (value) {
625
+ this.sumDistance = value
626
+ }
627
+
628
+ setbSpline (value) {
629
+ this.bSpline = value
630
+ }
631
+
632
+ setNextPos (value) {
633
+ this.nextPos = value
634
+ }
635
+
636
+ setNextParameter (value) {
637
+ this.nextParameter = value
638
+ }
639
+
640
+ setCurText (key, value) {
641
+ this.curText[key] = value
642
+ }
643
+
644
+ setFreehand (key, value) {
645
+ this.freehand[key] = value
646
+ }
647
+
648
+ setCurBBoxes (value) {
649
+ this.curBBoxes = value
650
+ }
651
+
652
+ getCurBBoxes () {
653
+ return this.curBBoxes
654
+ }
655
+
656
+ setInitBbox (value) {
657
+ this.initBbox = value
658
+ }
659
+
660
+ setRootSctm (value) {
661
+ this.rootSctm = value
662
+ }
663
+
664
+ setCurrentResizeMode (value) {
665
+ this.currentResizeMode = value
666
+ }
667
+
668
+ getLastClickPoint (key) {
669
+ return this.lastClickPoint[key]
670
+ }
671
+
672
+ setLastClickPoint (value) {
673
+ this.lastClickPoint = value
674
+ }
675
+
676
+ getId () {
677
+ return this.getCurrentDrawing().getId()
678
+ }
679
+
680
+ getUIStrings () {
681
+ return this.uiStrings
682
+ }
683
+
684
+ getNsMap () {
685
+ return this.nsMap
686
+ }
687
+
688
+ getSvgOptionApply () {
689
+ return this.saveOptions.apply
690
+ }
691
+
692
+ getSvgOptionImages () {
693
+ return this.saveOptions.images
694
+ }
695
+
696
+ getEncodableImages (key) {
697
+ return this.encodableImages[key]
698
+ }
699
+
700
+ setEncodableImages (key, value) {
701
+ this.encodableImages[key] = value
702
+ }
703
+
704
+ getVisElems () {
705
+ return visElems
706
+ }
707
+
708
+ getIdPrefix () {
709
+ return this.idprefix
710
+ }
711
+
712
+ getDataStorage () {
713
+ return dataStorage
714
+ }
715
+
716
+ setZoom (value) {
717
+ this.zoom = value
718
+ }
719
+
720
+ getImportIds (key) {
721
+ return this.importIds[key]
722
+ }
723
+
724
+ setImportIds (key, value) {
725
+ this.importIds[key] = value
726
+ }
727
+
728
+ setRemovedElements (key, value) {
729
+ this.removedElements[key] = value
730
+ }
731
+
732
+ setSvgContent (value) {
733
+ this.svgContent = value
734
+ }
735
+
736
+ getrefAttrs () {
737
+ return refAttrs
738
+ }
739
+
740
+ setCanvas (key, value) {
741
+ this[key] = value
742
+ }
743
+
744
+ setCurProperties (key, value) {
745
+ this.curProperties[key] = value
746
+ }
747
+
748
+ getCurProperties (key) {
749
+ return this.curProperties[key]
750
+ }
751
+
752
+ setCurShape (key, value) {
753
+ this.curShape[key] = value
754
+ }
755
+
756
+ gettingSelectorManager () {
757
+ return this.selectorManager
758
+ }
759
+
760
+ getContentW () {
761
+ return this.contentW
762
+ }
763
+
764
+ getContentH () {
765
+ return this.contentH
766
+ }
767
+
768
+ getClipboardID () {
769
+ return CLIPBOARD_ID
770
+ }
771
+
772
+ getSvgContent () {
773
+ return this.svgContent
774
+ }
775
+
776
+ getExtensions () {
777
+ return this.extensions
778
+ }
779
+
780
+ getSelector () {
781
+ return Selector
782
+ }
783
+
784
+ getMode () {
785
+ return this.currentMode
786
+ } // The current editor mode string
787
+
788
+ getNextId () {
789
+ return this.getCurrentDrawing().getNextId()
790
+ }
791
+
792
+ getCurCommand () {
793
+ return this.curCommand
794
+ }
795
+
796
+ setCurCommand (value) {
797
+ this.curCommand = value
798
+ }
799
+
800
+ getFilter () {
801
+ return this.filter
802
+ }
803
+
804
+ setFilter (value) {
805
+ this.filter = value
806
+ }
807
+
808
+ getFilterHidden () {
809
+ return this.filterHidden
810
+ }
811
+
812
+ setFilterHidden (value) {
813
+ this.filterHidden = value
814
+ }
815
+
816
+ /**
817
+ * Sets the editor's mode to the given string.
818
+ * @function module:svgcanvas.SvgCanvas#setMode
819
+ * @param {string} name - String with the new mode to change to
820
+ * @returns {void}
821
+ */
822
+ setMode (name) {
823
+ this.pathActions.clear(true)
824
+ this.textActions.clear()
825
+ this.curProperties =
826
+ this.selectedElements[0]?.nodeName === 'text'
827
+ ? this.curText
828
+ : this.curShape
829
+ this.currentMode = name
830
+ }
831
+
832
+ /**
833
+ * Clears the current document. This is not an undoable action.
834
+ * @function module:svgcanvas.SvgCanvas#clear
835
+ * @fires module:svgcanvas.SvgCanvas#event:beforeClear|afterClear
836
+ * @returns {void}
837
+ */
838
+ clear () {
839
+ this.call('beforeClear')
840
+ this.pathActions.clear()
841
+ this.clearSelection()
842
+ // clear the svgcontent node
843
+ this.clearSvgContentElement()
844
+ // create new document
845
+ this.current_drawing_ = new draw.Drawing(this.svgContent)
846
+ // create empty first layer
847
+ this.createLayer('Layer 1')
848
+ // clear the undo stack
849
+ this.undoMgr.resetUndoStack()
850
+ // reset the selector manager
851
+ this.selectorManager.initGroup()
852
+ // reset the rubber band box
853
+ this.rubberBox = this.selectorManager.getRubberBandBox()
854
+ this.call('afterClear')
855
+ }
856
+
857
+ async addExtension (name, extInitFunc, { importLocale }) {
858
+ if (typeof extInitFunc !== 'function') {
859
+ throw new TypeError(
860
+ 'Function argument expected for `svgcanvas.addExtension`'
861
+ )
862
+ }
863
+ if (name in this.extensions) {
864
+ throw new Error(
865
+ 'Cannot add extension "' +
866
+ name +
867
+ '", an extension by that name already exists.'
868
+ )
869
+ }
870
+ const argObj = {
871
+ importLocale,
872
+ svgroot: this.svgroot,
873
+ svgContent: this.svgContent,
874
+ nonce: this.getCurrentDrawing().getNonce(),
875
+ selectorManager: this.selectorManager
876
+ }
877
+ const extObj = await extInitFunc(argObj)
878
+ if (extObj) {
879
+ extObj.name = name
880
+ }
881
+ this.extensions[name] = extObj
882
+ return this.call('extension_added', extObj)
883
+ }
884
+
885
+ addCommandToHistory (cmd) {
886
+ this.undoMgr.addCommandToHistory(cmd)
887
+ }
888
+
889
+ restoreRefElements (elem) {
890
+ // Look for missing reference elements, restore any found
891
+ const attrs = {}
892
+ refAttrs.forEach((item, _) => {
893
+ attrs[item] = elem.getAttribute(item)
894
+ })
895
+ Object.values(attrs).forEach(val => {
896
+ if (val?.startsWith('url(')) {
897
+ const id = getUrlFromAttr(val).substr(1)
898
+ const ref = getElement(id)
899
+ if (!ref) {
900
+ findDefs().append(this.removedElements[id])
901
+ delete this.removedElements[id]
902
+ }
903
+ }
904
+ })
905
+ const childs = elem.getElementsByTagName('*')
906
+
907
+ if (childs.length) {
908
+ for (let i = 0, l = childs.length; i < l; i++) {
909
+ this.restoreRefElements(childs[i])
910
+ }
911
+ }
912
+ }
913
+
914
+ call (ev, arg) {
915
+ if (this.events[ev]) {
916
+ return this.events[ev](window, arg)
917
+ }
918
+ return undefined
919
+ }
920
+
921
+ /**
922
+ * Attaches a callback function to an event.
923
+ * @function module:svgcanvas.SvgCanvas#bind
924
+ * @param {string} ev - String indicating the name of the event
925
+ * @param {module:svgcanvas.EventHandler} f - The callback function to bind to the event
926
+ * @returns {module:svgcanvas.EventHandler} The previous event
927
+ */
928
+ bind (ev, f) {
929
+ const old = this.events[ev]
930
+ this.events[ev] = f
931
+ return old
932
+ }
933
+
934
+ /**
935
+ * Flash the clipboard data momentarily on localStorage so all tabs can see.
936
+ * @returns {void}
937
+ */
938
+ flashStorage () {
939
+ const data = sessionStorage.getItem(CLIPBOARD_ID)
940
+ localStorage.setItem(CLIPBOARD_ID, data)
941
+ setTimeout(() => {
942
+ localStorage.removeItem(CLIPBOARD_ID)
943
+ }, 1)
944
+ }
945
+
946
+ /**
947
+ * Selects only the given elements, shortcut for `clearSelection(); addToSelection()`.
948
+ * @function module:svgcanvas.SvgCanvas#selectOnly
949
+ * @param {Element[]} elems - an array of DOM elements to be selected
950
+ * @param {boolean} showGrips - Indicates whether the resize grips should be shown
951
+ * @returns {void}
952
+ */
953
+ selectOnly (elems, showGrips) {
954
+ this.clearSelection(true)
955
+ this.addToSelection(elems, showGrips)
956
+ }
957
+
958
+ /**
959
+ * Removes elements from the selection.
960
+ * @function module:svgcanvas.SvgCanvas#removeFromSelection
961
+ * @param {Element[]} elemsToRemove - An array of elements to remove from selection
962
+ * @returns {void}
963
+ */
964
+ removeFromSelection (elemsToRemove) {
965
+ if (!this.selectedElements[0]) {
966
+ return
967
+ }
968
+ if (!elemsToRemove.length) {
969
+ return
970
+ }
971
+
972
+ // find every element and remove it from our array copy
973
+ const newSelectedItems = []
974
+ const len = this.selectedElements.length
975
+ for (let i = 0; i < len; ++i) {
976
+ const elem = this.selectedElements[i]
977
+ if (elem) {
978
+ // keep the item
979
+ if (!elemsToRemove.includes(elem)) {
980
+ newSelectedItems.push(elem)
981
+ } else {
982
+ // remove the item and its selector
983
+ this.selectorManager.releaseSelector(elem)
984
+ }
985
+ }
986
+ }
987
+ // the copy becomes the master now
988
+ this.selectedElements = newSelectedItems
989
+ }
990
+
991
+ /**
992
+ * Clears the selection, then adds all elements in the current layer to the selection.
993
+ * @function module:svgcanvas.SvgCanvas#selectAllInCurrentLayer
994
+ * @returns {void}
995
+ */
996
+ selectAllInCurrentLayer () {
997
+ const currentLayer = this.getCurrentDrawing().getCurrentLayer()
998
+ if (currentLayer) {
999
+ this.currentMode = 'select'
1000
+ if (this.currentGroup) {
1001
+ this.selectOnly(this.currentGroup.children)
1002
+ } else {
1003
+ this.selectOnly(currentLayer.children)
1004
+ }
1005
+ }
1006
+ }
1007
+
1008
+ getOpacity () {
1009
+ return this.curShape.opacity
1010
+ }
1011
+
1012
+ /**
1013
+ * @function module:svgcanvas.SvgCanvas#getSnapToGrid
1014
+ * @returns {boolean} The current snap to grid setting
1015
+ */
1016
+ getSnapToGrid () {
1017
+ return this.curConfig.gridSnapping
1018
+ }
1019
+
1020
+ /**
1021
+ * @function module:svgcanvas.SvgCanvas#getVersion
1022
+ * @returns {string} A string which describes the revision number of SvgCanvas.
1023
+ */
1024
+ getVersion () {
1025
+ return 'svgcanvas.js ($Rev$)'
1026
+ }
1027
+
1028
+ /**
1029
+ * Update interface strings with given values.
1030
+ * @function module:svgcanvas.SvgCanvas#setUiStrings
1031
+ * @param {module:path.uiStrings} strs - Object with strings (see the [locales API]{@link module:locale.LocaleStrings} and the [tutorial]{@tutorial LocaleDocs})
1032
+ * @returns {void}
1033
+ */
1034
+ setUiStrings (strs) {
1035
+ Object.assign(this.uiStrings, strs.notification)
1036
+ pathModule.setUiStrings(strs)
1037
+ }
1038
+
1039
+ /**
1040
+ * Update configuration options with given values.
1041
+ * @function module:svgcanvas.SvgCanvas#setConfig
1042
+ * @param {module:SVGEditor.Config} opts - Object with options
1043
+ * @returns {void}
1044
+ */
1045
+ setConfig (opts) {
1046
+ Object.assign(this.curConfig, opts)
1047
+ }
1048
+
1049
+ /**
1050
+ * @function module:svgcanvas.SvgCanvas#getDocumentTitle
1051
+ * @returns {string|void} The current document title or an empty string if not found
1052
+ */
1053
+ getDocumentTitle () {
1054
+ return this.getTitle(this.svgContent)
1055
+ }
1056
+
1057
+ getOffset () {
1058
+ return {
1059
+ x: Number(this.svgContent.getAttribute('x')),
1060
+ y: Number(this.svgContent.getAttribute('y'))
1061
+ }
1062
+ }
1063
+
1064
+ getColor (type) {
1065
+ return this.curProperties[type]
1066
+ }
1067
+
1068
+ setStrokePaint (paint) {
1069
+ this.setPaint('stroke', paint)
1070
+ }
1071
+
1072
+ /**
1073
+ * @function module:svgcanvas.SvgCanvas#setFillPaint
1074
+ * @param {module:jGraduate~Paint} paint
1075
+ * @returns {void}
1076
+ */
1077
+ setFillPaint (paint) {
1078
+ this.setPaint('fill', paint)
1079
+ }
1080
+
1081
+ /**
1082
+ * @function module:svgcanvas.SvgCanvas#getStrokeWidth
1083
+ * @returns {Float|string} The current stroke-width value
1084
+ */
1085
+ getStrokeWidth () {
1086
+ return this.curProperties.stroke_width
1087
+ }
1088
+
1089
+ /**
1090
+ * @function module:svgcanvas.SvgCanvas#getStyle
1091
+ * @returns {module:svgcanvas.StyleOptions} current style options
1092
+ */
1093
+ getStyle () {
1094
+ return this.curShape
1095
+ }
1096
+
1097
+ /**
1098
+ * Sets the given opacity on the current selected elements.
1099
+ * @function module:svgcanvas.SvgCanvas#setOpacity
1100
+ * @param {string} val
1101
+ * @returns {void}
1102
+ */
1103
+ setOpacity (val) {
1104
+ this.curShape.opacity = val
1105
+ this.changeSelectedAttribute('opacity', val)
1106
+ }
1107
+
1108
+ /**
1109
+ * @function module:svgcanvas.SvgCanvas#getFillOpacity
1110
+ * @returns {Float} the current fill opacity
1111
+ */
1112
+ getFillOpacity () {
1113
+ return this.curShape.fill_opacity
1114
+ }
1115
+
1116
+ /**
1117
+ * @function module:svgcanvas.SvgCanvas#getStrokeOpacity
1118
+ * @returns {string} the current stroke opacity
1119
+ */
1120
+ getStrokeOpacity () {
1121
+ return this.curShape.stroke_opacity
1122
+ }
1123
+
1124
+ /**
1125
+ * Sets the current fill/stroke opacity.
1126
+ * @function module:svgcanvas.SvgCanvas#setPaintOpacity
1127
+ * @param {string} type - String with "fill" or "stroke"
1128
+ * @param {Float} val - Float with the new opacity value
1129
+ * @param {boolean} preventUndo - Indicates whether or not this should be an undoable action
1130
+ * @returns {void}
1131
+ */
1132
+ setPaintOpacity (type, val, preventUndo) {
1133
+ this.curShape[type + '_opacity'] = val
1134
+ if (!preventUndo) {
1135
+ this.changeSelectedAttribute(type + '-opacity', val)
1136
+ } else {
1137
+ this.changeSelectedAttributeNoUndo(type + '-opacity', val)
1138
+ }
1139
+ }
1140
+
1141
+ /**
1142
+ * Gets the current fill/stroke opacity.
1143
+ * @function module:svgcanvas.SvgCanvas#getPaintOpacity
1144
+ * @param {"fill"|"stroke"} type - String with "fill" or "stroke"
1145
+ * @returns {Float} Fill/stroke opacity
1146
+ */
1147
+ getPaintOpacity (type) {
1148
+ return type === 'fill' ? this.getFillOpacity() : this.getStrokeOpacity()
1149
+ }
1150
+
1151
+ /**
1152
+ * Gets the `stdDeviation` blur value of the given element.
1153
+ * @function module:svgcanvas.SvgCanvas#getBlur
1154
+ * @param {Element} elem - The element to check the blur value for
1155
+ * @returns {string} stdDeviation blur attribute value
1156
+ */
1157
+ getBlur (elem) {
1158
+ let val = 0
1159
+ if (elem) {
1160
+ const filterUrl = elem.getAttribute('filter')
1161
+ if (filterUrl) {
1162
+ const blur = getElement(elem.id + '_blur')
1163
+ if (blur) {
1164
+ val = blur.firstChild.getAttribute('stdDeviation')
1165
+ } else {
1166
+ const filterElem = getRefElem(filterUrl)
1167
+ const blurElem = getFeGaussianBlur(filterElem)
1168
+ if (blurElem !== null) {
1169
+ val = blurElem.getAttribute('stdDeviation')
1170
+ }
1171
+ }
1172
+ }
1173
+ }
1174
+ return val
1175
+ }
1176
+
1177
+ /**
1178
+ * Sets a given URL to be a "last good image" URL.
1179
+ * @function module:svgcanvas.SvgCanvas#setGoodImage
1180
+ * @param {string} val
1181
+ * @returns {void}
1182
+ */
1183
+ setGoodImage (val) {
1184
+ this.lastGoodImgUrl = val
1185
+ }
1186
+
1187
+ /**
1188
+ * Returns the current drawing as raw SVG XML text.
1189
+ * @function module:svgcanvas.SvgCanvas#getSvgString
1190
+ * @returns {string} The current drawing as raw SVG XML text.
1191
+ */
1192
+ getSvgString () {
1193
+ this.saveOptions.apply = false
1194
+ return this.svgCanvasToString()
1195
+ }
1196
+
1197
+ /**
1198
+ * This function determines whether to use a nonce in the prefix, when
1199
+ * generating IDs for future documents in SVG-Edit.
1200
+ * If you're controlling SVG-Edit externally, and want randomized IDs, call
1201
+ * this BEFORE calling `svgCanvas.setSvgString`.
1202
+ * @function module:svgcanvas.SvgCanvas#randomizeIds
1203
+ * @param {boolean} [enableRandomization] If true, adds a nonce to the prefix. Thus
1204
+ * `svgCanvas.randomizeIds() <==> svgCanvas.randomizeIds(true)`
1205
+ * @returns {void}
1206
+ */
1207
+ randomizeIds (enableRandomization) {
1208
+ if (arguments.length > 0 && enableRandomization === false) {
1209
+ draw.randomizeIds(false, this.getCurrentDrawing())
1210
+ } else {
1211
+ draw.randomizeIds(true, this.getCurrentDrawing())
1212
+ }
1213
+ }
1214
+
1215
+ /**
1216
+ * Convert selected element to a path, or get the BBox of an element-as-path.
1217
+ * @function module:svgcanvas.SvgCanvas#convertToPath
1218
+ * @todo (codedread): Remove the getBBox argument and split this function into two.
1219
+ * @param {Element} elem - The DOM element to be converted
1220
+ * @param {boolean} getBBox - Boolean on whether or not to only return the path's BBox
1221
+ * @returns {void|DOMRect|false|SVGPathElement|null} If the getBBox flag is true, the resulting path's bounding box object.
1222
+ * Otherwise the resulting path element is returned.
1223
+ */
1224
+ convertToPath (elem, getBBox) {
1225
+ // if elems not given, recursively call convertPath for all selected elements.
1226
+ if (!elem) {
1227
+ const elems = this.selectedElements
1228
+ elems.forEach(el => {
1229
+ if (el) {
1230
+ this.convertToPath(el)
1231
+ }
1232
+ })
1233
+ return undefined
1234
+ }
1235
+ if (getBBox) {
1236
+ return getBBoxOfElementAsPath(
1237
+ elem,
1238
+ this.addSVGElementsFromJson,
1239
+ this.pathActions
1240
+ )
1241
+ }
1242
+ // TODO: Why is this applying attributes from this.curShape, then inside utilities.convertToPath it's pulling addition attributes from elem?
1243
+ // TODO: If convertToPath is called with one elem, this.curShape and elem are probably the same; but calling with multiple is a bug or cool feature.
1244
+ const attrs = {
1245
+ fill: this.curShape.fill,
1246
+ 'fill-opacity': this.curShape.fill_opacity,
1247
+ stroke: this.curShape.stroke,
1248
+ 'stroke-width': this.curShape.stroke_width,
1249
+ 'stroke-dasharray': this.curShape.stroke_dasharray,
1250
+ 'stroke-linejoin': this.curShape.stroke_linejoin,
1251
+ 'stroke-linecap': this.curShape.stroke_linecap,
1252
+ 'stroke-opacity': this.curShape.stroke_opacity,
1253
+ opacity: this.curShape.opacity,
1254
+ visibility: 'hidden'
1255
+ }
1256
+ return convertToPath(elem, attrs, this) // call convertToPath from utilities.js
1257
+ }
1258
+
1259
+ /**
1260
+ * Removes all selected elements from the DOM and adds the change to the
1261
+ * history stack. Remembers removed elements on the clipboard.
1262
+ * @function module:svgcanvas.SvgCanvas#cutSelectedElements
1263
+ * @returns {void}
1264
+ */
1265
+ cutSelectedElements () {
1266
+ this.copySelectedElements()
1267
+ this.deleteSelectedElements()
1268
+ }
1269
+
1270
+ initializeSvgCanvasMethods () {
1271
+ this.getJsonFromSvgElements = getJsonFromSvgElements
1272
+ this.addSVGElementsFromJson = addSVGElementsFromJson
1273
+ this.clearSvgContentElement = clearSvgContentElementInit
1274
+ this.textActions = textActionsMethod
1275
+ this.getStrokedBBox = getStrokedBBoxDefaultVisible
1276
+ this.getVisibleElements = getVisibleElements
1277
+ this.stringToHTML = stringToHTML
1278
+ this.insertChildAtIndex = insertChildAtIndex
1279
+ this.getClosest = getClosest
1280
+ this.getParents = getParents
1281
+ this.isLayer = draw.Layer.isLayer
1282
+ this.matrixMultiply = matrixMultiply
1283
+ this.hasMatrixTransform = hasMatrixTransform
1284
+ this.transformListToTransform = transformListToTransform
1285
+ this.convertToNum = convertToNum
1286
+ this.findDefs = findDefs
1287
+ this.getUrlFromAttr = getUrlFromAttr
1288
+ this.getHref = getHref
1289
+ this.setHref = setHref
1290
+ this.getBBox = utilsGetBBox
1291
+ this.getRotationAngle = getRotationAngle
1292
+ this.getElement = getElement
1293
+ this.getRefElem = getRefElem
1294
+ this.assignAttributes = assignAttributes
1295
+ this.cleanupElement = cleanupElement
1296
+ this.remapElement = remapElement
1297
+ this.recalculateDimensions = recalculateDimensions
1298
+ this.sanitizeSvg = sanitizeSvg
1299
+ this.pasteElements = pasteElementsMethod // Remembers the current selected elements on the clipboard.
1300
+ this.identifyLayers = draw.identifyLayers
1301
+ this.createLayer = draw.createLayer
1302
+ this.cloneLayer = draw.cloneLayer
1303
+ this.deleteCurrentLayer = draw.deleteCurrentLayer
1304
+ this.setCurrentLayer = draw.setCurrentLayer
1305
+ this.renameCurrentLayer = draw.renameCurrentLayer
1306
+ this.setCurrentLayerPosition = draw.setCurrentLayerPosition
1307
+ this.indexCurrentLayer = draw.indexCurrentLayer
1308
+ this.setLayerVisibility = draw.setLayerVisibility
1309
+ this.moveSelectedToLayer = draw.moveSelectedToLayer
1310
+ this.mergeLayer = draw.mergeLayer
1311
+ this.mergeAllLayers = draw.mergeAllLayers
1312
+ this.leaveContext = draw.leaveContext
1313
+ this.setContext = draw.setContext
1314
+ this.changeSelectedAttributeNoUndo = changeSelectedAttributeNoUndoMethod // This function makes the changes to the elements. It does not add the change to the history stack.
1315
+ this.changeSelectedAttribute = changeSelectedAttributeMethod // Change the given/selected element and add the original value to the history stack.
1316
+ this.setBlurNoUndo = setBlurNoUndo // Sets the `stdDeviation` blur value on the selected element without being undoable.
1317
+ this.setBlurOffsets = setBlurOffsets // Sets the `x`, `y`, `width`, `height` values of the filter element in order to make the blur not be clipped. Removes them if not neeeded.
1318
+ this.setBlur = setBlur // Adds/updates the blur filter to the selected element.
1319
+ this.smoothControlPoints = pathModule.smoothControlPoints
1320
+ this.getTypeMap = getTypeMap
1321
+ this.history = history // object with all histor methods
1322
+ this.NS = NS
1323
+ this.$id = $id
1324
+ this.$qq = $qq
1325
+ this.$qa = $qa
1326
+ this.$click = $click
1327
+ this.encode64 = encode64
1328
+ this.decode64 = decode64
1329
+ this.mergeDeep = mergeDeep
1330
+ }
1331
+ } // End class
1332
+
1333
+ // attach utilities function to the class that are used by SvgEdit so
1334
+ // we can avoid using the whole utilities.js file in svgEdit.js
1335
+ SvgCanvas.$id = $id
1336
+ SvgCanvas.$qq = $qq
1337
+ SvgCanvas.$qa = $qa
1338
+ SvgCanvas.$click = $click
1339
+ SvgCanvas.encode64 = encode64
1340
+ SvgCanvas.decode64 = decode64
1341
+ SvgCanvas.mergeDeep = mergeDeep
1342
+ SvgCanvas.getClosest = getClosest
1343
+ SvgCanvas.getParents = getParents
1344
+ SvgCanvas.blankPageObjectURL = blankPageObjectURL
1345
+ SvgCanvas.Paint = Paint
1346
+
1347
+ export default SvgCanvas