@formulaxjs/editor 0.2.0 → 0.3.0
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/README.md +42 -10
- package/dist/index.cjs +194 -445
- package/dist/index.cjs.map +1 -1
- package/dist/index.global.js +1064 -765
- package/dist/index.global.js.map +1 -1
- package/dist/index.js +186 -431
- package/dist/index.js.map +1 -1
- package/package.json +5 -4
package/dist/index.js
CHANGED
|
@@ -1,76 +1,164 @@
|
|
|
1
1
|
// src/formula-modal.ts
|
|
2
2
|
import { createEmptyState, parseLatex } from "@formulaxjs/core";
|
|
3
3
|
import { mountKityEditor } from "@formulaxjs/kity-runtime";
|
|
4
|
+
import { escapeHtml, ensureFormulaXBaseStyles } from "@formulaxjs/renderer";
|
|
5
|
+
import {
|
|
6
|
+
serializeKityFormulaFromRoot,
|
|
7
|
+
waitForKityFormulaSvgLayout
|
|
8
|
+
} from "@formulaxjs/renderer-kity";
|
|
4
9
|
|
|
5
|
-
// src/
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
function createFormulaMarkup(latex, options = {}) {
|
|
16
|
-
const attributeName = options.attributeName ?? DEFAULT_FORMULA_ATTRIBUTE;
|
|
17
|
-
const className = options.className ?? DEFAULT_FORMULA_CLASS;
|
|
18
|
-
const displayClass = options.displayMode ? `${className} ${className}--block` : className;
|
|
19
|
-
const safeLatex = escapeAttribute(latex);
|
|
20
|
-
const cursorStyle = options.cursorStyle?.trim() || "pointer";
|
|
21
|
-
const extraAttributes = {
|
|
22
|
-
...options.extraAttributes ?? {},
|
|
23
|
-
style: mergeInlineStyles(
|
|
24
|
-
typeof options.extraAttributes?.style === "string" ? options.extraAttributes.style : "",
|
|
25
|
-
cursorStyle ? `cursor: ${cursorStyle}` : ""
|
|
26
|
-
)
|
|
10
|
+
// src/perf.ts
|
|
11
|
+
import { ensureKityRuntime } from "@formulaxjs/kity-runtime";
|
|
12
|
+
function getPerfHost() {
|
|
13
|
+
return globalThis;
|
|
14
|
+
}
|
|
15
|
+
function getPerfState() {
|
|
16
|
+
const host = getPerfHost();
|
|
17
|
+
host.__FORMULAX_PERF_STATE__ ??= {
|
|
18
|
+
reportedMeasureCount: 0,
|
|
19
|
+
reportScheduled: false
|
|
27
20
|
};
|
|
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
|
-
const
|
|
73
|
-
|
|
21
|
+
return host.__FORMULAX_PERF_STATE__;
|
|
22
|
+
}
|
|
23
|
+
function hasPerfSupport() {
|
|
24
|
+
return typeof performance !== "undefined" && typeof performance.mark === "function" && typeof performance.measure === "function" && typeof performance.getEntriesByType === "function";
|
|
25
|
+
}
|
|
26
|
+
function isPerfDebugEnabled() {
|
|
27
|
+
return getPerfHost().__FORMULAX_PERF__ === true;
|
|
28
|
+
}
|
|
29
|
+
function schedulePerfReport() {
|
|
30
|
+
if (!hasPerfSupport() || !isPerfDebugEnabled()) {
|
|
31
|
+
return;
|
|
32
|
+
}
|
|
33
|
+
const state = getPerfState();
|
|
34
|
+
if (state.reportScheduled) {
|
|
35
|
+
return;
|
|
36
|
+
}
|
|
37
|
+
state.reportScheduled = true;
|
|
38
|
+
queueMicrotask(() => {
|
|
39
|
+
state.reportScheduled = false;
|
|
40
|
+
const entries = performance.getEntriesByType("measure").filter((entry) => entry.name.startsWith("fx:")).sort((left, right) => left.startTime - right.startTime);
|
|
41
|
+
const nextEntries = entries.slice(state.reportedMeasureCount);
|
|
42
|
+
state.reportedMeasureCount = entries.length;
|
|
43
|
+
if (!nextEntries.length) {
|
|
44
|
+
return;
|
|
45
|
+
}
|
|
46
|
+
console.table(nextEntries.map((entry) => ({
|
|
47
|
+
name: entry.name,
|
|
48
|
+
duration: Number(entry.duration.toFixed(2)),
|
|
49
|
+
startTime: Number(entry.startTime.toFixed(2))
|
|
50
|
+
})));
|
|
51
|
+
});
|
|
52
|
+
}
|
|
53
|
+
function markFormulaXPerf(name) {
|
|
54
|
+
if (!hasPerfSupport()) {
|
|
55
|
+
return null;
|
|
56
|
+
}
|
|
57
|
+
const markName = `${name}::${Date.now()}::${Math.random().toString(36).slice(2, 8)}`;
|
|
58
|
+
performance.mark(markName);
|
|
59
|
+
return markName;
|
|
60
|
+
}
|
|
61
|
+
function measureFormulaXPerf(name, startMark, endMark) {
|
|
62
|
+
if (!hasPerfSupport() || !startMark) {
|
|
63
|
+
return null;
|
|
64
|
+
}
|
|
65
|
+
const resolvedEndMark = endMark ?? markFormulaXPerf(`${name}:end`);
|
|
66
|
+
if (!resolvedEndMark) {
|
|
67
|
+
return null;
|
|
68
|
+
}
|
|
69
|
+
performance.measure(name, startMark, resolvedEndMark);
|
|
70
|
+
schedulePerfReport();
|
|
71
|
+
return resolvedEndMark;
|
|
72
|
+
}
|
|
73
|
+
function recordFormulaXPerfPoint(name) {
|
|
74
|
+
const markName = markFormulaXPerf(name);
|
|
75
|
+
if (!markName) {
|
|
76
|
+
return;
|
|
77
|
+
}
|
|
78
|
+
measureFormulaXPerf(name, markName, markName);
|
|
79
|
+
clearFormulaXPerfMarks(markName);
|
|
80
|
+
}
|
|
81
|
+
function clearFormulaXPerfMarks(...marks) {
|
|
82
|
+
if (!hasPerfSupport()) {
|
|
83
|
+
return;
|
|
84
|
+
}
|
|
85
|
+
for (const mark of marks) {
|
|
86
|
+
if (!mark) {
|
|
87
|
+
continue;
|
|
88
|
+
}
|
|
89
|
+
performance.clearMarks(mark);
|
|
90
|
+
}
|
|
91
|
+
}
|
|
92
|
+
async function preloadFormulaXEditor() {
|
|
93
|
+
await ensureKityRuntime();
|
|
94
|
+
}
|
|
95
|
+
function scheduleFormulaXEditorPreload(mode, target) {
|
|
96
|
+
if (mode === false || typeof window === "undefined") {
|
|
97
|
+
return () => void 0;
|
|
98
|
+
}
|
|
99
|
+
let disposed = false;
|
|
100
|
+
let triggered = false;
|
|
101
|
+
const cleanupCallbacks = [];
|
|
102
|
+
const trigger = () => {
|
|
103
|
+
if (disposed || triggered) {
|
|
104
|
+
return;
|
|
105
|
+
}
|
|
106
|
+
triggered = true;
|
|
107
|
+
while (cleanupCallbacks.length) {
|
|
108
|
+
cleanupCallbacks.pop()?.();
|
|
109
|
+
}
|
|
110
|
+
void preloadFormulaXEditor();
|
|
111
|
+
};
|
|
112
|
+
if (mode === "idle") {
|
|
113
|
+
const host = getPerfHost();
|
|
114
|
+
if (typeof host.requestIdleCallback === "function") {
|
|
115
|
+
const handle = host.requestIdleCallback(() => {
|
|
116
|
+
trigger();
|
|
117
|
+
});
|
|
118
|
+
cleanupCallbacks.push(() => {
|
|
119
|
+
host.cancelIdleCallback?.(handle);
|
|
120
|
+
});
|
|
121
|
+
} else {
|
|
122
|
+
const handle = window.setTimeout(() => {
|
|
123
|
+
trigger();
|
|
124
|
+
}, 1);
|
|
125
|
+
cleanupCallbacks.push(() => {
|
|
126
|
+
window.clearTimeout(handle);
|
|
127
|
+
});
|
|
128
|
+
}
|
|
129
|
+
return () => {
|
|
130
|
+
disposed = true;
|
|
131
|
+
while (cleanupCallbacks.length) {
|
|
132
|
+
cleanupCallbacks.pop()?.();
|
|
133
|
+
}
|
|
134
|
+
};
|
|
135
|
+
}
|
|
136
|
+
if (target && "addEventListener" in target && "removeEventListener" in target) {
|
|
137
|
+
const eventTarget = target;
|
|
138
|
+
const onActivate = () => {
|
|
139
|
+
trigger();
|
|
140
|
+
};
|
|
141
|
+
eventTarget.addEventListener("pointerenter", onActivate, { once: true, passive: true });
|
|
142
|
+
eventTarget.addEventListener("focusin", onActivate, { once: true });
|
|
143
|
+
cleanupCallbacks.push(() => {
|
|
144
|
+
eventTarget.removeEventListener("pointerenter", onActivate);
|
|
145
|
+
eventTarget.removeEventListener("focusin", onActivate);
|
|
146
|
+
});
|
|
147
|
+
}
|
|
148
|
+
return () => {
|
|
149
|
+
disposed = true;
|
|
150
|
+
while (cleanupCallbacks.length) {
|
|
151
|
+
cleanupCallbacks.pop()?.();
|
|
152
|
+
}
|
|
153
|
+
};
|
|
154
|
+
}
|
|
155
|
+
function waitForFormulaXAnimationFrame() {
|
|
156
|
+
if (typeof window === "undefined" || typeof window.requestAnimationFrame !== "function") {
|
|
157
|
+
return Promise.resolve();
|
|
158
|
+
}
|
|
159
|
+
return new Promise((resolve) => {
|
|
160
|
+
window.requestAnimationFrame(() => resolve());
|
|
161
|
+
});
|
|
74
162
|
}
|
|
75
163
|
|
|
76
164
|
// src/formula-modal.ts
|
|
@@ -211,8 +299,7 @@ var formulaXModalStyles = `
|
|
|
211
299
|
.fx-formula-kity-host .kf-editor svg text,
|
|
212
300
|
.fx-formula-kity-host .kf-editor-ui-area-item-text,
|
|
213
301
|
.fx-formula-kity-host .kf-editor-ui-box-item-text,
|
|
214
|
-
.fx-formula-kity-host .kf-editor-ui-box-item-val
|
|
215
|
-
.formulax-math__render {
|
|
302
|
+
.fx-formula-kity-host .kf-editor-ui-box-item-val {
|
|
216
303
|
font-family: "KF AMS MAIN", "Cambria Math", "Latin Modern Math", "Times New Roman", serif !important;
|
|
217
304
|
}
|
|
218
305
|
|
|
@@ -287,59 +374,34 @@ var formulaXModalStyles = `
|
|
|
287
374
|
background: #2563eb;
|
|
288
375
|
color: #fff;
|
|
289
376
|
}
|
|
290
|
-
|
|
291
|
-
.formulax-math {
|
|
292
|
-
display: inline-flex;
|
|
293
|
-
align-items: center;
|
|
294
|
-
vertical-align: middle;
|
|
295
|
-
line-height: 1;
|
|
296
|
-
padding: 0 2px;
|
|
297
|
-
margin: 0 1px;
|
|
298
|
-
border-radius: 3px;
|
|
299
|
-
background: transparent;
|
|
300
|
-
cursor: pointer;
|
|
301
|
-
user-select: none;
|
|
302
|
-
}
|
|
303
|
-
|
|
304
|
-
.formulax-math:hover {
|
|
305
|
-
outline: 1px solid rgba(37, 99, 235, 0.35);
|
|
306
|
-
background: rgba(37, 99, 235, 0.06);
|
|
307
|
-
}
|
|
308
|
-
|
|
309
|
-
.formulax-math__svg {
|
|
310
|
-
display: inline-block;
|
|
311
|
-
flex: 0 0 auto;
|
|
312
|
-
max-width: 100%;
|
|
313
|
-
vertical-align: -0.35em;
|
|
314
|
-
pointer-events: none;
|
|
315
|
-
}
|
|
316
|
-
|
|
317
|
-
.formulax-math__image {
|
|
318
|
-
display: inline-block;
|
|
319
|
-
max-width: 100%;
|
|
320
|
-
height: auto;
|
|
321
|
-
vertical-align: middle;
|
|
322
|
-
pointer-events: none;
|
|
323
|
-
}
|
|
324
377
|
`;
|
|
325
378
|
function ensureFormulaXModalStyles(doc = document) {
|
|
379
|
+
ensureFormulaXBaseStyles(doc);
|
|
326
380
|
if (doc.getElementById(STYLE_ID)) return;
|
|
327
381
|
const style = doc.createElement("style");
|
|
328
382
|
style.id = STYLE_ID;
|
|
329
383
|
style.textContent = formulaXModalStyles;
|
|
330
384
|
doc.head.appendChild(style);
|
|
331
385
|
}
|
|
332
|
-
function
|
|
333
|
-
let destroyed = false;
|
|
334
|
-
let latestLatex = options.initialLatex ?? "";
|
|
335
|
-
let handle = null;
|
|
336
|
-
const initialLatex = latestLatex.trim() ? latestLatex : EMPTY_FORMULA_PLACEHOLDER;
|
|
386
|
+
function renderFormulaXEditorLoadingState(root) {
|
|
337
387
|
root.classList.add("fx-formula-kity-host");
|
|
338
388
|
root.innerHTML = `
|
|
339
389
|
<div class="fx-formula-editor-loading" role="status" aria-live="polite">
|
|
340
390
|
Loading FormulaX editor...
|
|
341
391
|
</div>
|
|
342
392
|
`;
|
|
393
|
+
}
|
|
394
|
+
function mountFormulaXEditor(root, options = {}) {
|
|
395
|
+
recordFormulaXPerfPoint("fx:formula-editor:mount:start");
|
|
396
|
+
const mountStart = markFormulaXPerf("fx:formula-editor:mount:start:scope");
|
|
397
|
+
let destroyed = false;
|
|
398
|
+
let latestLatex = options.initialLatex ?? "";
|
|
399
|
+
let handle = null;
|
|
400
|
+
const initialLatex = latestLatex.trim() ? latestLatex : EMPTY_FORMULA_PLACEHOLDER;
|
|
401
|
+
renderFormulaXEditorLoadingState(root);
|
|
402
|
+
const loadingVisibleMark = markFormulaXPerf("fx:formula-editor:loading-visible");
|
|
403
|
+
measureFormulaXPerf("fx:formula-editor:loading-visible", mountStart, loadingVisibleMark);
|
|
404
|
+
clearFormulaXPerfMarks(loadingVisibleMark);
|
|
343
405
|
const readyPromise = mountKityEditor(root, {
|
|
344
406
|
initialLatex,
|
|
345
407
|
height: options.height ?? "100%",
|
|
@@ -353,6 +415,9 @@ function mountFormulaXEditor(root, options = {}) {
|
|
|
353
415
|
nextHandle.destroy();
|
|
354
416
|
throw new Error("FormulaX editor mount cancelled");
|
|
355
417
|
}
|
|
418
|
+
const readyMark = markFormulaXPerf("fx:kity-editor:ready");
|
|
419
|
+
measureFormulaXPerf("fx:kity-editor:ready", mountStart, readyMark);
|
|
420
|
+
clearFormulaXPerfMarks(readyMark);
|
|
356
421
|
handle = nextHandle;
|
|
357
422
|
return nextHandle;
|
|
358
423
|
}).catch((error) => {
|
|
@@ -366,6 +431,8 @@ function mountFormulaXEditor(root, options = {}) {
|
|
|
366
431
|
`;
|
|
367
432
|
}
|
|
368
433
|
throw error;
|
|
434
|
+
}).finally(() => {
|
|
435
|
+
clearFormulaXPerfMarks(mountStart);
|
|
369
436
|
});
|
|
370
437
|
const getCurrentLatex = async () => {
|
|
371
438
|
const readyHandle = handle ?? await readyPromise;
|
|
@@ -391,8 +458,8 @@ function mountFormulaXEditor(root, options = {}) {
|
|
|
391
458
|
},
|
|
392
459
|
async getRenderHtml() {
|
|
393
460
|
await readyPromise;
|
|
394
|
-
await
|
|
395
|
-
return
|
|
461
|
+
await waitForKityFormulaSvgLayout(root);
|
|
462
|
+
return serializeKityFormulaFromRoot(root);
|
|
396
463
|
},
|
|
397
464
|
destroy() {
|
|
398
465
|
if (destroyed) return;
|
|
@@ -442,329 +509,17 @@ async function tryReadLatexFromKityHandle(handle) {
|
|
|
442
509
|
}
|
|
443
510
|
return null;
|
|
444
511
|
}
|
|
445
|
-
function renderCurrentFormulaAsSvgHtml(root) {
|
|
446
|
-
const svg = findFormulaSvg(root);
|
|
447
|
-
if (!svg) {
|
|
448
|
-
return "";
|
|
449
|
-
}
|
|
450
|
-
return serializeSvgForInsertion(svg);
|
|
451
|
-
}
|
|
452
|
-
async function waitForFormulaSvgLayout(root) {
|
|
453
|
-
const doc = root.ownerDocument ?? document;
|
|
454
|
-
const view = doc.defaultView ?? window;
|
|
455
|
-
await waitForDocumentFonts(doc);
|
|
456
|
-
let previous = readRenderedFormulaBox(root);
|
|
457
|
-
for (let attempt = 0; attempt < 4; attempt += 1) {
|
|
458
|
-
await waitForAnimationFrame(view);
|
|
459
|
-
const current = readRenderedFormulaBox(root);
|
|
460
|
-
if (previous && current && areSvgBoxesClose(previous, current)) {
|
|
461
|
-
return;
|
|
462
|
-
}
|
|
463
|
-
previous = current;
|
|
464
|
-
}
|
|
465
|
-
}
|
|
466
|
-
function findFormulaSvg(root) {
|
|
467
|
-
return root.querySelector(
|
|
468
|
-
".kf-editor-edit-area svg, .kf-editor-canvas-container svg, svg"
|
|
469
|
-
);
|
|
470
|
-
}
|
|
471
|
-
function readRenderedFormulaBox(root) {
|
|
472
|
-
const svg = findFormulaSvg(root);
|
|
473
|
-
if (!svg) {
|
|
474
|
-
return null;
|
|
475
|
-
}
|
|
476
|
-
return getInlineSvgContent(svg)?.box ?? readSvgBox(svg);
|
|
477
|
-
}
|
|
478
|
-
function areSvgBoxesClose(left, right) {
|
|
479
|
-
return Math.abs(left.x - right.x) < 0.01 && Math.abs(left.y - right.y) < 0.01 && Math.abs(left.width - right.width) < 0.01 && Math.abs(left.height - right.height) < 0.01;
|
|
480
|
-
}
|
|
481
|
-
async function waitForDocumentFonts(doc) {
|
|
482
|
-
if (!doc.fonts?.ready) {
|
|
483
|
-
return;
|
|
484
|
-
}
|
|
485
|
-
try {
|
|
486
|
-
await doc.fonts.ready;
|
|
487
|
-
} catch {
|
|
488
|
-
}
|
|
489
|
-
}
|
|
490
|
-
function waitForAnimationFrame(view) {
|
|
491
|
-
return new Promise((resolve) => {
|
|
492
|
-
view.requestAnimationFrame(() => resolve());
|
|
493
|
-
});
|
|
494
|
-
}
|
|
495
|
-
function serializeSvgForInsertion(svg) {
|
|
496
|
-
const content = getInlineSvgContent(svg);
|
|
497
|
-
const inlineViewport = content ? createInlineSvgViewport(content.box) : null;
|
|
498
|
-
const clone = content && inlineViewport ? createInlineSvgClone(svg, content, inlineViewport) : svg.cloneNode(true);
|
|
499
|
-
uniquifySvgIds(clone);
|
|
500
|
-
sizeSvgForInlineDisplay(clone, svg, inlineViewport);
|
|
501
|
-
clone.removeAttribute("id");
|
|
502
|
-
clone.removeAttribute("xmlns");
|
|
503
|
-
clone.removeAttribute("xmlns:xlink");
|
|
504
|
-
clone.setAttribute("class", mergeClassNames(clone.getAttribute("class"), "formulax-math__svg"));
|
|
505
|
-
clone.setAttribute("focusable", "false");
|
|
506
|
-
clone.setAttribute("aria-hidden", "true");
|
|
507
|
-
clone.setAttribute("preserveAspectRatio", clone.getAttribute("preserveAspectRatio") || "xMinYMin meet");
|
|
508
|
-
return new XMLSerializer().serializeToString(clone);
|
|
509
|
-
}
|
|
510
|
-
function getInlineSvgContent(svg) {
|
|
511
|
-
const candidates = [
|
|
512
|
-
'[data-root="true"] > g[data-type="kf-editor-exp-content-box"]',
|
|
513
|
-
'g[data-type="kf-editor-exp-content-box"]',
|
|
514
|
-
'g[data-type="kf-container"]',
|
|
515
|
-
"svg > g, g"
|
|
516
|
-
];
|
|
517
|
-
for (const selector of candidates) {
|
|
518
|
-
const content = svg.querySelector(selector);
|
|
519
|
-
const rootSpace = content ? readSvgBoxInRootSpace(content) : null;
|
|
520
|
-
if (content && rootSpace) {
|
|
521
|
-
return {
|
|
522
|
-
root: content,
|
|
523
|
-
box: rootSpace.box,
|
|
524
|
-
matrix: rootSpace.matrix
|
|
525
|
-
};
|
|
526
|
-
}
|
|
527
|
-
}
|
|
528
|
-
return null;
|
|
529
|
-
}
|
|
530
|
-
function readSvgBoxInRootSpace(element) {
|
|
531
|
-
const box = readSvgBox(element);
|
|
532
|
-
const matrix = getSvgRootSpaceMatrix(element);
|
|
533
|
-
if (!box || !matrix) {
|
|
534
|
-
return null;
|
|
535
|
-
}
|
|
536
|
-
const points = [
|
|
537
|
-
{ x: box.x, y: box.y },
|
|
538
|
-
{ x: box.x + box.width, y: box.y },
|
|
539
|
-
{ x: box.x, y: box.y + box.height },
|
|
540
|
-
{ x: box.x + box.width, y: box.y + box.height }
|
|
541
|
-
].map((point) => ({
|
|
542
|
-
x: matrix.a * point.x + matrix.c * point.y + matrix.e,
|
|
543
|
-
y: matrix.b * point.x + matrix.d * point.y + matrix.f
|
|
544
|
-
}));
|
|
545
|
-
const xs = points.map((point) => point.x);
|
|
546
|
-
const ys = points.map((point) => point.y);
|
|
547
|
-
const x = Math.min(...xs);
|
|
548
|
-
const y = Math.min(...ys);
|
|
549
|
-
const width = Math.max(...xs) - x;
|
|
550
|
-
const height = Math.max(...ys) - y;
|
|
551
|
-
if (!Number.isFinite(width) || !Number.isFinite(height) || width <= 0 || height <= 0) {
|
|
552
|
-
return null;
|
|
553
|
-
}
|
|
554
|
-
return {
|
|
555
|
-
box: { x, y, width, height },
|
|
556
|
-
matrix
|
|
557
|
-
};
|
|
558
|
-
}
|
|
559
|
-
function getSvgRootSpaceMatrix(element) {
|
|
560
|
-
const elementMatrix = typeof element.getCTM === "function" ? element.getCTM() : null;
|
|
561
|
-
const rootMatrix = typeof element.ownerSVGElement?.getCTM === "function" ? element.ownerSVGElement.getCTM() : null;
|
|
562
|
-
if (!elementMatrix) {
|
|
563
|
-
return null;
|
|
564
|
-
}
|
|
565
|
-
return rootMatrix ? multiplySvgMatrices(invertSvgMatrix(rootMatrix), elementMatrix) : toSvgMatrixLike(elementMatrix);
|
|
566
|
-
}
|
|
567
|
-
function invertSvgMatrix(matrix) {
|
|
568
|
-
const determinant = matrix.a * matrix.d - matrix.b * matrix.c;
|
|
569
|
-
if (!Number.isFinite(determinant) || determinant === 0) {
|
|
570
|
-
return {
|
|
571
|
-
a: 1,
|
|
572
|
-
b: 0,
|
|
573
|
-
c: 0,
|
|
574
|
-
d: 1,
|
|
575
|
-
e: 0,
|
|
576
|
-
f: 0
|
|
577
|
-
};
|
|
578
|
-
}
|
|
579
|
-
return {
|
|
580
|
-
a: matrix.d / determinant,
|
|
581
|
-
b: -matrix.b / determinant,
|
|
582
|
-
c: -matrix.c / determinant,
|
|
583
|
-
d: matrix.a / determinant,
|
|
584
|
-
e: (matrix.c * matrix.f - matrix.d * matrix.e) / determinant,
|
|
585
|
-
f: (matrix.b * matrix.e - matrix.a * matrix.f) / determinant
|
|
586
|
-
};
|
|
587
|
-
}
|
|
588
|
-
function multiplySvgMatrices(left, right) {
|
|
589
|
-
return {
|
|
590
|
-
a: left.a * right.a + left.c * right.b,
|
|
591
|
-
b: left.b * right.a + left.d * right.b,
|
|
592
|
-
c: left.a * right.c + left.c * right.d,
|
|
593
|
-
d: left.b * right.c + left.d * right.d,
|
|
594
|
-
e: left.a * right.e + left.c * right.f + left.e,
|
|
595
|
-
f: left.b * right.e + left.d * right.f + left.f
|
|
596
|
-
};
|
|
597
|
-
}
|
|
598
|
-
function toSvgMatrixLike(matrix) {
|
|
599
|
-
return {
|
|
600
|
-
a: matrix.a,
|
|
601
|
-
b: matrix.b,
|
|
602
|
-
c: matrix.c,
|
|
603
|
-
d: matrix.d,
|
|
604
|
-
e: matrix.e,
|
|
605
|
-
f: matrix.f
|
|
606
|
-
};
|
|
607
|
-
}
|
|
608
|
-
function readSvgBox(element) {
|
|
609
|
-
if (typeof element.getBBox !== "function") {
|
|
610
|
-
return null;
|
|
611
|
-
}
|
|
612
|
-
try {
|
|
613
|
-
const box = element.getBBox();
|
|
614
|
-
if (!Number.isFinite(box.width) || !Number.isFinite(box.height) || box.width <= 0 || box.height <= 0) {
|
|
615
|
-
return null;
|
|
616
|
-
}
|
|
617
|
-
return {
|
|
618
|
-
x: box.x,
|
|
619
|
-
y: box.y,
|
|
620
|
-
width: box.width,
|
|
621
|
-
height: box.height
|
|
622
|
-
};
|
|
623
|
-
} catch {
|
|
624
|
-
return null;
|
|
625
|
-
}
|
|
626
|
-
}
|
|
627
|
-
function createInlineSvgViewport(contentBox) {
|
|
628
|
-
const edgePadding = Math.max(0.5, Math.min(contentBox.width, contentBox.height) * 6e-3);
|
|
629
|
-
const inset = edgePadding / 2;
|
|
630
|
-
return {
|
|
631
|
-
x: contentBox.x - inset,
|
|
632
|
-
y: contentBox.y - inset,
|
|
633
|
-
width: contentBox.width + edgePadding,
|
|
634
|
-
height: contentBox.height + edgePadding
|
|
635
|
-
};
|
|
636
|
-
}
|
|
637
|
-
function createInlineSvgClone(source, content, viewport) {
|
|
638
|
-
const clone = source.cloneNode(false);
|
|
639
|
-
const ownerDocument = source.ownerDocument;
|
|
640
|
-
copySvgRootAttributes(source, clone);
|
|
641
|
-
clone.setAttribute(
|
|
642
|
-
"viewBox",
|
|
643
|
-
`0 0 ${roundLength(viewport.width)} ${roundLength(viewport.height)}`
|
|
644
|
-
);
|
|
645
|
-
Array.from(source.children).forEach((child) => {
|
|
646
|
-
if (child.tagName.toLowerCase() === "defs") {
|
|
647
|
-
clone.appendChild(child.cloneNode(true));
|
|
648
|
-
}
|
|
649
|
-
});
|
|
650
|
-
const wrapper = ownerDocument.createElementNS("http://www.w3.org/2000/svg", "g");
|
|
651
|
-
wrapper.setAttribute(
|
|
652
|
-
"transform",
|
|
653
|
-
`translate(${roundLength(-viewport.x)} ${roundLength(-viewport.y)})`
|
|
654
|
-
);
|
|
655
|
-
const flattened = ownerDocument.createElementNS("http://www.w3.org/2000/svg", "g");
|
|
656
|
-
flattened.setAttribute(
|
|
657
|
-
"transform",
|
|
658
|
-
`matrix(${roundLength(content.matrix.a)} ${roundLength(content.matrix.b)} ${roundLength(content.matrix.c)} ${roundLength(content.matrix.d)} ${roundLength(content.matrix.e)} ${roundLength(content.matrix.f)})`
|
|
659
|
-
);
|
|
660
|
-
flattened.appendChild(content.root.cloneNode(true));
|
|
661
|
-
wrapper.appendChild(flattened);
|
|
662
|
-
clone.appendChild(wrapper);
|
|
663
|
-
return clone;
|
|
664
|
-
}
|
|
665
|
-
function copySvgRootAttributes(source, target) {
|
|
666
|
-
const excluded = /* @__PURE__ */ new Set([
|
|
667
|
-
"id",
|
|
668
|
-
"width",
|
|
669
|
-
"height",
|
|
670
|
-
"viewBox",
|
|
671
|
-
"class",
|
|
672
|
-
"focusable",
|
|
673
|
-
"aria-hidden",
|
|
674
|
-
"xmlns",
|
|
675
|
-
"xmlns:xlink"
|
|
676
|
-
]);
|
|
677
|
-
Array.from(source.attributes).forEach((attribute) => {
|
|
678
|
-
if (excluded.has(attribute.name)) return;
|
|
679
|
-
target.setAttribute(attribute.name, attribute.value);
|
|
680
|
-
});
|
|
681
|
-
}
|
|
682
|
-
function sizeSvgForInlineDisplay(clone, source, viewport) {
|
|
683
|
-
const viewBox = clone.viewBox?.baseVal;
|
|
684
|
-
const rect = source.getBoundingClientRect();
|
|
685
|
-
const width = viewport?.width || viewBox?.width || rect.width || Number(clone.getAttribute("width")) || 1;
|
|
686
|
-
const height = viewport?.height || viewBox?.height || rect.height || Number(clone.getAttribute("height")) || 1;
|
|
687
|
-
const ratio = Math.max(0.1, width / Math.max(1, height));
|
|
688
|
-
const inlineHeightEm = 0.875;
|
|
689
|
-
const inlineWidthEm = Math.min(40, Math.max(0.75, ratio * inlineHeightEm));
|
|
690
|
-
clone.setAttribute("width", roundLength(width));
|
|
691
|
-
clone.setAttribute("height", roundLength(height));
|
|
692
|
-
clone.setAttribute(
|
|
693
|
-
"style",
|
|
694
|
-
mergeInlineStyles2(
|
|
695
|
-
clone.getAttribute("style"),
|
|
696
|
-
`width:${roundLength(inlineWidthEm)}em`,
|
|
697
|
-
`height:${inlineHeightEm}em`
|
|
698
|
-
)
|
|
699
|
-
);
|
|
700
|
-
}
|
|
701
|
-
function roundLength(value) {
|
|
702
|
-
return String(Math.round(value * 1e3) / 1e3);
|
|
703
|
-
}
|
|
704
|
-
function uniquifySvgIds(svg) {
|
|
705
|
-
const idMap = /* @__PURE__ */ new Map();
|
|
706
|
-
const prefix = `fx-${randomIdPrefix()}-`;
|
|
707
|
-
const elementsWithId = svg.querySelectorAll("[id]");
|
|
708
|
-
elementsWithId.forEach((element) => {
|
|
709
|
-
const id = element.getAttribute("id");
|
|
710
|
-
if (!id) return;
|
|
711
|
-
const nextId = `${prefix}${id}`;
|
|
712
|
-
idMap.set(id, nextId);
|
|
713
|
-
element.setAttribute("id", nextId);
|
|
714
|
-
});
|
|
715
|
-
if (!idMap.size) return;
|
|
716
|
-
svg.querySelectorAll("*").forEach((element) => {
|
|
717
|
-
Array.from(element.attributes).forEach((attribute) => {
|
|
718
|
-
const nextValue = rewriteSvgReferences(attribute.value, idMap);
|
|
719
|
-
if (nextValue !== attribute.value) {
|
|
720
|
-
element.setAttribute(attribute.name, nextValue);
|
|
721
|
-
}
|
|
722
|
-
});
|
|
723
|
-
});
|
|
724
|
-
}
|
|
725
|
-
function randomIdPrefix() {
|
|
726
|
-
return Math.random().toString(36).slice(2, 5).padEnd(3, "0");
|
|
727
|
-
}
|
|
728
|
-
function rewriteSvgReferences(value, idMap) {
|
|
729
|
-
let nextValue = value;
|
|
730
|
-
idMap.forEach((nextId, id) => {
|
|
731
|
-
nextValue = nextValue.replaceAll(`#${id}`, `#${nextId}`).replaceAll(`url(${id})`, `url(${nextId})`).replaceAll(`url(#${id})`, `url(#${nextId})`);
|
|
732
|
-
});
|
|
733
|
-
return nextValue;
|
|
734
|
-
}
|
|
735
|
-
function mergeClassNames(...values) {
|
|
736
|
-
return values.flatMap((value) => value?.split(/\s+/) ?? []).filter(Boolean).filter((value, index, list) => list.indexOf(value) === index).join(" ");
|
|
737
|
-
}
|
|
738
|
-
function mergeInlineStyles2(...values) {
|
|
739
|
-
return values.flatMap((value) => value?.split(";") ?? []).map((value) => value.trim()).filter(Boolean).join("; ");
|
|
740
|
-
}
|
|
741
|
-
|
|
742
|
-
// src/index.ts
|
|
743
|
-
import {
|
|
744
|
-
FormulaXEditor,
|
|
745
|
-
createKityEditor,
|
|
746
|
-
ensureKityRuntime,
|
|
747
|
-
mountKityEditor as mountKityEditor2
|
|
748
|
-
} from "@formulaxjs/kity-runtime";
|
|
749
512
|
export {
|
|
750
|
-
|
|
751
|
-
DEFAULT_FORMULA_CLASS,
|
|
752
|
-
FORMULA_FLAG_ATTRIBUTE,
|
|
753
|
-
FormulaXEditor,
|
|
754
|
-
createFormulaElement,
|
|
755
|
-
createFormulaMarkup,
|
|
756
|
-
createKityEditor,
|
|
513
|
+
clearFormulaXPerfMarks,
|
|
757
514
|
ensureFormulaXModalStyles,
|
|
758
|
-
ensureKityRuntime,
|
|
759
|
-
escapeAttribute,
|
|
760
|
-
escapeHtml,
|
|
761
|
-
findFormulaElement,
|
|
762
515
|
formulaXModalStyles,
|
|
763
|
-
|
|
764
|
-
|
|
516
|
+
markFormulaXPerf,
|
|
517
|
+
measureFormulaXPerf,
|
|
765
518
|
mountFormulaXEditor,
|
|
766
|
-
|
|
767
|
-
|
|
768
|
-
|
|
519
|
+
preloadFormulaXEditor,
|
|
520
|
+
recordFormulaXPerfPoint,
|
|
521
|
+
renderFormulaXEditorLoadingState,
|
|
522
|
+
scheduleFormulaXEditorPreload,
|
|
523
|
+
waitForFormulaXAnimationFrame
|
|
769
524
|
};
|
|
770
525
|
//# sourceMappingURL=index.js.map
|