Definable.js
5.91 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
var _core = require("../core");
var createElement = _core.createElement;
var zrUtil = require("../../core/util");
var Path = require("../../graphic/Path");
var ZImage = require("../../graphic/Image");
var ZText = require("../../graphic/Text");
var _graphic = require("../graphic");
var svgPath = _graphic.path;
var svgImage = _graphic.image;
var svgText = _graphic.text;
/**
* @file Manages elements that can be defined in <defs> in SVG,
* e.g., gradients, clip path, etc.
* @author Zhang Wenli
*/
var MARK_UNUSED = '0';
var MARK_USED = '1';
/**
* Manages elements that can be defined in <defs> in SVG,
* e.g., gradients, clip path, etc.
*
* @class
* @param {number} zrId zrender instance id
* @param {SVGElement} svgRoot root of SVG document
* @param {string|string[]} tagNames possible tag names
* @param {string} markLabel label name to make if the element
* is used
*/
function Definable(zrId, svgRoot, tagNames, markLabel, domName) {
this._zrId = zrId;
this._svgRoot = svgRoot;
this._tagNames = typeof tagNames === 'string' ? [tagNames] : tagNames;
this._markLabel = markLabel;
this._domName = domName || '_dom';
this.nextId = 0;
}
Definable.prototype.createElement = createElement;
/**
* Get the <defs> tag for svgRoot; optionally creates one if not exists.
*
* @param {boolean} isForceCreating if need to create when not exists
* @return {SVGDefsElement} SVG <defs> element, null if it doesn't
* exist and isForceCreating is false
*/
Definable.prototype.getDefs = function (isForceCreating) {
var svgRoot = this._svgRoot;
var defs = this._svgRoot.getElementsByTagName('defs');
if (defs.length === 0) {
// Not exist
if (isForceCreating) {
defs = svgRoot.insertBefore(this.createElement('defs'), // Create new tag
svgRoot.firstChild // Insert in the front of svg
);
if (!defs.contains) {
// IE doesn't support contains method
defs.contains = function (el) {
var children = defs.children;
if (!children) {
return false;
}
for (var i = children.length - 1; i >= 0; --i) {
if (children[i] === el) {
return true;
}
}
return false;
};
}
return defs;
} else {
return null;
}
} else {
return defs[0];
}
};
/**
* Update DOM element if necessary.
*
* @param {Object|string} element style element. e.g., for gradient,
* it may be '#ccc' or {type: 'linear', ...}
* @param {Function|undefined} onUpdate update callback
*/
Definable.prototype.update = function (element, onUpdate) {
if (!element) {
return;
}
var defs = this.getDefs(false);
if (element[this._domName] && defs.contains(element[this._domName])) {
// Update DOM
if (typeof onUpdate === 'function') {
onUpdate(element);
}
} else {
// No previous dom, create new
var dom = this.add(element);
if (dom) {
element[this._domName] = dom;
}
}
};
/**
* Add gradient dom to defs
*
* @param {SVGElement} dom DOM to be added to <defs>
*/
Definable.prototype.addDom = function (dom) {
var defs = this.getDefs(true);
defs.appendChild(dom);
};
/**
* Remove DOM of a given element.
*
* @param {SVGElement} element element to remove dom
*/
Definable.prototype.removeDom = function (element) {
var defs = this.getDefs(false);
if (defs && element[this._domName]) {
defs.removeChild(element[this._domName]);
element[this._domName] = null;
}
};
/**
* Get DOMs of this element.
*
* @return {HTMLDomElement} doms of this defineable elements in <defs>
*/
Definable.prototype.getDoms = function () {
var defs = this.getDefs(false);
if (!defs) {
// No dom when defs is not defined
return [];
}
var doms = [];
zrUtil.each(this._tagNames, function (tagName) {
var tags = defs.getElementsByTagName(tagName); // Note that tags is HTMLCollection, which is array-like
// rather than real array.
// So `doms.concat(tags)` add tags as one object.
doms = doms.concat([].slice.call(tags));
});
return doms;
};
/**
* Mark DOMs to be unused before painting, and clear unused ones at the end
* of the painting.
*/
Definable.prototype.markAllUnused = function () {
var doms = this.getDoms();
var that = this;
zrUtil.each(doms, function (dom) {
dom[that._markLabel] = MARK_UNUSED;
});
};
/**
* Mark a single DOM to be used.
*
* @param {SVGElement} dom DOM to mark
*/
Definable.prototype.markUsed = function (dom) {
if (dom) {
dom[this._markLabel] = MARK_USED;
}
};
/**
* Remove unused DOMs defined in <defs>
*/
Definable.prototype.removeUnused = function () {
var defs = this.getDefs(false);
if (!defs) {
// Nothing to remove
return;
}
var doms = this.getDoms();
var that = this;
zrUtil.each(doms, function (dom) {
if (dom[that._markLabel] !== MARK_USED) {
// Remove gradient
defs.removeChild(dom);
}
});
};
/**
* Get SVG proxy.
*
* @param {Displayable} displayable displayable element
* @return {Path|Image|Text} svg proxy of given element
*/
Definable.prototype.getSvgProxy = function (displayable) {
if (displayable instanceof Path) {
return svgPath;
} else if (displayable instanceof ZImage) {
return svgImage;
} else if (displayable instanceof ZText) {
return svgText;
} else {
return svgPath;
}
};
/**
* Get text SVG element.
*
* @param {Displayable} displayable displayable element
* @return {SVGElement} SVG element of text
*/
Definable.prototype.getTextSvgElement = function (displayable) {
return displayable.__textSvgEl;
};
/**
* Get SVG element.
*
* @param {Displayable} displayable displayable element
* @return {SVGElement} SVG element
*/
Definable.prototype.getSvgElement = function (displayable) {
return displayable.__svgEl;
};
var _default = Definable;
module.exports = _default;