@svgedit/svgcanvas 7.1.4
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/blur-event.js +156 -0
- package/clear.js +43 -0
- package/coords.js +298 -0
- package/copy-elem.js +45 -0
- package/dataStorage.js +28 -0
- package/dist/svgcanvas.js +515 -0
- package/dist/svgcanvas.js.map +1 -0
- package/draw.js +1064 -0
- package/elem-get-set.js +1077 -0
- package/event.js +1388 -0
- package/history.js +619 -0
- package/historyrecording.js +161 -0
- package/json.js +110 -0
- package/layer.js +228 -0
- package/math.js +221 -0
- package/namespaces.js +40 -0
- package/package.json +54 -0
- package/paint.js +88 -0
- package/paste-elem.js +127 -0
- package/path-actions.js +1237 -0
- package/path-method.js +1012 -0
- package/path.js +781 -0
- package/recalculate.js +794 -0
- package/rollup.config.js +40 -0
- package/sanitize.js +252 -0
- package/select.js +543 -0
- package/selected-elem.js +1297 -0
- package/selection.js +482 -0
- package/svg-exec.js +1289 -0
- package/svgcanvas.js +1347 -0
- package/svgroot.js +36 -0
- package/text-actions.js +530 -0
- package/touch.js +51 -0
- package/undo.js +279 -0
- package/utilities.js +1214 -0
|
@@ -0,0 +1,161 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* HistoryRecordingService component of history.
|
|
3
|
+
* @module history
|
|
4
|
+
* @license MIT
|
|
5
|
+
* @copyright 2016 Flint O'Brien
|
|
6
|
+
*/
|
|
7
|
+
|
|
8
|
+
import {
|
|
9
|
+
BatchCommand, MoveElementCommand, InsertElementCommand, RemoveElementCommand,
|
|
10
|
+
ChangeElementCommand
|
|
11
|
+
} from './history.js'
|
|
12
|
+
|
|
13
|
+
/**
|
|
14
|
+
* History recording service.
|
|
15
|
+
*
|
|
16
|
+
* A self-contained service interface for recording history. Once injected, no other dependencies
|
|
17
|
+
* or globals are required (example: UndoManager, command types, etc.). Easy to mock for unit tests.
|
|
18
|
+
* Built on top of history classes in history.js.
|
|
19
|
+
*
|
|
20
|
+
* There is a simple start/end interface for batch commands.
|
|
21
|
+
*
|
|
22
|
+
* HistoryRecordingService.NO_HISTORY is a singleton that can be passed in to functions
|
|
23
|
+
* that record history. This helps when the caller requires that no history be recorded.
|
|
24
|
+
*
|
|
25
|
+
* The following will record history: insert, batch, insert.
|
|
26
|
+
* @example
|
|
27
|
+
* hrService = new HistoryRecordingService(this.undoMgr);
|
|
28
|
+
* hrService.insertElement(elem, text); // add simple command to history.
|
|
29
|
+
* hrService.startBatchCommand('create two elements');
|
|
30
|
+
* hrService.changeElement(elem, attrs, text); // add to batchCommand
|
|
31
|
+
* hrService.changeElement(elem, attrs2, text); // add to batchCommand
|
|
32
|
+
* hrService.endBatchCommand(); // add batch command with two change commands to history.
|
|
33
|
+
* hrService.insertElement(elem, text); // add simple command to history.
|
|
34
|
+
*
|
|
35
|
+
* @example
|
|
36
|
+
* // Note that all functions return this, so commands can be chained, like so:
|
|
37
|
+
* hrService
|
|
38
|
+
* .startBatchCommand('create two elements')
|
|
39
|
+
* .insertElement(elem, text)
|
|
40
|
+
* .changeElement(elem, attrs, text)
|
|
41
|
+
* .endBatchCommand();
|
|
42
|
+
*
|
|
43
|
+
* @memberof module:history
|
|
44
|
+
*/
|
|
45
|
+
class HistoryRecordingService {
|
|
46
|
+
/**
|
|
47
|
+
* @param {history.UndoManager|null} undoManager - The undo manager.
|
|
48
|
+
* A value of `null` is valid for cases where no history recording is required.
|
|
49
|
+
* See singleton: {@link module:history.HistoryRecordingService.HistoryRecordingService.NO_HISTORY}
|
|
50
|
+
*/
|
|
51
|
+
constructor (undoManager) {
|
|
52
|
+
this.undoManager_ = undoManager
|
|
53
|
+
this.currentBatchCommand_ = null
|
|
54
|
+
this.batchCommandStack_ = []
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
/**
|
|
58
|
+
* Start a batch command so multiple commands can recorded as a single history command.
|
|
59
|
+
* Requires a corresponding call to endBatchCommand. Start and end commands can be nested.
|
|
60
|
+
*
|
|
61
|
+
* @param {string} text - Optional string describing the batch command.
|
|
62
|
+
* @returns {module:history.HistoryRecordingService}
|
|
63
|
+
*/
|
|
64
|
+
startBatchCommand (text) {
|
|
65
|
+
if (!this.undoManager_) { return this }
|
|
66
|
+
this.currentBatchCommand_ = new BatchCommand(text)
|
|
67
|
+
this.batchCommandStack_.push(this.currentBatchCommand_)
|
|
68
|
+
return this
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
/**
|
|
72
|
+
* End a batch command and add it to the history or a parent batch command.
|
|
73
|
+
* @returns {module:history.HistoryRecordingService}
|
|
74
|
+
*/
|
|
75
|
+
endBatchCommand () {
|
|
76
|
+
if (!this.undoManager_) { return this }
|
|
77
|
+
if (this.currentBatchCommand_) {
|
|
78
|
+
const batchCommand = this.currentBatchCommand_
|
|
79
|
+
this.batchCommandStack_.pop()
|
|
80
|
+
const { length: len } = this.batchCommandStack_
|
|
81
|
+
this.currentBatchCommand_ = len ? this.batchCommandStack_[len - 1] : null
|
|
82
|
+
this.addCommand_(batchCommand)
|
|
83
|
+
}
|
|
84
|
+
return this
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
/**
|
|
88
|
+
* Add a `MoveElementCommand` to the history or current batch command.
|
|
89
|
+
* @param {Element} elem - The DOM element that was moved
|
|
90
|
+
* @param {Element} oldNextSibling - The element's next sibling before it was moved
|
|
91
|
+
* @param {Element} oldParent - The element's parent before it was moved
|
|
92
|
+
* @param {string} [text] - An optional string visible to user related to this change
|
|
93
|
+
* @returns {module:history.HistoryRecordingService}
|
|
94
|
+
*/
|
|
95
|
+
moveElement (elem, oldNextSibling, oldParent, text) {
|
|
96
|
+
if (!this.undoManager_) { return this }
|
|
97
|
+
this.addCommand_(new MoveElementCommand(elem, oldNextSibling, oldParent, text))
|
|
98
|
+
return this
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
/**
|
|
102
|
+
* Add an `InsertElementCommand` to the history or current batch command.
|
|
103
|
+
* @param {Element} elem - The DOM element that was added
|
|
104
|
+
* @param {string} [text] - An optional string visible to user related to this change
|
|
105
|
+
* @returns {module:history.HistoryRecordingService}
|
|
106
|
+
*/
|
|
107
|
+
insertElement (elem, text) {
|
|
108
|
+
if (!this.undoManager_) { return this }
|
|
109
|
+
this.addCommand_(new InsertElementCommand(elem, text))
|
|
110
|
+
return this
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
/**
|
|
114
|
+
* Add a `RemoveElementCommand` to the history or current batch command.
|
|
115
|
+
* @param {Element} elem - The DOM element that was removed
|
|
116
|
+
* @param {Element} oldNextSibling - The element's next sibling before it was removed
|
|
117
|
+
* @param {Element} oldParent - The element's parent before it was removed
|
|
118
|
+
* @param {string} [text] - An optional string visible to user related to this change
|
|
119
|
+
* @returns {module:history.HistoryRecordingService}
|
|
120
|
+
*/
|
|
121
|
+
removeElement (elem, oldNextSibling, oldParent, text) {
|
|
122
|
+
if (!this.undoManager_) { return this }
|
|
123
|
+
this.addCommand_(new RemoveElementCommand(elem, oldNextSibling, oldParent, text))
|
|
124
|
+
return this
|
|
125
|
+
}
|
|
126
|
+
|
|
127
|
+
/**
|
|
128
|
+
* Add a `ChangeElementCommand` to the history or current batch command.
|
|
129
|
+
* @param {Element} elem - The DOM element that was changed
|
|
130
|
+
* @param {module:history.CommandAttributes} attrs - An object with the attributes to be changed and the values they had *before* the change
|
|
131
|
+
* @param {string} [text] - An optional string visible to user related to this change
|
|
132
|
+
* @returns {module:history.HistoryRecordingService}
|
|
133
|
+
*/
|
|
134
|
+
changeElement (elem, attrs, text) {
|
|
135
|
+
if (!this.undoManager_) { return this }
|
|
136
|
+
this.addCommand_(new ChangeElementCommand(elem, attrs, text))
|
|
137
|
+
return this
|
|
138
|
+
}
|
|
139
|
+
|
|
140
|
+
/**
|
|
141
|
+
* Private function to add a command to the history or current batch command.
|
|
142
|
+
* @private
|
|
143
|
+
* @param {Command} cmd
|
|
144
|
+
* @returns {module:history.HistoryRecordingService|void}
|
|
145
|
+
*/
|
|
146
|
+
addCommand_ (cmd) {
|
|
147
|
+
if (!this.undoManager_) { return this }
|
|
148
|
+
if (this.currentBatchCommand_) {
|
|
149
|
+
this.currentBatchCommand_.addSubCommand(cmd)
|
|
150
|
+
} else {
|
|
151
|
+
this.undoManager_.addCommandToHistory(cmd)
|
|
152
|
+
}
|
|
153
|
+
return undefined
|
|
154
|
+
}
|
|
155
|
+
}
|
|
156
|
+
/**
|
|
157
|
+
* @memberof module:history.HistoryRecordingService
|
|
158
|
+
* @property {module:history.HistoryRecordingService} NO_HISTORY - Singleton that can be passed to functions that record history, but the caller requires that no history be recorded.
|
|
159
|
+
*/
|
|
160
|
+
HistoryRecordingService.NO_HISTORY = new HistoryRecordingService()
|
|
161
|
+
export default HistoryRecordingService
|
package/json.js
ADDED
|
@@ -0,0 +1,110 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Tools for SVG handle on JSON format.
|
|
3
|
+
* @module svgcanvas
|
|
4
|
+
* @license MIT
|
|
5
|
+
*
|
|
6
|
+
* @copyright 2010 Alexis Deveria, 2010 Jeff Schiller
|
|
7
|
+
*/
|
|
8
|
+
import { getElement, assignAttributes, cleanupElement } from './utilities.js'
|
|
9
|
+
import { NS } from './namespaces.js'
|
|
10
|
+
|
|
11
|
+
let svgCanvas = null
|
|
12
|
+
let svgdoc_ = null
|
|
13
|
+
|
|
14
|
+
/**
|
|
15
|
+
* @function module:json.jsonContext#getSelectedElements
|
|
16
|
+
* @returns {Element[]} the array with selected DOM elements
|
|
17
|
+
*/
|
|
18
|
+
/**
|
|
19
|
+
* @function module:json.jsonContext#getDOMDocument
|
|
20
|
+
* @returns {HTMLDocument}
|
|
21
|
+
*/
|
|
22
|
+
|
|
23
|
+
/**
|
|
24
|
+
* @function module:json.init
|
|
25
|
+
* @param {module:json.jsonContext} jsonContext
|
|
26
|
+
* @returns {void}
|
|
27
|
+
*/
|
|
28
|
+
export const init = (canvas) => {
|
|
29
|
+
svgCanvas = canvas
|
|
30
|
+
svgdoc_ = canvas.getDOMDocument()
|
|
31
|
+
}
|
|
32
|
+
/**
|
|
33
|
+
* @function module:json.getJsonFromSvgElements Iterate element and return json format
|
|
34
|
+
* @param {ArgumentsArray} data - element
|
|
35
|
+
* @returns {svgRootElement}
|
|
36
|
+
*/
|
|
37
|
+
export const getJsonFromSvgElements = (data) => {
|
|
38
|
+
// Text node
|
|
39
|
+
if (data.nodeType === 3) return data.nodeValue
|
|
40
|
+
|
|
41
|
+
const retval = {
|
|
42
|
+
element: data.tagName,
|
|
43
|
+
// namespace: nsMap[data.namespaceURI],
|
|
44
|
+
attr: {},
|
|
45
|
+
children: []
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
// Iterate attributes
|
|
49
|
+
for (let i = 0, attr; (attr = data.attributes[i]); i++) {
|
|
50
|
+
retval.attr[attr.name] = attr.value
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
// Iterate children
|
|
54
|
+
for (let i = 0, node; (node = data.childNodes[i]); i++) {
|
|
55
|
+
retval.children[i] = getJsonFromSvgElements(node)
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
return retval
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
/**
|
|
62
|
+
* This should really be an intersection implementing all rather than a union.
|
|
63
|
+
* @name module:json.addSVGElementsFromJson
|
|
64
|
+
* @type {module:utilities.EditorContext#addSVGElementsFromJson|module:path.EditorContext#addSVGElementsFromJson}
|
|
65
|
+
*/
|
|
66
|
+
|
|
67
|
+
export const addSVGElementsFromJson = (data) => {
|
|
68
|
+
if (typeof data === 'string') return svgdoc_.createTextNode(data)
|
|
69
|
+
|
|
70
|
+
let shape = getElement(data.attr.id)
|
|
71
|
+
// if shape is a path but we need to create a rect/ellipse, then remove the path
|
|
72
|
+
const currentLayer = svgCanvas.getDrawing().getCurrentLayer()
|
|
73
|
+
if (shape && data.element !== shape.tagName) {
|
|
74
|
+
shape.remove()
|
|
75
|
+
shape = null
|
|
76
|
+
}
|
|
77
|
+
if (!shape) {
|
|
78
|
+
const ns = data.namespace || NS.SVG
|
|
79
|
+
shape = svgdoc_.createElementNS(ns, data.element)
|
|
80
|
+
if (currentLayer) {
|
|
81
|
+
(svgCanvas.getCurrentGroup() || currentLayer).append(shape)
|
|
82
|
+
}
|
|
83
|
+
}
|
|
84
|
+
const curShape = svgCanvas.getCurShape()
|
|
85
|
+
if (data.curStyles) {
|
|
86
|
+
assignAttributes(shape, {
|
|
87
|
+
fill: curShape.fill,
|
|
88
|
+
stroke: curShape.stroke,
|
|
89
|
+
'stroke-width': curShape.strokeWidth,
|
|
90
|
+
'stroke-dasharray': curShape.stroke_dasharray,
|
|
91
|
+
'stroke-linejoin': curShape.stroke_linejoin,
|
|
92
|
+
'stroke-linecap': curShape.stroke_linecap,
|
|
93
|
+
'stroke-opacity': curShape.stroke_opacity,
|
|
94
|
+
'fill-opacity': curShape.fill_opacity,
|
|
95
|
+
opacity: curShape.opacity / 2,
|
|
96
|
+
style: 'pointer-events:inherit'
|
|
97
|
+
}, 100)
|
|
98
|
+
}
|
|
99
|
+
assignAttributes(shape, data.attr, 100)
|
|
100
|
+
cleanupElement(shape)
|
|
101
|
+
|
|
102
|
+
// Children
|
|
103
|
+
if (data.children) {
|
|
104
|
+
data.children.forEach((child) => {
|
|
105
|
+
shape.append(addSVGElementsFromJson(child))
|
|
106
|
+
})
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
return shape
|
|
110
|
+
}
|
package/layer.js
ADDED
|
@@ -0,0 +1,228 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Provides tools for the layer concept.
|
|
3
|
+
* @module layer
|
|
4
|
+
* @license MIT
|
|
5
|
+
*
|
|
6
|
+
* @copyright 2011 Jeff Schiller, 2016 Flint O'Brien
|
|
7
|
+
*/
|
|
8
|
+
|
|
9
|
+
import { NS } from './namespaces.js'
|
|
10
|
+
import { toXml, walkTree } from './utilities.js'
|
|
11
|
+
|
|
12
|
+
/**
|
|
13
|
+
* This class encapsulates the concept of a layer in the drawing. It can be constructed with
|
|
14
|
+
* an existing group element or, with three parameters, will create a new layer group element.
|
|
15
|
+
*
|
|
16
|
+
* @example
|
|
17
|
+
* const l1 = new Layer('name', group); // Use the existing group for this layer.
|
|
18
|
+
* const l2 = new Layer('name', group, svgElem); // Create a new group and add it to the DOM after group.
|
|
19
|
+
* const l3 = new Layer('name', null, svgElem); // Create a new group and add it to the DOM as the last layer.
|
|
20
|
+
* @memberof module:layer
|
|
21
|
+
*/
|
|
22
|
+
class Layer {
|
|
23
|
+
/**
|
|
24
|
+
* @param {string} name - Layer name
|
|
25
|
+
* @param {SVGGElement|null} group - An existing SVG group element or null.
|
|
26
|
+
* If group and no svgElem, use group for this layer.
|
|
27
|
+
* If group and svgElem, create a new group element and insert it in the DOM after group.
|
|
28
|
+
* If no group and svgElem, create a new group element and insert it in the DOM as the last layer.
|
|
29
|
+
* @param {SVGGElement} [svgElem] - The SVG DOM element. If defined, use this to add
|
|
30
|
+
* a new layer to the document.
|
|
31
|
+
*/
|
|
32
|
+
constructor (name, group, svgElem) {
|
|
33
|
+
this.name_ = name
|
|
34
|
+
this.group_ = svgElem ? null : group
|
|
35
|
+
|
|
36
|
+
if (svgElem) {
|
|
37
|
+
// Create a group element with title and add it to the DOM.
|
|
38
|
+
const svgdoc = svgElem.ownerDocument
|
|
39
|
+
this.group_ = svgdoc.createElementNS(NS.SVG, 'g')
|
|
40
|
+
const layerTitle = svgdoc.createElementNS(NS.SVG, 'title')
|
|
41
|
+
layerTitle.textContent = name
|
|
42
|
+
this.group_.append(layerTitle)
|
|
43
|
+
if (group) {
|
|
44
|
+
group.insertAdjacentElement('afterend', this.group_)
|
|
45
|
+
} else {
|
|
46
|
+
svgElem.append(this.group_)
|
|
47
|
+
}
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
addLayerClass(this.group_)
|
|
51
|
+
walkTree(this.group_, function (e) {
|
|
52
|
+
e.setAttribute('style', 'pointer-events:inherit')
|
|
53
|
+
})
|
|
54
|
+
|
|
55
|
+
this.group_.setAttribute('style', svgElem ? 'pointer-events:all' : 'pointer-events:none')
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
/**
|
|
59
|
+
* Get the layer's name.
|
|
60
|
+
* @returns {string} The layer name
|
|
61
|
+
*/
|
|
62
|
+
getName () {
|
|
63
|
+
return this.name_
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
/**
|
|
67
|
+
* Get the group element for this layer.
|
|
68
|
+
* @returns {SVGGElement} The layer SVG group
|
|
69
|
+
*/
|
|
70
|
+
getGroup () {
|
|
71
|
+
return this.group_
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
/**
|
|
75
|
+
* Active this layer so it takes pointer events.
|
|
76
|
+
* @returns {void}
|
|
77
|
+
*/
|
|
78
|
+
activate () {
|
|
79
|
+
this.group_.setAttribute('style', 'pointer-events:all')
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
/**
|
|
83
|
+
* Deactive this layer so it does NOT take pointer events.
|
|
84
|
+
* @returns {void}
|
|
85
|
+
*/
|
|
86
|
+
deactivate () {
|
|
87
|
+
this.group_.setAttribute('style', 'pointer-events:none')
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
/**
|
|
91
|
+
* Set this layer visible or hidden based on 'visible' parameter.
|
|
92
|
+
* @param {boolean} visible - If true, make visible; otherwise, hide it.
|
|
93
|
+
* @returns {void}
|
|
94
|
+
*/
|
|
95
|
+
setVisible (visible) {
|
|
96
|
+
const expected = visible === undefined || visible ? 'inline' : 'none'
|
|
97
|
+
const oldDisplay = this.group_.getAttribute('display')
|
|
98
|
+
if (oldDisplay !== expected) {
|
|
99
|
+
this.group_.setAttribute('display', expected)
|
|
100
|
+
}
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
/**
|
|
104
|
+
* Is this layer visible?
|
|
105
|
+
* @returns {boolean} True if visible.
|
|
106
|
+
*/
|
|
107
|
+
isVisible () {
|
|
108
|
+
return this.group_.getAttribute('display') !== 'none'
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
/**
|
|
112
|
+
* Get layer opacity.
|
|
113
|
+
* @returns {Float} Opacity value.
|
|
114
|
+
*/
|
|
115
|
+
getOpacity () {
|
|
116
|
+
const opacity = this.group_.getAttribute('opacity')
|
|
117
|
+
if (!opacity) {
|
|
118
|
+
return 1
|
|
119
|
+
}
|
|
120
|
+
return Number.parseFloat(opacity)
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
/**
|
|
124
|
+
* Sets the opacity of this layer. If opacity is not a value between 0.0 and 1.0,
|
|
125
|
+
* nothing happens.
|
|
126
|
+
* @param {Float} opacity - A float value in the range 0.0-1.0
|
|
127
|
+
* @returns {void}
|
|
128
|
+
*/
|
|
129
|
+
setOpacity (opacity) {
|
|
130
|
+
if (typeof opacity === 'number' && opacity >= 0.0 && opacity <= 1.0) {
|
|
131
|
+
this.group_.setAttribute('opacity', opacity)
|
|
132
|
+
}
|
|
133
|
+
}
|
|
134
|
+
|
|
135
|
+
/**
|
|
136
|
+
* Append children to this layer.
|
|
137
|
+
* @param {SVGGElement} children - The children to append to this layer.
|
|
138
|
+
* @returns {void}
|
|
139
|
+
*/
|
|
140
|
+
appendChildren (children) {
|
|
141
|
+
for (const child of children) {
|
|
142
|
+
this.group_.append(child)
|
|
143
|
+
}
|
|
144
|
+
}
|
|
145
|
+
|
|
146
|
+
/**
|
|
147
|
+
* @returns {SVGTitleElement|null}
|
|
148
|
+
*/
|
|
149
|
+
getTitleElement () {
|
|
150
|
+
const len = this.group_.childNodes.length
|
|
151
|
+
for (let i = 0; i < len; ++i) {
|
|
152
|
+
const child = this.group_.childNodes.item(i)
|
|
153
|
+
if (child?.tagName === 'title') {
|
|
154
|
+
return child
|
|
155
|
+
}
|
|
156
|
+
}
|
|
157
|
+
return null
|
|
158
|
+
}
|
|
159
|
+
|
|
160
|
+
/**
|
|
161
|
+
* Set the name of this layer.
|
|
162
|
+
* @param {string} name - The new name.
|
|
163
|
+
* @param {module:history.HistoryRecordingService} hrService - History recording service
|
|
164
|
+
* @returns {string|null} The new name if changed; otherwise, null.
|
|
165
|
+
*/
|
|
166
|
+
setName (name, hrService) {
|
|
167
|
+
const previousName = this.name_
|
|
168
|
+
name = toXml(name)
|
|
169
|
+
// now change the underlying title element contents
|
|
170
|
+
const title = this.getTitleElement()
|
|
171
|
+
if (title) {
|
|
172
|
+
while (title.firstChild) { title.removeChild(title.firstChild) }
|
|
173
|
+
title.textContent = name
|
|
174
|
+
this.name_ = name
|
|
175
|
+
if (hrService) {
|
|
176
|
+
hrService.changeElement(title, { '#text': previousName })
|
|
177
|
+
}
|
|
178
|
+
return this.name_
|
|
179
|
+
}
|
|
180
|
+
return null
|
|
181
|
+
}
|
|
182
|
+
|
|
183
|
+
/**
|
|
184
|
+
* Remove this layer's group from the DOM. No more functions on group can be called after this.
|
|
185
|
+
* @returns {SVGGElement} The layer SVG group that was just removed.
|
|
186
|
+
*/
|
|
187
|
+
removeGroup () {
|
|
188
|
+
const group = this.group_
|
|
189
|
+
this.group_.remove()
|
|
190
|
+
this.group_ = undefined
|
|
191
|
+
return group
|
|
192
|
+
}
|
|
193
|
+
|
|
194
|
+
/**
|
|
195
|
+
* Test whether an element is a layer or not.
|
|
196
|
+
* @param {SVGGElement} elem - The SVGGElement to test.
|
|
197
|
+
* @returns {boolean} True if the element is a layer
|
|
198
|
+
*/
|
|
199
|
+
static isLayer (elem) {
|
|
200
|
+
return elem && elem.tagName === 'g' && Layer.CLASS_REGEX.test(elem.getAttribute('class'))
|
|
201
|
+
}
|
|
202
|
+
}
|
|
203
|
+
/**
|
|
204
|
+
* @property {string} CLASS_NAME - class attribute assigned to all layer groups.
|
|
205
|
+
*/
|
|
206
|
+
Layer.CLASS_NAME = 'layer'
|
|
207
|
+
|
|
208
|
+
/**
|
|
209
|
+
* @property {RegExp} CLASS_REGEX - Used to test presence of class Layer.CLASS_NAME
|
|
210
|
+
*/
|
|
211
|
+
Layer.CLASS_REGEX = new RegExp('(\\s|^)' + Layer.CLASS_NAME + '(\\s|$)')
|
|
212
|
+
|
|
213
|
+
/**
|
|
214
|
+
* Add class `Layer.CLASS_NAME` to the element (usually `class='layer'`).
|
|
215
|
+
*
|
|
216
|
+
* @param {SVGGElement} elem - The SVG element to update
|
|
217
|
+
* @returns {void}
|
|
218
|
+
*/
|
|
219
|
+
function addLayerClass (elem) {
|
|
220
|
+
const classes = elem.getAttribute('class')
|
|
221
|
+
if (!classes || !classes.length) {
|
|
222
|
+
elem.setAttribute('class', Layer.CLASS_NAME)
|
|
223
|
+
} else if (!Layer.CLASS_REGEX.test(classes)) {
|
|
224
|
+
elem.setAttribute('class', classes + ' ' + Layer.CLASS_NAME)
|
|
225
|
+
}
|
|
226
|
+
}
|
|
227
|
+
|
|
228
|
+
export default Layer
|