rtl-text-transform 0.0.0-stage → 0.1.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/LICENSE +21 -0
- package/README.md +529 -2
- package/dist/chunk-QDWUKRPS.js +1853 -0
- package/dist/chunk-QDWUKRPS.js.map +1 -0
- package/dist/index.cjs +1867 -0
- package/dist/index.cjs.map +1 -0
- package/dist/index.d.cts +261 -0
- package/dist/index.d.ts +261 -0
- package/dist/index.js +3 -0
- package/dist/index.js.map +1 -0
- package/dist/react.cjs +1951 -0
- package/dist/react.cjs.map +1 -0
- package/dist/react.d.cts +37 -0
- package/dist/react.d.ts +37 -0
- package/dist/react.js +84 -0
- package/dist/react.js.map +1 -0
- package/package.json +104 -4
package/dist/index.cjs
ADDED
|
@@ -0,0 +1,1867 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
3
|
+
// src/core/easing.ts
|
|
4
|
+
function linear(t) {
|
|
5
|
+
return t;
|
|
6
|
+
}
|
|
7
|
+
function clamp01(value) {
|
|
8
|
+
if (value <= 0) return 0;
|
|
9
|
+
if (value >= 1) return 1;
|
|
10
|
+
return value;
|
|
11
|
+
}
|
|
12
|
+
function cubicBezier(x1, y1, x2, y2) {
|
|
13
|
+
const cx = 3 * clamp01(x1);
|
|
14
|
+
const bx = 3 * (clamp01(x2) - clamp01(x1)) - cx;
|
|
15
|
+
const ax = 1 - cx - bx;
|
|
16
|
+
const cy = 3 * y1;
|
|
17
|
+
const by = 3 * (y2 - y1) - cy;
|
|
18
|
+
const ay = 1 - cy - by;
|
|
19
|
+
const sampleX = (t) => ((ax * t + bx) * t + cx) * t;
|
|
20
|
+
const sampleY = (t) => ((ay * t + by) * t + cy) * t;
|
|
21
|
+
const sampleDerivativeX = (t) => (3 * ax * t + 2 * bx) * t + cx;
|
|
22
|
+
return (input) => {
|
|
23
|
+
if (input <= 0) return 0;
|
|
24
|
+
if (input >= 1) return 1;
|
|
25
|
+
let t = input;
|
|
26
|
+
for (let step = 0; step < 8; step += 1) {
|
|
27
|
+
const error = sampleX(t) - input;
|
|
28
|
+
if (Math.abs(error) < 1e-6) return sampleY(t);
|
|
29
|
+
const derivative = sampleDerivativeX(t);
|
|
30
|
+
if (Math.abs(derivative) < 1e-6) break;
|
|
31
|
+
t -= error / derivative;
|
|
32
|
+
}
|
|
33
|
+
let lower = 0;
|
|
34
|
+
let upper = 1;
|
|
35
|
+
t = clamp01(t);
|
|
36
|
+
for (let step = 0; step < 20; step += 1) {
|
|
37
|
+
const value = sampleX(t);
|
|
38
|
+
if (Math.abs(value - input) < 1e-6) break;
|
|
39
|
+
if (value < input) lower = t;
|
|
40
|
+
else upper = t;
|
|
41
|
+
t = (lower + upper) / 2;
|
|
42
|
+
}
|
|
43
|
+
return sampleY(t);
|
|
44
|
+
};
|
|
45
|
+
}
|
|
46
|
+
var easeInOutCubic = cubicBezier(0.65, 0, 0.35, 1);
|
|
47
|
+
var easeOutCubic = cubicBezier(0.33, 1, 0.68, 1);
|
|
48
|
+
var DEFAULT_MORPH_EASING = easeInOutCubic;
|
|
49
|
+
var DEFAULT_RISE_EASING = easeOutCubic;
|
|
50
|
+
|
|
51
|
+
// src/core/math.ts
|
|
52
|
+
var MAX_BLUR_PX = 100;
|
|
53
|
+
var OPACITY_EXPONENT = 0.4;
|
|
54
|
+
function clamp012(value) {
|
|
55
|
+
if (Number.isNaN(value)) return 0;
|
|
56
|
+
if (value <= 0) return 0;
|
|
57
|
+
if (value >= 1) return 1;
|
|
58
|
+
return value;
|
|
59
|
+
}
|
|
60
|
+
function blurForFraction(fraction, fontSizePx, blurEm) {
|
|
61
|
+
const f = clamp012(fraction);
|
|
62
|
+
if (f >= 1) return 0;
|
|
63
|
+
if (f <= 0) return MAX_BLUR_PX;
|
|
64
|
+
const fontSize = Number.isFinite(fontSizePx) && fontSizePx > 0 ? fontSizePx : 16;
|
|
65
|
+
const blurEmSafe = Number.isFinite(blurEm) && blurEm > 0 ? blurEm : 0;
|
|
66
|
+
const blur = blurEmSafe * fontSize * (1 / f - 1);
|
|
67
|
+
return Math.min(MAX_BLUR_PX, Math.max(0, blur));
|
|
68
|
+
}
|
|
69
|
+
function opacityForFraction(fraction) {
|
|
70
|
+
return Math.pow(clamp012(fraction), OPACITY_EXPONENT);
|
|
71
|
+
}
|
|
72
|
+
function visualForFraction(fraction, fontSizePx, blurEm) {
|
|
73
|
+
return {
|
|
74
|
+
blurPx: blurForFraction(fraction, fontSizePx, blurEm),
|
|
75
|
+
opacity: opacityForFraction(fraction)
|
|
76
|
+
};
|
|
77
|
+
}
|
|
78
|
+
function thresholdMatrix(threshold) {
|
|
79
|
+
const t = Math.max(0, Math.min(255, threshold));
|
|
80
|
+
return `1 0 0 0 0 0 1 0 0 0 0 0 1 0 0 0 0 0 255 -${t}`;
|
|
81
|
+
}
|
|
82
|
+
function wrapIndex(index, length, loop) {
|
|
83
|
+
if (length <= 0) return 0;
|
|
84
|
+
if (loop) return (index % length + length) % length;
|
|
85
|
+
return Math.max(0, Math.min(length - 1, index));
|
|
86
|
+
}
|
|
87
|
+
function canAdvance(index, length, loop) {
|
|
88
|
+
if (length <= 1) return false;
|
|
89
|
+
if (loop) return true;
|
|
90
|
+
return index < length - 1;
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
// src/core/effects/rise.ts
|
|
94
|
+
function withoutNegativeZero(value) {
|
|
95
|
+
return value === 0 ? 0 : value;
|
|
96
|
+
}
|
|
97
|
+
var MAX_STAGGER_SHARE = 0.35;
|
|
98
|
+
var EXIT_DURATION = 0.7;
|
|
99
|
+
function riseTiming(durationSeconds, hunkCount, staggerMs, overlap) {
|
|
100
|
+
const duration = Math.max(0, durationSeconds);
|
|
101
|
+
const count = Math.max(1, Math.floor(hunkCount));
|
|
102
|
+
const configuredStagger = Math.max(0, staggerMs) / 1e3;
|
|
103
|
+
const stagger = count > 1 ? Math.min(configuredStagger, duration * MAX_STAGGER_SHARE / (count - 1)) : 0;
|
|
104
|
+
const maxDelay = (count - 1) * stagger;
|
|
105
|
+
const window = Math.max(0, duration - maxDelay) / (1 + overlap);
|
|
106
|
+
return { window, stagger, overlap };
|
|
107
|
+
}
|
|
108
|
+
function enterProgress(elapsedSeconds, order, timing) {
|
|
109
|
+
if (timing.window <= 0) return 1;
|
|
110
|
+
const start = order * timing.stagger + timing.window * timing.overlap;
|
|
111
|
+
return clamp012((elapsedSeconds - start) / timing.window);
|
|
112
|
+
}
|
|
113
|
+
function exitProgress(elapsedSeconds, order, timing) {
|
|
114
|
+
const duration = timing.window * EXIT_DURATION;
|
|
115
|
+
if (duration <= 0) return 1;
|
|
116
|
+
const start = order * timing.stagger;
|
|
117
|
+
return clamp012((elapsedSeconds - start) / duration);
|
|
118
|
+
}
|
|
119
|
+
function enterTransform(progress, travelEm, scale, blurEm, flow) {
|
|
120
|
+
const eased = clamp012(progress);
|
|
121
|
+
const distance = travelEm * (1 - eased);
|
|
122
|
+
let xEm = 0;
|
|
123
|
+
let yEm = 0;
|
|
124
|
+
if (flow === "up") yEm = distance;
|
|
125
|
+
else if (flow === "down") yEm = -distance;
|
|
126
|
+
else if (flow === "left") xEm = distance;
|
|
127
|
+
else xEm = -distance;
|
|
128
|
+
return {
|
|
129
|
+
xEm: withoutNegativeZero(xEm),
|
|
130
|
+
yEm: withoutNegativeZero(yEm),
|
|
131
|
+
scale: 1 - (1 - scale) * (1 - eased),
|
|
132
|
+
blurEm: blurEm * (1 - eased)
|
|
133
|
+
};
|
|
134
|
+
}
|
|
135
|
+
function exitTransform(progress, travelEm, scale, blurEm, flow) {
|
|
136
|
+
const eased = clamp012(progress);
|
|
137
|
+
const distance = travelEm * 0.6 * eased;
|
|
138
|
+
let xEm = 0;
|
|
139
|
+
let yEm = 0;
|
|
140
|
+
if (flow === "up") yEm = -distance;
|
|
141
|
+
else if (flow === "down") yEm = distance;
|
|
142
|
+
else if (flow === "left") xEm = -distance;
|
|
143
|
+
else xEm = distance;
|
|
144
|
+
return {
|
|
145
|
+
xEm: withoutNegativeZero(xEm),
|
|
146
|
+
yEm: withoutNegativeZero(yEm),
|
|
147
|
+
scale: 1 - (1 - scale) * eased,
|
|
148
|
+
blurEm: blurEm * eased
|
|
149
|
+
};
|
|
150
|
+
}
|
|
151
|
+
|
|
152
|
+
// src/core/filter.ts
|
|
153
|
+
var SVG_NS = "http://www.w3.org/2000/svg";
|
|
154
|
+
var registries = /* @__PURE__ */ new WeakMap();
|
|
155
|
+
var idCounter = 0;
|
|
156
|
+
function normalizeThreshold(threshold) {
|
|
157
|
+
if (!Number.isFinite(threshold)) return 140;
|
|
158
|
+
return Math.max(0, Math.min(255, Math.round(threshold)));
|
|
159
|
+
}
|
|
160
|
+
function registryFor(doc) {
|
|
161
|
+
let registry = registries.get(doc);
|
|
162
|
+
if (!registry) {
|
|
163
|
+
registry = /* @__PURE__ */ new Map();
|
|
164
|
+
registries.set(doc, registry);
|
|
165
|
+
}
|
|
166
|
+
return registry;
|
|
167
|
+
}
|
|
168
|
+
function setStyles(el, styles) {
|
|
169
|
+
for (const [name, value] of Object.entries(styles)) {
|
|
170
|
+
el.style.setProperty(name, value);
|
|
171
|
+
}
|
|
172
|
+
}
|
|
173
|
+
function createFilterSvg(doc, threshold) {
|
|
174
|
+
const id = `goo-morph-${++idCounter}`;
|
|
175
|
+
const svg = doc.createElementNS(SVG_NS, "svg");
|
|
176
|
+
svg.setAttribute("aria-hidden", "true");
|
|
177
|
+
svg.setAttribute("focusable", "false");
|
|
178
|
+
svg.setAttribute("width", "0");
|
|
179
|
+
svg.setAttribute("height", "0");
|
|
180
|
+
setStyles(svg, {
|
|
181
|
+
position: "absolute",
|
|
182
|
+
width: "0",
|
|
183
|
+
height: "0",
|
|
184
|
+
overflow: "hidden",
|
|
185
|
+
"pointer-events": "none"
|
|
186
|
+
});
|
|
187
|
+
const defs = doc.createElementNS(SVG_NS, "defs");
|
|
188
|
+
const filter = doc.createElementNS(SVG_NS, "filter");
|
|
189
|
+
filter.setAttribute("id", id);
|
|
190
|
+
filter.setAttribute("x", "-25%");
|
|
191
|
+
filter.setAttribute("y", "-35%");
|
|
192
|
+
filter.setAttribute("width", "150%");
|
|
193
|
+
filter.setAttribute("height", "170%");
|
|
194
|
+
filter.setAttribute("color-interpolation-filters", "sRGB");
|
|
195
|
+
const matrix = doc.createElementNS(SVG_NS, "feColorMatrix");
|
|
196
|
+
matrix.setAttribute("in", "SourceGraphic");
|
|
197
|
+
matrix.setAttribute("type", "matrix");
|
|
198
|
+
matrix.setAttribute("values", thresholdMatrix(threshold));
|
|
199
|
+
filter.appendChild(matrix);
|
|
200
|
+
defs.appendChild(filter);
|
|
201
|
+
svg.appendChild(defs);
|
|
202
|
+
(doc.body ?? doc.documentElement).appendChild(svg);
|
|
203
|
+
return { id, threshold, svg, refs: 0 };
|
|
204
|
+
}
|
|
205
|
+
function acquireGooFilter(threshold, doc = typeof document === "undefined" ? void 0 : document) {
|
|
206
|
+
if (!doc) return null;
|
|
207
|
+
const normalized = normalizeThreshold(threshold);
|
|
208
|
+
const registry = registryFor(doc);
|
|
209
|
+
let entry = registry.get(normalized);
|
|
210
|
+
if (!entry || !entry.svg.isConnected) {
|
|
211
|
+
if (entry) registry.delete(normalized);
|
|
212
|
+
entry = createFilterSvg(doc, normalized);
|
|
213
|
+
registry.set(normalized, entry);
|
|
214
|
+
}
|
|
215
|
+
entry.refs += 1;
|
|
216
|
+
let released = false;
|
|
217
|
+
return {
|
|
218
|
+
id: entry.id,
|
|
219
|
+
threshold: normalized,
|
|
220
|
+
release() {
|
|
221
|
+
if (released) return;
|
|
222
|
+
released = true;
|
|
223
|
+
const current = registry.get(normalized);
|
|
224
|
+
if (!current || current !== entry) return;
|
|
225
|
+
current.refs = Math.max(0, current.refs - 1);
|
|
226
|
+
if (current.refs === 0) {
|
|
227
|
+
current.svg.remove();
|
|
228
|
+
registry.delete(normalized);
|
|
229
|
+
}
|
|
230
|
+
}
|
|
231
|
+
};
|
|
232
|
+
}
|
|
233
|
+
|
|
234
|
+
// src/core/layers.ts
|
|
235
|
+
var HIDDEN_STYLE = {
|
|
236
|
+
position: "absolute",
|
|
237
|
+
width: "1px",
|
|
238
|
+
height: "1px",
|
|
239
|
+
padding: "0",
|
|
240
|
+
margin: "-1px",
|
|
241
|
+
overflow: "hidden",
|
|
242
|
+
clip: "rect(0 0 0 0)",
|
|
243
|
+
"clip-path": "inset(50%)",
|
|
244
|
+
"white-space": "nowrap",
|
|
245
|
+
border: "0"
|
|
246
|
+
};
|
|
247
|
+
function styleShell(el) {
|
|
248
|
+
const style = el.style;
|
|
249
|
+
style.position = "absolute";
|
|
250
|
+
style.inset = "0";
|
|
251
|
+
style.display = "flex";
|
|
252
|
+
style.alignItems = "center";
|
|
253
|
+
style.justifyContent = "center";
|
|
254
|
+
style.width = "100%";
|
|
255
|
+
style.height = "100%";
|
|
256
|
+
style.margin = "0";
|
|
257
|
+
style.padding = "0";
|
|
258
|
+
style.pointerEvents = "none";
|
|
259
|
+
style.fontFamily = "inherit";
|
|
260
|
+
style.fontSize = "inherit";
|
|
261
|
+
style.fontWeight = "inherit";
|
|
262
|
+
style.fontStyle = "inherit";
|
|
263
|
+
style.fontStretch = "inherit";
|
|
264
|
+
style.fontVariant = "inherit";
|
|
265
|
+
style.lineHeight = "inherit";
|
|
266
|
+
style.color = "inherit";
|
|
267
|
+
style.textAlign = "center";
|
|
268
|
+
style.overflow = "visible";
|
|
269
|
+
}
|
|
270
|
+
function styleLine(line) {
|
|
271
|
+
const style = line.style;
|
|
272
|
+
style.display = "block";
|
|
273
|
+
style.margin = "0";
|
|
274
|
+
style.padding = "0";
|
|
275
|
+
style.whiteSpace = "nowrap";
|
|
276
|
+
style.direction = "inherit";
|
|
277
|
+
style.textAlign = "center";
|
|
278
|
+
style.userSelect = "none";
|
|
279
|
+
style.webkitUserSelect = "none";
|
|
280
|
+
style.pointerEvents = "none";
|
|
281
|
+
style.transformOrigin = "center center";
|
|
282
|
+
style.opacity = "1";
|
|
283
|
+
style.filter = "none";
|
|
284
|
+
style.setProperty("letter-spacing", "0", "important");
|
|
285
|
+
}
|
|
286
|
+
function createLayer(doc, role) {
|
|
287
|
+
const el = doc.createElement("div");
|
|
288
|
+
const line = doc.createElement("span");
|
|
289
|
+
el.setAttribute("aria-hidden", "true");
|
|
290
|
+
el.dataset.morphLayer = role;
|
|
291
|
+
styleShell(el);
|
|
292
|
+
line.dataset.morphLine = role;
|
|
293
|
+
styleLine(line);
|
|
294
|
+
el.append(line);
|
|
295
|
+
return { el, line };
|
|
296
|
+
}
|
|
297
|
+
function createStage(host) {
|
|
298
|
+
const doc = host.ownerDocument;
|
|
299
|
+
const outgoing = createLayer(doc, "outgoing");
|
|
300
|
+
const incoming = createLayer(doc, "incoming");
|
|
301
|
+
const live = doc.createElement("span");
|
|
302
|
+
live.setAttribute("aria-live", "polite");
|
|
303
|
+
live.setAttribute("aria-atomic", "true");
|
|
304
|
+
live.dataset.morphLive = "";
|
|
305
|
+
for (const [name, value] of Object.entries(HIDDEN_STYLE)) {
|
|
306
|
+
live.style.setProperty(name, value);
|
|
307
|
+
}
|
|
308
|
+
host.replaceChildren(outgoing.el, incoming.el, live);
|
|
309
|
+
return { outgoing, incoming, live };
|
|
310
|
+
}
|
|
311
|
+
function defaultView(host) {
|
|
312
|
+
return host.ownerDocument.defaultView;
|
|
313
|
+
}
|
|
314
|
+
function sizeHost(host) {
|
|
315
|
+
const view = defaultView(host);
|
|
316
|
+
const computed = view?.getComputedStyle(host);
|
|
317
|
+
const computedPosition = computed?.position ?? "";
|
|
318
|
+
if (!host.style.position && (computedPosition === "" || computedPosition === "static")) {
|
|
319
|
+
host.style.position = "relative";
|
|
320
|
+
}
|
|
321
|
+
if (computed?.display === "inline") {
|
|
322
|
+
host.style.display = "block";
|
|
323
|
+
if (!host.style.width) host.style.width = "100%";
|
|
324
|
+
}
|
|
325
|
+
const computedMinHeight = computed?.minHeight ?? "";
|
|
326
|
+
if (!host.style.minHeight && (!computedMinHeight || computedMinHeight === "0px" || computedMinHeight === "auto")) {
|
|
327
|
+
host.style.minHeight = "1.6em";
|
|
328
|
+
}
|
|
329
|
+
}
|
|
330
|
+
function resetLayer(layer) {
|
|
331
|
+
layer.line.style.opacity = "1";
|
|
332
|
+
layer.line.style.filter = "none";
|
|
333
|
+
layer.line.style.transform = "none";
|
|
334
|
+
}
|
|
335
|
+
function clearLayer(layer) {
|
|
336
|
+
layer.line.replaceChildren();
|
|
337
|
+
resetLayer(layer);
|
|
338
|
+
}
|
|
339
|
+
function setLayerText(layer, text) {
|
|
340
|
+
if (layer.line.childNodes.length === 1 && layer.line.firstChild?.nodeType === 3 && layer.line.textContent === text) {
|
|
341
|
+
return;
|
|
342
|
+
}
|
|
343
|
+
layer.line.replaceChildren(text);
|
|
344
|
+
}
|
|
345
|
+
function applyGooState(layer, lightFraction, fontSizePx, blurEm) {
|
|
346
|
+
const visual = visualForFraction(lightFraction, fontSizePx, blurEm);
|
|
347
|
+
layer.line.style.opacity = String(visual.opacity);
|
|
348
|
+
layer.line.style.filter = visual.blurPx > 0 ? `blur(${visual.blurPx}px)` : "none";
|
|
349
|
+
}
|
|
350
|
+
function applyFadeState(layer, opacity) {
|
|
351
|
+
layer.line.style.opacity = String(Math.max(0, Math.min(1, opacity)));
|
|
352
|
+
layer.line.style.filter = "none";
|
|
353
|
+
}
|
|
354
|
+
function setLiveText(el, text) {
|
|
355
|
+
if (el.textContent !== text) el.textContent = text;
|
|
356
|
+
}
|
|
357
|
+
function removeStage(stage) {
|
|
358
|
+
stage.outgoing.el.remove();
|
|
359
|
+
stage.incoming.el.remove();
|
|
360
|
+
stage.live.remove();
|
|
361
|
+
}
|
|
362
|
+
|
|
363
|
+
// src/core/options.ts
|
|
364
|
+
var DEFAULT_GOO = {
|
|
365
|
+
enabled: true,
|
|
366
|
+
blur: 0.075,
|
|
367
|
+
threshold: 140,
|
|
368
|
+
atRest: true
|
|
369
|
+
};
|
|
370
|
+
var DEFAULT_RISE = {
|
|
371
|
+
enabled: false,
|
|
372
|
+
blur: 0.1,
|
|
373
|
+
travel: 0.3,
|
|
374
|
+
scale: 0.94,
|
|
375
|
+
stagger: 35,
|
|
376
|
+
overlap: 0.15,
|
|
377
|
+
easing: DEFAULT_RISE_EASING,
|
|
378
|
+
flow: "up",
|
|
379
|
+
minChunk: 2,
|
|
380
|
+
normalizeVariants: true,
|
|
381
|
+
ignoreDiacritics: true
|
|
382
|
+
};
|
|
383
|
+
function nonNegative(value, fallback) {
|
|
384
|
+
return typeof value === "number" && Number.isFinite(value) ? Math.max(0, value) : fallback;
|
|
385
|
+
}
|
|
386
|
+
function clamp(value, min, max, fallback) {
|
|
387
|
+
if (typeof value !== "number" || !Number.isFinite(value)) return fallback;
|
|
388
|
+
return Math.max(min, Math.min(max, value));
|
|
389
|
+
}
|
|
390
|
+
function normalizeThreshold2(value) {
|
|
391
|
+
if (typeof value !== "number" || !Number.isFinite(value)) return 140;
|
|
392
|
+
return Math.max(0, Math.min(255, Math.round(value)));
|
|
393
|
+
}
|
|
394
|
+
function gooFromOptions(options, base) {
|
|
395
|
+
return {
|
|
396
|
+
enabled: true,
|
|
397
|
+
blur: nonNegative(options.blur, base.blur),
|
|
398
|
+
threshold: options.threshold === void 0 ? base.threshold : normalizeThreshold2(options.threshold),
|
|
399
|
+
atRest: typeof options.atRest === "boolean" ? options.atRest : base.atRest
|
|
400
|
+
};
|
|
401
|
+
}
|
|
402
|
+
function riseFromOptions(options, base) {
|
|
403
|
+
return {
|
|
404
|
+
enabled: true,
|
|
405
|
+
blur: nonNegative(options.blur, base.blur),
|
|
406
|
+
travel: nonNegative(options.travel, base.travel),
|
|
407
|
+
scale: clamp(options.scale, 0, 1, base.scale),
|
|
408
|
+
stagger: nonNegative(options.stagger, base.stagger),
|
|
409
|
+
overlap: clamp(options.overlap, 0, 1, base.overlap),
|
|
410
|
+
easing: typeof options.easing === "function" ? options.easing : base.easing,
|
|
411
|
+
flow: options.flow === "up" || options.flow === "down" || options.flow === "left" || options.flow === "right" || typeof options.flow === "function" ? options.flow : base.flow,
|
|
412
|
+
minChunk: typeof options.minChunk === "number" && Number.isFinite(options.minChunk) ? Math.max(0, Math.floor(options.minChunk)) : base.minChunk,
|
|
413
|
+
normalizeVariants: typeof options.normalizeVariants === "boolean" ? options.normalizeVariants : base.normalizeVariants,
|
|
414
|
+
ignoreDiacritics: typeof options.ignoreDiacritics === "boolean" ? options.ignoreDiacritics : base.ignoreDiacritics
|
|
415
|
+
};
|
|
416
|
+
}
|
|
417
|
+
function resolveInitialEffects(effects) {
|
|
418
|
+
const gooValue = effects?.goo;
|
|
419
|
+
const riseValue = effects?.rise;
|
|
420
|
+
const goo = gooValue === false ? { ...DEFAULT_GOO, enabled: false } : gooValue === void 0 || gooValue === true ? { ...DEFAULT_GOO } : gooFromOptions(gooValue, DEFAULT_GOO);
|
|
421
|
+
const rise = riseValue === void 0 || riseValue === false ? { ...DEFAULT_RISE, enabled: false } : riseValue === true ? { ...DEFAULT_RISE, enabled: true } : riseFromOptions(riseValue, DEFAULT_RISE);
|
|
422
|
+
return { goo, rise };
|
|
423
|
+
}
|
|
424
|
+
function applyGooPatch(current, patch) {
|
|
425
|
+
if (patch === void 0) return current;
|
|
426
|
+
if (patch === false) return { ...current, enabled: false };
|
|
427
|
+
if (patch === true) return { ...current, enabled: true };
|
|
428
|
+
return gooFromOptions(patch, current);
|
|
429
|
+
}
|
|
430
|
+
function applyRisePatch(current, patch) {
|
|
431
|
+
if (patch === void 0) return current;
|
|
432
|
+
if (patch === false) return { ...current, enabled: false };
|
|
433
|
+
if (patch === true) return { ...current, enabled: true };
|
|
434
|
+
return riseFromOptions(patch, current);
|
|
435
|
+
}
|
|
436
|
+
|
|
437
|
+
// src/core/presets.ts
|
|
438
|
+
function firstNumber(value) {
|
|
439
|
+
const normalised = value.replace(/[۰-۹]/g, (digit) => String((digit.codePointAt(0) ?? 1776) - 1776)).replace(/[٠-٩]/g, (digit) => String((digit.codePointAt(0) ?? 1632) - 1632)).replace(/٬/g, "").replace(/(\d),(?=\d)/g, "$1").replace(/٫/g, ".");
|
|
440
|
+
const match = normalised.match(/-?\d+(?:\.\d+)?/);
|
|
441
|
+
if (!match) return void 0;
|
|
442
|
+
const number = Number(match[0]);
|
|
443
|
+
return Number.isFinite(number) ? number : void 0;
|
|
444
|
+
}
|
|
445
|
+
function flowByNumberChange(context) {
|
|
446
|
+
const from = firstNumber(context.from);
|
|
447
|
+
const to = firstNumber(context.to);
|
|
448
|
+
if (from === void 0 || to === void 0 || to === from) return "up";
|
|
449
|
+
return to > from ? "up" : "down";
|
|
450
|
+
}
|
|
451
|
+
function flowByHunkPosition(context) {
|
|
452
|
+
const atStart = context.hunk.position < 0.5;
|
|
453
|
+
if (context.rtl) return atStart ? "right" : "left";
|
|
454
|
+
return atStart ? "left" : "right";
|
|
455
|
+
}
|
|
456
|
+
var wordGoo = { blur: 0.1, threshold: 70, atRest: false };
|
|
457
|
+
var wordRise = { blur: 0.04, travel: 0.24, scale: 0.96 };
|
|
458
|
+
var presets = {
|
|
459
|
+
numbers: {
|
|
460
|
+
effects: {
|
|
461
|
+
goo: { ...wordGoo },
|
|
462
|
+
rise: {
|
|
463
|
+
...wordRise,
|
|
464
|
+
blur: 0.1,
|
|
465
|
+
travel: 0.6,
|
|
466
|
+
scale: 0.9,
|
|
467
|
+
flow: flowByNumberChange
|
|
468
|
+
}
|
|
469
|
+
}
|
|
470
|
+
},
|
|
471
|
+
wordsHorizontal: {
|
|
472
|
+
effects: {
|
|
473
|
+
goo: { ...wordGoo },
|
|
474
|
+
rise: { ...wordRise, flow: flowByHunkPosition }
|
|
475
|
+
}
|
|
476
|
+
},
|
|
477
|
+
wordsUp: {
|
|
478
|
+
effects: {
|
|
479
|
+
goo: { ...wordGoo },
|
|
480
|
+
rise: { ...wordRise, flow: "up" }
|
|
481
|
+
}
|
|
482
|
+
},
|
|
483
|
+
wordsDown: {
|
|
484
|
+
effects: {
|
|
485
|
+
goo: { ...wordGoo },
|
|
486
|
+
rise: { ...wordRise, flow: "down" }
|
|
487
|
+
}
|
|
488
|
+
}
|
|
489
|
+
};
|
|
490
|
+
var motions = {
|
|
491
|
+
default: {
|
|
492
|
+
morphTime: 0.3,
|
|
493
|
+
easing: easeInOutCubic,
|
|
494
|
+
effects: {
|
|
495
|
+
rise: { easing: easeOutCubic, stagger: 70, overlap: 0 }
|
|
496
|
+
}
|
|
497
|
+
},
|
|
498
|
+
smooth: {
|
|
499
|
+
morphTime: 0.8,
|
|
500
|
+
easing: easeInOutCubic,
|
|
501
|
+
effects: {
|
|
502
|
+
rise: {
|
|
503
|
+
easing: cubicBezier(0.25, 0.8, 0.35, 1),
|
|
504
|
+
stagger: 30,
|
|
505
|
+
overlap: 0.18
|
|
506
|
+
}
|
|
507
|
+
}
|
|
508
|
+
},
|
|
509
|
+
snappy: {
|
|
510
|
+
morphTime: 0.2,
|
|
511
|
+
easing: cubicBezier(0.2, 0.8, 0.2, 1),
|
|
512
|
+
effects: {
|
|
513
|
+
rise: {
|
|
514
|
+
easing: cubicBezier(0.16, 1, 0.3, 1),
|
|
515
|
+
stagger: 50,
|
|
516
|
+
overlap: 0
|
|
517
|
+
}
|
|
518
|
+
}
|
|
519
|
+
},
|
|
520
|
+
gentle: {
|
|
521
|
+
morphTime: 1.1,
|
|
522
|
+
easing: easeInOutCubic,
|
|
523
|
+
effects: {
|
|
524
|
+
rise: {
|
|
525
|
+
easing: cubicBezier(0.42, 0, 0.58, 1),
|
|
526
|
+
stagger: 45,
|
|
527
|
+
overlap: 0.2
|
|
528
|
+
}
|
|
529
|
+
}
|
|
530
|
+
}
|
|
531
|
+
};
|
|
532
|
+
var presetNames = Object.keys(presets);
|
|
533
|
+
var motionNames = Object.keys(motions);
|
|
534
|
+
function isRecord(value) {
|
|
535
|
+
return typeof value === "object" && value !== null && !Array.isArray(value);
|
|
536
|
+
}
|
|
537
|
+
function mergeValue(base, override) {
|
|
538
|
+
if (override === void 0) return base;
|
|
539
|
+
if (Array.isArray(override)) return [...override];
|
|
540
|
+
if (isRecord(base) && isRecord(override)) return mergeOptions(base, override);
|
|
541
|
+
return override;
|
|
542
|
+
}
|
|
543
|
+
function mergeOptions(...sources) {
|
|
544
|
+
const merged = {};
|
|
545
|
+
for (const source of sources) {
|
|
546
|
+
if (!source) continue;
|
|
547
|
+
for (const [key, value] of Object.entries(source)) {
|
|
548
|
+
if (key === "preset" || key === "motion" || value === void 0) continue;
|
|
549
|
+
merged[key] = mergeValue(merged[key], value);
|
|
550
|
+
}
|
|
551
|
+
}
|
|
552
|
+
return merged;
|
|
553
|
+
}
|
|
554
|
+
function resolveMorphOptions(options) {
|
|
555
|
+
const { preset, motion, ...overrides } = options;
|
|
556
|
+
if (!preset && !motion) return options;
|
|
557
|
+
const motionName = motion ?? (preset ? "default" : void 0);
|
|
558
|
+
return mergeOptions(
|
|
559
|
+
preset ? presets[preset] : void 0,
|
|
560
|
+
motionName ? motions[motionName] : void 0,
|
|
561
|
+
overrides
|
|
562
|
+
);
|
|
563
|
+
}
|
|
564
|
+
function resolvePreset(name, overrides = {}) {
|
|
565
|
+
return resolveMorphOptions({ ...overrides, preset: name });
|
|
566
|
+
}
|
|
567
|
+
function isRiseFlow(value) {
|
|
568
|
+
return value === "up" || value === "down" || value === "left" || value === "right";
|
|
569
|
+
}
|
|
570
|
+
|
|
571
|
+
// src/core/atoms.ts
|
|
572
|
+
var ZWNJ = "\u200C";
|
|
573
|
+
var ZWJ = "\u200D";
|
|
574
|
+
var DIACRITICS_RE = /[\u0610-\u061A\u064B-\u065F\u0670\u06D6-\u06ED\u08D3-\u08E1\u08E3-\u08FF]/g;
|
|
575
|
+
var ALEF_VARIANTS = /* @__PURE__ */ new Set([
|
|
576
|
+
"\u0622",
|
|
577
|
+
// آ
|
|
578
|
+
"\u0623",
|
|
579
|
+
// أ
|
|
580
|
+
"\u0625",
|
|
581
|
+
// إ
|
|
582
|
+
"\u0627",
|
|
583
|
+
// ا
|
|
584
|
+
"\u0671"
|
|
585
|
+
// ٱ
|
|
586
|
+
]);
|
|
587
|
+
var RIGHT_JOINING = new Set(
|
|
588
|
+
[
|
|
589
|
+
"\u0622",
|
|
590
|
+
// آ
|
|
591
|
+
"\u0623",
|
|
592
|
+
// أ
|
|
593
|
+
"\u0624",
|
|
594
|
+
// ؤ
|
|
595
|
+
"\u0625",
|
|
596
|
+
// إ
|
|
597
|
+
"\u0627",
|
|
598
|
+
// ا
|
|
599
|
+
"\u0629",
|
|
600
|
+
// ة
|
|
601
|
+
"\u062F",
|
|
602
|
+
// د
|
|
603
|
+
"\u0630",
|
|
604
|
+
// ذ
|
|
605
|
+
"\u0631",
|
|
606
|
+
// ر
|
|
607
|
+
"\u0632",
|
|
608
|
+
// ز
|
|
609
|
+
"\u0648",
|
|
610
|
+
// و
|
|
611
|
+
"\u0698"
|
|
612
|
+
// ژ
|
|
613
|
+
].map((char) => char.normalize("NFC"))
|
|
614
|
+
);
|
|
615
|
+
var NON_JOINING = /* @__PURE__ */ new Set([
|
|
616
|
+
"\u0621"
|
|
617
|
+
// ء
|
|
618
|
+
]);
|
|
619
|
+
function isMark(char) {
|
|
620
|
+
return /^\p{M}$/u.test(char);
|
|
621
|
+
}
|
|
622
|
+
function isArabicLetter(char) {
|
|
623
|
+
return /\p{L}/u.test(char) && /\p{Script=Arabic}/u.test(char);
|
|
624
|
+
}
|
|
625
|
+
function canJoinPrevious(char) {
|
|
626
|
+
return isArabicLetter(char) && !NON_JOINING.has(char);
|
|
627
|
+
}
|
|
628
|
+
function canJoinNext(char) {
|
|
629
|
+
return isArabicLetter(char) && !RIGHT_JOINING.has(char) && !NON_JOINING.has(char);
|
|
630
|
+
}
|
|
631
|
+
function neighbor(text, index, direction) {
|
|
632
|
+
let cursor = index + direction;
|
|
633
|
+
while (cursor >= 0 && cursor < text.length) {
|
|
634
|
+
const char = text[cursor];
|
|
635
|
+
if (isMark(char) || char === ZWJ) {
|
|
636
|
+
cursor += direction;
|
|
637
|
+
continue;
|
|
638
|
+
}
|
|
639
|
+
if (char === ZWNJ || /\s/u.test(char) || !isArabicLetter(char)) return null;
|
|
640
|
+
return { char, index: cursor };
|
|
641
|
+
}
|
|
642
|
+
return null;
|
|
643
|
+
}
|
|
644
|
+
function formOf(text, index) {
|
|
645
|
+
const char = text[index];
|
|
646
|
+
if (!isArabicLetter(char)) return "x";
|
|
647
|
+
const previous = neighbor(text, index, -1);
|
|
648
|
+
const next = neighbor(text, index, 1);
|
|
649
|
+
const joinsPrevious = previous !== null && canJoinNext(previous.char) && canJoinPrevious(char);
|
|
650
|
+
const joinsNext = next !== null && canJoinNext(char) && canJoinPrevious(next.char);
|
|
651
|
+
if (joinsPrevious && joinsNext) return "medial";
|
|
652
|
+
if (joinsPrevious) return "final";
|
|
653
|
+
if (joinsNext) return "initial";
|
|
654
|
+
return "isolated";
|
|
655
|
+
}
|
|
656
|
+
function formSignature(text, start, end) {
|
|
657
|
+
const forms = [];
|
|
658
|
+
for (let index = start; index < end; index += 1) {
|
|
659
|
+
if (isArabicLetter(text[index])) forms.push(formOf(text, index));
|
|
660
|
+
}
|
|
661
|
+
return forms.length > 0 ? forms.join(",") : "x";
|
|
662
|
+
}
|
|
663
|
+
var cachedSegmenter;
|
|
664
|
+
function getSegmenter() {
|
|
665
|
+
if (cachedSegmenter !== void 0) return cachedSegmenter;
|
|
666
|
+
const intl = Intl;
|
|
667
|
+
if (typeof intl.Segmenter !== "function") {
|
|
668
|
+
cachedSegmenter = null;
|
|
669
|
+
return cachedSegmenter;
|
|
670
|
+
}
|
|
671
|
+
try {
|
|
672
|
+
cachedSegmenter = new intl.Segmenter(void 0, { granularity: "grapheme" });
|
|
673
|
+
} catch {
|
|
674
|
+
cachedSegmenter = null;
|
|
675
|
+
}
|
|
676
|
+
return cachedSegmenter;
|
|
677
|
+
}
|
|
678
|
+
function fallbackSegments(text) {
|
|
679
|
+
const segments = [];
|
|
680
|
+
let start = 0;
|
|
681
|
+
let current = "";
|
|
682
|
+
let previousWasZWJ = false;
|
|
683
|
+
for (let index = 0; index < text.length; ) {
|
|
684
|
+
const codePoint = text.codePointAt(index);
|
|
685
|
+
if (codePoint === void 0) break;
|
|
686
|
+
const char = String.fromCodePoint(codePoint);
|
|
687
|
+
const mark = /^\p{M}$/u.test(char);
|
|
688
|
+
const variation = /^[\uFE00-\uFE0F]$/u.test(char);
|
|
689
|
+
const modifier = /^[\u{1F3FB}-\u{1F3FF}]$/u.test(char);
|
|
690
|
+
const joinControl = char === ZWJ;
|
|
691
|
+
const joinAfter = previousWasZWJ;
|
|
692
|
+
const join = mark || variation || modifier || joinControl || joinAfter;
|
|
693
|
+
if (!join && current.length > 0) {
|
|
694
|
+
segments.push({ segment: current, index: start });
|
|
695
|
+
start = index;
|
|
696
|
+
current = "";
|
|
697
|
+
}
|
|
698
|
+
if (current.length === 0) start = index;
|
|
699
|
+
current += char;
|
|
700
|
+
previousWasZWJ = char === ZWJ;
|
|
701
|
+
index += char.length;
|
|
702
|
+
}
|
|
703
|
+
if (current.length > 0) segments.push({ segment: current, index: start });
|
|
704
|
+
return segments;
|
|
705
|
+
}
|
|
706
|
+
function segmentGraphemes(text) {
|
|
707
|
+
if (text.length === 0) return [];
|
|
708
|
+
const segmenter = getSegmenter();
|
|
709
|
+
if (!segmenter) return fallbackSegments(text);
|
|
710
|
+
return Array.from(segmenter.segment(text), (part) => ({
|
|
711
|
+
segment: part.segment,
|
|
712
|
+
index: part.index
|
|
713
|
+
}));
|
|
714
|
+
}
|
|
715
|
+
function firstArabicLetter(text) {
|
|
716
|
+
for (const char of text) {
|
|
717
|
+
if (isArabicLetter(char)) return char;
|
|
718
|
+
}
|
|
719
|
+
return "";
|
|
720
|
+
}
|
|
721
|
+
function isLamSegment(text) {
|
|
722
|
+
return firstArabicLetter(text) === "\u0644";
|
|
723
|
+
}
|
|
724
|
+
function isAlefSegment(text) {
|
|
725
|
+
return ALEF_VARIANTS.has(firstArabicLetter(text));
|
|
726
|
+
}
|
|
727
|
+
function atomRanges(text) {
|
|
728
|
+
const segments = segmentGraphemes(text);
|
|
729
|
+
const ranges = [];
|
|
730
|
+
for (let index = 0; index < segments.length; index += 1) {
|
|
731
|
+
const current = segments[index];
|
|
732
|
+
if (!current) continue;
|
|
733
|
+
const next = segments[index + 1];
|
|
734
|
+
if (next && isLamSegment(current.segment) && !current.segment.includes(ZWNJ) && isAlefSegment(next.segment)) {
|
|
735
|
+
ranges.push({ start: current.index, end: next.index + next.segment.length });
|
|
736
|
+
index += 1;
|
|
737
|
+
} else {
|
|
738
|
+
ranges.push({ start: current.index, end: current.index + current.segment.length });
|
|
739
|
+
}
|
|
740
|
+
}
|
|
741
|
+
return ranges;
|
|
742
|
+
}
|
|
743
|
+
function createAtoms(text) {
|
|
744
|
+
return atomRanges(text).map(({ start, end }) => {
|
|
745
|
+
const atomText = text.slice(start, end);
|
|
746
|
+
const formKey = formSignature(text, start, end);
|
|
747
|
+
return {
|
|
748
|
+
text: atomText,
|
|
749
|
+
key: `${atomText}\0${formKey}`,
|
|
750
|
+
start,
|
|
751
|
+
end,
|
|
752
|
+
formKey
|
|
753
|
+
};
|
|
754
|
+
});
|
|
755
|
+
}
|
|
756
|
+
function isWordBreakAtom(atom) {
|
|
757
|
+
if (atom.text.includes(ZWNJ) || atom.text.includes(ZWJ)) return false;
|
|
758
|
+
return !/[\p{L}\p{N}]/u.test(atom.text);
|
|
759
|
+
}
|
|
760
|
+
|
|
761
|
+
// src/core/chunks.ts
|
|
762
|
+
function atWordEdge(atoms, start, end) {
|
|
763
|
+
const before = start === 0 || isWordBreakAtom(atoms[start - 1]);
|
|
764
|
+
const after = end === atoms.length - 1 || isWordBreakAtom(atoms[end + 1]);
|
|
765
|
+
return before && after;
|
|
766
|
+
}
|
|
767
|
+
function isWholeWord(run, a, b) {
|
|
768
|
+
const aStart = run[0].aIndex;
|
|
769
|
+
const aEnd = run[run.length - 1].aIndex;
|
|
770
|
+
const bStart = run[0].bIndex;
|
|
771
|
+
const bEnd = run[run.length - 1].bIndex;
|
|
772
|
+
return atWordEdge(a, aStart, aEnd) && atWordEdge(b, bStart, bEnd);
|
|
773
|
+
}
|
|
774
|
+
function diffToChunks(a, b, operations, options = {}) {
|
|
775
|
+
const minChunk = Math.max(0, Math.floor(options.minChunk ?? 2));
|
|
776
|
+
const chunks = [];
|
|
777
|
+
let id = 0;
|
|
778
|
+
const push = (kind, aIndices, bIndices) => {
|
|
779
|
+
chunks.push({ id: `chunk-${id++}`, kind, aIndices, bIndices });
|
|
780
|
+
};
|
|
781
|
+
let index = 0;
|
|
782
|
+
while (index < operations.length) {
|
|
783
|
+
const operation = operations[index];
|
|
784
|
+
if (operation.type === "match") {
|
|
785
|
+
const run = [operation];
|
|
786
|
+
let nextIndex2 = index + 1;
|
|
787
|
+
while (nextIndex2 < operations.length) {
|
|
788
|
+
const next = operations[nextIndex2];
|
|
789
|
+
if (next.type !== "match") break;
|
|
790
|
+
const last = run[run.length - 1];
|
|
791
|
+
if (next.aIndex !== last.aIndex + 1 || next.bIndex !== last.bIndex + 1) {
|
|
792
|
+
break;
|
|
793
|
+
}
|
|
794
|
+
run.push(next);
|
|
795
|
+
nextIndex2 += 1;
|
|
796
|
+
}
|
|
797
|
+
if (run.length >= minChunk || isWholeWord(run, a, b)) {
|
|
798
|
+
push(
|
|
799
|
+
"match",
|
|
800
|
+
run.map((match) => match.aIndex),
|
|
801
|
+
run.map((match) => match.bIndex)
|
|
802
|
+
);
|
|
803
|
+
} else {
|
|
804
|
+
for (const match of run) {
|
|
805
|
+
push("remove", [match.aIndex], []);
|
|
806
|
+
push("add", [], [match.bIndex]);
|
|
807
|
+
}
|
|
808
|
+
}
|
|
809
|
+
index = nextIndex2;
|
|
810
|
+
continue;
|
|
811
|
+
}
|
|
812
|
+
if (operation.type === "delete") {
|
|
813
|
+
const indices2 = [];
|
|
814
|
+
let nextIndex2 = index;
|
|
815
|
+
while (nextIndex2 < operations.length) {
|
|
816
|
+
const current = operations[nextIndex2];
|
|
817
|
+
if (current.type !== "delete") break;
|
|
818
|
+
indices2.push(current.aIndex);
|
|
819
|
+
nextIndex2 += 1;
|
|
820
|
+
}
|
|
821
|
+
push("remove", indices2, []);
|
|
822
|
+
index = nextIndex2;
|
|
823
|
+
continue;
|
|
824
|
+
}
|
|
825
|
+
const indices = [];
|
|
826
|
+
let nextIndex = index;
|
|
827
|
+
while (nextIndex < operations.length) {
|
|
828
|
+
const current = operations[nextIndex];
|
|
829
|
+
if (current.type !== "insert") break;
|
|
830
|
+
indices.push(current.bIndex);
|
|
831
|
+
nextIndex += 1;
|
|
832
|
+
}
|
|
833
|
+
push("add", [], indices);
|
|
834
|
+
index = nextIndex;
|
|
835
|
+
}
|
|
836
|
+
const mergeSide = (kind) => {
|
|
837
|
+
const sideChunks = chunks.filter((chunk) => chunk.kind === kind).sort((left, right) => {
|
|
838
|
+
const aIndex = kind === "remove" ? left.aIndices[0] : left.bIndices[0];
|
|
839
|
+
const bIndex = kind === "remove" ? right.aIndices[0] : right.bIndices[0];
|
|
840
|
+
return (aIndex ?? 0) - (bIndex ?? 0);
|
|
841
|
+
});
|
|
842
|
+
const merged2 = [];
|
|
843
|
+
for (const chunk of sideChunks) {
|
|
844
|
+
const last = merged2[merged2.length - 1];
|
|
845
|
+
const lastIndices = last ? kind === "remove" ? last.aIndices : last.bIndices : [];
|
|
846
|
+
const indices = kind === "remove" ? chunk.aIndices : chunk.bIndices;
|
|
847
|
+
if (last && lastIndices.length > 0 && indices.length > 0 && indices[0] === lastIndices[lastIndices.length - 1] + 1) {
|
|
848
|
+
if (kind === "remove") last.aIndices.push(...chunk.aIndices);
|
|
849
|
+
else last.bIndices.push(...chunk.bIndices);
|
|
850
|
+
} else {
|
|
851
|
+
merged2.push({
|
|
852
|
+
id: "",
|
|
853
|
+
kind,
|
|
854
|
+
aIndices: [...chunk.aIndices],
|
|
855
|
+
bIndices: [...chunk.bIndices]
|
|
856
|
+
});
|
|
857
|
+
}
|
|
858
|
+
}
|
|
859
|
+
return merged2;
|
|
860
|
+
};
|
|
861
|
+
const merged = [
|
|
862
|
+
...mergeSide("remove"),
|
|
863
|
+
...mergeSide("add"),
|
|
864
|
+
...chunks.filter((chunk) => chunk.kind === "match")
|
|
865
|
+
];
|
|
866
|
+
const splitChanged = (chunk) => {
|
|
867
|
+
if (chunk.kind === "match") return [chunk];
|
|
868
|
+
const indices = chunk.kind === "remove" ? chunk.aIndices : chunk.bIndices;
|
|
869
|
+
const atoms = chunk.kind === "remove" ? a : b;
|
|
870
|
+
const pieces = [];
|
|
871
|
+
let current = [];
|
|
872
|
+
const flush = () => {
|
|
873
|
+
if (current.length === 0) return;
|
|
874
|
+
pieces.push({
|
|
875
|
+
id: "",
|
|
876
|
+
kind: chunk.kind,
|
|
877
|
+
aIndices: chunk.kind === "remove" ? current : [],
|
|
878
|
+
bIndices: chunk.kind === "add" ? current : []
|
|
879
|
+
});
|
|
880
|
+
current = [];
|
|
881
|
+
};
|
|
882
|
+
for (const atomIndex of indices) {
|
|
883
|
+
current.push(atomIndex);
|
|
884
|
+
if (/\s/u.test(atoms[atomIndex]?.text ?? "")) flush();
|
|
885
|
+
}
|
|
886
|
+
flush();
|
|
887
|
+
return pieces.length > 0 ? pieces : [chunk];
|
|
888
|
+
};
|
|
889
|
+
const split = merged.flatMap(splitChanged);
|
|
890
|
+
split.forEach((chunk, chunkIndex) => {
|
|
891
|
+
chunk.id = `chunk-${chunkIndex}`;
|
|
892
|
+
});
|
|
893
|
+
return split;
|
|
894
|
+
}
|
|
895
|
+
|
|
896
|
+
// src/core/diff.ts
|
|
897
|
+
var VARIANT_MAP = {
|
|
898
|
+
"\u064A": "\u06CC",
|
|
899
|
+
// ي -> ی
|
|
900
|
+
"\u0643": "\u06A9"
|
|
901
|
+
// ك -> ک
|
|
902
|
+
};
|
|
903
|
+
function normalizeVariantText(text) {
|
|
904
|
+
return text.replace(/[\u064A\u0643]/g, (char) => VARIANT_MAP[char] ?? char);
|
|
905
|
+
}
|
|
906
|
+
function normalizedText(atom, options) {
|
|
907
|
+
let text = atom.text.normalize("NFC").split(ZWNJ).join("");
|
|
908
|
+
if (options.normalizeVariants) text = normalizeVariantText(text);
|
|
909
|
+
if (options.ignoreDiacritics) text = text.replace(DIACRITICS_RE, "");
|
|
910
|
+
return text;
|
|
911
|
+
}
|
|
912
|
+
function atomsEqual(a, b, options = {}) {
|
|
913
|
+
if (a.formKey !== b.formKey) return false;
|
|
914
|
+
return normalizedText(a, options) === normalizedText(b, options);
|
|
915
|
+
}
|
|
916
|
+
function diffAtoms(a, b, options = {}) {
|
|
917
|
+
const rows = a.length;
|
|
918
|
+
const columns = b.length;
|
|
919
|
+
const table = Array.from({ length: rows + 1 }, () => new Uint32Array(columns + 1));
|
|
920
|
+
for (let row2 = rows - 1; row2 >= 0; row2 -= 1) {
|
|
921
|
+
for (let column2 = columns - 1; column2 >= 0; column2 -= 1) {
|
|
922
|
+
table[row2][column2] = atomsEqual(a[row2], b[column2], options) ? table[row2 + 1][column2 + 1] + 1 : Math.max(table[row2 + 1][column2], table[row2][column2 + 1]);
|
|
923
|
+
}
|
|
924
|
+
}
|
|
925
|
+
const operations = [];
|
|
926
|
+
let row = 0;
|
|
927
|
+
let column = 0;
|
|
928
|
+
while (row < rows && column < columns) {
|
|
929
|
+
if (atomsEqual(a[row], b[column], options)) {
|
|
930
|
+
operations.push({ type: "match", aIndex: row, bIndex: column });
|
|
931
|
+
row += 1;
|
|
932
|
+
column += 1;
|
|
933
|
+
} else if (table[row + 1][column] >= table[row][column + 1]) {
|
|
934
|
+
operations.push({ type: "delete", aIndex: row });
|
|
935
|
+
row += 1;
|
|
936
|
+
} else {
|
|
937
|
+
operations.push({ type: "insert", bIndex: column });
|
|
938
|
+
column += 1;
|
|
939
|
+
}
|
|
940
|
+
}
|
|
941
|
+
while (row < rows) {
|
|
942
|
+
operations.push({ type: "delete", aIndex: row });
|
|
943
|
+
row += 1;
|
|
944
|
+
}
|
|
945
|
+
while (column < columns) {
|
|
946
|
+
operations.push({ type: "insert", bIndex: column });
|
|
947
|
+
column += 1;
|
|
948
|
+
}
|
|
949
|
+
return operations;
|
|
950
|
+
}
|
|
951
|
+
|
|
952
|
+
// src/core/scene.ts
|
|
953
|
+
function previousBase(text, index) {
|
|
954
|
+
let cursor = index - 1;
|
|
955
|
+
while (cursor >= 0 && isMark(text[cursor])) cursor -= 1;
|
|
956
|
+
return cursor >= 0 ? text[cursor] : "";
|
|
957
|
+
}
|
|
958
|
+
function nextBase(text, index) {
|
|
959
|
+
let cursor = index;
|
|
960
|
+
while (cursor < text.length && isMark(text[cursor])) cursor += 1;
|
|
961
|
+
return cursor < text.length ? text[cursor] : "";
|
|
962
|
+
}
|
|
963
|
+
function firstBaseBetween(text, start, end) {
|
|
964
|
+
let cursor = start;
|
|
965
|
+
while (cursor < end && isMark(text[cursor])) cursor += 1;
|
|
966
|
+
return cursor < end ? text[cursor] : "";
|
|
967
|
+
}
|
|
968
|
+
function lastBaseBefore(text, end) {
|
|
969
|
+
let cursor = end - 1;
|
|
970
|
+
while (cursor >= 0 && isMark(text[cursor])) cursor -= 1;
|
|
971
|
+
return cursor >= 0 ? text[cursor] : "";
|
|
972
|
+
}
|
|
973
|
+
function shouldPadStart(text, start, end) {
|
|
974
|
+
if (start <= 0) return false;
|
|
975
|
+
const first = firstBaseBetween(text, start, end);
|
|
976
|
+
const before = previousBase(text, start);
|
|
977
|
+
if (!isArabicLetter(first) || !isArabicLetter(before)) return false;
|
|
978
|
+
return canJoinPrevious(first) && canJoinNext(before);
|
|
979
|
+
}
|
|
980
|
+
function shouldPadEnd(text, start, end) {
|
|
981
|
+
if (end >= text.length) return false;
|
|
982
|
+
const last = lastBaseBefore(text, end);
|
|
983
|
+
const after = nextBase(text, end);
|
|
984
|
+
if (!isArabicLetter(last) || !isArabicLetter(after)) return false;
|
|
985
|
+
return canJoinNext(last) && canJoinPrevious(after);
|
|
986
|
+
}
|
|
987
|
+
function atomSourceRange(atoms, indices) {
|
|
988
|
+
let start = Number.POSITIVE_INFINITY;
|
|
989
|
+
let end = Number.NEGATIVE_INFINITY;
|
|
990
|
+
for (const index of indices) {
|
|
991
|
+
const atom = atoms[index];
|
|
992
|
+
if (!atom) continue;
|
|
993
|
+
start = Math.min(start, atom.start);
|
|
994
|
+
end = Math.max(end, atom.end);
|
|
995
|
+
}
|
|
996
|
+
if (!Number.isFinite(start) || !Number.isFinite(end)) {
|
|
997
|
+
return { start: 0, end: 0 };
|
|
998
|
+
}
|
|
999
|
+
return { start, end };
|
|
1000
|
+
}
|
|
1001
|
+
function styleChunk(span, dir) {
|
|
1002
|
+
span.dir = dir;
|
|
1003
|
+
span.style.display = "inline-block";
|
|
1004
|
+
span.style.whiteSpace = "pre";
|
|
1005
|
+
span.style.unicodeBidi = "isolate";
|
|
1006
|
+
span.style.direction = dir === "auto" ? "inherit" : dir;
|
|
1007
|
+
span.style.verticalAlign = "baseline";
|
|
1008
|
+
span.style.transformOrigin = "center center";
|
|
1009
|
+
span.style.willChange = "transform, opacity, filter";
|
|
1010
|
+
span.style.pointerEvents = "none";
|
|
1011
|
+
span.style.setProperty("letter-spacing", "0", "important");
|
|
1012
|
+
}
|
|
1013
|
+
function renderLayer(layer, source, atoms, chunks, side, options) {
|
|
1014
|
+
resetLayer(layer);
|
|
1015
|
+
layer.line.replaceChildren();
|
|
1016
|
+
const records = [];
|
|
1017
|
+
for (const chunk of chunks) {
|
|
1018
|
+
const indices = side === "a" ? chunk.aIndices : chunk.bIndices;
|
|
1019
|
+
if (indices.length === 0) continue;
|
|
1020
|
+
const range = atomSourceRange(atoms, indices);
|
|
1021
|
+
if (range.end <= range.start) continue;
|
|
1022
|
+
const text = source.slice(range.start, range.end);
|
|
1023
|
+
const leadingPad = shouldPadStart(source, range.start, range.end) ? ZWJ : "";
|
|
1024
|
+
const trailingPad = shouldPadEnd(source, range.start, range.end) ? ZWJ : "";
|
|
1025
|
+
const span = layer.el.ownerDocument.createElement("span");
|
|
1026
|
+
span.className = `rtm-chunk rtm-chunk--${chunk.kind}`;
|
|
1027
|
+
span.dataset.chunkId = chunk.id;
|
|
1028
|
+
span.dataset.chunkKind = chunk.kind;
|
|
1029
|
+
span.dataset.chunkSide = side;
|
|
1030
|
+
styleChunk(span, options.dir);
|
|
1031
|
+
span.textContent = leadingPad + text + trailingPad;
|
|
1032
|
+
records.push({
|
|
1033
|
+
chunk,
|
|
1034
|
+
span,
|
|
1035
|
+
text,
|
|
1036
|
+
sourceStart: range.start,
|
|
1037
|
+
sourceEnd: range.end,
|
|
1038
|
+
side
|
|
1039
|
+
});
|
|
1040
|
+
}
|
|
1041
|
+
records.sort((left, right) => left.sourceStart - right.sourceStart);
|
|
1042
|
+
for (const record of records) layer.line.appendChild(record.span);
|
|
1043
|
+
return records;
|
|
1044
|
+
}
|
|
1045
|
+
function measureSpan(span) {
|
|
1046
|
+
const range = span.ownerDocument.createRange();
|
|
1047
|
+
range.selectNodeContents(span);
|
|
1048
|
+
const rangeRect = typeof range.getBoundingClientRect === "function" ? range.getBoundingClientRect() : null;
|
|
1049
|
+
const spanRect = span.getBoundingClientRect();
|
|
1050
|
+
if (!rangeRect) return spanRect;
|
|
1051
|
+
if (rangeRect.width === 0 && rangeRect.height === 0 && (spanRect.width > 0 || spanRect.height > 0)) {
|
|
1052
|
+
return spanRect;
|
|
1053
|
+
}
|
|
1054
|
+
return rangeRect;
|
|
1055
|
+
}
|
|
1056
|
+
function readingOrder(records, rtl) {
|
|
1057
|
+
return records.map((record) => {
|
|
1058
|
+
const rect = record.span.getBoundingClientRect();
|
|
1059
|
+
return { record, x: rtl ? rect.right : rect.left };
|
|
1060
|
+
}).sort((left, right) => rtl ? right.x - left.x : left.x - right.x).map((entry) => entry.record);
|
|
1061
|
+
}
|
|
1062
|
+
function buildDiffScene(stage, from, to, options) {
|
|
1063
|
+
const atomsA = createAtoms(from);
|
|
1064
|
+
const atomsB = createAtoms(to);
|
|
1065
|
+
const operations = diffAtoms(atomsA, atomsB, {
|
|
1066
|
+
normalizeVariants: options.normalizeVariants,
|
|
1067
|
+
ignoreDiacritics: options.ignoreDiacritics
|
|
1068
|
+
});
|
|
1069
|
+
const chunks = diffToChunks(atomsA, atomsB, operations, {
|
|
1070
|
+
minChunk: options.minChunk
|
|
1071
|
+
});
|
|
1072
|
+
const oldRecords = renderLayer(stage.outgoing, from, atomsA, chunks, "a", options);
|
|
1073
|
+
const newRecords = renderLayer(stage.incoming, to, atomsB, chunks, "b", options);
|
|
1074
|
+
const oldMatched = /* @__PURE__ */ new Map();
|
|
1075
|
+
for (const record of oldRecords) {
|
|
1076
|
+
if (record.chunk.kind === "match") oldMatched.set(record.chunk.id, record);
|
|
1077
|
+
}
|
|
1078
|
+
const matched = [];
|
|
1079
|
+
for (const record of newRecords) {
|
|
1080
|
+
if (record.chunk.kind !== "match") continue;
|
|
1081
|
+
const old = oldMatched.get(record.chunk.id);
|
|
1082
|
+
if (!old) continue;
|
|
1083
|
+
old.span.style.opacity = "0";
|
|
1084
|
+
const oldRect = measureSpan(old.span);
|
|
1085
|
+
const newRect = measureSpan(record.span);
|
|
1086
|
+
matched.push({
|
|
1087
|
+
...record,
|
|
1088
|
+
dx: oldRect.left - newRect.left,
|
|
1089
|
+
dy: oldRect.top - newRect.top
|
|
1090
|
+
});
|
|
1091
|
+
}
|
|
1092
|
+
const enter = newRecords.filter((record) => record.chunk.kind === "add");
|
|
1093
|
+
for (const record of enter) record.span.style.opacity = "0";
|
|
1094
|
+
const exit = oldRecords.filter((record) => record.chunk.kind === "remove");
|
|
1095
|
+
return {
|
|
1096
|
+
from,
|
|
1097
|
+
to,
|
|
1098
|
+
atomsA,
|
|
1099
|
+
atomsB,
|
|
1100
|
+
chunks,
|
|
1101
|
+
enter,
|
|
1102
|
+
exit,
|
|
1103
|
+
matched
|
|
1104
|
+
};
|
|
1105
|
+
}
|
|
1106
|
+
function enterOrder(scene, rtl) {
|
|
1107
|
+
return readingOrder(scene.enter, rtl);
|
|
1108
|
+
}
|
|
1109
|
+
function exitOrder(scene, rtl) {
|
|
1110
|
+
return readingOrder(scene.exit, rtl);
|
|
1111
|
+
}
|
|
1112
|
+
|
|
1113
|
+
// src/core/transition.ts
|
|
1114
|
+
var DEFAULT_FONT_SIZE = 16;
|
|
1115
|
+
var MAX_FRAME_DELTA = 0.1;
|
|
1116
|
+
var REDUCED_MOTION_FADE = 0.18;
|
|
1117
|
+
var NO_EFFECT_FADE = 0.3;
|
|
1118
|
+
var FAST_FORWARD = 0.12;
|
|
1119
|
+
var GOO_SOFTEN = " blur(0.6px)";
|
|
1120
|
+
function nonNegative2(value, fallback) {
|
|
1121
|
+
return typeof value === "number" && Number.isFinite(value) ? Math.max(0, value) : fallback;
|
|
1122
|
+
}
|
|
1123
|
+
function sameWords(a, b) {
|
|
1124
|
+
return a.length === b.length && a.every((word, index) => word === b[index]);
|
|
1125
|
+
}
|
|
1126
|
+
function prefersReducedMotion(doc) {
|
|
1127
|
+
const view = doc.defaultView;
|
|
1128
|
+
if (!view || typeof view.matchMedia !== "function") return false;
|
|
1129
|
+
try {
|
|
1130
|
+
return view.matchMedia("(prefers-reduced-motion: reduce)").matches;
|
|
1131
|
+
} catch {
|
|
1132
|
+
return false;
|
|
1133
|
+
}
|
|
1134
|
+
}
|
|
1135
|
+
function reportCallbackError(error) {
|
|
1136
|
+
if (typeof console !== "undefined" && typeof console.error === "function") {
|
|
1137
|
+
console.error("[rtl-text-transform]", error);
|
|
1138
|
+
}
|
|
1139
|
+
}
|
|
1140
|
+
function combineBlur(gooPx, risePx) {
|
|
1141
|
+
const goo = Math.max(0, gooPx);
|
|
1142
|
+
const rise = Math.max(0, risePx);
|
|
1143
|
+
if (goo < 0.01 && rise < 0.01) return "none";
|
|
1144
|
+
if (goo < 0.01) return `blur(${rise.toFixed(2)}px)`;
|
|
1145
|
+
if (rise < 0.01) return `blur(${goo.toFixed(2)}px)`;
|
|
1146
|
+
return `blur(${goo.toFixed(2)}px) blur(${rise.toFixed(2)}px)`;
|
|
1147
|
+
}
|
|
1148
|
+
function resolveFlow(flow, context) {
|
|
1149
|
+
if (typeof flow !== "function") return flow;
|
|
1150
|
+
try {
|
|
1151
|
+
const resolved = flow(context);
|
|
1152
|
+
return isRiseFlow(resolved) ? resolved : "up";
|
|
1153
|
+
} catch (error) {
|
|
1154
|
+
reportCallbackError(error);
|
|
1155
|
+
return "up";
|
|
1156
|
+
}
|
|
1157
|
+
}
|
|
1158
|
+
function flowForTransition(flow, scene, from, to, dir, rtl) {
|
|
1159
|
+
if (typeof flow !== "function") {
|
|
1160
|
+
return { fallback: flow };
|
|
1161
|
+
}
|
|
1162
|
+
const byChunk = /* @__PURE__ */ new Map();
|
|
1163
|
+
for (const chunk of scene.chunks) {
|
|
1164
|
+
if (chunk.kind === "match") continue;
|
|
1165
|
+
const useTarget = chunk.bIndices.length > 0;
|
|
1166
|
+
const source = useTarget ? to : from;
|
|
1167
|
+
const atoms = useTarget ? scene.atomsB : scene.atomsA;
|
|
1168
|
+
const range = atomSourceRange(atoms, useTarget ? chunk.bIndices : chunk.aIndices);
|
|
1169
|
+
const text = source.slice(range.start, range.end);
|
|
1170
|
+
const length = source.length;
|
|
1171
|
+
const position = length > 0 ? Math.min(1, Math.max(0, (range.start + range.end) / 2 / length)) : 0;
|
|
1172
|
+
const context = {
|
|
1173
|
+
from,
|
|
1174
|
+
to,
|
|
1175
|
+
dir,
|
|
1176
|
+
rtl,
|
|
1177
|
+
hunk: {
|
|
1178
|
+
id: chunk.id,
|
|
1179
|
+
kind: chunk.kind === "add" ? "add" : "remove",
|
|
1180
|
+
text,
|
|
1181
|
+
start: range.start,
|
|
1182
|
+
end: range.end,
|
|
1183
|
+
length,
|
|
1184
|
+
position
|
|
1185
|
+
}
|
|
1186
|
+
};
|
|
1187
|
+
byChunk.set(chunk.id, resolveFlow(flow, context));
|
|
1188
|
+
}
|
|
1189
|
+
return { fallback: "up", byChunk };
|
|
1190
|
+
}
|
|
1191
|
+
|
|
1192
|
+
// src/core/controller.ts
|
|
1193
|
+
function createMorph(el, options = {}) {
|
|
1194
|
+
if (!el || !el.ownerDocument) {
|
|
1195
|
+
throw new TypeError("createMorph(el): el must be an HTMLElement");
|
|
1196
|
+
}
|
|
1197
|
+
const resolvedOptions = resolveMorphOptions(options);
|
|
1198
|
+
const doc = el.ownerDocument;
|
|
1199
|
+
const originalNodes = Array.from(el.childNodes);
|
|
1200
|
+
const originalDir = el.getAttribute("dir");
|
|
1201
|
+
const originalLang = el.getAttribute("lang");
|
|
1202
|
+
const savedStyle = {
|
|
1203
|
+
position: el.style.position,
|
|
1204
|
+
display: el.style.display,
|
|
1205
|
+
minHeight: el.style.minHeight,
|
|
1206
|
+
width: el.style.width,
|
|
1207
|
+
filter: el.style.filter
|
|
1208
|
+
};
|
|
1209
|
+
const initialText = (el.textContent ?? "").trim();
|
|
1210
|
+
let words = Array.isArray(resolvedOptions.words) ? resolvedOptions.words.map((word) => String(word)) : [];
|
|
1211
|
+
const initialEffects = resolveInitialEffects(resolvedOptions.effects);
|
|
1212
|
+
let goo = initialEffects.goo;
|
|
1213
|
+
let rise = initialEffects.rise;
|
|
1214
|
+
let morphTime = nonNegative2(resolvedOptions.morphTime, 1);
|
|
1215
|
+
let restTime = nonNegative2(resolvedOptions.restTime, 0.25);
|
|
1216
|
+
let dir = resolvedOptions.dir;
|
|
1217
|
+
let lang = resolvedOptions.lang;
|
|
1218
|
+
let autoplay = resolvedOptions.autoplay ?? true;
|
|
1219
|
+
let loop = resolvedOptions.loop ?? true;
|
|
1220
|
+
let easing = typeof resolvedOptions.easing === "function" ? resolvedOptions.easing : DEFAULT_MORPH_EASING;
|
|
1221
|
+
let onMorphStart = resolvedOptions.onMorphStart;
|
|
1222
|
+
let onMorphEnd = resolvedOptions.onMorphEnd;
|
|
1223
|
+
let currentPreset = options.preset;
|
|
1224
|
+
let currentMotion = options.motion;
|
|
1225
|
+
if (dir) el.setAttribute("dir", dir);
|
|
1226
|
+
if (lang) el.setAttribute("lang", lang);
|
|
1227
|
+
let currentText = words.length > 0 ? words[0] : initialText;
|
|
1228
|
+
let wordIndex = 0;
|
|
1229
|
+
let auto = autoplay;
|
|
1230
|
+
let autoRest = auto && words.length > 1 && canAdvance(wordIndex, words.length, loop) ? restTime : null;
|
|
1231
|
+
let paused = false;
|
|
1232
|
+
let destroyed = false;
|
|
1233
|
+
let hidden = doc.visibilityState === "hidden";
|
|
1234
|
+
let offscreen = false;
|
|
1235
|
+
let transition = null;
|
|
1236
|
+
let pending = null;
|
|
1237
|
+
let rafId = null;
|
|
1238
|
+
let lastTimestamp = null;
|
|
1239
|
+
let filter = goo.enabled ? acquireGooFilter(goo.threshold, doc) : null;
|
|
1240
|
+
sizeHost(el);
|
|
1241
|
+
const stage = createStage(el);
|
|
1242
|
+
function readFontSize() {
|
|
1243
|
+
const view2 = doc.defaultView;
|
|
1244
|
+
const computed = view2?.getComputedStyle(el).fontSize;
|
|
1245
|
+
const parsed = computed ? Number.parseFloat(computed) : Number.NaN;
|
|
1246
|
+
if (Number.isFinite(parsed) && parsed > 0) return parsed;
|
|
1247
|
+
const inline = Number.parseFloat(el.style.fontSize);
|
|
1248
|
+
return Number.isFinite(inline) && inline > 0 ? inline : DEFAULT_FONT_SIZE;
|
|
1249
|
+
}
|
|
1250
|
+
function readDirection() {
|
|
1251
|
+
const attr = dir ?? el.getAttribute("dir") ?? "rtl";
|
|
1252
|
+
const view2 = doc.defaultView;
|
|
1253
|
+
const computed = view2?.getComputedStyle(el).direction;
|
|
1254
|
+
const rtl = computed === "rtl" ? true : computed === "ltr" ? false : attr !== "ltr";
|
|
1255
|
+
return { dir: attr === "ltr" || attr === "rtl" || attr === "auto" ? attr : "rtl", rtl };
|
|
1256
|
+
}
|
|
1257
|
+
function applyEasing(curve, value) {
|
|
1258
|
+
try {
|
|
1259
|
+
const eased = curve(value);
|
|
1260
|
+
return Number.isFinite(eased) ? clamp012(eased) : value;
|
|
1261
|
+
} catch {
|
|
1262
|
+
return value;
|
|
1263
|
+
}
|
|
1264
|
+
}
|
|
1265
|
+
function ease(value) {
|
|
1266
|
+
return applyEasing(easing, value);
|
|
1267
|
+
}
|
|
1268
|
+
function easeRise(value) {
|
|
1269
|
+
return applyEasing(rise.easing, value);
|
|
1270
|
+
}
|
|
1271
|
+
function renderSettled(text) {
|
|
1272
|
+
clearLayer(stage.outgoing);
|
|
1273
|
+
clearLayer(stage.incoming);
|
|
1274
|
+
setLayerText(stage.outgoing, text);
|
|
1275
|
+
setLayerText(stage.incoming, text);
|
|
1276
|
+
stage.outgoing.line.style.opacity = "0";
|
|
1277
|
+
stage.outgoing.line.style.filter = "none";
|
|
1278
|
+
stage.incoming.line.style.opacity = "1";
|
|
1279
|
+
stage.incoming.line.style.filter = "none";
|
|
1280
|
+
stage.incoming.line.style.transform = "none";
|
|
1281
|
+
}
|
|
1282
|
+
function syncFilter() {
|
|
1283
|
+
if (destroyed) return;
|
|
1284
|
+
const reduced = prefersReducedMotion(doc);
|
|
1285
|
+
const activeGoo = transition !== null && (transition.mode === "goo" || transition.mode === "rise");
|
|
1286
|
+
const canUseGoo = goo.enabled && filter !== null && !reduced && (transition === null ? goo.atRest : activeGoo);
|
|
1287
|
+
if (!canUseGoo) {
|
|
1288
|
+
el.style.filter = "none";
|
|
1289
|
+
return;
|
|
1290
|
+
}
|
|
1291
|
+
const soften = rise.enabled ? "" : GOO_SOFTEN;
|
|
1292
|
+
el.style.filter = `url(#${filter.id})${soften}`;
|
|
1293
|
+
}
|
|
1294
|
+
function refreshFilter() {
|
|
1295
|
+
if (destroyed) return;
|
|
1296
|
+
if (!goo.enabled) {
|
|
1297
|
+
filter?.release();
|
|
1298
|
+
filter = null;
|
|
1299
|
+
syncFilter();
|
|
1300
|
+
return;
|
|
1301
|
+
}
|
|
1302
|
+
if (!filter || filter.threshold !== goo.threshold) {
|
|
1303
|
+
const next2 = acquireGooFilter(goo.threshold, doc);
|
|
1304
|
+
filter?.release();
|
|
1305
|
+
filter = next2;
|
|
1306
|
+
}
|
|
1307
|
+
syncFilter();
|
|
1308
|
+
}
|
|
1309
|
+
function safeCallback(callback, from, to) {
|
|
1310
|
+
if (!callback) return;
|
|
1311
|
+
try {
|
|
1312
|
+
callback(from, to);
|
|
1313
|
+
} catch (error) {
|
|
1314
|
+
reportCallbackError(error);
|
|
1315
|
+
}
|
|
1316
|
+
}
|
|
1317
|
+
function canAutoAdvance() {
|
|
1318
|
+
return canAdvance(wordIndex, words.length, loop);
|
|
1319
|
+
}
|
|
1320
|
+
function needsLoop() {
|
|
1321
|
+
if (destroyed || paused || hidden || offscreen) return false;
|
|
1322
|
+
if (transition) return true;
|
|
1323
|
+
return auto && words.length > 1 && canAutoAdvance();
|
|
1324
|
+
}
|
|
1325
|
+
function requestFrame() {
|
|
1326
|
+
if (typeof requestAnimationFrame === "function") {
|
|
1327
|
+
return requestAnimationFrame(tick);
|
|
1328
|
+
}
|
|
1329
|
+
return setTimeout(() => tick(Date.now()), 16);
|
|
1330
|
+
}
|
|
1331
|
+
function cancelFrame(id) {
|
|
1332
|
+
if (typeof cancelAnimationFrame === "function") {
|
|
1333
|
+
cancelAnimationFrame(id);
|
|
1334
|
+
} else {
|
|
1335
|
+
clearTimeout(id);
|
|
1336
|
+
}
|
|
1337
|
+
}
|
|
1338
|
+
function stopLoop() {
|
|
1339
|
+
if (rafId !== null) {
|
|
1340
|
+
cancelFrame(rafId);
|
|
1341
|
+
rafId = null;
|
|
1342
|
+
}
|
|
1343
|
+
lastTimestamp = null;
|
|
1344
|
+
}
|
|
1345
|
+
function syncLoop() {
|
|
1346
|
+
if (!needsLoop()) {
|
|
1347
|
+
stopLoop();
|
|
1348
|
+
return;
|
|
1349
|
+
}
|
|
1350
|
+
if (rafId !== null) return;
|
|
1351
|
+
lastTimestamp = null;
|
|
1352
|
+
rafId = requestFrame();
|
|
1353
|
+
}
|
|
1354
|
+
function renderGooOrFade(tr, eased) {
|
|
1355
|
+
if (tr.mode === "goo" && goo.enabled) {
|
|
1356
|
+
const fontSize = readFontSize();
|
|
1357
|
+
applyGooState(stage.outgoing, tr.outStart * (1 - eased), fontSize, goo.blur);
|
|
1358
|
+
applyGooState(stage.incoming, eased, fontSize, goo.blur);
|
|
1359
|
+
} else {
|
|
1360
|
+
applyFadeState(stage.outgoing, tr.outStart * (1 - eased));
|
|
1361
|
+
applyFadeState(stage.incoming, eased);
|
|
1362
|
+
}
|
|
1363
|
+
}
|
|
1364
|
+
function renderRise(tr) {
|
|
1365
|
+
const scene = tr.scene;
|
|
1366
|
+
const timing = tr.timing;
|
|
1367
|
+
if (!scene || !timing) return;
|
|
1368
|
+
const duration = tr.duration;
|
|
1369
|
+
const elapsed = Math.max(0, Math.min(duration, tr.elapsed));
|
|
1370
|
+
const global = duration > 0 ? clamp012(elapsed / duration) : 1;
|
|
1371
|
+
const globalEased = ease(global);
|
|
1372
|
+
const fontSize = readFontSize();
|
|
1373
|
+
for (let order = 0; order < tr.enter.length; order += 1) {
|
|
1374
|
+
const record = tr.enter[order];
|
|
1375
|
+
const local = easeRise(enterProgress(elapsed, order, timing));
|
|
1376
|
+
const gooOpacity = goo.enabled ? opacityForFraction(local) : local;
|
|
1377
|
+
const gooBlur = goo.enabled ? blurForFraction(local, fontSize, goo.blur) : 0;
|
|
1378
|
+
const motion = enterTransform(
|
|
1379
|
+
local,
|
|
1380
|
+
rise.travel,
|
|
1381
|
+
rise.scale,
|
|
1382
|
+
rise.blur,
|
|
1383
|
+
tr.flowByChunk?.get(record.chunk.id) ?? tr.flow
|
|
1384
|
+
);
|
|
1385
|
+
record.span.style.opacity = String(gooOpacity);
|
|
1386
|
+
record.span.style.filter = combineBlur(gooBlur, motion.blurEm * fontSize);
|
|
1387
|
+
record.span.style.transform = `translate(${(motion.xEm * fontSize).toFixed(2)}px, ${(motion.yEm * fontSize).toFixed(2)}px) scale(${motion.scale.toFixed(4)})`;
|
|
1388
|
+
}
|
|
1389
|
+
for (let order = 0; order < tr.exit.length; order += 1) {
|
|
1390
|
+
const record = tr.exit[order];
|
|
1391
|
+
const local = easeRise(exitProgress(elapsed, order, timing));
|
|
1392
|
+
const remaining = 1 - local;
|
|
1393
|
+
const gooOpacity = goo.enabled ? opacityForFraction(remaining) : remaining;
|
|
1394
|
+
const gooBlur = goo.enabled ? blurForFraction(remaining, fontSize, goo.blur) : 0;
|
|
1395
|
+
const motion = exitTransform(
|
|
1396
|
+
local,
|
|
1397
|
+
rise.travel,
|
|
1398
|
+
rise.scale,
|
|
1399
|
+
rise.blur,
|
|
1400
|
+
tr.flowByChunk?.get(record.chunk.id) ?? tr.flow
|
|
1401
|
+
);
|
|
1402
|
+
record.span.style.opacity = String(gooOpacity);
|
|
1403
|
+
record.span.style.filter = combineBlur(gooBlur, motion.blurEm * fontSize);
|
|
1404
|
+
record.span.style.transform = `translate(${(motion.xEm * fontSize).toFixed(2)}px, ${(motion.yEm * fontSize).toFixed(2)}px) scale(${motion.scale.toFixed(4)})`;
|
|
1405
|
+
}
|
|
1406
|
+
for (const record of scene.matched) {
|
|
1407
|
+
record.span.style.opacity = "1";
|
|
1408
|
+
record.span.style.filter = "none";
|
|
1409
|
+
record.span.style.transform = `translate(${(record.dx * (1 - globalEased)).toFixed(2)}px, ${(record.dy * (1 - globalEased)).toFixed(2)}px)`;
|
|
1410
|
+
}
|
|
1411
|
+
}
|
|
1412
|
+
function renderTransition(tr, raw) {
|
|
1413
|
+
tr.raw = clamp012(raw);
|
|
1414
|
+
const eased = ease(tr.raw);
|
|
1415
|
+
tr.eased = eased;
|
|
1416
|
+
if (tr.mode === "rise") {
|
|
1417
|
+
renderRise(tr);
|
|
1418
|
+
} else {
|
|
1419
|
+
renderGooOrFade(tr, eased);
|
|
1420
|
+
}
|
|
1421
|
+
}
|
|
1422
|
+
function clearPending() {
|
|
1423
|
+
if (!pending) return;
|
|
1424
|
+
const queued = pending;
|
|
1425
|
+
pending = null;
|
|
1426
|
+
queued.resolve();
|
|
1427
|
+
}
|
|
1428
|
+
function completeTransition(tr) {
|
|
1429
|
+
if (transition !== tr) return;
|
|
1430
|
+
transition = null;
|
|
1431
|
+
tr.elapsed = tr.duration;
|
|
1432
|
+
tr.raw = 1;
|
|
1433
|
+
tr.eased = 1;
|
|
1434
|
+
renderTransition(tr, 1);
|
|
1435
|
+
currentText = tr.to;
|
|
1436
|
+
const completedIndex = words.indexOf(tr.to);
|
|
1437
|
+
if (completedIndex >= 0) wordIndex = completedIndex;
|
|
1438
|
+
renderSettled(currentText);
|
|
1439
|
+
setLiveText(stage.live, currentText);
|
|
1440
|
+
tr.resolve();
|
|
1441
|
+
syncFilter();
|
|
1442
|
+
safeCallback(onMorphEnd, tr.from, tr.to);
|
|
1443
|
+
const queued = pending;
|
|
1444
|
+
pending = null;
|
|
1445
|
+
if (queued && !destroyed) {
|
|
1446
|
+
void startMorph(queued.text).then(queued.resolve, queued.resolve);
|
|
1447
|
+
return;
|
|
1448
|
+
}
|
|
1449
|
+
if (!destroyed) {
|
|
1450
|
+
if (auto && !paused) {
|
|
1451
|
+
if (canAutoAdvance()) {
|
|
1452
|
+
autoRest = restTime;
|
|
1453
|
+
} else {
|
|
1454
|
+
auto = false;
|
|
1455
|
+
autoRest = null;
|
|
1456
|
+
}
|
|
1457
|
+
}
|
|
1458
|
+
syncLoop();
|
|
1459
|
+
}
|
|
1460
|
+
}
|
|
1461
|
+
function snapTo(text) {
|
|
1462
|
+
clearPending();
|
|
1463
|
+
if (transition) {
|
|
1464
|
+
const tr = transition;
|
|
1465
|
+
transition = null;
|
|
1466
|
+
tr.resolve();
|
|
1467
|
+
}
|
|
1468
|
+
currentText = text;
|
|
1469
|
+
const settledIndex = words.indexOf(text);
|
|
1470
|
+
if (settledIndex >= 0) wordIndex = settledIndex;
|
|
1471
|
+
renderSettled(text);
|
|
1472
|
+
setLiveText(stage.live, text);
|
|
1473
|
+
autoRest = null;
|
|
1474
|
+
if (auto && !paused && words.length > 1) {
|
|
1475
|
+
if (canAutoAdvance()) autoRest = restTime;
|
|
1476
|
+
else auto = false;
|
|
1477
|
+
}
|
|
1478
|
+
syncFilter();
|
|
1479
|
+
syncLoop();
|
|
1480
|
+
return Promise.resolve();
|
|
1481
|
+
}
|
|
1482
|
+
function queueMorph(to) {
|
|
1483
|
+
if (pending?.text === to) return pending.promise;
|
|
1484
|
+
if (pending) pending.resolve();
|
|
1485
|
+
let resolve = () => void 0;
|
|
1486
|
+
const promise = new Promise((done) => {
|
|
1487
|
+
resolve = done;
|
|
1488
|
+
});
|
|
1489
|
+
pending = { text: to, promise, resolve };
|
|
1490
|
+
if (transition) transition.fastForward = true;
|
|
1491
|
+
syncLoop();
|
|
1492
|
+
return promise;
|
|
1493
|
+
}
|
|
1494
|
+
function startMorph(to) {
|
|
1495
|
+
if (destroyed) return Promise.resolve();
|
|
1496
|
+
if (paused) return snapTo(to);
|
|
1497
|
+
if (transition && transition.to === to) return transition.promise;
|
|
1498
|
+
if (!transition && to === currentText) return Promise.resolve();
|
|
1499
|
+
let fromText;
|
|
1500
|
+
let outStart = 1;
|
|
1501
|
+
const previous = transition;
|
|
1502
|
+
if (previous) {
|
|
1503
|
+
if (previous.mode === "rise") return queueMorph(to);
|
|
1504
|
+
const progress = previous.eased;
|
|
1505
|
+
const outValue = previous.outStart * (1 - progress);
|
|
1506
|
+
const outOpacity = previous.mode === "goo" ? opacityForFraction(outValue) : outValue;
|
|
1507
|
+
const inOpacity = previous.mode === "goo" ? opacityForFraction(progress) : progress;
|
|
1508
|
+
const dominantOpacity = Math.max(outOpacity, inOpacity);
|
|
1509
|
+
if (inOpacity > outOpacity) fromText = previous.to;
|
|
1510
|
+
else fromText = previous.from;
|
|
1511
|
+
transition = null;
|
|
1512
|
+
previous.resolve();
|
|
1513
|
+
const reduced2 = prefersReducedMotion(doc);
|
|
1514
|
+
const nextMode = reduced2 ? "fade" : rise.enabled ? "rise" : goo.enabled ? "goo" : "fade";
|
|
1515
|
+
outStart = nextMode === "goo" ? Math.pow(clamp012(dominantOpacity), 1 / 0.4) : clamp012(dominantOpacity);
|
|
1516
|
+
} else {
|
|
1517
|
+
fromText = currentText;
|
|
1518
|
+
}
|
|
1519
|
+
const reduced = prefersReducedMotion(doc);
|
|
1520
|
+
const mode = reduced ? "fade" : rise.enabled ? "rise" : goo.enabled ? "goo" : "fade";
|
|
1521
|
+
const duration = mode === "fade" ? morphTime <= 0 ? 0 : Math.min(reduced ? REDUCED_MOTION_FADE : NO_EFFECT_FADE, morphTime) : morphTime;
|
|
1522
|
+
let resolve = () => void 0;
|
|
1523
|
+
const promise = new Promise((done) => {
|
|
1524
|
+
resolve = done;
|
|
1525
|
+
});
|
|
1526
|
+
const tr = {
|
|
1527
|
+
from: fromText,
|
|
1528
|
+
to,
|
|
1529
|
+
mode,
|
|
1530
|
+
duration,
|
|
1531
|
+
elapsed: 0,
|
|
1532
|
+
raw: 0,
|
|
1533
|
+
eased: 0,
|
|
1534
|
+
outStart,
|
|
1535
|
+
enter: [],
|
|
1536
|
+
exit: [],
|
|
1537
|
+
flow: "up",
|
|
1538
|
+
fastForward: false,
|
|
1539
|
+
promise,
|
|
1540
|
+
resolve
|
|
1541
|
+
};
|
|
1542
|
+
transition = tr;
|
|
1543
|
+
autoRest = null;
|
|
1544
|
+
if (mode === "rise") {
|
|
1545
|
+
const direction = readDirection();
|
|
1546
|
+
const scene = buildDiffScene(stage, fromText, to, {
|
|
1547
|
+
dir: direction.dir,
|
|
1548
|
+
minChunk: rise.minChunk,
|
|
1549
|
+
normalizeVariants: rise.normalizeVariants,
|
|
1550
|
+
ignoreDiacritics: rise.ignoreDiacritics
|
|
1551
|
+
});
|
|
1552
|
+
tr.scene = scene;
|
|
1553
|
+
tr.enter = enterOrder(scene, direction.rtl);
|
|
1554
|
+
tr.exit = exitOrder(scene, direction.rtl);
|
|
1555
|
+
const flowState = flowForTransition(
|
|
1556
|
+
rise.flow,
|
|
1557
|
+
scene,
|
|
1558
|
+
fromText,
|
|
1559
|
+
to,
|
|
1560
|
+
direction.dir,
|
|
1561
|
+
direction.rtl
|
|
1562
|
+
);
|
|
1563
|
+
tr.flow = flowState.fallback;
|
|
1564
|
+
tr.flowByChunk = flowState.byChunk;
|
|
1565
|
+
tr.timing = riseTiming(
|
|
1566
|
+
duration,
|
|
1567
|
+
Math.max(tr.enter.length, tr.exit.length),
|
|
1568
|
+
rise.stagger,
|
|
1569
|
+
rise.overlap
|
|
1570
|
+
);
|
|
1571
|
+
renderRise(tr);
|
|
1572
|
+
} else {
|
|
1573
|
+
resetLayer(stage.outgoing);
|
|
1574
|
+
resetLayer(stage.incoming);
|
|
1575
|
+
setLayerText(stage.outgoing, fromText);
|
|
1576
|
+
setLayerText(stage.incoming, to);
|
|
1577
|
+
renderGooOrFade(tr, 0);
|
|
1578
|
+
}
|
|
1579
|
+
setLiveText(stage.live, to);
|
|
1580
|
+
syncFilter();
|
|
1581
|
+
safeCallback(onMorphStart, fromText, to);
|
|
1582
|
+
if (destroyed) {
|
|
1583
|
+
if (transition === tr) transition = null;
|
|
1584
|
+
return promise;
|
|
1585
|
+
}
|
|
1586
|
+
if (duration <= 0) {
|
|
1587
|
+
completeTransition(tr);
|
|
1588
|
+
return promise;
|
|
1589
|
+
}
|
|
1590
|
+
syncLoop();
|
|
1591
|
+
return promise;
|
|
1592
|
+
}
|
|
1593
|
+
function tick(timestamp) {
|
|
1594
|
+
rafId = null;
|
|
1595
|
+
if (!needsLoop()) {
|
|
1596
|
+
lastTimestamp = null;
|
|
1597
|
+
return;
|
|
1598
|
+
}
|
|
1599
|
+
if (lastTimestamp === null) {
|
|
1600
|
+
lastTimestamp = timestamp;
|
|
1601
|
+
rafId = requestFrame();
|
|
1602
|
+
return;
|
|
1603
|
+
}
|
|
1604
|
+
const dt = Math.min(Math.max((timestamp - lastTimestamp) / 1e3, 0), MAX_FRAME_DELTA);
|
|
1605
|
+
lastTimestamp = timestamp;
|
|
1606
|
+
if (transition) {
|
|
1607
|
+
const tr = transition;
|
|
1608
|
+
let step = dt;
|
|
1609
|
+
if (tr.fastForward && tr.duration > 0) {
|
|
1610
|
+
const remaining = Math.max(0, tr.duration - tr.elapsed);
|
|
1611
|
+
step *= Math.max(1, remaining / FAST_FORWARD);
|
|
1612
|
+
}
|
|
1613
|
+
tr.elapsed = Math.min(tr.duration, tr.elapsed + step);
|
|
1614
|
+
const raw = tr.duration > 0 ? clamp012(tr.elapsed / tr.duration) : 1;
|
|
1615
|
+
renderTransition(tr, raw);
|
|
1616
|
+
if (raw >= 1) completeTransition(tr);
|
|
1617
|
+
} else if (auto && !paused) {
|
|
1618
|
+
if (autoRest === null) {
|
|
1619
|
+
if (canAutoAdvance()) autoRest = restTime;
|
|
1620
|
+
else auto = false;
|
|
1621
|
+
}
|
|
1622
|
+
if (autoRest !== null) {
|
|
1623
|
+
autoRest -= dt;
|
|
1624
|
+
if (autoRest <= 0) {
|
|
1625
|
+
if (!canAutoAdvance()) {
|
|
1626
|
+
auto = false;
|
|
1627
|
+
autoRest = null;
|
|
1628
|
+
} else {
|
|
1629
|
+
wordIndex = wrapIndex(wordIndex + 1, words.length, loop);
|
|
1630
|
+
const nextWord = words[wordIndex];
|
|
1631
|
+
if (nextWord !== void 0) {
|
|
1632
|
+
autoRest = null;
|
|
1633
|
+
void startMorph(nextWord);
|
|
1634
|
+
}
|
|
1635
|
+
}
|
|
1636
|
+
}
|
|
1637
|
+
}
|
|
1638
|
+
}
|
|
1639
|
+
if (rafId === null && needsLoop()) {
|
|
1640
|
+
rafId = requestFrame();
|
|
1641
|
+
} else if (!needsLoop()) {
|
|
1642
|
+
lastTimestamp = null;
|
|
1643
|
+
}
|
|
1644
|
+
}
|
|
1645
|
+
function onVisibilityChange() {
|
|
1646
|
+
hidden = doc.visibilityState === "hidden";
|
|
1647
|
+
syncLoop();
|
|
1648
|
+
}
|
|
1649
|
+
doc.addEventListener("visibilitychange", onVisibilityChange);
|
|
1650
|
+
let observer = null;
|
|
1651
|
+
const view = doc.defaultView;
|
|
1652
|
+
if (view && typeof view.IntersectionObserver === "function") {
|
|
1653
|
+
observer = new view.IntersectionObserver(
|
|
1654
|
+
(entries) => {
|
|
1655
|
+
const entry = entries[0];
|
|
1656
|
+
if (!entry) return;
|
|
1657
|
+
offscreen = !entry.isIntersecting;
|
|
1658
|
+
syncLoop();
|
|
1659
|
+
},
|
|
1660
|
+
{ rootMargin: "80px" }
|
|
1661
|
+
);
|
|
1662
|
+
observer.observe(el);
|
|
1663
|
+
}
|
|
1664
|
+
function setText(text) {
|
|
1665
|
+
if (destroyed) return Promise.resolve();
|
|
1666
|
+
const value = String(text);
|
|
1667
|
+
if (paused) return snapTo(value);
|
|
1668
|
+
return startMorph(value);
|
|
1669
|
+
}
|
|
1670
|
+
function next() {
|
|
1671
|
+
if (destroyed || words.length === 0) return Promise.resolve();
|
|
1672
|
+
if (!canAdvance(wordIndex, words.length, loop)) {
|
|
1673
|
+
return Promise.resolve();
|
|
1674
|
+
}
|
|
1675
|
+
wordIndex = wrapIndex(wordIndex + 1, words.length, loop);
|
|
1676
|
+
const target = words[wordIndex];
|
|
1677
|
+
if (target === void 0) return Promise.resolve();
|
|
1678
|
+
return startMorph(target);
|
|
1679
|
+
}
|
|
1680
|
+
function setWords(nextWords) {
|
|
1681
|
+
if (destroyed) return;
|
|
1682
|
+
words = Array.isArray(nextWords) ? nextWords.map((word) => String(word)) : [];
|
|
1683
|
+
if (words.length === 0) {
|
|
1684
|
+
wordIndex = 0;
|
|
1685
|
+
autoRest = null;
|
|
1686
|
+
syncLoop();
|
|
1687
|
+
return;
|
|
1688
|
+
}
|
|
1689
|
+
const reference = pending?.text ?? transition?.to ?? currentText;
|
|
1690
|
+
const found = words.indexOf(reference);
|
|
1691
|
+
wordIndex = found >= 0 ? found : 0;
|
|
1692
|
+
autoRest = null;
|
|
1693
|
+
if (autoplay && !paused && words.length > 1 && canAutoAdvance()) {
|
|
1694
|
+
auto = true;
|
|
1695
|
+
autoRest = restTime;
|
|
1696
|
+
}
|
|
1697
|
+
syncLoop();
|
|
1698
|
+
}
|
|
1699
|
+
function play() {
|
|
1700
|
+
if (destroyed) return;
|
|
1701
|
+
paused = false;
|
|
1702
|
+
if (!transition && pending) {
|
|
1703
|
+
const queued = pending;
|
|
1704
|
+
pending = null;
|
|
1705
|
+
void startMorph(queued.text).then(queued.resolve, queued.resolve);
|
|
1706
|
+
}
|
|
1707
|
+
if (words.length > 1 && canAutoAdvance()) {
|
|
1708
|
+
auto = true;
|
|
1709
|
+
if (!transition && autoRest === null) autoRest = restTime;
|
|
1710
|
+
}
|
|
1711
|
+
syncLoop();
|
|
1712
|
+
}
|
|
1713
|
+
function pause() {
|
|
1714
|
+
if (destroyed) return;
|
|
1715
|
+
paused = true;
|
|
1716
|
+
stopLoop();
|
|
1717
|
+
}
|
|
1718
|
+
function applyUpdate(partial) {
|
|
1719
|
+
if (destroyed) return;
|
|
1720
|
+
if (partial.words !== void 0) {
|
|
1721
|
+
const nextWords = Array.isArray(partial.words) ? partial.words.map((word) => String(word)) : [];
|
|
1722
|
+
if (!sameWords(nextWords, words)) setWords(nextWords);
|
|
1723
|
+
}
|
|
1724
|
+
if (partial.morphTime !== void 0) {
|
|
1725
|
+
morphTime = nonNegative2(partial.morphTime, morphTime);
|
|
1726
|
+
}
|
|
1727
|
+
if (partial.restTime !== void 0) {
|
|
1728
|
+
restTime = nonNegative2(partial.restTime, restTime);
|
|
1729
|
+
}
|
|
1730
|
+
let effectsChanged = false;
|
|
1731
|
+
if (partial.effects !== void 0) {
|
|
1732
|
+
if (partial.effects.goo !== void 0) {
|
|
1733
|
+
const nextGoo = applyGooPatch(goo, partial.effects.goo);
|
|
1734
|
+
effectsChanged = effectsChanged || nextGoo.enabled !== goo.enabled || nextGoo.threshold !== goo.threshold || nextGoo.blur !== goo.blur || nextGoo.atRest !== goo.atRest;
|
|
1735
|
+
goo = nextGoo;
|
|
1736
|
+
}
|
|
1737
|
+
if (partial.effects.rise !== void 0) {
|
|
1738
|
+
const nextRise = applyRisePatch(rise, partial.effects.rise);
|
|
1739
|
+
effectsChanged = effectsChanged || nextRise.enabled !== rise.enabled;
|
|
1740
|
+
rise = nextRise;
|
|
1741
|
+
}
|
|
1742
|
+
refreshFilter();
|
|
1743
|
+
}
|
|
1744
|
+
if (partial.dir !== void 0) {
|
|
1745
|
+
dir = partial.dir;
|
|
1746
|
+
el.setAttribute("dir", partial.dir);
|
|
1747
|
+
}
|
|
1748
|
+
if (partial.lang !== void 0) {
|
|
1749
|
+
lang = partial.lang;
|
|
1750
|
+
el.setAttribute("lang", partial.lang);
|
|
1751
|
+
}
|
|
1752
|
+
if (partial.easing !== void 0) {
|
|
1753
|
+
easing = typeof partial.easing === "function" ? partial.easing : DEFAULT_MORPH_EASING;
|
|
1754
|
+
}
|
|
1755
|
+
if (partial.onMorphStart !== void 0) {
|
|
1756
|
+
onMorphStart = partial.onMorphStart;
|
|
1757
|
+
}
|
|
1758
|
+
if (partial.onMorphEnd !== void 0) {
|
|
1759
|
+
onMorphEnd = partial.onMorphEnd;
|
|
1760
|
+
}
|
|
1761
|
+
if (partial.loop !== void 0 && partial.loop !== loop) {
|
|
1762
|
+
loop = partial.loop;
|
|
1763
|
+
if (autoplay && !paused && !transition && words.length > 1 && canAutoAdvance()) {
|
|
1764
|
+
auto = true;
|
|
1765
|
+
autoRest = restTime;
|
|
1766
|
+
} else if (auto && words.length > 1 && canAutoAdvance() && autoRest === null) {
|
|
1767
|
+
autoRest = restTime;
|
|
1768
|
+
}
|
|
1769
|
+
}
|
|
1770
|
+
if (partial.autoplay !== void 0 && partial.autoplay !== autoplay) {
|
|
1771
|
+
autoplay = partial.autoplay;
|
|
1772
|
+
auto = partial.autoplay;
|
|
1773
|
+
autoRest = null;
|
|
1774
|
+
if (auto && !paused && words.length > 1 && canAutoAdvance()) {
|
|
1775
|
+
autoRest = restTime;
|
|
1776
|
+
}
|
|
1777
|
+
}
|
|
1778
|
+
if (effectsChanged && !transition) {
|
|
1779
|
+
renderSettled(currentText);
|
|
1780
|
+
}
|
|
1781
|
+
syncFilter();
|
|
1782
|
+
syncLoop();
|
|
1783
|
+
}
|
|
1784
|
+
function update(partial) {
|
|
1785
|
+
if (destroyed) return;
|
|
1786
|
+
if (partial.preset !== void 0 || partial.motion !== void 0) {
|
|
1787
|
+
const nextPreset = partial.preset ?? currentPreset;
|
|
1788
|
+
const nextMotion = partial.motion ?? (partial.preset !== void 0 ? "default" : currentMotion);
|
|
1789
|
+
const resolved = resolveMorphOptions({
|
|
1790
|
+
...partial,
|
|
1791
|
+
preset: nextPreset,
|
|
1792
|
+
motion: nextMotion
|
|
1793
|
+
});
|
|
1794
|
+
currentPreset = nextPreset;
|
|
1795
|
+
currentMotion = nextMotion;
|
|
1796
|
+
applyUpdate(resolved);
|
|
1797
|
+
return;
|
|
1798
|
+
}
|
|
1799
|
+
applyUpdate(partial);
|
|
1800
|
+
}
|
|
1801
|
+
function restoreStyle(name, value) {
|
|
1802
|
+
if (value) {
|
|
1803
|
+
el.style[name] = value;
|
|
1804
|
+
} else {
|
|
1805
|
+
el.style.removeProperty(
|
|
1806
|
+
name === "minHeight" ? "min-height" : name === "filter" ? "filter" : name
|
|
1807
|
+
);
|
|
1808
|
+
}
|
|
1809
|
+
}
|
|
1810
|
+
function destroy() {
|
|
1811
|
+
if (destroyed) return;
|
|
1812
|
+
destroyed = true;
|
|
1813
|
+
paused = true;
|
|
1814
|
+
stopLoop();
|
|
1815
|
+
doc.removeEventListener("visibilitychange", onVisibilityChange);
|
|
1816
|
+
observer?.disconnect();
|
|
1817
|
+
observer = null;
|
|
1818
|
+
clearPending();
|
|
1819
|
+
if (transition) {
|
|
1820
|
+
const tr = transition;
|
|
1821
|
+
transition = null;
|
|
1822
|
+
tr.resolve();
|
|
1823
|
+
}
|
|
1824
|
+
filter?.release();
|
|
1825
|
+
filter = null;
|
|
1826
|
+
removeStage(stage);
|
|
1827
|
+
if (originalNodes.length > 0) el.append(...originalNodes);
|
|
1828
|
+
restoreStyle("position", savedStyle.position);
|
|
1829
|
+
restoreStyle("display", savedStyle.display);
|
|
1830
|
+
restoreStyle("minHeight", savedStyle.minHeight);
|
|
1831
|
+
restoreStyle("width", savedStyle.width);
|
|
1832
|
+
restoreStyle("filter", savedStyle.filter);
|
|
1833
|
+
if (originalDir === null) el.removeAttribute("dir");
|
|
1834
|
+
else el.setAttribute("dir", originalDir);
|
|
1835
|
+
if (originalLang === null) el.removeAttribute("lang");
|
|
1836
|
+
else el.setAttribute("lang", originalLang);
|
|
1837
|
+
}
|
|
1838
|
+
renderSettled(currentText);
|
|
1839
|
+
setLiveText(stage.live, currentText);
|
|
1840
|
+
syncFilter();
|
|
1841
|
+
syncLoop();
|
|
1842
|
+
return {
|
|
1843
|
+
setText,
|
|
1844
|
+
next,
|
|
1845
|
+
setWords,
|
|
1846
|
+
play,
|
|
1847
|
+
pause,
|
|
1848
|
+
destroy,
|
|
1849
|
+
update
|
|
1850
|
+
};
|
|
1851
|
+
}
|
|
1852
|
+
|
|
1853
|
+
exports.createMorph = createMorph;
|
|
1854
|
+
exports.cubicBezier = cubicBezier;
|
|
1855
|
+
exports.easeInOutCubic = easeInOutCubic;
|
|
1856
|
+
exports.easeOutCubic = easeOutCubic;
|
|
1857
|
+
exports.firstNumber = firstNumber;
|
|
1858
|
+
exports.flowByHunkPosition = flowByHunkPosition;
|
|
1859
|
+
exports.flowByNumberChange = flowByNumberChange;
|
|
1860
|
+
exports.linear = linear;
|
|
1861
|
+
exports.motionNames = motionNames;
|
|
1862
|
+
exports.motions = motions;
|
|
1863
|
+
exports.presetNames = presetNames;
|
|
1864
|
+
exports.presets = presets;
|
|
1865
|
+
exports.resolvePreset = resolvePreset;
|
|
1866
|
+
//# sourceMappingURL=index.cjs.map
|
|
1867
|
+
//# sourceMappingURL=index.cjs.map
|