plotly-print 0.1.0__py3-none-any.whl
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.
- plotly_print/__init__.py +20 -0
- plotly_print/core.py +76 -0
- plotly_print/engine.py +172 -0
- plotly_print/fonts.py +143 -0
- plotly_print/js/micro_dom.js +1195 -0
- plotly_print/js/plotly-cartesian.min.js +149 -0
- plotly_print/plotly_hook.py +95 -0
- plotly_print/raster.py +186 -0
- plotly_print-0.1.0.dist-info/METADATA +66 -0
- plotly_print-0.1.0.dist-info/RECORD +13 -0
- plotly_print-0.1.0.dist-info/WHEEL +4 -0
- plotly_print-0.1.0.dist-info/licenses/LICENSE +22 -0
- plotly_print-0.1.0.dist-info/licenses/THIRD_PARTY_NOTICES.md +40 -0
|
@@ -0,0 +1,1195 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* plotly-print Micro-DOM for Plotly.js / D3 v3 static rendering in QuickJS.
|
|
3
|
+
*/
|
|
4
|
+
(function() {
|
|
5
|
+
'use strict';
|
|
6
|
+
|
|
7
|
+
var globalScope = typeof globalThis !== 'undefined' ? globalThis : this;
|
|
8
|
+
if (typeof globalScope.process === 'undefined') {
|
|
9
|
+
globalScope.process = { env: { NODE_ENV: 'production' }, browser: true, version: '' };
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
// --- Helpers ---
|
|
13
|
+
function camelToDashed(str) {
|
|
14
|
+
return str.replace(/[A-Z]/g, function(m) { return '-' + m.toLowerCase(); });
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
// --- CSSStyleDeclaration ---
|
|
18
|
+
function CSSStyleDeclaration() {
|
|
19
|
+
this._properties = {};
|
|
20
|
+
}
|
|
21
|
+
CSSStyleDeclaration.prototype.setProperty = function(name, value, priority) {
|
|
22
|
+
if (value === null || value === undefined || value === '') {
|
|
23
|
+
delete this._properties[name];
|
|
24
|
+
} else {
|
|
25
|
+
this._properties[name] = String(value);
|
|
26
|
+
}
|
|
27
|
+
};
|
|
28
|
+
CSSStyleDeclaration.prototype.getPropertyValue = function(name) {
|
|
29
|
+
return this._properties[name] || '';
|
|
30
|
+
};
|
|
31
|
+
CSSStyleDeclaration.prototype.removeProperty = function(name) {
|
|
32
|
+
var val = this._properties[name] || '';
|
|
33
|
+
delete this._properties[name];
|
|
34
|
+
return val;
|
|
35
|
+
};
|
|
36
|
+
CSSStyleDeclaration.prototype.cssText = function() {
|
|
37
|
+
var parts = [];
|
|
38
|
+
for (var k in this._properties) {
|
|
39
|
+
parts.push(camelToDashed(k) + ': ' + this._properties[k]);
|
|
40
|
+
}
|
|
41
|
+
return parts.join('; ');
|
|
42
|
+
};
|
|
43
|
+
|
|
44
|
+
// Style proxy or property reflection
|
|
45
|
+
function createStyle(element) {
|
|
46
|
+
var decl = new CSSStyleDeclaration();
|
|
47
|
+
decl._element = element;
|
|
48
|
+
// Return a proxy if available, else plain decl with getters/setters for common props
|
|
49
|
+
if (typeof Proxy !== 'undefined') {
|
|
50
|
+
return new Proxy(decl, {
|
|
51
|
+
get: function(target, prop) {
|
|
52
|
+
if (prop in target || typeof prop === 'symbol') {
|
|
53
|
+
return target[prop];
|
|
54
|
+
}
|
|
55
|
+
var dashed = camelToDashed(String(prop));
|
|
56
|
+
return target.getPropertyValue(dashed);
|
|
57
|
+
},
|
|
58
|
+
set: function(target, prop, value) {
|
|
59
|
+
if (prop === '_properties' || prop in target) {
|
|
60
|
+
target[prop] = value;
|
|
61
|
+
} else {
|
|
62
|
+
var dashed = camelToDashed(String(prop));
|
|
63
|
+
target.setProperty(dashed, value);
|
|
64
|
+
}
|
|
65
|
+
return true;
|
|
66
|
+
}
|
|
67
|
+
});
|
|
68
|
+
}
|
|
69
|
+
return decl;
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
// --- DOM Node ---
|
|
73
|
+
function Node() {
|
|
74
|
+
this.childNodes = [];
|
|
75
|
+
this.parentNode = null;
|
|
76
|
+
this._ownerDoc = null;
|
|
77
|
+
this.nodeType = 1;
|
|
78
|
+
this.nodeName = '';
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
Object.defineProperty(Node.prototype, 'ownerDocument', {
|
|
82
|
+
get: function() {
|
|
83
|
+
if (this.nodeType === 9) return null;
|
|
84
|
+
return this._ownerDoc || (globalScope.document || null);
|
|
85
|
+
},
|
|
86
|
+
set: function(v) {
|
|
87
|
+
this._ownerDoc = v;
|
|
88
|
+
}
|
|
89
|
+
});
|
|
90
|
+
|
|
91
|
+
Object.defineProperty(Node.prototype, 'firstChild', {
|
|
92
|
+
get: function() { return this.childNodes[0] || null; }
|
|
93
|
+
});
|
|
94
|
+
Object.defineProperty(Node.prototype, 'lastChild', {
|
|
95
|
+
get: function() { return this.childNodes[this.childNodes.length - 1] || null; }
|
|
96
|
+
});
|
|
97
|
+
Object.defineProperty(Node.prototype, 'nextSibling', {
|
|
98
|
+
get: function() {
|
|
99
|
+
if (!this.parentNode) return null;
|
|
100
|
+
var idx = this.parentNode.childNodes.indexOf(this);
|
|
101
|
+
return (idx !== -1 && idx + 1 < this.parentNode.childNodes.length) ? this.parentNode.childNodes[idx + 1] : null;
|
|
102
|
+
}
|
|
103
|
+
});
|
|
104
|
+
Object.defineProperty(Node.prototype, 'previousSibling', {
|
|
105
|
+
get: function() {
|
|
106
|
+
if (!this.parentNode) return null;
|
|
107
|
+
var idx = this.parentNode.childNodes.indexOf(this);
|
|
108
|
+
return (idx > 0) ? this.parentNode.childNodes[idx - 1] : null;
|
|
109
|
+
}
|
|
110
|
+
});
|
|
111
|
+
|
|
112
|
+
Node.prototype.appendChild = function(child) {
|
|
113
|
+
if (child.nodeType === 11) { // DocumentFragment
|
|
114
|
+
var children = child.childNodes.slice();
|
|
115
|
+
for (var i = 0; i < children.length; i++) {
|
|
116
|
+
this.appendChild(children[i]);
|
|
117
|
+
}
|
|
118
|
+
return child;
|
|
119
|
+
}
|
|
120
|
+
if (child.parentNode) {
|
|
121
|
+
child.parentNode.removeChild(child);
|
|
122
|
+
}
|
|
123
|
+
child.parentNode = this;
|
|
124
|
+
child._ownerDoc = this.nodeType === 9 ? this : (this.ownerDocument || globalScope.document);
|
|
125
|
+
this.childNodes.push(child);
|
|
126
|
+
return child;
|
|
127
|
+
};
|
|
128
|
+
|
|
129
|
+
Node.prototype.removeChild = function(child) {
|
|
130
|
+
var idx = this.childNodes.indexOf(child);
|
|
131
|
+
if (idx !== -1) {
|
|
132
|
+
this.childNodes.splice(idx, 1);
|
|
133
|
+
child.parentNode = null;
|
|
134
|
+
return child;
|
|
135
|
+
}
|
|
136
|
+
return child;
|
|
137
|
+
};
|
|
138
|
+
|
|
139
|
+
Node.prototype.insertBefore = function(newChild, refChild) {
|
|
140
|
+
if (!refChild) return this.appendChild(newChild);
|
|
141
|
+
if (newChild.nodeType === 11) { // DocumentFragment
|
|
142
|
+
var children = newChild.childNodes.slice();
|
|
143
|
+
for (var i = 0; i < children.length; i++) {
|
|
144
|
+
this.insertBefore(children[i], refChild);
|
|
145
|
+
}
|
|
146
|
+
return newChild;
|
|
147
|
+
}
|
|
148
|
+
if (newChild.parentNode) {
|
|
149
|
+
newChild.parentNode.removeChild(newChild);
|
|
150
|
+
}
|
|
151
|
+
var idx = this.childNodes.indexOf(refChild);
|
|
152
|
+
if (idx !== -1) {
|
|
153
|
+
newChild.parentNode = this;
|
|
154
|
+
newChild.ownerDocument = this.ownerDocument || this;
|
|
155
|
+
this.childNodes.splice(idx, 0, newChild);
|
|
156
|
+
return newChild;
|
|
157
|
+
}
|
|
158
|
+
return this.appendChild(newChild);
|
|
159
|
+
};
|
|
160
|
+
|
|
161
|
+
Node.prototype.replaceChild = function(newChild, oldChild) {
|
|
162
|
+
this.insertBefore(newChild, oldChild);
|
|
163
|
+
return this.removeChild(oldChild);
|
|
164
|
+
};
|
|
165
|
+
|
|
166
|
+
Node.prototype.cloneNode = function(deep) {
|
|
167
|
+
var clone;
|
|
168
|
+
if (this.nodeType === 3) {
|
|
169
|
+
clone = new Text(this.nodeValue);
|
|
170
|
+
} else if (this.nodeType === 1) {
|
|
171
|
+
clone = this.namespaceURI
|
|
172
|
+
? (this.ownerDocument || document).createElementNS(this.namespaceURI, this.tagName.toLowerCase())
|
|
173
|
+
: (this.ownerDocument || document).createElement(this.tagName.toLowerCase());
|
|
174
|
+
for (var k in this._attributes) {
|
|
175
|
+
clone.setAttribute(k, this._attributes[k]);
|
|
176
|
+
}
|
|
177
|
+
for (var prop in this.style._properties) {
|
|
178
|
+
clone.style.setProperty(prop, this.style.getPropertyValue(prop));
|
|
179
|
+
}
|
|
180
|
+
} else {
|
|
181
|
+
clone = new Node();
|
|
182
|
+
}
|
|
183
|
+
if (deep && this.childNodes) {
|
|
184
|
+
for (var i = 0; i < this.childNodes.length; i++) {
|
|
185
|
+
clone.appendChild(this.childNodes[i].cloneNode(true));
|
|
186
|
+
}
|
|
187
|
+
}
|
|
188
|
+
return clone;
|
|
189
|
+
};
|
|
190
|
+
|
|
191
|
+
Object.defineProperty(Node.prototype, 'textContent', {
|
|
192
|
+
get: function() {
|
|
193
|
+
if (this.nodeType === 3) return this.nodeValue;
|
|
194
|
+
var txt = '';
|
|
195
|
+
for (var i = 0; i < this.childNodes.length; i++) {
|
|
196
|
+
txt += this.childNodes[i].textContent || '';
|
|
197
|
+
}
|
|
198
|
+
return txt;
|
|
199
|
+
},
|
|
200
|
+
set: function(val) {
|
|
201
|
+
this.childNodes = [];
|
|
202
|
+
if (val !== null && val !== undefined && val !== '') {
|
|
203
|
+
this.appendChild(new Text(String(val)));
|
|
204
|
+
}
|
|
205
|
+
}
|
|
206
|
+
});
|
|
207
|
+
|
|
208
|
+
// --- Text Node ---
|
|
209
|
+
function Text(text) {
|
|
210
|
+
Node.call(this);
|
|
211
|
+
this.nodeType = 3;
|
|
212
|
+
this.nodeName = '#text';
|
|
213
|
+
this.nodeValue = String(text !== undefined && text !== null ? text : '');
|
|
214
|
+
}
|
|
215
|
+
Text.prototype = Object.create(Node.prototype);
|
|
216
|
+
Object.defineProperty(Text.prototype, 'data', {
|
|
217
|
+
get: function() { return this.nodeValue; },
|
|
218
|
+
set: function(v) { this.nodeValue = String(v); }
|
|
219
|
+
});
|
|
220
|
+
|
|
221
|
+
// --- DocumentFragment ---
|
|
222
|
+
function DocumentFragment() {
|
|
223
|
+
Node.call(this);
|
|
224
|
+
this.nodeType = 11;
|
|
225
|
+
this.nodeName = '#document-fragment';
|
|
226
|
+
}
|
|
227
|
+
DocumentFragment.prototype = Object.create(Node.prototype);
|
|
228
|
+
|
|
229
|
+
// --- Element ---
|
|
230
|
+
function Element(tagName, namespaceURI) {
|
|
231
|
+
Node.call(this);
|
|
232
|
+
this.nodeType = 1;
|
|
233
|
+
this.namespaceURI = namespaceURI || null;
|
|
234
|
+
var tagStr = String(tagName);
|
|
235
|
+
if (this.namespaceURI === 'http://www.w3.org/2000/svg') {
|
|
236
|
+
var lower = tagStr.toLowerCase();
|
|
237
|
+
this.tagName = SVG_CAMEL_TAGS[lower] || lower;
|
|
238
|
+
this.nodeName = this.tagName;
|
|
239
|
+
} else {
|
|
240
|
+
this.tagName = tagStr.toUpperCase();
|
|
241
|
+
this.nodeName = this.tagName;
|
|
242
|
+
}
|
|
243
|
+
this._attributes = {};
|
|
244
|
+
this.style = createStyle(this);
|
|
245
|
+
this.classList = new ClassList(this);
|
|
246
|
+
this._eventListeners = {};
|
|
247
|
+
|
|
248
|
+
// Default dimensions
|
|
249
|
+
this.clientWidth = 0;
|
|
250
|
+
this.clientHeight = 0;
|
|
251
|
+
this.offsetWidth = 0;
|
|
252
|
+
this.offsetHeight = 0;
|
|
253
|
+
}
|
|
254
|
+
Element.prototype = Object.create(Node.prototype);
|
|
255
|
+
|
|
256
|
+
Object.defineProperty(Element.prototype, 'id', {
|
|
257
|
+
get: function() { return this.getAttribute('id') || ''; },
|
|
258
|
+
set: function(v) { this.setAttribute('id', v); }
|
|
259
|
+
});
|
|
260
|
+
|
|
261
|
+
Object.defineProperty(Element.prototype, 'className', {
|
|
262
|
+
get: function() { return this.getAttribute('class') || ''; },
|
|
263
|
+
set: function(v) { this.setAttribute('class', v); }
|
|
264
|
+
});
|
|
265
|
+
|
|
266
|
+
function ClassList(el) { this._el = el; }
|
|
267
|
+
ClassList.prototype.add = function() {
|
|
268
|
+
var current = (this._el.getAttribute('class') || '').split(/\s+/).filter(Boolean);
|
|
269
|
+
for (var i = 0; i < arguments.length; i++) {
|
|
270
|
+
if (current.indexOf(arguments[i]) === -1) current.push(arguments[i]);
|
|
271
|
+
}
|
|
272
|
+
this._el.setAttribute('class', current.join(' '));
|
|
273
|
+
};
|
|
274
|
+
ClassList.prototype.remove = function() {
|
|
275
|
+
var current = (this._el.getAttribute('class') || '').split(/\s+/).filter(Boolean);
|
|
276
|
+
for (var i = 0; i < arguments.length; i++) {
|
|
277
|
+
var idx = current.indexOf(arguments[i]);
|
|
278
|
+
if (idx !== -1) current.splice(idx, 1);
|
|
279
|
+
}
|
|
280
|
+
this._el.setAttribute('class', current.join(' '));
|
|
281
|
+
};
|
|
282
|
+
ClassList.prototype.contains = function(name) {
|
|
283
|
+
var current = (this._el.getAttribute('class') || '').split(/\s+/).filter(Boolean);
|
|
284
|
+
return current.indexOf(name) !== -1;
|
|
285
|
+
};
|
|
286
|
+
ClassList.prototype.toggle = function(name) {
|
|
287
|
+
if (this.contains(name)) { this.remove(name); return false; }
|
|
288
|
+
else { this.add(name); return true; }
|
|
289
|
+
};
|
|
290
|
+
|
|
291
|
+
Element.prototype.setAttribute = function(name, value) {
|
|
292
|
+
this._attributes[name] = String(value);
|
|
293
|
+
};
|
|
294
|
+
Element.prototype.getAttribute = function(name) {
|
|
295
|
+
return this._attributes.hasOwnProperty(name) ? this._attributes[name] : null;
|
|
296
|
+
};
|
|
297
|
+
Element.prototype.hasAttribute = function(name) {
|
|
298
|
+
return this._attributes.hasOwnProperty(name);
|
|
299
|
+
};
|
|
300
|
+
Element.prototype.removeAttribute = function(name) {
|
|
301
|
+
delete this._attributes[name];
|
|
302
|
+
};
|
|
303
|
+
Element.prototype.setAttributeNS = function(ns, name, value) {
|
|
304
|
+
this.setAttribute(name, value);
|
|
305
|
+
if (name === 'href' && ns && String(ns).indexOf('xlink') !== -1) {
|
|
306
|
+
this.setAttribute('xlink:href', value);
|
|
307
|
+
} else if (name === 'xlink:href') {
|
|
308
|
+
this.setAttribute('href', value);
|
|
309
|
+
}
|
|
310
|
+
};
|
|
311
|
+
Element.prototype.getAttributeNS = function(ns, name) {
|
|
312
|
+
return this.getAttribute(name);
|
|
313
|
+
};
|
|
314
|
+
Element.prototype.hasAttributeNS = function(ns, name) {
|
|
315
|
+
return this.hasAttribute(name);
|
|
316
|
+
};
|
|
317
|
+
Element.prototype.removeAttributeNS = function(ns, name) {
|
|
318
|
+
this.removeAttribute(name);
|
|
319
|
+
};
|
|
320
|
+
|
|
321
|
+
// Event listeners
|
|
322
|
+
Element.prototype.addEventListener = function(event, fn) {
|
|
323
|
+
if (!this._eventListeners[event]) this._eventListeners[event] = [];
|
|
324
|
+
this._eventListeners[event].push(fn);
|
|
325
|
+
};
|
|
326
|
+
Element.prototype.removeEventListener = function(event, fn) {
|
|
327
|
+
if (!this._eventListeners[event]) return;
|
|
328
|
+
var idx = this._eventListeners[event].indexOf(fn);
|
|
329
|
+
if (idx !== -1) this._eventListeners[event].splice(idx, 1);
|
|
330
|
+
};
|
|
331
|
+
Element.prototype.dispatchEvent = function(evt) {
|
|
332
|
+
var fns = this._eventListeners[evt.type];
|
|
333
|
+
if (fns) {
|
|
334
|
+
for (var i = 0; i < fns.length; i++) {
|
|
335
|
+
try { fns[i].call(this, evt); } catch(e) {}
|
|
336
|
+
}
|
|
337
|
+
}
|
|
338
|
+
return true;
|
|
339
|
+
};
|
|
340
|
+
|
|
341
|
+
// D3 / Plotly Query Selectors
|
|
342
|
+
Element.prototype.matches = function(selector) {
|
|
343
|
+
if (!selector) return false;
|
|
344
|
+
if (selector === '*') return true;
|
|
345
|
+
if (selector === ':first-child') {
|
|
346
|
+
if (!this.parentNode) return false;
|
|
347
|
+
for (var i = 0; i < this.parentNode.childNodes.length; i++) {
|
|
348
|
+
var ch = this.parentNode.childNodes[i];
|
|
349
|
+
if (ch.nodeType === 1) return ch === this;
|
|
350
|
+
}
|
|
351
|
+
return false;
|
|
352
|
+
}
|
|
353
|
+
if (selector[0] === '#') return this.id === selector.slice(1);
|
|
354
|
+
if (selector[0] === '.') {
|
|
355
|
+
var cls = selector.slice(1);
|
|
356
|
+
return this.classList.contains(cls);
|
|
357
|
+
}
|
|
358
|
+
|
|
359
|
+
// Attribute selector e.g. g[class^="barlayer"], [class^="barlayer"], [id="xyz"]
|
|
360
|
+
var attrMatch = selector.match(/^([a-zA-Z0-9_-]*)\[([a-zA-Z0-9_-]+)([\^$*~|]?=)?(["']?)(.*?)\4\]$/);
|
|
361
|
+
if (attrMatch) {
|
|
362
|
+
var tag = attrMatch[1];
|
|
363
|
+
var attr = attrMatch[2];
|
|
364
|
+
var op = attrMatch[3];
|
|
365
|
+
var val = attrMatch[5];
|
|
366
|
+
if (tag && this.tagName.toLowerCase() !== tag.toLowerCase()) return false;
|
|
367
|
+
if (!this.hasAttribute(attr)) return false;
|
|
368
|
+
if (!op) return true;
|
|
369
|
+
var actual = this.getAttribute(attr) || '';
|
|
370
|
+
if (op === '=') return actual === val;
|
|
371
|
+
if (op === '^=') return actual.indexOf(val) === 0;
|
|
372
|
+
if (op === '$=') return actual.slice(-val.length) === val;
|
|
373
|
+
if (op === '*=') return actual.indexOf(val) !== -1;
|
|
374
|
+
if (op === '~=') return (' ' + actual + ' ').indexOf(' ' + val + ' ') !== -1;
|
|
375
|
+
return false;
|
|
376
|
+
}
|
|
377
|
+
|
|
378
|
+
// Tag with class(es): e.g. svg.main-svg or g.trace.bars
|
|
379
|
+
if (selector.indexOf('.') !== -1) {
|
|
380
|
+
var parts = selector.split('.');
|
|
381
|
+
var tagMatch = !parts[0] || this.tagName.toLowerCase() === parts[0].toLowerCase();
|
|
382
|
+
if (!tagMatch) return false;
|
|
383
|
+
for (var p = 1; p < parts.length; p++) {
|
|
384
|
+
if (!this.classList.contains(parts[p])) return false;
|
|
385
|
+
}
|
|
386
|
+
return true;
|
|
387
|
+
}
|
|
388
|
+
return this.tagName.toLowerCase() === selector.toLowerCase();
|
|
389
|
+
};
|
|
390
|
+
|
|
391
|
+
Element.prototype.querySelector = function(selector) {
|
|
392
|
+
if (selector === ':first-child') {
|
|
393
|
+
for (var i = 0; i < this.childNodes.length; i++) {
|
|
394
|
+
var ch = this.childNodes[i];
|
|
395
|
+
if (ch.nodeType === 1) return ch;
|
|
396
|
+
}
|
|
397
|
+
return null;
|
|
398
|
+
}
|
|
399
|
+
var results = this.querySelectorAll(selector);
|
|
400
|
+
return results.length > 0 ? results[0] : null;
|
|
401
|
+
};
|
|
402
|
+
|
|
403
|
+
function querySelectorAllSimple(root, selector) {
|
|
404
|
+
var matches = [];
|
|
405
|
+
function walk(node) {
|
|
406
|
+
for (var i = 0; i < node.childNodes.length; i++) {
|
|
407
|
+
var child = node.childNodes[i];
|
|
408
|
+
if (child.nodeType === 1) {
|
|
409
|
+
if (child.matches(selector)) matches.push(child);
|
|
410
|
+
walk(child);
|
|
411
|
+
}
|
|
412
|
+
}
|
|
413
|
+
}
|
|
414
|
+
walk(root);
|
|
415
|
+
return matches;
|
|
416
|
+
}
|
|
417
|
+
|
|
418
|
+
function querySelectorAllSingle(root, selector) {
|
|
419
|
+
selector = selector.trim();
|
|
420
|
+
if (!selector) return [];
|
|
421
|
+
|
|
422
|
+
// Handle child combinators: e.g. "a > b > c"
|
|
423
|
+
if (selector.indexOf('>') !== -1) {
|
|
424
|
+
var segments = selector.split('>');
|
|
425
|
+
var current = [root];
|
|
426
|
+
for (var s = 0; s < segments.length; s++) {
|
|
427
|
+
var seg = segments[s].trim();
|
|
428
|
+
var next = [];
|
|
429
|
+
for (var c = 0; c < current.length; c++) {
|
|
430
|
+
var parentNode = current[c];
|
|
431
|
+
if (s === 0) {
|
|
432
|
+
var matched = querySelectorAllSingle(parentNode, seg);
|
|
433
|
+
for (var m = 0; m < matched.length; m++) {
|
|
434
|
+
if (next.indexOf(matched[m]) === -1) next.push(matched[m]);
|
|
435
|
+
}
|
|
436
|
+
} else {
|
|
437
|
+
for (var ch = 0; ch < parentNode.childNodes.length; ch++) {
|
|
438
|
+
var kid = parentNode.childNodes[ch];
|
|
439
|
+
if (kid.nodeType === 1 && kid.matches(seg)) {
|
|
440
|
+
if (next.indexOf(kid) === -1) next.push(kid);
|
|
441
|
+
}
|
|
442
|
+
}
|
|
443
|
+
}
|
|
444
|
+
}
|
|
445
|
+
current = next;
|
|
446
|
+
}
|
|
447
|
+
return current;
|
|
448
|
+
}
|
|
449
|
+
|
|
450
|
+
// Handle space descendant combinators: e.g. "a b c"
|
|
451
|
+
var inBracket = false;
|
|
452
|
+
var hasSpace = false;
|
|
453
|
+
for (var i = 0; i < selector.length; i++) {
|
|
454
|
+
if (selector[i] === '[') inBracket = true;
|
|
455
|
+
else if (selector[i] === ']') inBracket = false;
|
|
456
|
+
else if (selector[i] === ' ' && !inBracket) {
|
|
457
|
+
hasSpace = true;
|
|
458
|
+
break;
|
|
459
|
+
}
|
|
460
|
+
}
|
|
461
|
+
if (hasSpace) {
|
|
462
|
+
var tokens = [];
|
|
463
|
+
var curr = '';
|
|
464
|
+
for (var i = 0; i < selector.length; i++) {
|
|
465
|
+
if (selector[i] === '[') inBracket = true;
|
|
466
|
+
else if (selector[i] === ']') inBracket = false;
|
|
467
|
+
if (selector[i] === ' ' && !inBracket) {
|
|
468
|
+
if (curr.trim()) tokens.push(curr.trim());
|
|
469
|
+
curr = '';
|
|
470
|
+
} else {
|
|
471
|
+
curr += selector[i];
|
|
472
|
+
}
|
|
473
|
+
}
|
|
474
|
+
if (curr.trim()) tokens.push(curr.trim());
|
|
475
|
+
|
|
476
|
+
var current = [root];
|
|
477
|
+
for (var t = 0; t < tokens.length; t++) {
|
|
478
|
+
var tok = tokens[t];
|
|
479
|
+
var next = [];
|
|
480
|
+
for (var c = 0; c < current.length; c++) {
|
|
481
|
+
var descendants = querySelectorAllSimple(current[c], tok);
|
|
482
|
+
for (var d = 0; d < descendants.length; d++) {
|
|
483
|
+
if (next.indexOf(descendants[d]) === -1) next.push(descendants[d]);
|
|
484
|
+
}
|
|
485
|
+
}
|
|
486
|
+
current = next;
|
|
487
|
+
}
|
|
488
|
+
return current;
|
|
489
|
+
}
|
|
490
|
+
|
|
491
|
+
return querySelectorAllSimple(root, selector);
|
|
492
|
+
}
|
|
493
|
+
|
|
494
|
+
Element.prototype.querySelectorAll = function(selector) {
|
|
495
|
+
if (!selector) return [];
|
|
496
|
+
if (selector.indexOf(',') !== -1) {
|
|
497
|
+
var parts = selector.split(',');
|
|
498
|
+
var all = [];
|
|
499
|
+
for (var p = 0; p < parts.length; p++) {
|
|
500
|
+
var res = querySelectorAllSingle(this, parts[p]);
|
|
501
|
+
for (var r = 0; r < res.length; r++) {
|
|
502
|
+
if (all.indexOf(res[r]) === -1) all.push(res[r]);
|
|
503
|
+
}
|
|
504
|
+
}
|
|
505
|
+
return all;
|
|
506
|
+
}
|
|
507
|
+
return querySelectorAllSingle(this, selector);
|
|
508
|
+
};
|
|
509
|
+
|
|
510
|
+
Element.prototype.getElementsByTagName = function(tagName) {
|
|
511
|
+
var target = tagName.toUpperCase();
|
|
512
|
+
var matches = [];
|
|
513
|
+
function walk(node) {
|
|
514
|
+
for (var i = 0; i < node.childNodes.length; i++) {
|
|
515
|
+
var child = node.childNodes[i];
|
|
516
|
+
if (child.nodeType === 1) {
|
|
517
|
+
if (target === '*' || child.tagName === target) matches.push(child);
|
|
518
|
+
walk(child);
|
|
519
|
+
}
|
|
520
|
+
}
|
|
521
|
+
}
|
|
522
|
+
walk(this);
|
|
523
|
+
return matches;
|
|
524
|
+
};
|
|
525
|
+
|
|
526
|
+
Element.prototype.getElementsByClassName = function(className) {
|
|
527
|
+
return this.querySelectorAll('.' + className);
|
|
528
|
+
};
|
|
529
|
+
|
|
530
|
+
// --- Geometry & Font Measurement ---
|
|
531
|
+
Element.prototype.getCTM = function() {
|
|
532
|
+
return { a: 1, b: 0, c: 0, d: 1, e: 0, f: 0 };
|
|
533
|
+
};
|
|
534
|
+
Element.prototype.getScreenCTM = function() {
|
|
535
|
+
return { a: 1, b: 0, c: 0, d: 1, e: 0, f: 0 };
|
|
536
|
+
};
|
|
537
|
+
|
|
538
|
+
function identityMatrix() {
|
|
539
|
+
return { a: 1, b: 0, c: 0, d: 1, e: 0, f: 0 };
|
|
540
|
+
}
|
|
541
|
+
|
|
542
|
+
function multiplyMatrices(m1, m2) {
|
|
543
|
+
// Column-vector convention: result = m1 * m2 (m2 applied first).
|
|
544
|
+
return {
|
|
545
|
+
a: m1.a * m2.a + m1.c * m2.b,
|
|
546
|
+
b: m1.b * m2.a + m1.d * m2.b,
|
|
547
|
+
c: m1.a * m2.c + m1.c * m2.d,
|
|
548
|
+
d: m1.b * m2.c + m1.d * m2.d,
|
|
549
|
+
e: m1.a * m2.e + m1.c * m2.f + m1.e,
|
|
550
|
+
f: m1.b * m2.e + m1.d * m2.f + m1.f
|
|
551
|
+
};
|
|
552
|
+
}
|
|
553
|
+
|
|
554
|
+
function transformPoint(m, x, y) {
|
|
555
|
+
return {
|
|
556
|
+
x: m.a * x + m.c * y + m.e,
|
|
557
|
+
y: m.b * x + m.d * y + m.f
|
|
558
|
+
};
|
|
559
|
+
}
|
|
560
|
+
|
|
561
|
+
function translateMatrix(tx, ty) {
|
|
562
|
+
return { a: 1, b: 0, c: 0, d: 1, e: tx || 0, f: ty || 0 };
|
|
563
|
+
}
|
|
564
|
+
|
|
565
|
+
/**
|
|
566
|
+
* Parse a full SVG transform attribute into a 2D affine matrix.
|
|
567
|
+
* SVG post-multiplies the list left-to-right (M = T1*T2*...*Tn), so the
|
|
568
|
+
* rightmost transform is applied to a point first.
|
|
569
|
+
*/
|
|
570
|
+
function parseTransform(transform) {
|
|
571
|
+
if (!transform) return identityMatrix();
|
|
572
|
+
var result = identityMatrix();
|
|
573
|
+
var re = /(matrix|translate|scale|rotate|skewX|skewY)\s*\(\s*([^)]*)\s*\)/g;
|
|
574
|
+
var match;
|
|
575
|
+
while ((match = re.exec(String(transform))) !== null) {
|
|
576
|
+
var type = match[1];
|
|
577
|
+
var rawArgs = match[2].trim();
|
|
578
|
+
var args = rawArgs ? rawArgs.split(/[\s,]+/).map(parseFloat) : [];
|
|
579
|
+
var m = identityMatrix();
|
|
580
|
+
|
|
581
|
+
if (type === 'matrix' && args.length >= 6) {
|
|
582
|
+
m = {
|
|
583
|
+
a: args[0], b: args[1], c: args[2],
|
|
584
|
+
d: args[3], e: args[4], f: args[5]
|
|
585
|
+
};
|
|
586
|
+
} else if (type === 'translate') {
|
|
587
|
+
m.e = args[0] || 0;
|
|
588
|
+
m.f = args.length > 1 ? (args[1] || 0) : 0;
|
|
589
|
+
} else if (type === 'scale') {
|
|
590
|
+
var sx = args.length > 0 && !isNaN(args[0]) ? args[0] : 1;
|
|
591
|
+
var sy = args.length > 1 && !isNaN(args[1]) ? args[1] : sx;
|
|
592
|
+
m.a = sx;
|
|
593
|
+
m.d = sy;
|
|
594
|
+
} else if (type === 'rotate') {
|
|
595
|
+
var angle = ((args[0] || 0) * Math.PI) / 180;
|
|
596
|
+
var cos = Math.cos(angle);
|
|
597
|
+
var sin = Math.sin(angle);
|
|
598
|
+
var rot = { a: cos, b: sin, c: -sin, d: cos, e: 0, f: 0 };
|
|
599
|
+
if (args.length >= 3) {
|
|
600
|
+
var cx = args[1] || 0;
|
|
601
|
+
var cy = args[2] || 0;
|
|
602
|
+
// translate(cx,cy) rotate(a) translate(-cx,-cy)
|
|
603
|
+
m = multiplyMatrices(
|
|
604
|
+
translateMatrix(cx, cy),
|
|
605
|
+
multiplyMatrices(rot, translateMatrix(-cx, -cy))
|
|
606
|
+
);
|
|
607
|
+
} else {
|
|
608
|
+
m = rot;
|
|
609
|
+
}
|
|
610
|
+
} else if (type === 'skewX') {
|
|
611
|
+
m.c = Math.tan(((args[0] || 0) * Math.PI) / 180);
|
|
612
|
+
} else if (type === 'skewY') {
|
|
613
|
+
m.b = Math.tan(((args[0] || 0) * Math.PI) / 180);
|
|
614
|
+
}
|
|
615
|
+
result = multiplyMatrices(result, m);
|
|
616
|
+
}
|
|
617
|
+
return result;
|
|
618
|
+
}
|
|
619
|
+
|
|
620
|
+
/** Axis-aligned bounds of a bbox after applying an affine matrix to its corners. */
|
|
621
|
+
function transformBBox(bb, matrix) {
|
|
622
|
+
var corners = [
|
|
623
|
+
transformPoint(matrix, bb.x, bb.y),
|
|
624
|
+
transformPoint(matrix, bb.x + bb.width, bb.y),
|
|
625
|
+
transformPoint(matrix, bb.x, bb.y + bb.height),
|
|
626
|
+
transformPoint(matrix, bb.x + bb.width, bb.y + bb.height)
|
|
627
|
+
];
|
|
628
|
+
var minX = Infinity, minY = Infinity, maxX = -Infinity, maxY = -Infinity;
|
|
629
|
+
for (var i = 0; i < 4; i++) {
|
|
630
|
+
var p = corners[i];
|
|
631
|
+
if (p.x < minX) minX = p.x;
|
|
632
|
+
if (p.y < minY) minY = p.y;
|
|
633
|
+
if (p.x > maxX) maxX = p.x;
|
|
634
|
+
if (p.y > maxY) maxY = p.y;
|
|
635
|
+
}
|
|
636
|
+
return {
|
|
637
|
+
x: minX,
|
|
638
|
+
y: minY,
|
|
639
|
+
width: maxX - minX,
|
|
640
|
+
height: maxY - minY,
|
|
641
|
+
left: minX,
|
|
642
|
+
top: minY,
|
|
643
|
+
right: maxX,
|
|
644
|
+
bottom: maxY
|
|
645
|
+
};
|
|
646
|
+
}
|
|
647
|
+
|
|
648
|
+
function getTextAnchor(node) {
|
|
649
|
+
var cur = node;
|
|
650
|
+
while (cur && cur.nodeType === 1) {
|
|
651
|
+
var anchor = cur.style ? (cur.style.getPropertyValue('text-anchor') || cur.getAttribute('text-anchor')) : null;
|
|
652
|
+
if (anchor) return anchor.trim().toLowerCase();
|
|
653
|
+
cur = cur.parentNode;
|
|
654
|
+
}
|
|
655
|
+
return 'start';
|
|
656
|
+
}
|
|
657
|
+
|
|
658
|
+
Element.prototype.getBBox = function() {
|
|
659
|
+
var tag = this.tagName.toLowerCase();
|
|
660
|
+
var x = parseFloat(this.getAttribute('x')) || 0;
|
|
661
|
+
var y = parseFloat(this.getAttribute('y')) || 0;
|
|
662
|
+
|
|
663
|
+
if (tag === 'text') {
|
|
664
|
+
// Check if <text> contains child <tspan> elements (e.g. multi-line text)
|
|
665
|
+
var tspans = [];
|
|
666
|
+
for (var i = 0; i < this.childNodes.length; i++) {
|
|
667
|
+
var ch = this.childNodes[i];
|
|
668
|
+
if (ch.nodeType === 1 && ch.tagName.toLowerCase() === 'tspan') {
|
|
669
|
+
tspans.push(ch);
|
|
670
|
+
}
|
|
671
|
+
}
|
|
672
|
+
|
|
673
|
+
if (tspans.length > 0) {
|
|
674
|
+
var parentFontSize = parseFloat(this.style.getPropertyValue('font-size') || this.getAttribute('font-size')) || 12;
|
|
675
|
+
var parentFontFamily = this.style.getPropertyValue('font-family') || this.getAttribute('font-family') || 'sans-serif';
|
|
676
|
+
var parentFontWeight = this.style.getPropertyValue('font-weight') || this.getAttribute('font-weight') || 'normal';
|
|
677
|
+
var parentAnchor = getTextAnchor(this);
|
|
678
|
+
var baseX = x;
|
|
679
|
+
var baseY = y;
|
|
680
|
+
|
|
681
|
+
var minX = Infinity, minY = Infinity, maxX = -Infinity, maxY = -Infinity;
|
|
682
|
+
var curY = baseY;
|
|
683
|
+
|
|
684
|
+
for (var j = 0; j < tspans.length; j++) {
|
|
685
|
+
var tsp = tspans[j];
|
|
686
|
+
var tText = tsp.textContent || '';
|
|
687
|
+
var tFontSize = parseFloat(tsp.style.getPropertyValue('font-size') || tsp.getAttribute('font-size')) || parentFontSize;
|
|
688
|
+
var tFontFamily = tsp.style.getPropertyValue('font-family') || tsp.getAttribute('font-family') || parentFontFamily;
|
|
689
|
+
var tFontWeight = tsp.style.getPropertyValue('font-weight') || tsp.getAttribute('font-weight') || parentFontWeight;
|
|
690
|
+
var tAnchor = (tsp.style.getPropertyValue('text-anchor') || tsp.getAttribute('text-anchor') || parentAnchor).trim().toLowerCase();
|
|
691
|
+
|
|
692
|
+
var tx = tsp.hasAttribute('x') ? (parseFloat(tsp.getAttribute('x')) || 0) : baseX;
|
|
693
|
+
if (tsp.hasAttribute('y')) {
|
|
694
|
+
curY = parseFloat(tsp.getAttribute('y')) || 0;
|
|
695
|
+
}
|
|
696
|
+
if (tsp.hasAttribute('dy')) {
|
|
697
|
+
var dyStr = tsp.getAttribute('dy') || '';
|
|
698
|
+
var dyVal = 0;
|
|
699
|
+
if (dyStr.indexOf('em') !== -1) {
|
|
700
|
+
dyVal = parseFloat(dyStr) * tFontSize;
|
|
701
|
+
} else {
|
|
702
|
+
dyVal = parseFloat(dyStr) || 0;
|
|
703
|
+
}
|
|
704
|
+
curY += dyVal;
|
|
705
|
+
}
|
|
706
|
+
|
|
707
|
+
var tw = 0, th = tFontSize * 1.2, ta = tFontSize * 0.8;
|
|
708
|
+
if (typeof globalScope._measureTextRaw === 'function') {
|
|
709
|
+
try {
|
|
710
|
+
var raw = globalScope._measureTextRaw(tText, tFontFamily, String(tFontSize), tFontWeight);
|
|
711
|
+
var parts = raw.split(',');
|
|
712
|
+
tw = parseFloat(parts[0]) || 0;
|
|
713
|
+
th = parseFloat(parts[1]) || (tFontSize * 1.2);
|
|
714
|
+
ta = parseFloat(parts[2]) || (tFontSize * 0.8);
|
|
715
|
+
} catch(e) {
|
|
716
|
+
tw = tText.length * tFontSize * 0.6;
|
|
717
|
+
}
|
|
718
|
+
} else {
|
|
719
|
+
tw = tText.length * tFontSize * 0.6;
|
|
720
|
+
}
|
|
721
|
+
|
|
722
|
+
var tMinX;
|
|
723
|
+
if (tAnchor === 'middle') {
|
|
724
|
+
tMinX = tx - tw / 2;
|
|
725
|
+
} else if (tAnchor === 'end') {
|
|
726
|
+
tMinX = tx - tw;
|
|
727
|
+
} else {
|
|
728
|
+
tMinX = tx;
|
|
729
|
+
}
|
|
730
|
+
var tMaxX = tMinX + tw;
|
|
731
|
+
var tMinY = curY - ta;
|
|
732
|
+
var tMaxY = tMinY + th;
|
|
733
|
+
|
|
734
|
+
if (tMinX < minX) minX = tMinX;
|
|
735
|
+
if (tMinY < minY) minY = tMinY;
|
|
736
|
+
if (tMaxX > maxX) maxX = tMaxX;
|
|
737
|
+
if (tMaxY > maxY) maxY = tMaxY;
|
|
738
|
+
}
|
|
739
|
+
|
|
740
|
+
if (minX !== Infinity) {
|
|
741
|
+
var w = maxX - minX;
|
|
742
|
+
var h = maxY - minY;
|
|
743
|
+
return {
|
|
744
|
+
x: minX,
|
|
745
|
+
y: minY,
|
|
746
|
+
width: w,
|
|
747
|
+
height: h,
|
|
748
|
+
left: minX,
|
|
749
|
+
top: minY,
|
|
750
|
+
right: maxX,
|
|
751
|
+
bottom: maxY
|
|
752
|
+
};
|
|
753
|
+
}
|
|
754
|
+
}
|
|
755
|
+
}
|
|
756
|
+
|
|
757
|
+
if (tag === 'text' || tag === 'tspan') {
|
|
758
|
+
var text = this.textContent || '';
|
|
759
|
+
var fontSize = parseFloat(this.style.getPropertyValue('font-size') || this.getAttribute('font-size')) || 12;
|
|
760
|
+
var fontFamily = this.style.getPropertyValue('font-family') || this.getAttribute('font-family') || 'sans-serif';
|
|
761
|
+
var fontWeight = this.style.getPropertyValue('font-weight') || this.getAttribute('font-weight') || 'normal';
|
|
762
|
+
var anchor = getTextAnchor(this);
|
|
763
|
+
|
|
764
|
+
var w = 0, h = fontSize * 1.2, a = fontSize * 0.8, d = fontSize * 0.2;
|
|
765
|
+
if (typeof globalScope._measureTextRaw === 'function') {
|
|
766
|
+
try {
|
|
767
|
+
var raw = globalScope._measureTextRaw(text, fontFamily, String(fontSize), fontWeight);
|
|
768
|
+
var parts = raw.split(',');
|
|
769
|
+
w = parseFloat(parts[0]) || 0;
|
|
770
|
+
h = parseFloat(parts[1]) || (fontSize * 1.2);
|
|
771
|
+
a = parseFloat(parts[2]) || (fontSize * 0.8);
|
|
772
|
+
d = parseFloat(parts[3]) || (fontSize * 0.2);
|
|
773
|
+
} catch(e) {
|
|
774
|
+
w = text.length * fontSize * 0.6;
|
|
775
|
+
}
|
|
776
|
+
} else {
|
|
777
|
+
w = text.length * fontSize * 0.6;
|
|
778
|
+
}
|
|
779
|
+
|
|
780
|
+
var minX;
|
|
781
|
+
if (anchor === 'middle') {
|
|
782
|
+
minX = x - w / 2;
|
|
783
|
+
} else if (anchor === 'end') {
|
|
784
|
+
minX = x - w;
|
|
785
|
+
} else {
|
|
786
|
+
minX = x;
|
|
787
|
+
}
|
|
788
|
+
|
|
789
|
+
return {
|
|
790
|
+
x: minX,
|
|
791
|
+
y: y - a,
|
|
792
|
+
width: w,
|
|
793
|
+
height: h,
|
|
794
|
+
left: minX,
|
|
795
|
+
top: y - a,
|
|
796
|
+
right: minX + w,
|
|
797
|
+
bottom: y - a + h
|
|
798
|
+
};
|
|
799
|
+
}
|
|
800
|
+
|
|
801
|
+
// For groups (<g>, <svg>), aggregate children bounding boxes.
|
|
802
|
+
// Each child's transform is applied to its bbox corners (SVG getBBox on a
|
|
803
|
+
// group includes child transforms; an element's own transform is NOT part
|
|
804
|
+
// of that element's own getBBox).
|
|
805
|
+
var minX = Infinity, minY = Infinity, maxX = -Infinity, maxY = -Infinity;
|
|
806
|
+
var hasKids = false;
|
|
807
|
+
for (var i = 0; i < this.childNodes.length; i++) {
|
|
808
|
+
var child = this.childNodes[i];
|
|
809
|
+
if (child.nodeType === 1 && typeof child.getBBox === 'function') {
|
|
810
|
+
var bb = child.getBBox();
|
|
811
|
+
var childMatrix = parseTransform(child.getAttribute('transform'));
|
|
812
|
+
var transformed = transformBBox(bb, childMatrix);
|
|
813
|
+
var cw = transformed.width;
|
|
814
|
+
var ch = transformed.height;
|
|
815
|
+
if (cw > 0 || ch > 0) {
|
|
816
|
+
hasKids = true;
|
|
817
|
+
if (transformed.x < minX) minX = transformed.x;
|
|
818
|
+
if (transformed.y < minY) minY = transformed.y;
|
|
819
|
+
if (transformed.x + cw > maxX) maxX = transformed.x + cw;
|
|
820
|
+
if (transformed.y + ch > maxY) maxY = transformed.y + ch;
|
|
821
|
+
}
|
|
822
|
+
}
|
|
823
|
+
}
|
|
824
|
+
if (!hasKids) {
|
|
825
|
+
return { x: 0, y: 0, width: 0, height: 0, left: 0, top: 0, right: 0, bottom: 0 };
|
|
826
|
+
}
|
|
827
|
+
return {
|
|
828
|
+
x: minX,
|
|
829
|
+
y: minY,
|
|
830
|
+
width: maxX - minX,
|
|
831
|
+
height: maxY - minY,
|
|
832
|
+
left: minX,
|
|
833
|
+
top: minY,
|
|
834
|
+
right: maxX,
|
|
835
|
+
bottom: maxY
|
|
836
|
+
};
|
|
837
|
+
};
|
|
838
|
+
|
|
839
|
+
Element.prototype.getBoundingClientRect = function() {
|
|
840
|
+
var bb = this.getBBox();
|
|
841
|
+
// Compose full ancestor matrices (including this element's transform).
|
|
842
|
+
// Walk self → root so final = T_root * ... * T_parent * T_self.
|
|
843
|
+
var matrix = identityMatrix();
|
|
844
|
+
var cur = this;
|
|
845
|
+
while (cur && cur.nodeType === 1) {
|
|
846
|
+
var local = cur.getAttribute
|
|
847
|
+
? parseTransform(cur.getAttribute('transform'))
|
|
848
|
+
: identityMatrix();
|
|
849
|
+
var ox = cur.offsetLeft || 0;
|
|
850
|
+
var oy = cur.offsetTop || 0;
|
|
851
|
+
if (ox || oy) {
|
|
852
|
+
local = multiplyMatrices(translateMatrix(ox, oy), local);
|
|
853
|
+
}
|
|
854
|
+
matrix = multiplyMatrices(local, matrix);
|
|
855
|
+
cur = cur.parentNode;
|
|
856
|
+
}
|
|
857
|
+
var transformed = transformBBox(bb, matrix);
|
|
858
|
+
// Prefer explicit layout size when present (HTML elements); otherwise use
|
|
859
|
+
// the transformed SVG bbox so rotations expand width/height correctly.
|
|
860
|
+
var width = this.offsetWidth || this.clientWidth || transformed.width;
|
|
861
|
+
var height = this.offsetHeight || this.clientHeight || transformed.height;
|
|
862
|
+
return {
|
|
863
|
+
left: transformed.x,
|
|
864
|
+
top: transformed.y,
|
|
865
|
+
right: transformed.x + width,
|
|
866
|
+
bottom: transformed.y + height,
|
|
867
|
+
width: width,
|
|
868
|
+
height: height
|
|
869
|
+
};
|
|
870
|
+
};
|
|
871
|
+
|
|
872
|
+
Element.prototype.getComputedTextLength = function() {
|
|
873
|
+
return this.getBBox().width;
|
|
874
|
+
};
|
|
875
|
+
|
|
876
|
+
Element.prototype.getTotalLength = function() {
|
|
877
|
+
var bb = this.getBBox();
|
|
878
|
+
var p = (bb.width + bb.height) * 2;
|
|
879
|
+
return p > 0 ? p : 100;
|
|
880
|
+
};
|
|
881
|
+
|
|
882
|
+
Element.prototype.getPointAtLength = function(len) {
|
|
883
|
+
var bb = this.getBBox();
|
|
884
|
+
return { x: bb.x, y: bb.y };
|
|
885
|
+
};
|
|
886
|
+
|
|
887
|
+
var SVG_CAMEL_TAGS = {
|
|
888
|
+
'lineargradient': 'linearGradient',
|
|
889
|
+
'radialgradient': 'radialGradient',
|
|
890
|
+
'clippath': 'clipPath',
|
|
891
|
+
'textpath': 'textPath',
|
|
892
|
+
'foreignobject': 'foreignObject',
|
|
893
|
+
'animatetransform': 'animateTransform',
|
|
894
|
+
'fecolormatrix': 'feColorMatrix',
|
|
895
|
+
'fecomposite': 'feComposite',
|
|
896
|
+
'fegaussianblur': 'feGaussianBlur',
|
|
897
|
+
'femerge': 'feMerge',
|
|
898
|
+
'femergenode': 'feMergeNode',
|
|
899
|
+
'feoffset': 'feOffset'
|
|
900
|
+
};
|
|
901
|
+
|
|
902
|
+
// Serialization
|
|
903
|
+
function serializeNode(node) {
|
|
904
|
+
if (node.nodeType === 3) {
|
|
905
|
+
return node.nodeValue
|
|
906
|
+
.replace(/&/g, '&')
|
|
907
|
+
.replace(/</g, '<')
|
|
908
|
+
.replace(/>/g, '>');
|
|
909
|
+
}
|
|
910
|
+
if (node.nodeType === 1) {
|
|
911
|
+
var lowerTag = node.tagName.toLowerCase();
|
|
912
|
+
var tag = SVG_CAMEL_TAGS[lowerTag] || lowerTag;
|
|
913
|
+
var out = '<' + tag;
|
|
914
|
+
var css = (node.style && typeof node.style.cssText === 'function') ? node.style.cssText() : '';
|
|
915
|
+
for (var k in node._attributes) {
|
|
916
|
+
if (k === 'style') continue;
|
|
917
|
+
out += ' ' + k + '="' + String(node._attributes[k]).replace(/"/g, '"') + '"';
|
|
918
|
+
}
|
|
919
|
+
if (css) {
|
|
920
|
+
out += ' style="' + css.replace(/"/g, '"') + '"';
|
|
921
|
+
} else if (node._attributes['style']) {
|
|
922
|
+
out += ' style="' + String(node._attributes['style']).replace(/"/g, '"') + '"';
|
|
923
|
+
}
|
|
924
|
+
if (node.childNodes.length === 0) {
|
|
925
|
+
if (['circle', 'path', 'line', 'rect', 'polygon', 'polyline', 'ellipse'].indexOf(lowerTag) !== -1) {
|
|
926
|
+
return out + '/>';
|
|
927
|
+
}
|
|
928
|
+
return out + '></' + tag + '>';
|
|
929
|
+
}
|
|
930
|
+
out += '>';
|
|
931
|
+
for (var i = 0; i < node.childNodes.length; i++) {
|
|
932
|
+
out += serializeNode(node.childNodes[i]);
|
|
933
|
+
}
|
|
934
|
+
out += '</' + tag + '>';
|
|
935
|
+
return out;
|
|
936
|
+
}
|
|
937
|
+
return '';
|
|
938
|
+
}
|
|
939
|
+
|
|
940
|
+
Object.defineProperty(Element.prototype, 'outerHTML', {
|
|
941
|
+
get: function() { return serializeNode(this); }
|
|
942
|
+
});
|
|
943
|
+
Object.defineProperty(Element.prototype, 'innerHTML', {
|
|
944
|
+
get: function() {
|
|
945
|
+
var out = '';
|
|
946
|
+
for (var i = 0; i < this.childNodes.length; i++) {
|
|
947
|
+
out += serializeNode(this.childNodes[i]);
|
|
948
|
+
}
|
|
949
|
+
return out;
|
|
950
|
+
},
|
|
951
|
+
set: function(html) {
|
|
952
|
+
this.childNodes = [];
|
|
953
|
+
// Basic text assignment if no parser needed
|
|
954
|
+
if (html && html.indexOf('<') === -1) {
|
|
955
|
+
this.appendChild(new Text(html));
|
|
956
|
+
}
|
|
957
|
+
}
|
|
958
|
+
});
|
|
959
|
+
|
|
960
|
+
// --- HTML / SVG Specifics ---
|
|
961
|
+
function HTMLElement(tagName) { Element.call(this, tagName, null); }
|
|
962
|
+
HTMLElement.prototype = Object.create(Element.prototype);
|
|
963
|
+
|
|
964
|
+
function SVGElement(tagName) { Element.call(this, tagName, 'http://www.w3.org/2000/svg'); }
|
|
965
|
+
SVGElement.prototype = Object.create(Element.prototype);
|
|
966
|
+
|
|
967
|
+
function HTMLStyleElement() {
|
|
968
|
+
HTMLElement.call(this, 'style');
|
|
969
|
+
this.sheet = {
|
|
970
|
+
insertRule: function(rule, index) {},
|
|
971
|
+
addRule: function(selector, style, index) {},
|
|
972
|
+
cssRules: []
|
|
973
|
+
};
|
|
974
|
+
}
|
|
975
|
+
HTMLStyleElement.prototype = Object.create(HTMLElement.prototype);
|
|
976
|
+
|
|
977
|
+
function HTMLCanvasElement() {
|
|
978
|
+
HTMLElement.call(this, 'canvas');
|
|
979
|
+
this._width = 300;
|
|
980
|
+
this._height = 150;
|
|
981
|
+
this._ctx2d = null;
|
|
982
|
+
}
|
|
983
|
+
HTMLCanvasElement.prototype = Object.create(HTMLElement.prototype);
|
|
984
|
+
Object.defineProperty(HTMLCanvasElement.prototype, 'width', {
|
|
985
|
+
get: function() { return this._width; },
|
|
986
|
+
set: function(v) {
|
|
987
|
+
this._width = parseInt(v, 10) || 0;
|
|
988
|
+
this.setAttribute('width', this._width);
|
|
989
|
+
if (this._ctx2d) { this._ctx2d._commands = []; }
|
|
990
|
+
}
|
|
991
|
+
});
|
|
992
|
+
Object.defineProperty(HTMLCanvasElement.prototype, 'height', {
|
|
993
|
+
get: function() { return this._height; },
|
|
994
|
+
set: function(v) {
|
|
995
|
+
this._height = parseInt(v, 10) || 0;
|
|
996
|
+
this.setAttribute('height', this._height);
|
|
997
|
+
if (this._ctx2d) { this._ctx2d._commands = []; }
|
|
998
|
+
}
|
|
999
|
+
});
|
|
1000
|
+
HTMLCanvasElement.prototype.getContext = function(type) {
|
|
1001
|
+
if (type === '2d') {
|
|
1002
|
+
if (!this._ctx2d) {
|
|
1003
|
+
var self = this;
|
|
1004
|
+
this._ctx2d = {
|
|
1005
|
+
canvas: self,
|
|
1006
|
+
fillStyle: '#000000',
|
|
1007
|
+
strokeStyle: '#000000',
|
|
1008
|
+
lineWidth: 1,
|
|
1009
|
+
_commands: [],
|
|
1010
|
+
fillRect: function(x, y, w, h) {
|
|
1011
|
+
this._commands.push({
|
|
1012
|
+
op: 'fillRect',
|
|
1013
|
+
x: x,
|
|
1014
|
+
y: y,
|
|
1015
|
+
w: w,
|
|
1016
|
+
h: h,
|
|
1017
|
+
fill: this.fillStyle
|
|
1018
|
+
});
|
|
1019
|
+
},
|
|
1020
|
+
clearRect: function(x, y, w, h) {
|
|
1021
|
+
this._commands.push({
|
|
1022
|
+
op: 'clearRect',
|
|
1023
|
+
x: x,
|
|
1024
|
+
y: y,
|
|
1025
|
+
w: w,
|
|
1026
|
+
h: h
|
|
1027
|
+
});
|
|
1028
|
+
},
|
|
1029
|
+
save: function() {},
|
|
1030
|
+
restore: function() {},
|
|
1031
|
+
beginPath: function() {},
|
|
1032
|
+
closePath: function() {},
|
|
1033
|
+
moveTo: function() {},
|
|
1034
|
+
lineTo: function() {},
|
|
1035
|
+
stroke: function() {},
|
|
1036
|
+
fill: function() {}
|
|
1037
|
+
};
|
|
1038
|
+
}
|
|
1039
|
+
return this._ctx2d;
|
|
1040
|
+
}
|
|
1041
|
+
return null;
|
|
1042
|
+
};
|
|
1043
|
+
HTMLCanvasElement.prototype.toDataURL = function(type) {
|
|
1044
|
+
if (typeof globalScope._encodeCanvasToPNG === 'function') {
|
|
1045
|
+
var cmds = this._ctx2d ? this._ctx2d._commands : [];
|
|
1046
|
+
return globalScope._encodeCanvasToPNG(this.width, this.height, JSON.stringify(cmds));
|
|
1047
|
+
}
|
|
1048
|
+
return 'data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAQAAAC1HAwCAAAAC0lEQVR42mNkYAAAAAYAAjCB0C8AAAAASUVORK5CYII=';
|
|
1049
|
+
};
|
|
1050
|
+
|
|
1051
|
+
// --- Document ---
|
|
1052
|
+
function Document() {
|
|
1053
|
+
Node.call(this);
|
|
1054
|
+
this.nodeType = 9;
|
|
1055
|
+
this.nodeName = '#document';
|
|
1056
|
+
this._ownerDoc = null;
|
|
1057
|
+
this.documentElement = new HTMLElement('html');
|
|
1058
|
+
this.documentElement._ownerDoc = this;
|
|
1059
|
+
this.head = new HTMLElement('head');
|
|
1060
|
+
this.head._ownerDoc = this;
|
|
1061
|
+
this.body = new HTMLElement('body');
|
|
1062
|
+
this.body._ownerDoc = this;
|
|
1063
|
+
this.documentElement.appendChild(this.head);
|
|
1064
|
+
this.documentElement.appendChild(this.body);
|
|
1065
|
+
this._elementsById = {};
|
|
1066
|
+
}
|
|
1067
|
+
Document.prototype = Object.create(Node.prototype);
|
|
1068
|
+
|
|
1069
|
+
Document.prototype.createElement = function(tag) {
|
|
1070
|
+
var lower = String(tag).toLowerCase();
|
|
1071
|
+
var el;
|
|
1072
|
+
if (lower === 'style') el = new HTMLStyleElement();
|
|
1073
|
+
else if (lower === 'canvas') el = new HTMLCanvasElement();
|
|
1074
|
+
else el = new HTMLElement(lower);
|
|
1075
|
+
el.ownerDocument = this;
|
|
1076
|
+
return el;
|
|
1077
|
+
};
|
|
1078
|
+
|
|
1079
|
+
Document.prototype.createElementNS = function(ns, tag) {
|
|
1080
|
+
var lower = String(tag).toLowerCase();
|
|
1081
|
+
var el = new SVGElement(lower);
|
|
1082
|
+
el.namespaceURI = ns;
|
|
1083
|
+
el.ownerDocument = this;
|
|
1084
|
+
return el;
|
|
1085
|
+
};
|
|
1086
|
+
|
|
1087
|
+
Document.prototype.createTextNode = function(text) {
|
|
1088
|
+
var t = new Text(text);
|
|
1089
|
+
t.ownerDocument = this;
|
|
1090
|
+
return t;
|
|
1091
|
+
};
|
|
1092
|
+
|
|
1093
|
+
Document.prototype.createDocumentFragment = function() {
|
|
1094
|
+
var frag = new DocumentFragment();
|
|
1095
|
+
frag.ownerDocument = this;
|
|
1096
|
+
return frag;
|
|
1097
|
+
};
|
|
1098
|
+
|
|
1099
|
+
Document.prototype.getElementById = function(id) {
|
|
1100
|
+
return this.documentElement.querySelector('#' + id);
|
|
1101
|
+
};
|
|
1102
|
+
|
|
1103
|
+
Document.prototype.querySelector = function(sel) {
|
|
1104
|
+
return this.documentElement.querySelector(sel);
|
|
1105
|
+
};
|
|
1106
|
+
|
|
1107
|
+
Document.prototype.querySelectorAll = function(sel) {
|
|
1108
|
+
return this.documentElement.querySelectorAll(sel);
|
|
1109
|
+
};
|
|
1110
|
+
|
|
1111
|
+
Document.prototype.getElementsByTagName = function(tag) {
|
|
1112
|
+
return this.documentElement.getElementsByTagName(tag);
|
|
1113
|
+
};
|
|
1114
|
+
|
|
1115
|
+
Document.prototype.getElementsByClassName = function(cls) {
|
|
1116
|
+
return this.documentElement.getElementsByClassName(cls);
|
|
1117
|
+
};
|
|
1118
|
+
|
|
1119
|
+
// --- Global Setup ---
|
|
1120
|
+
globalScope.console = {
|
|
1121
|
+
log: function() {
|
|
1122
|
+
if (typeof globalScope._printLog === 'function') {
|
|
1123
|
+
globalScope._printLog(Array.prototype.slice.call(arguments).map(String).join(' '));
|
|
1124
|
+
}
|
|
1125
|
+
},
|
|
1126
|
+
warn: function() {
|
|
1127
|
+
if (typeof globalScope._printLog === 'function') {
|
|
1128
|
+
globalScope._printLog('[WARN] ' + Array.prototype.slice.call(arguments).map(String).join(' '));
|
|
1129
|
+
}
|
|
1130
|
+
},
|
|
1131
|
+
error: function() {
|
|
1132
|
+
if (typeof globalScope._printLog === 'function') {
|
|
1133
|
+
globalScope._printLog('[ERROR] ' + Array.prototype.slice.call(arguments).map(String).join(' '));
|
|
1134
|
+
}
|
|
1135
|
+
}
|
|
1136
|
+
};
|
|
1137
|
+
|
|
1138
|
+
var doc = new Document();
|
|
1139
|
+
globalScope.window = globalScope;
|
|
1140
|
+
globalScope.self = globalScope;
|
|
1141
|
+
globalScope.document = doc;
|
|
1142
|
+
globalScope.Node = Node;
|
|
1143
|
+
globalScope.Element = Element;
|
|
1144
|
+
globalScope.HTMLElement = HTMLElement;
|
|
1145
|
+
globalScope.HTMLCanvasElement = HTMLCanvasElement;
|
|
1146
|
+
globalScope.SVGElement = SVGElement;
|
|
1147
|
+
globalScope.DocumentFragment = DocumentFragment;
|
|
1148
|
+
globalScope.Text = Text;
|
|
1149
|
+
|
|
1150
|
+
globalScope.Event = function(type) { this.type = type; };
|
|
1151
|
+
globalScope.CustomEvent = function(type, p) { this.type = type; this.detail = p ? p.detail : null; };
|
|
1152
|
+
globalScope.MouseEvent = function(type) { this.type = type; };
|
|
1153
|
+
|
|
1154
|
+
globalScope.DOMParser = function() {};
|
|
1155
|
+
globalScope.DOMParser.prototype.parseFromString = function(str, mime) {
|
|
1156
|
+
var d = new Document();
|
|
1157
|
+
var root = new SVGElement('svg');
|
|
1158
|
+
d.appendChild(root);
|
|
1159
|
+
return d;
|
|
1160
|
+
};
|
|
1161
|
+
|
|
1162
|
+
globalScope.XMLSerializer = function() {};
|
|
1163
|
+
globalScope.XMLSerializer.prototype.serializeToString = function(node) {
|
|
1164
|
+
return serializeNode(node);
|
|
1165
|
+
};
|
|
1166
|
+
|
|
1167
|
+
globalScope.getComputedStyle = function(el) {
|
|
1168
|
+
return el ? el.style : new CSSStyleDeclaration();
|
|
1169
|
+
};
|
|
1170
|
+
|
|
1171
|
+
globalScope.setTimeout = function(fn, ms) { return 1; };
|
|
1172
|
+
globalScope.clearTimeout = function(id) {};
|
|
1173
|
+
globalScope.setInterval = function(fn, ms) { return 1; };
|
|
1174
|
+
globalScope.clearInterval = function(id) {};
|
|
1175
|
+
globalScope.requestAnimationFrame = function(fn) {
|
|
1176
|
+
try { fn(Date.now()); } catch(e) {}
|
|
1177
|
+
return 1;
|
|
1178
|
+
};
|
|
1179
|
+
globalScope.cancelAnimationFrame = function(id) {};
|
|
1180
|
+
|
|
1181
|
+
globalScope.navigator = {
|
|
1182
|
+
userAgent: "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko)",
|
|
1183
|
+
platform: "MacIntel"
|
|
1184
|
+
};
|
|
1185
|
+
|
|
1186
|
+
globalScope.location = {
|
|
1187
|
+
href: "http://localhost/",
|
|
1188
|
+
protocol: "http:",
|
|
1189
|
+
host: "localhost",
|
|
1190
|
+
pathname: "/"
|
|
1191
|
+
};
|
|
1192
|
+
|
|
1193
|
+
globalScope.screen = { width: 1920, height: 1080 };
|
|
1194
|
+
|
|
1195
|
+
})();
|