pixi-glyphs 4.0.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.md +21 -0
- package/README.md +258 -0
- package/dist/Glyphs.d.ts +64 -0
- package/dist/defaultOptions.d.ts +3 -0
- package/dist/defaultStyle.d.ts +3 -0
- package/dist/errorMessaging.d.ts +4 -0
- package/dist/functionalUtils.d.ts +17 -0
- package/dist/index.d.ts +3 -0
- package/dist/layout.d.ts +25 -0
- package/dist/pixi-glyphs.js +2034 -0
- package/dist/pixi-glyphs.js.map +1 -0
- package/dist/pixi-glyphs.m.js +2013 -0
- package/dist/pixi-glyphs.m.js.map +1 -0
- package/dist/pixi-glyphs.modern.js +1997 -0
- package/dist/pixi-glyphs.modern.js.map +1 -0
- package/dist/pixi-glyphs.umd.js +2039 -0
- package/dist/pixi-glyphs.umd.js.map +1 -0
- package/dist/pixiUtils.d.ts +8 -0
- package/dist/stringUtil.d.ts +3 -0
- package/dist/style.d.ts +13 -0
- package/dist/tags.d.ts +14 -0
- package/dist/types.d.ts +226 -0
- package/package.json +100 -0
|
@@ -0,0 +1,2034 @@
|
|
|
1
|
+
var PIXI = require('pixi.js');
|
|
2
|
+
|
|
3
|
+
function _interopNamespace(e) {
|
|
4
|
+
if (e && e.__esModule) return e;
|
|
5
|
+
var n = Object.create(null);
|
|
6
|
+
if (e) {
|
|
7
|
+
Object.keys(e).forEach(function (k) {
|
|
8
|
+
if (k !== 'default') {
|
|
9
|
+
var d = Object.getOwnPropertyDescriptor(e, k);
|
|
10
|
+
Object.defineProperty(n, k, d.get ? d : {
|
|
11
|
+
enumerable: true,
|
|
12
|
+
get: function () { return e[k]; }
|
|
13
|
+
});
|
|
14
|
+
}
|
|
15
|
+
});
|
|
16
|
+
}
|
|
17
|
+
n["default"] = e;
|
|
18
|
+
return n;
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
var PIXI__namespace = /*#__PURE__*/_interopNamespace(PIXI);
|
|
22
|
+
|
|
23
|
+
const combineRecords = (a, b) => ({
|
|
24
|
+
...a,
|
|
25
|
+
...b
|
|
26
|
+
});
|
|
27
|
+
const first = a => a[0];
|
|
28
|
+
const last = a => a[a.length - 1];
|
|
29
|
+
const isDefined = a => a !== undefined;
|
|
30
|
+
const complement = predicate => input => !predicate(input);
|
|
31
|
+
const pluck = key => objects => objects.map(o => o[key]);
|
|
32
|
+
const assoc = key => value => object => ({
|
|
33
|
+
...object,
|
|
34
|
+
...{
|
|
35
|
+
[key]: value
|
|
36
|
+
}
|
|
37
|
+
});
|
|
38
|
+
const flatReduce = (f, acc) => nested => [nested].flat(255).reduce(f, acc);
|
|
39
|
+
const flatEvery = p => flatReduce((acc, t) => acc && p(t), true);
|
|
40
|
+
|
|
41
|
+
const log = type => (handler, supressConsole = false, target) => (code, message) => {
|
|
42
|
+
if (supressConsole !== true) {
|
|
43
|
+
const method = type === "warning" ? console.warn : console.error;
|
|
44
|
+
method(`[${code}] ${message}`);
|
|
45
|
+
}
|
|
46
|
+
if (handler) {
|
|
47
|
+
handler({
|
|
48
|
+
target,
|
|
49
|
+
code,
|
|
50
|
+
message,
|
|
51
|
+
type
|
|
52
|
+
});
|
|
53
|
+
}
|
|
54
|
+
};
|
|
55
|
+
const logWarning = log("warning");
|
|
56
|
+
|
|
57
|
+
const isSpriteSource = s => typeof s === "string" || s instanceof PIXI__namespace.Texture || s instanceof HTMLCanvasElement || s instanceof HTMLVideoElement;
|
|
58
|
+
const isBaseTexture = s => s instanceof PIXI__namespace.Texture;
|
|
59
|
+
const isImageElement = s => s instanceof HTMLImageElement;
|
|
60
|
+
const isTextureSource = s => isImageElement(s) || isBaseTexture(s);
|
|
61
|
+
const IMG_REFERENCE_PROPERTY = "imgSrc";
|
|
62
|
+
const IMG_DISPLAY_PROPERTY = "imgDisplay";
|
|
63
|
+
const DEFAULT_KEY = "default";
|
|
64
|
+
var MeasurementUnit;
|
|
65
|
+
(function (MeasurementUnit) {
|
|
66
|
+
MeasurementUnit["default"] = "px";
|
|
67
|
+
MeasurementUnit["px"] = "px";
|
|
68
|
+
MeasurementUnit["em"] = "em";
|
|
69
|
+
MeasurementUnit["rem"] = "rem";
|
|
70
|
+
MeasurementUnit["pt"] = "pt";
|
|
71
|
+
MeasurementUnit["pc"] = "pc";
|
|
72
|
+
MeasurementUnit["in"] = "in";
|
|
73
|
+
MeasurementUnit["cm"] = "cm";
|
|
74
|
+
MeasurementUnit["mm"] = "mm";
|
|
75
|
+
MeasurementUnit["percent"] = "%";
|
|
76
|
+
MeasurementUnit["unknown"] = "unknown";
|
|
77
|
+
})(MeasurementUnit || (MeasurementUnit = {}));
|
|
78
|
+
const DEFAULT_MEASUREMENT_UNIT = MeasurementUnit.default;
|
|
79
|
+
const createEmptySegmentToken = () => ({
|
|
80
|
+
content: "",
|
|
81
|
+
bounds: new PIXI__namespace.Rectangle(0, 0, 0, 0),
|
|
82
|
+
fontProperties: {
|
|
83
|
+
ascent: 0,
|
|
84
|
+
descent: 0,
|
|
85
|
+
fontSize: 0
|
|
86
|
+
},
|
|
87
|
+
style: {},
|
|
88
|
+
tags: "",
|
|
89
|
+
textDecorations: []
|
|
90
|
+
});
|
|
91
|
+
const isWhitespace = s => s !== "" && s.split("").every(char => char.search(/\s/) === 0);
|
|
92
|
+
const isNewline = s => isWhitespace(s) && s === "\n";
|
|
93
|
+
const _isSpriteToken = t => t.content instanceof PIXI__namespace.Sprite;
|
|
94
|
+
const isSpriteToken = flatEvery(_isSpriteToken);
|
|
95
|
+
const _isTextToken = t => typeof t.content === "string";
|
|
96
|
+
const isTextToken = flatEvery(_isTextToken);
|
|
97
|
+
const _isWhitespaceToken = t => t !== undefined && _isTextToken(t) && isWhitespace(t.content);
|
|
98
|
+
const isWhitespaceToken = flatEvery(_isWhitespaceToken);
|
|
99
|
+
const _isNewlineToken = t => t !== undefined && _isTextToken(t) && isNewline(t.content);
|
|
100
|
+
const isNewlineToken = t => t === undefined ? false : flatEvery(_isNewlineToken)(t);
|
|
101
|
+
const isNotWhitespaceToken = complement(isWhitespaceToken);
|
|
102
|
+
const isEmptyObject = a => a instanceof Object && Object.keys(a).length === 0;
|
|
103
|
+
const measurementValueToComponents = input => {
|
|
104
|
+
if (input === undefined) {
|
|
105
|
+
throw new Error("value is undefined!");
|
|
106
|
+
}
|
|
107
|
+
if (typeof input === "number") {
|
|
108
|
+
return {
|
|
109
|
+
value: input,
|
|
110
|
+
unit: DEFAULT_MEASUREMENT_UNIT
|
|
111
|
+
};
|
|
112
|
+
}
|
|
113
|
+
input = input.trim();
|
|
114
|
+
const pattern = new RegExp(Object.values(MeasurementUnit).join("|") + "$");
|
|
115
|
+
const i = input.search(pattern);
|
|
116
|
+
if (i !== -1) {
|
|
117
|
+
return {
|
|
118
|
+
value: parseFloat(input.slice(0, i)),
|
|
119
|
+
unit: input.slice(i)
|
|
120
|
+
};
|
|
121
|
+
}
|
|
122
|
+
const isAllDigits = input.search(/^[\d.]+$/) === 0;
|
|
123
|
+
if (isAllDigits) {
|
|
124
|
+
const forcedNumberConversion = parseFloat(input);
|
|
125
|
+
if (isNaN(forcedNumberConversion) === false) {
|
|
126
|
+
return {
|
|
127
|
+
value: parseFloat(input),
|
|
128
|
+
unit: DEFAULT_MEASUREMENT_UNIT
|
|
129
|
+
};
|
|
130
|
+
}
|
|
131
|
+
}
|
|
132
|
+
logWarning()("invalid-units", `${input} is not a valid measurement value. Please use one of the following units: ${Object.keys(MeasurementUnit).join(", ")}`);
|
|
133
|
+
return {
|
|
134
|
+
value: NaN,
|
|
135
|
+
unit: MeasurementUnit.unknown
|
|
136
|
+
};
|
|
137
|
+
};
|
|
138
|
+
|
|
139
|
+
var RGI_Emoji = () => {
|
|
140
|
+
// https://mths.be/emoji
|
|
141
|
+
return /\u{1F3F4}\u{E0067}\u{E0062}(?:\u{E0077}\u{E006C}\u{E0073}|\u{E0073}\u{E0063}\u{E0074}|\u{E0065}\u{E006E}\u{E0067})\u{E007F}|(?:\u{1F9D1}\u{1F3FF}\u200D\u2764\uFE0F\u200D(?:\u{1F48B}\u200D)?\u{1F9D1}|\u{1F469}\u{1F3FF}\u200D\u{1F91D}\u200D[\u{1F468}\u{1F469}])[\u{1F3FB}-\u{1F3FE}]|(?:\u{1F9D1}\u{1F3FE}\u200D\u2764\uFE0F\u200D(?:\u{1F48B}\u200D)?\u{1F9D1}|\u{1F469}\u{1F3FE}\u200D\u{1F91D}\u200D[\u{1F468}\u{1F469}])[\u{1F3FB}-\u{1F3FD}\u{1F3FF}]|(?:\u{1F9D1}\u{1F3FD}\u200D\u2764\uFE0F\u200D(?:\u{1F48B}\u200D)?\u{1F9D1}|\u{1F469}\u{1F3FD}\u200D\u{1F91D}\u200D[\u{1F468}\u{1F469}])[\u{1F3FB}\u{1F3FC}\u{1F3FE}\u{1F3FF}]|(?:\u{1F9D1}\u{1F3FC}\u200D\u2764\uFE0F\u200D(?:\u{1F48B}\u200D)?\u{1F9D1}|\u{1F469}\u{1F3FC}\u200D\u{1F91D}\u200D[\u{1F468}\u{1F469}])[\u{1F3FB}\u{1F3FD}-\u{1F3FF}]|(?:\u{1F9D1}\u{1F3FB}\u200D\u2764\uFE0F\u200D(?:\u{1F48B}\u200D)?\u{1F9D1}|\u{1F469}\u{1F3FB}\u200D\u{1F91D}\u200D[\u{1F468}\u{1F469}])[\u{1F3FC}-\u{1F3FF}]|\u{1F468}(?:\u{1F3FB}(?:\u200D(?:\u2764\uFE0F\u200D(?:\u{1F48B}\u200D\u{1F468}[\u{1F3FB}-\u{1F3FF}]|\u{1F468}[\u{1F3FB}-\u{1F3FF}])|\u{1F91D}\u200D\u{1F468}[\u{1F3FC}-\u{1F3FF}]|[\u2695\u2696\u2708]\uFE0F|[\u{1F33E}\u{1F373}\u{1F37C}\u{1F393}\u{1F3A4}\u{1F3A8}\u{1F3EB}\u{1F3ED}\u{1F4BB}\u{1F4BC}\u{1F527}\u{1F52C}\u{1F680}\u{1F692}\u{1F9AF}-\u{1F9B3}\u{1F9BC}\u{1F9BD}]))?|[\u{1F3FC}-\u{1F3FF}]\u200D\u2764\uFE0F\u200D(?:\u{1F48B}\u200D\u{1F468}[\u{1F3FB}-\u{1F3FF}]|\u{1F468}[\u{1F3FB}-\u{1F3FF}])|\u200D(?:\u2764\uFE0F\u200D(?:\u{1F48B}\u200D)?\u{1F468}|[\u{1F468}\u{1F469}]\u200D(?:\u{1F466}\u200D\u{1F466}|\u{1F467}\u200D[\u{1F466}\u{1F467}])|\u{1F466}\u200D\u{1F466}|\u{1F467}\u200D[\u{1F466}\u{1F467}]|[\u{1F33E}\u{1F373}\u{1F37C}\u{1F393}\u{1F3A4}\u{1F3A8}\u{1F3EB}\u{1F3ED}\u{1F4BB}\u{1F4BC}\u{1F527}\u{1F52C}\u{1F680}\u{1F692}\u{1F9AF}-\u{1F9B3}\u{1F9BC}\u{1F9BD}])|\u{1F3FF}\u200D(?:\u{1F91D}\u200D\u{1F468}[\u{1F3FB}-\u{1F3FE}]|[\u{1F33E}\u{1F373}\u{1F37C}\u{1F393}\u{1F3A4}\u{1F3A8}\u{1F3EB}\u{1F3ED}\u{1F4BB}\u{1F4BC}\u{1F527}\u{1F52C}\u{1F680}\u{1F692}\u{1F9AF}-\u{1F9B3}\u{1F9BC}\u{1F9BD}])|\u{1F3FE}\u200D(?:\u{1F91D}\u200D\u{1F468}[\u{1F3FB}-\u{1F3FD}\u{1F3FF}]|[\u{1F33E}\u{1F373}\u{1F37C}\u{1F393}\u{1F3A4}\u{1F3A8}\u{1F3EB}\u{1F3ED}\u{1F4BB}\u{1F4BC}\u{1F527}\u{1F52C}\u{1F680}\u{1F692}\u{1F9AF}-\u{1F9B3}\u{1F9BC}\u{1F9BD}])|\u{1F3FD}\u200D(?:\u{1F91D}\u200D\u{1F468}[\u{1F3FB}\u{1F3FC}\u{1F3FE}\u{1F3FF}]|[\u{1F33E}\u{1F373}\u{1F37C}\u{1F393}\u{1F3A4}\u{1F3A8}\u{1F3EB}\u{1F3ED}\u{1F4BB}\u{1F4BC}\u{1F527}\u{1F52C}\u{1F680}\u{1F692}\u{1F9AF}-\u{1F9B3}\u{1F9BC}\u{1F9BD}])|\u{1F3FC}\u200D(?:\u{1F91D}\u200D\u{1F468}[\u{1F3FB}\u{1F3FD}-\u{1F3FF}]|[\u{1F33E}\u{1F373}\u{1F37C}\u{1F393}\u{1F3A4}\u{1F3A8}\u{1F3EB}\u{1F3ED}\u{1F4BB}\u{1F4BC}\u{1F527}\u{1F52C}\u{1F680}\u{1F692}\u{1F9AF}-\u{1F9B3}\u{1F9BC}\u{1F9BD}])|(?:\u{1F3FF}\u200D[\u2695\u2696\u2708]|\u{1F3FE}\u200D[\u2695\u2696\u2708]|\u{1F3FD}\u200D[\u2695\u2696\u2708]|\u{1F3FC}\u200D[\u2695\u2696\u2708]|\u200D[\u2695\u2696\u2708])\uFE0F|\u200D(?:[\u{1F468}\u{1F469}]\u200D[\u{1F466}\u{1F467}]|[\u{1F466}\u{1F467}])|\u{1F3FF}|\u{1F3FE}|\u{1F3FD}|\u{1F3FC})?|(?:\u{1F469}(?:\u{1F3FB}\u200D\u2764\uFE0F\u200D(?:\u{1F48B}\u200D[\u{1F468}\u{1F469}]|[\u{1F468}\u{1F469}])|[\u{1F3FC}-\u{1F3FF}]\u200D\u2764\uFE0F\u200D(?:\u{1F48B}\u200D[\u{1F468}\u{1F469}]|[\u{1F468}\u{1F469}]))|\u{1F9D1}[\u{1F3FB}-\u{1F3FF}]\u200D\u{1F91D}\u200D\u{1F9D1})[\u{1F3FB}-\u{1F3FF}]|\u{1F469}\u200D\u{1F469}\u200D(?:\u{1F466}\u200D\u{1F466}|\u{1F467}\u200D[\u{1F466}\u{1F467}])|\u{1F469}(?:\u200D(?:\u2764\uFE0F\u200D(?:\u{1F48B}\u200D[\u{1F468}\u{1F469}]|[\u{1F468}\u{1F469}])|[\u{1F33E}\u{1F373}\u{1F37C}\u{1F393}\u{1F3A4}\u{1F3A8}\u{1F3EB}\u{1F3ED}\u{1F4BB}\u{1F4BC}\u{1F527}\u{1F52C}\u{1F680}\u{1F692}\u{1F9AF}-\u{1F9B3}\u{1F9BC}\u{1F9BD}])|\u{1F3FF}\u200D[\u{1F33E}\u{1F373}\u{1F37C}\u{1F393}\u{1F3A4}\u{1F3A8}\u{1F3EB}\u{1F3ED}\u{1F4BB}\u{1F4BC}\u{1F527}\u{1F52C}\u{1F680}\u{1F692}\u{1F9AF}-\u{1F9B3}\u{1F9BC}\u{1F9BD}]|\u{1F3FE}\u200D[\u{1F33E}\u{1F373}\u{1F37C}\u{1F393}\u{1F3A4}\u{1F3A8}\u{1F3EB}\u{1F3ED}\u{1F4BB}\u{1F4BC}\u{1F527}\u{1F52C}\u{1F680}\u{1F692}\u{1F9AF}-\u{1F9B3}\u{1F9BC}\u{1F9BD}]|\u{1F3FD}\u200D[\u{1F33E}\u{1F373}\u{1F37C}\u{1F393}\u{1F3A4}\u{1F3A8}\u{1F3EB}\u{1F3ED}\u{1F4BB}\u{1F4BC}\u{1F527}\u{1F52C}\u{1F680}\u{1F692}\u{1F9AF}-\u{1F9B3}\u{1F9BC}\u{1F9BD}]|\u{1F3FC}\u200D[\u{1F33E}\u{1F373}\u{1F37C}\u{1F393}\u{1F3A4}\u{1F3A8}\u{1F3EB}\u{1F3ED}\u{1F4BB}\u{1F4BC}\u{1F527}\u{1F52C}\u{1F680}\u{1F692}\u{1F9AF}-\u{1F9B3}\u{1F9BC}\u{1F9BD}]|\u{1F3FB}\u200D[\u{1F33E}\u{1F373}\u{1F37C}\u{1F393}\u{1F3A4}\u{1F3A8}\u{1F3EB}\u{1F3ED}\u{1F4BB}\u{1F4BC}\u{1F527}\u{1F52C}\u{1F680}\u{1F692}\u{1F9AF}-\u{1F9B3}\u{1F9BC}\u{1F9BD}])|\u{1F9D1}(?:\u200D(?:\u{1F91D}\u200D\u{1F9D1}|[\u{1F33E}\u{1F373}\u{1F37C}\u{1F384}\u{1F393}\u{1F3A4}\u{1F3A8}\u{1F3EB}\u{1F3ED}\u{1F4BB}\u{1F4BC}\u{1F527}\u{1F52C}\u{1F680}\u{1F692}\u{1F9AF}-\u{1F9B3}\u{1F9BC}\u{1F9BD}])|\u{1F3FF}\u200D[\u{1F33E}\u{1F373}\u{1F37C}\u{1F384}\u{1F393}\u{1F3A4}\u{1F3A8}\u{1F3EB}\u{1F3ED}\u{1F4BB}\u{1F4BC}\u{1F527}\u{1F52C}\u{1F680}\u{1F692}\u{1F9AF}-\u{1F9B3}\u{1F9BC}\u{1F9BD}]|\u{1F3FE}\u200D[\u{1F33E}\u{1F373}\u{1F37C}\u{1F384}\u{1F393}\u{1F3A4}\u{1F3A8}\u{1F3EB}\u{1F3ED}\u{1F4BB}\u{1F4BC}\u{1F527}\u{1F52C}\u{1F680}\u{1F692}\u{1F9AF}-\u{1F9B3}\u{1F9BC}\u{1F9BD}]|\u{1F3FD}\u200D[\u{1F33E}\u{1F373}\u{1F37C}\u{1F384}\u{1F393}\u{1F3A4}\u{1F3A8}\u{1F3EB}\u{1F3ED}\u{1F4BB}\u{1F4BC}\u{1F527}\u{1F52C}\u{1F680}\u{1F692}\u{1F9AF}-\u{1F9B3}\u{1F9BC}\u{1F9BD}]|\u{1F3FC}\u200D[\u{1F33E}\u{1F373}\u{1F37C}\u{1F384}\u{1F393}\u{1F3A4}\u{1F3A8}\u{1F3EB}\u{1F3ED}\u{1F4BB}\u{1F4BC}\u{1F527}\u{1F52C}\u{1F680}\u{1F692}\u{1F9AF}-\u{1F9B3}\u{1F9BC}\u{1F9BD}]|\u{1F3FB}\u200D[\u{1F33E}\u{1F373}\u{1F37C}\u{1F384}\u{1F393}\u{1F3A4}\u{1F3A8}\u{1F3EB}\u{1F3ED}\u{1F4BB}\u{1F4BC}\u{1F527}\u{1F52C}\u{1F680}\u{1F692}\u{1F9AF}-\u{1F9B3}\u{1F9BC}\u{1F9BD}])|\u{1F469}\u200D\u{1F466}\u200D\u{1F466}|\u{1F469}\u200D\u{1F469}\u200D[\u{1F466}\u{1F467}]|\u{1F469}\u200D\u{1F467}\u200D[\u{1F466}\u{1F467}]|(?:\u{1F441}\uFE0F\u200D\u{1F5E8}|\u{1F9D1}(?:\u{1F3FF}\u200D[\u2695\u2696\u2708]|\u{1F3FE}\u200D[\u2695\u2696\u2708]|\u{1F3FD}\u200D[\u2695\u2696\u2708]|\u{1F3FC}\u200D[\u2695\u2696\u2708]|\u{1F3FB}\u200D[\u2695\u2696\u2708]|\u200D[\u2695\u2696\u2708])|\u{1F469}(?:\u{1F3FF}\u200D[\u2695\u2696\u2708]|\u{1F3FE}\u200D[\u2695\u2696\u2708]|\u{1F3FD}\u200D[\u2695\u2696\u2708]|\u{1F3FC}\u200D[\u2695\u2696\u2708]|\u{1F3FB}\u200D[\u2695\u2696\u2708]|\u200D[\u2695\u2696\u2708])|\u{1F636}\u200D\u{1F32B}|\u{1F3F3}\uFE0F\u200D\u26A7|\u{1F43B}\u200D\u2744|(?:[\u{1F3C3}\u{1F3C4}\u{1F3CA}\u{1F46E}\u{1F470}\u{1F471}\u{1F473}\u{1F477}\u{1F481}\u{1F482}\u{1F486}\u{1F487}\u{1F645}-\u{1F647}\u{1F64B}\u{1F64D}\u{1F64E}\u{1F6A3}\u{1F6B4}-\u{1F6B6}\u{1F926}\u{1F935}\u{1F937}-\u{1F939}\u{1F93D}\u{1F93E}\u{1F9B8}\u{1F9B9}\u{1F9CD}-\u{1F9CF}\u{1F9D4}\u{1F9D6}-\u{1F9DD}][\u{1F3FB}-\u{1F3FF}]|[\u{1F46F}\u{1F93C}\u{1F9DE}\u{1F9DF}])\u200D[\u2640\u2642]|[\u26F9\u{1F3CB}\u{1F3CC}\u{1F575}][\uFE0F\u{1F3FB}-\u{1F3FF}]\u200D[\u2640\u2642]|\u{1F3F4}\u200D\u2620|[\u{1F3C3}\u{1F3C4}\u{1F3CA}\u{1F46E}\u{1F470}\u{1F471}\u{1F473}\u{1F477}\u{1F481}\u{1F482}\u{1F486}\u{1F487}\u{1F645}-\u{1F647}\u{1F64B}\u{1F64D}\u{1F64E}\u{1F6A3}\u{1F6B4}-\u{1F6B6}\u{1F926}\u{1F935}\u{1F937}-\u{1F939}\u{1F93D}\u{1F93E}\u{1F9B8}\u{1F9B9}\u{1F9CD}-\u{1F9CF}\u{1F9D4}\u{1F9D6}-\u{1F9DD}]\u200D[\u2640\u2642]|[\xA9\xAE\u203C\u2049\u2122\u2139\u2194-\u2199\u21A9\u21AA\u2328\u23CF\u23ED-\u23EF\u23F1\u23F2\u23F8-\u23FA\u24C2\u25AA\u25AB\u25B6\u25C0\u25FB\u25FC\u2600-\u2604\u260E\u2611\u2618\u2620\u2622\u2623\u2626\u262A\u262E\u262F\u2638-\u263A\u2640\u2642\u265F\u2660\u2663\u2665\u2666\u2668\u267B\u267E\u2692\u2694-\u2697\u2699\u269B\u269C\u26A0\u26A7\u26B0\u26B1\u26C8\u26CF\u26D1\u26D3\u26E9\u26F0\u26F1\u26F4\u26F7\u26F8\u2702\u2708\u2709\u270F\u2712\u2714\u2716\u271D\u2721\u2733\u2734\u2744\u2747\u2763\u27A1\u2934\u2935\u2B05-\u2B07\u3030\u303D\u3297\u3299\u{1F170}\u{1F171}\u{1F17E}\u{1F17F}\u{1F202}\u{1F237}\u{1F321}\u{1F324}-\u{1F32C}\u{1F336}\u{1F37D}\u{1F396}\u{1F397}\u{1F399}-\u{1F39B}\u{1F39E}\u{1F39F}\u{1F3CD}\u{1F3CE}\u{1F3D4}-\u{1F3DF}\u{1F3F5}\u{1F3F7}\u{1F43F}\u{1F4FD}\u{1F549}\u{1F54A}\u{1F56F}\u{1F570}\u{1F573}\u{1F576}-\u{1F579}\u{1F587}\u{1F58A}-\u{1F58D}\u{1F5A5}\u{1F5A8}\u{1F5B1}\u{1F5B2}\u{1F5BC}\u{1F5C2}-\u{1F5C4}\u{1F5D1}-\u{1F5D3}\u{1F5DC}-\u{1F5DE}\u{1F5E1}\u{1F5E3}\u{1F5E8}\u{1F5EF}\u{1F5F3}\u{1F5FA}\u{1F6CB}\u{1F6CD}-\u{1F6CF}\u{1F6E0}-\u{1F6E5}\u{1F6E9}\u{1F6F0}\u{1F6F3}])\uFE0F|\u{1F3F3}\uFE0F\u200D\u{1F308}|\u{1F469}\u200D\u{1F467}|\u{1F469}\u200D\u{1F466}|\u{1F635}\u200D\u{1F4AB}|\u{1F62E}\u200D\u{1F4A8}|\u{1F415}\u200D\u{1F9BA}|\u{1F9D1}(?:\u{1F3FF}|\u{1F3FE}|\u{1F3FD}|\u{1F3FC}|\u{1F3FB})?|\u{1F469}(?:\u{1F3FF}|\u{1F3FE}|\u{1F3FD}|\u{1F3FC}|\u{1F3FB})?|\u{1F1FD}\u{1F1F0}|\u{1F1F6}\u{1F1E6}|\u{1F1F4}\u{1F1F2}|\u{1F408}\u200D\u2B1B|\u2764\uFE0F\u200D[\u{1F525}\u{1FA79}]|\u{1F441}\uFE0F|\u{1F3F3}\uFE0F|\u{1F1FF}[\u{1F1E6}\u{1F1F2}\u{1F1FC}]|\u{1F1FE}[\u{1F1EA}\u{1F1F9}]|\u{1F1FC}[\u{1F1EB}\u{1F1F8}]|\u{1F1FB}[\u{1F1E6}\u{1F1E8}\u{1F1EA}\u{1F1EC}\u{1F1EE}\u{1F1F3}\u{1F1FA}]|\u{1F1FA}[\u{1F1E6}\u{1F1EC}\u{1F1F2}\u{1F1F3}\u{1F1F8}\u{1F1FE}\u{1F1FF}]|\u{1F1F9}[\u{1F1E6}\u{1F1E8}\u{1F1E9}\u{1F1EB}-\u{1F1ED}\u{1F1EF}-\u{1F1F4}\u{1F1F7}\u{1F1F9}\u{1F1FB}\u{1F1FC}\u{1F1FF}]|\u{1F1F8}[\u{1F1E6}-\u{1F1EA}\u{1F1EC}-\u{1F1F4}\u{1F1F7}-\u{1F1F9}\u{1F1FB}\u{1F1FD}-\u{1F1FF}]|\u{1F1F7}[\u{1F1EA}\u{1F1F4}\u{1F1F8}\u{1F1FA}\u{1F1FC}]|\u{1F1F5}[\u{1F1E6}\u{1F1EA}-\u{1F1ED}\u{1F1F0}-\u{1F1F3}\u{1F1F7}-\u{1F1F9}\u{1F1FC}\u{1F1FE}]|\u{1F1F3}[\u{1F1E6}\u{1F1E8}\u{1F1EA}-\u{1F1EC}\u{1F1EE}\u{1F1F1}\u{1F1F4}\u{1F1F5}\u{1F1F7}\u{1F1FA}\u{1F1FF}]|\u{1F1F2}[\u{1F1E6}\u{1F1E8}-\u{1F1ED}\u{1F1F0}-\u{1F1FF}]|\u{1F1F1}[\u{1F1E6}-\u{1F1E8}\u{1F1EE}\u{1F1F0}\u{1F1F7}-\u{1F1FB}\u{1F1FE}]|\u{1F1F0}[\u{1F1EA}\u{1F1EC}-\u{1F1EE}\u{1F1F2}\u{1F1F3}\u{1F1F5}\u{1F1F7}\u{1F1FC}\u{1F1FE}\u{1F1FF}]|\u{1F1EF}[\u{1F1EA}\u{1F1F2}\u{1F1F4}\u{1F1F5}]|\u{1F1EE}[\u{1F1E8}-\u{1F1EA}\u{1F1F1}-\u{1F1F4}\u{1F1F6}-\u{1F1F9}]|\u{1F1ED}[\u{1F1F0}\u{1F1F2}\u{1F1F3}\u{1F1F7}\u{1F1F9}\u{1F1FA}]|\u{1F1EC}[\u{1F1E6}\u{1F1E7}\u{1F1E9}-\u{1F1EE}\u{1F1F1}-\u{1F1F3}\u{1F1F5}-\u{1F1FA}\u{1F1FC}\u{1F1FE}]|\u{1F1EB}[\u{1F1EE}-\u{1F1F0}\u{1F1F2}\u{1F1F4}\u{1F1F7}]|\u{1F1EA}[\u{1F1E6}\u{1F1E8}\u{1F1EA}\u{1F1EC}\u{1F1ED}\u{1F1F7}-\u{1F1FA}]|\u{1F1E9}[\u{1F1EA}\u{1F1EC}\u{1F1EF}\u{1F1F0}\u{1F1F2}\u{1F1F4}\u{1F1FF}]|\u{1F1E8}[\u{1F1E6}\u{1F1E8}\u{1F1E9}\u{1F1EB}-\u{1F1EE}\u{1F1F0}-\u{1F1F5}\u{1F1F7}\u{1F1FA}-\u{1F1FF}]|\u{1F1E7}[\u{1F1E6}\u{1F1E7}\u{1F1E9}-\u{1F1EF}\u{1F1F1}-\u{1F1F4}\u{1F1F6}-\u{1F1F9}\u{1F1FB}\u{1F1FC}\u{1F1FE}\u{1F1FF}]|\u{1F1E6}[\u{1F1E8}-\u{1F1EC}\u{1F1EE}\u{1F1F1}\u{1F1F2}\u{1F1F4}\u{1F1F6}-\u{1F1FA}\u{1F1FC}\u{1F1FD}\u{1F1FF}]|[#\*0-9]\uFE0F\u20E3|\u2764\uFE0F|[\u{1F3C3}\u{1F3C4}\u{1F3CA}\u{1F46E}\u{1F470}\u{1F471}\u{1F473}\u{1F477}\u{1F481}\u{1F482}\u{1F486}\u{1F487}\u{1F645}-\u{1F647}\u{1F64B}\u{1F64D}\u{1F64E}\u{1F6A3}\u{1F6B4}-\u{1F6B6}\u{1F926}\u{1F935}\u{1F937}-\u{1F939}\u{1F93D}\u{1F93E}\u{1F9B8}\u{1F9B9}\u{1F9CD}-\u{1F9CF}\u{1F9D4}\u{1F9D6}-\u{1F9DD}][\u{1F3FB}-\u{1F3FF}]|[\u26F9\u{1F3CB}\u{1F3CC}\u{1F575}][\uFE0F\u{1F3FB}-\u{1F3FF}]|\u{1F3F4}|[\u270A\u270B\u{1F385}\u{1F3C2}\u{1F3C7}\u{1F442}\u{1F443}\u{1F446}-\u{1F450}\u{1F466}\u{1F467}\u{1F46B}-\u{1F46D}\u{1F472}\u{1F474}-\u{1F476}\u{1F478}\u{1F47C}\u{1F483}\u{1F485}\u{1F48F}\u{1F491}\u{1F4AA}\u{1F57A}\u{1F595}\u{1F596}\u{1F64C}\u{1F64F}\u{1F6C0}\u{1F6CC}\u{1F90C}\u{1F90F}\u{1F918}-\u{1F91C}\u{1F91E}\u{1F91F}\u{1F930}-\u{1F934}\u{1F936}\u{1F977}\u{1F9B5}\u{1F9B6}\u{1F9BB}\u{1F9D2}\u{1F9D3}\u{1F9D5}][\u{1F3FB}-\u{1F3FF}]|[\u261D\u270C\u270D\u{1F574}\u{1F590}][\uFE0F\u{1F3FB}-\u{1F3FF}]|[\u270A\u270B\u{1F385}\u{1F3C2}\u{1F3C7}\u{1F408}\u{1F415}\u{1F43B}\u{1F442}\u{1F443}\u{1F446}-\u{1F450}\u{1F466}\u{1F467}\u{1F46B}-\u{1F46D}\u{1F472}\u{1F474}-\u{1F476}\u{1F478}\u{1F47C}\u{1F483}\u{1F485}\u{1F48F}\u{1F491}\u{1F4AA}\u{1F57A}\u{1F595}\u{1F596}\u{1F62E}\u{1F635}\u{1F636}\u{1F64C}\u{1F64F}\u{1F6C0}\u{1F6CC}\u{1F90C}\u{1F90F}\u{1F918}-\u{1F91C}\u{1F91E}\u{1F91F}\u{1F930}-\u{1F934}\u{1F936}\u{1F977}\u{1F9B5}\u{1F9B6}\u{1F9BB}\u{1F9D2}\u{1F9D3}\u{1F9D5}]|[\u{1F3C3}\u{1F3C4}\u{1F3CA}\u{1F46E}\u{1F470}\u{1F471}\u{1F473}\u{1F477}\u{1F481}\u{1F482}\u{1F486}\u{1F487}\u{1F645}-\u{1F647}\u{1F64B}\u{1F64D}\u{1F64E}\u{1F6A3}\u{1F6B4}-\u{1F6B6}\u{1F926}\u{1F935}\u{1F937}-\u{1F939}\u{1F93D}\u{1F93E}\u{1F9B8}\u{1F9B9}\u{1F9CD}-\u{1F9CF}\u{1F9D4}\u{1F9D6}-\u{1F9DD}]|[\u{1F46F}\u{1F93C}\u{1F9DE}\u{1F9DF}]|[\u231A\u231B\u23E9-\u23EC\u23F0\u23F3\u25FD\u25FE\u2614\u2615\u2648-\u2653\u267F\u2693\u26A1\u26AA\u26AB\u26BD\u26BE\u26C4\u26C5\u26CE\u26D4\u26EA\u26F2\u26F3\u26F5\u26FA\u26FD\u2705\u2728\u274C\u274E\u2753-\u2755\u2757\u2795-\u2797\u27B0\u27BF\u2B1B\u2B1C\u2B50\u2B55\u{1F004}\u{1F0CF}\u{1F18E}\u{1F191}-\u{1F19A}\u{1F201}\u{1F21A}\u{1F22F}\u{1F232}-\u{1F236}\u{1F238}-\u{1F23A}\u{1F250}\u{1F251}\u{1F300}-\u{1F320}\u{1F32D}-\u{1F335}\u{1F337}-\u{1F37C}\u{1F37E}-\u{1F384}\u{1F386}-\u{1F393}\u{1F3A0}-\u{1F3C1}\u{1F3C5}\u{1F3C6}\u{1F3C8}\u{1F3C9}\u{1F3CF}-\u{1F3D3}\u{1F3E0}-\u{1F3F0}\u{1F3F8}-\u{1F407}\u{1F409}-\u{1F414}\u{1F416}-\u{1F43A}\u{1F43C}-\u{1F43E}\u{1F440}\u{1F444}\u{1F445}\u{1F451}-\u{1F465}\u{1F46A}\u{1F479}-\u{1F47B}\u{1F47D}-\u{1F480}\u{1F484}\u{1F488}-\u{1F48E}\u{1F490}\u{1F492}-\u{1F4A9}\u{1F4AB}-\u{1F4FC}\u{1F4FF}-\u{1F53D}\u{1F54B}-\u{1F54E}\u{1F550}-\u{1F567}\u{1F5A4}\u{1F5FB}-\u{1F62D}\u{1F62F}-\u{1F634}\u{1F637}-\u{1F644}\u{1F648}-\u{1F64A}\u{1F680}-\u{1F6A2}\u{1F6A4}-\u{1F6B3}\u{1F6B7}-\u{1F6BF}\u{1F6C1}-\u{1F6C5}\u{1F6D0}-\u{1F6D2}\u{1F6D5}-\u{1F6D7}\u{1F6EB}\u{1F6EC}\u{1F6F4}-\u{1F6FC}\u{1F7E0}-\u{1F7EB}\u{1F90D}\u{1F90E}\u{1F910}-\u{1F917}\u{1F91D}\u{1F920}-\u{1F925}\u{1F927}-\u{1F92F}\u{1F93A}\u{1F93F}-\u{1F945}\u{1F947}-\u{1F976}\u{1F978}\u{1F97A}-\u{1F9B4}\u{1F9B7}\u{1F9BA}\u{1F9BC}-\u{1F9CB}\u{1F9D0}\u{1F9E0}-\u{1F9FF}\u{1FA70}-\u{1FA74}\u{1FA78}-\u{1FA7A}\u{1FA80}-\u{1FA86}\u{1FA90}-\u{1FAA8}\u{1FAB0}-\u{1FAB6}\u{1FAC0}-\u{1FAC2}\u{1FAD0}-\u{1FAD6}]/gu;
|
|
142
|
+
};
|
|
143
|
+
|
|
144
|
+
const defaultLogWarning = logWarning();
|
|
145
|
+
const getTagRegex = (tagNamesToMatch = ["\\w+"]) => {
|
|
146
|
+
const matchingTagNames = tagNamesToMatch.join("|");
|
|
147
|
+
const captureGroup = a => `(${a})`;
|
|
148
|
+
const noCaptureGroup = a => `(?:${a})`;
|
|
149
|
+
const WHITESPACE = `\\s`;
|
|
150
|
+
const S = WHITESPACE + "*";
|
|
151
|
+
const SS = WHITESPACE + "+";
|
|
152
|
+
const TAG_NAMES = captureGroup(matchingTagNames);
|
|
153
|
+
const NOT_CLOSING_TAG = `[^>]`;
|
|
154
|
+
const ATTRIBUTES = captureGroup(noCaptureGroup(`${SS}${NOT_CLOSING_TAG}*`) + "*") + "+";
|
|
155
|
+
const TAG_OPEN = `<` + TAG_NAMES + ATTRIBUTES + S + `>`;
|
|
156
|
+
const TAG_CLOSE = `</${TAG_NAMES}${S}>`;
|
|
157
|
+
const pattern = `${TAG_OPEN}|${TAG_CLOSE}`;
|
|
158
|
+
return new RegExp(pattern, "g");
|
|
159
|
+
};
|
|
160
|
+
const EMOJI_TAG = "__EMOJI__";
|
|
161
|
+
const parseAttributes = (attributesString = "") => {
|
|
162
|
+
if (attributesString === "") {
|
|
163
|
+
return {};
|
|
164
|
+
}
|
|
165
|
+
const attributeMatch = /[a-zA-Z][a-zA-Z0-9]*=('|")[^'"]*('|")/g;
|
|
166
|
+
const attributes = attributesString.trim().match(attributeMatch);
|
|
167
|
+
if (attributes === null) {
|
|
168
|
+
throw new Error('Invalid attributes string: "' + attributesString + '"');
|
|
169
|
+
}
|
|
170
|
+
return [...attributes].reduce((obj, attribute) => {
|
|
171
|
+
const attributePair = [attribute.substring(0, attribute.indexOf("=")), attribute.substring(attribute.indexOf("=") + 1)];
|
|
172
|
+
const name = attributePair[0].trim();
|
|
173
|
+
const valueStr = attributePair[1].substring(1, attributePair[1].length - 1).trim();
|
|
174
|
+
obj[name] = valueStr;
|
|
175
|
+
return obj;
|
|
176
|
+
}, {});
|
|
177
|
+
};
|
|
178
|
+
const createTagMatchData = match => {
|
|
179
|
+
const {
|
|
180
|
+
0: tag,
|
|
181
|
+
1: openTagName,
|
|
182
|
+
2: attributes,
|
|
183
|
+
3: closeTagName,
|
|
184
|
+
index
|
|
185
|
+
} = match;
|
|
186
|
+
const tagName = openTagName ?? closeTagName;
|
|
187
|
+
const isOpening = openTagName !== undefined;
|
|
188
|
+
return {
|
|
189
|
+
tag,
|
|
190
|
+
tagName,
|
|
191
|
+
isOpening,
|
|
192
|
+
attributes: parseAttributes(attributes),
|
|
193
|
+
index
|
|
194
|
+
};
|
|
195
|
+
};
|
|
196
|
+
const extractSegments = (input, tagMatchData) => {
|
|
197
|
+
const segments = [];
|
|
198
|
+
let remaining = input;
|
|
199
|
+
let offset = 0;
|
|
200
|
+
let tagMatch;
|
|
201
|
+
for (tagMatch of tagMatchData) {
|
|
202
|
+
if (remaining !== undefined) {
|
|
203
|
+
const {
|
|
204
|
+
tag,
|
|
205
|
+
index
|
|
206
|
+
} = tagMatch;
|
|
207
|
+
const startOfTag = index - offset;
|
|
208
|
+
const endOfTag = startOfTag + tag.length;
|
|
209
|
+
offset += endOfTag;
|
|
210
|
+
const segment = remaining.substr(0, startOfTag);
|
|
211
|
+
segments.push(segment);
|
|
212
|
+
remaining = remaining.substr(endOfTag);
|
|
213
|
+
}
|
|
214
|
+
}
|
|
215
|
+
segments.push(remaining);
|
|
216
|
+
return segments;
|
|
217
|
+
};
|
|
218
|
+
const selfClosingTagSearch = (() => {
|
|
219
|
+
const group = s => `(${s})`;
|
|
220
|
+
const any = s => s + `*`;
|
|
221
|
+
const not = function () {
|
|
222
|
+
return `[^${[].slice.call(arguments).join("")}]`;
|
|
223
|
+
};
|
|
224
|
+
const WORD_START = `[A-Za-z_]`;
|
|
225
|
+
const WORD = `[A-Za-z0-9_]`;
|
|
226
|
+
const TAG_OPEN = `<`;
|
|
227
|
+
const TAG_SLASH = `/`;
|
|
228
|
+
const TAG_CLOSE = `>`;
|
|
229
|
+
const TAG_SELF_CLOSE = TAG_SLASH + TAG_CLOSE;
|
|
230
|
+
return new RegExp(TAG_OPEN + group(WORD_START + any(WORD)) + group(any(not(TAG_SLASH, TAG_CLOSE))) + TAG_SELF_CLOSE, `g`);
|
|
231
|
+
})();
|
|
232
|
+
const wrapEmoji = input => {
|
|
233
|
+
const emojiRegex = new RegExp(`((<|</)[^>]*)?(${RGI_Emoji().source})+`, "gum");
|
|
234
|
+
return input.replaceAll(emojiRegex, (match, tagStart) => {
|
|
235
|
+
if (tagStart?.length > 0) {
|
|
236
|
+
return match;
|
|
237
|
+
}
|
|
238
|
+
return `<${EMOJI_TAG}>${match}</${EMOJI_TAG}>`;
|
|
239
|
+
});
|
|
240
|
+
};
|
|
241
|
+
const replaceSelfClosingTags = input => input.replace(selfClosingTagSearch, (_, tag, attributes = "") => {
|
|
242
|
+
let output = `<${tag}${attributes}></${tag}>`;
|
|
243
|
+
output = output.replace(/\s+/g, " ");
|
|
244
|
+
output = output.replace(/\s>/g, ">");
|
|
245
|
+
return output;
|
|
246
|
+
});
|
|
247
|
+
const removeTags = input => input.replace(getTagRegex(), "");
|
|
248
|
+
const tagMatchToTagToken = tag => {
|
|
249
|
+
return {
|
|
250
|
+
tag: tag.tagName,
|
|
251
|
+
children: [],
|
|
252
|
+
...(isEmptyObject(tag.attributes) ? {} : {
|
|
253
|
+
attributes: tag.attributes
|
|
254
|
+
})
|
|
255
|
+
};
|
|
256
|
+
};
|
|
257
|
+
const createTokensNew = (segments, tags, logWarningFunction = defaultLogWarning) => {
|
|
258
|
+
const rootTokens = {
|
|
259
|
+
children: []
|
|
260
|
+
};
|
|
261
|
+
if (segments[0] !== "") {
|
|
262
|
+
rootTokens.children.push(segments[0]);
|
|
263
|
+
}
|
|
264
|
+
const tokenStack = [rootTokens];
|
|
265
|
+
for (let i = 0; i < tags.length; i++) {
|
|
266
|
+
const tag = tags[i];
|
|
267
|
+
const segment = segments[i + 1] ?? "";
|
|
268
|
+
if (tag.isOpening) {
|
|
269
|
+
const token = tagMatchToTagToken(tag);
|
|
270
|
+
if (segment !== "") {
|
|
271
|
+
token.children.push(segment);
|
|
272
|
+
}
|
|
273
|
+
last(tokenStack).children.push(token);
|
|
274
|
+
tokenStack.push(token);
|
|
275
|
+
} else {
|
|
276
|
+
const poppedToken = tokenStack.pop();
|
|
277
|
+
if (poppedToken === undefined || poppedToken.tag !== tag.tagName) {
|
|
278
|
+
throw new Error(`Unexpected tag nesting. Found a closing tag "${tag.tagName}" that doesn't match the previously open tag "${poppedToken?.tag}"`);
|
|
279
|
+
}
|
|
280
|
+
if (segment !== "") {
|
|
281
|
+
last(tokenStack).children.push(segment);
|
|
282
|
+
}
|
|
283
|
+
}
|
|
284
|
+
}
|
|
285
|
+
if (tokenStack.length > 1) {
|
|
286
|
+
logWarningFunction("unclosed-tags", `Found ${tokenStack.length - 1} unclosed tags in\n${tokenStack.map(token => token.tag).join("-")}`);
|
|
287
|
+
}
|
|
288
|
+
return rootTokens.children;
|
|
289
|
+
};
|
|
290
|
+
const containsEmoji = input => RGI_Emoji().test(input);
|
|
291
|
+
const parseTagsNew = (input, tagNamesToMatch = [], shouldWrapEmoji = false, logWarningFunction = defaultLogWarning) => {
|
|
292
|
+
if (shouldWrapEmoji && containsEmoji(input)) {
|
|
293
|
+
input = wrapEmoji(input);
|
|
294
|
+
}
|
|
295
|
+
input = replaceSelfClosingTags(input);
|
|
296
|
+
const re = getTagRegex(tagNamesToMatch);
|
|
297
|
+
const tagMatches = [];
|
|
298
|
+
let match;
|
|
299
|
+
while (match = re.exec(input)) {
|
|
300
|
+
const tagMatch = createTagMatchData(match);
|
|
301
|
+
tagMatches.push(tagMatch);
|
|
302
|
+
}
|
|
303
|
+
const segments = extractSegments(input, tagMatches);
|
|
304
|
+
const tokens = createTokensNew(segments, tagMatches, logWarningFunction);
|
|
305
|
+
return {
|
|
306
|
+
children: tokens
|
|
307
|
+
};
|
|
308
|
+
};
|
|
309
|
+
|
|
310
|
+
const capitalize = str => {
|
|
311
|
+
const chars = str.split(" ");
|
|
312
|
+
let converted = ``;
|
|
313
|
+
for (let i = 0; i < chars.length; i++) {
|
|
314
|
+
converted += `${chars[i].charAt(0).toUpperCase()}${chars[i].substr(1)} `;
|
|
315
|
+
}
|
|
316
|
+
return converted.trim();
|
|
317
|
+
};
|
|
318
|
+
const stringIsNumber = s => s.trim().search(/^-?[0-9]*\.?[0-9]+$/) === 0;
|
|
319
|
+
const isOnlyWhitespace = s => s.search(/^\s+$/) === 0;
|
|
320
|
+
|
|
321
|
+
const PX_PER_EM = 16;
|
|
322
|
+
const PX_PER_PERCENT = 16 / 100;
|
|
323
|
+
const PX_PER_PT = 1.3281472327365;
|
|
324
|
+
const fontMetricsCache = new Map();
|
|
325
|
+
const measureFont = font => {
|
|
326
|
+
if (fontMetricsCache.has(font)) {
|
|
327
|
+
return fontMetricsCache.get(font);
|
|
328
|
+
}
|
|
329
|
+
const canvas = document.createElement('canvas');
|
|
330
|
+
const context = canvas.getContext('2d');
|
|
331
|
+
if (!context) throw new Error('Cannot get 2D context');
|
|
332
|
+
context.font = font;
|
|
333
|
+
const measureString = "Mgpjy";
|
|
334
|
+
const metrics = context.measureText(measureString);
|
|
335
|
+
const fontSize = parseInt(font.match(/(\d+)px/)?.['1'] || '16');
|
|
336
|
+
const result = {
|
|
337
|
+
ascent: metrics.actualBoundingBoxAscent || fontSize * 0.88,
|
|
338
|
+
descent: metrics.actualBoundingBoxDescent || fontSize * 0.12,
|
|
339
|
+
fontSize: fontSize
|
|
340
|
+
};
|
|
341
|
+
fontMetricsCache.set(font, result);
|
|
342
|
+
return result;
|
|
343
|
+
};
|
|
344
|
+
const getFontPropertiesOfText = (textField, forceUpdate = false) => {
|
|
345
|
+
const style = textField.style;
|
|
346
|
+
const fontSize = typeof style.fontSize === 'number' ? style.fontSize : 16;
|
|
347
|
+
const fontFamily = style.fontFamily || 'Arial';
|
|
348
|
+
const fontWeight = style.fontWeight || 'normal';
|
|
349
|
+
const fontStyle = style.fontStyle || 'normal';
|
|
350
|
+
const font = `${fontStyle} ${fontWeight} ${fontSize}px ${fontFamily}`;
|
|
351
|
+
return measureFont(font);
|
|
352
|
+
};
|
|
353
|
+
const cloneSprite = sprite => new PIXI__namespace.Sprite(sprite.texture);
|
|
354
|
+
const fontSizeStringToNumber = size => {
|
|
355
|
+
const [valueString, unit] = size.split(/(%|pt|px|r?em)/);
|
|
356
|
+
const value = parseFloat(valueString);
|
|
357
|
+
switch (unit) {
|
|
358
|
+
case "%":
|
|
359
|
+
return value * PX_PER_PERCENT;
|
|
360
|
+
case "em":
|
|
361
|
+
case "rem":
|
|
362
|
+
return value * PX_PER_EM;
|
|
363
|
+
case "pt":
|
|
364
|
+
return value * PX_PER_PT;
|
|
365
|
+
case "px":
|
|
366
|
+
default:
|
|
367
|
+
return value;
|
|
368
|
+
}
|
|
369
|
+
};
|
|
370
|
+
|
|
371
|
+
const DEFAULT_STYLE = {
|
|
372
|
+
valign: "baseline",
|
|
373
|
+
dropShadowColor: 0x000000,
|
|
374
|
+
fill: 0x000000,
|
|
375
|
+
fontSize: 26,
|
|
376
|
+
stroke: 0x000000,
|
|
377
|
+
[IMG_DISPLAY_PROPERTY]: "inline",
|
|
378
|
+
wordWrap: true,
|
|
379
|
+
wordWrapWidth: 500,
|
|
380
|
+
iconScale: 1.0,
|
|
381
|
+
breakLines: true
|
|
382
|
+
};
|
|
383
|
+
Object.freeze(DEFAULT_STYLE);
|
|
384
|
+
|
|
385
|
+
const combineStyles = combineRecords;
|
|
386
|
+
const combineAllStyles = styles => styles.filter(isDefined).reduce(combineStyles, {});
|
|
387
|
+
const convertAttributeValues = attributes => {
|
|
388
|
+
const convertedAttributes = {};
|
|
389
|
+
for (const key in attributes) {
|
|
390
|
+
const value = attributes[key];
|
|
391
|
+
const isValueString = typeof value === "string";
|
|
392
|
+
const isStringNumber = isValueString && stringIsNumber(value);
|
|
393
|
+
if (isStringNumber) {
|
|
394
|
+
convertedAttributes[key] = parseFloat(value);
|
|
395
|
+
} else {
|
|
396
|
+
convertedAttributes[key] = value;
|
|
397
|
+
}
|
|
398
|
+
}
|
|
399
|
+
return convertedAttributes;
|
|
400
|
+
};
|
|
401
|
+
const injectAttributes = (attributes = {}, style = {}) => {
|
|
402
|
+
if (isEmptyObject(style) && isEmptyObject(attributes)) return undefined;
|
|
403
|
+
return combineRecords(style, convertAttributeValues(attributes));
|
|
404
|
+
};
|
|
405
|
+
const getStyleForTag = (tagName, tagStyles, attributes = {}) => {
|
|
406
|
+
const style = injectAttributes(attributes, tagStyles[tagName]) ?? {};
|
|
407
|
+
if (Object.values(style).length === 0) return undefined;
|
|
408
|
+
return style;
|
|
409
|
+
};
|
|
410
|
+
const tagWithAttributesToStyle = ({
|
|
411
|
+
tagName,
|
|
412
|
+
attributes
|
|
413
|
+
}, tagStyles) => getStyleForTag(tagName, tagStyles, attributes);
|
|
414
|
+
const getStyleForTags = (tags, tagStyles, styleCache) => {
|
|
415
|
+
const tagHash = JSON.stringify(tags);
|
|
416
|
+
if (styleCache[tagHash] === undefined) {
|
|
417
|
+
const defaultStyle = tagStyles.default;
|
|
418
|
+
const styles = tags.map(tag => tagWithAttributesToStyle(tag, tagStyles));
|
|
419
|
+
const stylesWithDefault = [defaultStyle, ...styles];
|
|
420
|
+
styleCache[tagHash] = combineAllStyles(stylesWithDefault);
|
|
421
|
+
}
|
|
422
|
+
return styleCache[tagHash];
|
|
423
|
+
};
|
|
424
|
+
const interpretFontSize = (baseFontSize, fontSize) => {
|
|
425
|
+
const {
|
|
426
|
+
value: baseValue,
|
|
427
|
+
unit: baseUnit
|
|
428
|
+
} = measurementValueToComponents(baseFontSize);
|
|
429
|
+
const {
|
|
430
|
+
value,
|
|
431
|
+
unit
|
|
432
|
+
} = measurementValueToComponents(fontSize);
|
|
433
|
+
if (unit === MeasurementUnit.percent) {
|
|
434
|
+
const percentage = value / 100;
|
|
435
|
+
return baseValue * percentage + baseUnit;
|
|
436
|
+
}
|
|
437
|
+
return fontSize;
|
|
438
|
+
};
|
|
439
|
+
const mapTagsToStyles = (tokens, styles, spriteTemplates) => {
|
|
440
|
+
const defaultStyle = convertDecorationToLineProps(styles.default ?? {});
|
|
441
|
+
const tagStack = [];
|
|
442
|
+
const fontSizeStack = [];
|
|
443
|
+
const styleCache = {};
|
|
444
|
+
const convertTagTokenToStyledToken = token => {
|
|
445
|
+
if (typeof token === "string") {
|
|
446
|
+
return token;
|
|
447
|
+
}
|
|
448
|
+
const {
|
|
449
|
+
tag,
|
|
450
|
+
attributes = {}
|
|
451
|
+
} = token;
|
|
452
|
+
let style = defaultStyle;
|
|
453
|
+
let tags = "";
|
|
454
|
+
const currentBaseFontSize = fontSizeStack[fontSizeStack.length - 1] ?? DEFAULT_STYLE.fontSize;
|
|
455
|
+
if (tag) {
|
|
456
|
+
tagStack.push({
|
|
457
|
+
tagName: tag,
|
|
458
|
+
attributes
|
|
459
|
+
});
|
|
460
|
+
tags = pluck("tagName")(tagStack).join(",");
|
|
461
|
+
style = getStyleForTags(tagStack, styles, styleCache);
|
|
462
|
+
style = convertDecorationToLineProps(style);
|
|
463
|
+
}
|
|
464
|
+
if (style.fontSize !== undefined) {
|
|
465
|
+
style.fontSize = interpretFontSize(currentBaseFontSize, style.fontSize);
|
|
466
|
+
} else {
|
|
467
|
+
style.fontSize = currentBaseFontSize;
|
|
468
|
+
}
|
|
469
|
+
const currentTagStyle = tag ? styles[tag] : {};
|
|
470
|
+
const currentTagColor = currentTagStyle.color;
|
|
471
|
+
const currentTagFill = currentTagStyle.fill;
|
|
472
|
+
if (currentTagColor !== undefined && currentTagFill === undefined) {
|
|
473
|
+
style.fill = style.color;
|
|
474
|
+
}
|
|
475
|
+
style.color = style.fill;
|
|
476
|
+
fontSizeStack.push(style.fontSize);
|
|
477
|
+
const styledToken = {
|
|
478
|
+
style,
|
|
479
|
+
tags,
|
|
480
|
+
children: token.children.map(convertTagTokenToStyledToken)
|
|
481
|
+
};
|
|
482
|
+
const imgKey = style[IMG_REFERENCE_PROPERTY] ?? "";
|
|
483
|
+
if (imgKey) {
|
|
484
|
+
if (spriteTemplates === undefined) {
|
|
485
|
+
throw new Error(`An image tag with ${IMG_REFERENCE_PROPERTY}="${imgKey}" was encountered, but no imgMap was provided. Please include a valid Sprite in the imgMap property in the options in your TaggedText constructor.`);
|
|
486
|
+
}
|
|
487
|
+
const sprite = spriteTemplates[imgKey];
|
|
488
|
+
if (sprite === undefined) {
|
|
489
|
+
throw new Error(`An image tag with ${IMG_REFERENCE_PROPERTY}="${imgKey}" was encountered, but there was no matching sprite in the sprite map. Please include a valid Sprite in the imgMap property in the options in your TaggedText constructor.`);
|
|
490
|
+
}
|
|
491
|
+
if (sprite instanceof PIXI__namespace.Sprite === false) {
|
|
492
|
+
throw new Error(`The image reference you provided for "${imgKey}" is not a Sprite. The imgMap can only accept PIXI.Sprite instances.`);
|
|
493
|
+
}
|
|
494
|
+
const cloneOfSprite = cloneSprite(sprite);
|
|
495
|
+
styledToken.children = [cloneOfSprite, ...styledToken.children];
|
|
496
|
+
}
|
|
497
|
+
tagStack.pop();
|
|
498
|
+
fontSizeStack.pop();
|
|
499
|
+
return styledToken;
|
|
500
|
+
};
|
|
501
|
+
return convertTagTokenToStyledToken(tokens);
|
|
502
|
+
};
|
|
503
|
+
const convertDecorationToLineProps = style => {
|
|
504
|
+
const {
|
|
505
|
+
textDecoration
|
|
506
|
+
} = style;
|
|
507
|
+
if (textDecoration === undefined || textDecoration === "normal" || textDecoration === "none") {
|
|
508
|
+
return style;
|
|
509
|
+
}
|
|
510
|
+
const {
|
|
511
|
+
decorationColor,
|
|
512
|
+
decorationThickness
|
|
513
|
+
} = style;
|
|
514
|
+
const defaultColor = decorationColor || style.fill || DEFAULT_STYLE.fill;
|
|
515
|
+
const defaultThickness = decorationThickness || 1;
|
|
516
|
+
const defaultOffset = 0;
|
|
517
|
+
function mergeDecoration(decorationLineType, decorationLineTypeCamelCase = decorationLineType) {
|
|
518
|
+
if (style.textDecoration?.includes(decorationLineType)) {
|
|
519
|
+
return {
|
|
520
|
+
[`${decorationLineTypeCamelCase}Color`]: style[`${decorationLineTypeCamelCase}Color`] ?? defaultColor,
|
|
521
|
+
[`${decorationLineTypeCamelCase}Thickness`]: style[`${decorationLineTypeCamelCase}Thickness`] ?? defaultThickness,
|
|
522
|
+
[`${decorationLineTypeCamelCase}Offset`]: style[`${decorationLineTypeCamelCase}Offset`] ?? defaultOffset
|
|
523
|
+
};
|
|
524
|
+
}
|
|
525
|
+
return {};
|
|
526
|
+
}
|
|
527
|
+
return {
|
|
528
|
+
...style,
|
|
529
|
+
...mergeDecoration("underline"),
|
|
530
|
+
...mergeDecoration("overline"),
|
|
531
|
+
...mergeDecoration("line-through", "lineThrough")
|
|
532
|
+
};
|
|
533
|
+
};
|
|
534
|
+
const extractDecorations = (style, textBounds, fontProperties) => {
|
|
535
|
+
const {
|
|
536
|
+
ascent,
|
|
537
|
+
descent
|
|
538
|
+
} = fontProperties;
|
|
539
|
+
const baseline = ascent;
|
|
540
|
+
const {
|
|
541
|
+
width
|
|
542
|
+
} = textBounds;
|
|
543
|
+
const x = 0;
|
|
544
|
+
function styleToMetrics(key) {
|
|
545
|
+
const color = style[`${key}Color`];
|
|
546
|
+
const height = style[`${key}Thickness`];
|
|
547
|
+
const offset = style[`${key}Offset`] ?? 0;
|
|
548
|
+
if (color === undefined || height === undefined) {
|
|
549
|
+
return undefined;
|
|
550
|
+
}
|
|
551
|
+
let y = offset;
|
|
552
|
+
if (key === "underline") {
|
|
553
|
+
y += baseline + descent / 2 + 4;
|
|
554
|
+
} else if (key === "lineThrough") {
|
|
555
|
+
const overlineY = 1;
|
|
556
|
+
const underlineY = baseline + descent / 2 + 4;
|
|
557
|
+
y += (overlineY + underlineY) / 2;
|
|
558
|
+
} else if (key === "overline") {
|
|
559
|
+
y += 1;
|
|
560
|
+
}
|
|
561
|
+
return {
|
|
562
|
+
color,
|
|
563
|
+
bounds: {
|
|
564
|
+
x,
|
|
565
|
+
y,
|
|
566
|
+
width,
|
|
567
|
+
height
|
|
568
|
+
}
|
|
569
|
+
};
|
|
570
|
+
}
|
|
571
|
+
const keySuffices = ["underline", "overline", "lineThrough"];
|
|
572
|
+
const metrics = keySuffices.map(styleToMetrics).filter(x => x !== undefined);
|
|
573
|
+
return metrics;
|
|
574
|
+
};
|
|
575
|
+
const convertUnsupportedAlignment = align => {
|
|
576
|
+
if (align === undefined) {
|
|
577
|
+
return undefined;
|
|
578
|
+
}
|
|
579
|
+
switch (align) {
|
|
580
|
+
case "justify":
|
|
581
|
+
case "justify-left":
|
|
582
|
+
case "justify-all":
|
|
583
|
+
return "left";
|
|
584
|
+
case "justify-center":
|
|
585
|
+
return "center";
|
|
586
|
+
case "justify-right":
|
|
587
|
+
return "right";
|
|
588
|
+
default:
|
|
589
|
+
return align;
|
|
590
|
+
}
|
|
591
|
+
};
|
|
592
|
+
|
|
593
|
+
const ICON_SCALE_BASE = 0.8;
|
|
594
|
+
new PIXI__namespace.Text({
|
|
595
|
+
text: ""
|
|
596
|
+
});
|
|
597
|
+
const rectFromContainer = (container, offset = {
|
|
598
|
+
x: 0,
|
|
599
|
+
y: 0
|
|
600
|
+
}) => {
|
|
601
|
+
const w = container.width;
|
|
602
|
+
const h = container.height;
|
|
603
|
+
const x = offset.x + container.x;
|
|
604
|
+
const y = offset.y + container.y;
|
|
605
|
+
return new PIXI__namespace.Rectangle(x, y, w, h);
|
|
606
|
+
};
|
|
607
|
+
const translatePoint = offset => point => {
|
|
608
|
+
const newX = point.x + offset.x;
|
|
609
|
+
return {
|
|
610
|
+
...point,
|
|
611
|
+
x: newX,
|
|
612
|
+
y: point.y + offset.y
|
|
613
|
+
};
|
|
614
|
+
};
|
|
615
|
+
const translateLine = offset => line => line.map(translatePoint(offset));
|
|
616
|
+
const lineWidth = wordsInLine => {
|
|
617
|
+
const firstWord = first(wordsInLine);
|
|
618
|
+
const lastWord = last(wordsInLine);
|
|
619
|
+
if (firstWord === undefined) {
|
|
620
|
+
return 0;
|
|
621
|
+
}
|
|
622
|
+
if (lastWord === firstWord) {
|
|
623
|
+
return firstWord.width;
|
|
624
|
+
}
|
|
625
|
+
const width = lastWord.x + lastWord.width - firstWord.x;
|
|
626
|
+
if (width < -1000 || width > 100000 || isNaN(width)) {
|
|
627
|
+
console.warn('Extreme lineWidth detected in animation demo:', {
|
|
628
|
+
width,
|
|
629
|
+
firstWord: {
|
|
630
|
+
x: firstWord.x,
|
|
631
|
+
width: firstWord.width
|
|
632
|
+
},
|
|
633
|
+
lastWord: {
|
|
634
|
+
x: lastWord.x,
|
|
635
|
+
width: lastWord.width
|
|
636
|
+
},
|
|
637
|
+
numWords: wordsInLine.length,
|
|
638
|
+
calculation: `${lastWord.x} + ${lastWord.width} - ${firstWord.x} = ${width}`
|
|
639
|
+
});
|
|
640
|
+
}
|
|
641
|
+
return width;
|
|
642
|
+
};
|
|
643
|
+
const center = (x, context) => {
|
|
644
|
+
if (!isFinite(context)) {
|
|
645
|
+
return 0;
|
|
646
|
+
}
|
|
647
|
+
return (context - x) / 2;
|
|
648
|
+
};
|
|
649
|
+
const setBoundsX = assoc("x");
|
|
650
|
+
const positionWordX = x => word => {
|
|
651
|
+
let prevBounds;
|
|
652
|
+
const positionRecursive = item => {
|
|
653
|
+
if (Array.isArray(item)) {
|
|
654
|
+
return item.map(positionRecursive);
|
|
655
|
+
}
|
|
656
|
+
if (item && item.bounds) {
|
|
657
|
+
if (prevBounds === undefined) {
|
|
658
|
+
item.bounds.x = x;
|
|
659
|
+
prevBounds = item.bounds;
|
|
660
|
+
} else {
|
|
661
|
+
item.bounds.x = prevBounds.x + prevBounds.width;
|
|
662
|
+
prevBounds = item.bounds;
|
|
663
|
+
}
|
|
664
|
+
}
|
|
665
|
+
return item;
|
|
666
|
+
};
|
|
667
|
+
return positionRecursive(word);
|
|
668
|
+
};
|
|
669
|
+
const concatBounds = (originalBounds = {
|
|
670
|
+
x: NaN,
|
|
671
|
+
y: NaN,
|
|
672
|
+
width: NaN,
|
|
673
|
+
height: NaN
|
|
674
|
+
}, bounds = {
|
|
675
|
+
x: NaN,
|
|
676
|
+
y: NaN,
|
|
677
|
+
width: NaN,
|
|
678
|
+
height: NaN
|
|
679
|
+
}) => {
|
|
680
|
+
if (isNaN(originalBounds.x)) {
|
|
681
|
+
return bounds;
|
|
682
|
+
}
|
|
683
|
+
const x = Math.min(originalBounds.x, bounds.x);
|
|
684
|
+
const y = Math.min(originalBounds.y, bounds.y);
|
|
685
|
+
const right = Math.max(originalBounds.x + originalBounds.width, bounds.x + bounds.width);
|
|
686
|
+
const bottom = Math.max(originalBounds.y + originalBounds.height, bounds.y + bounds.height);
|
|
687
|
+
const width = right - x;
|
|
688
|
+
const height = bottom - y;
|
|
689
|
+
return {
|
|
690
|
+
x,
|
|
691
|
+
y,
|
|
692
|
+
width,
|
|
693
|
+
height
|
|
694
|
+
};
|
|
695
|
+
};
|
|
696
|
+
const getCombinedBounds = bounds => bounds.reduce(concatBounds, {
|
|
697
|
+
x: NaN,
|
|
698
|
+
y: NaN,
|
|
699
|
+
width: NaN,
|
|
700
|
+
height: NaN
|
|
701
|
+
});
|
|
702
|
+
const getBoundsNested = flatReduce((acc, t) => {
|
|
703
|
+
if (isNaN(acc.x)) {
|
|
704
|
+
return {
|
|
705
|
+
...t.bounds
|
|
706
|
+
};
|
|
707
|
+
}
|
|
708
|
+
return concatBounds(acc, t.bounds);
|
|
709
|
+
}, {
|
|
710
|
+
x: NaN,
|
|
711
|
+
y: NaN,
|
|
712
|
+
width: NaN,
|
|
713
|
+
height: NaN
|
|
714
|
+
});
|
|
715
|
+
const alignLeft = line => {
|
|
716
|
+
return line.reduce((newLine, bounds, i) => {
|
|
717
|
+
if (i === 0) {
|
|
718
|
+
return [setBoundsX(0)(bounds)];
|
|
719
|
+
} else {
|
|
720
|
+
const prevBounds = newLine[i - 1];
|
|
721
|
+
const newX = prevBounds.x + prevBounds.width;
|
|
722
|
+
if (Math.abs(newX) > 10000 || prevBounds.width > 10000) {
|
|
723
|
+
console.error('EXTREME SPACING IN alignLeft:', {
|
|
724
|
+
wordIndex: i,
|
|
725
|
+
prevX: prevBounds.x,
|
|
726
|
+
prevWidth: prevBounds.width,
|
|
727
|
+
calculatedX: newX,
|
|
728
|
+
currentBounds: bounds
|
|
729
|
+
});
|
|
730
|
+
}
|
|
731
|
+
return newLine.concat([setBoundsX(newX)(bounds)]);
|
|
732
|
+
}
|
|
733
|
+
}, []);
|
|
734
|
+
};
|
|
735
|
+
const alignRight = maxWidth => line => {
|
|
736
|
+
const leftAligned = alignLeft(line);
|
|
737
|
+
return translateLine({
|
|
738
|
+
x: maxWidth - lineWidth(leftAligned),
|
|
739
|
+
y: 0
|
|
740
|
+
})(leftAligned);
|
|
741
|
+
};
|
|
742
|
+
const alignCenter = maxWidth => line => {
|
|
743
|
+
const leftAligned = alignLeft(line);
|
|
744
|
+
const width = lineWidth(leftAligned);
|
|
745
|
+
const centerOffset = center(width, maxWidth);
|
|
746
|
+
return translateLine({
|
|
747
|
+
x: centerOffset,
|
|
748
|
+
y: 0
|
|
749
|
+
})(leftAligned);
|
|
750
|
+
};
|
|
751
|
+
const alignJustify = maxLineWidth => line => {
|
|
752
|
+
const count = line.length;
|
|
753
|
+
if (count === 0) {
|
|
754
|
+
return [];
|
|
755
|
+
}
|
|
756
|
+
const nonZeroWidthWords = line.filter(({
|
|
757
|
+
width
|
|
758
|
+
}) => width > 0);
|
|
759
|
+
const countNonZeroWidthWords = nonZeroWidthWords.length;
|
|
760
|
+
if (countNonZeroWidthWords === 1) {
|
|
761
|
+
const [first, ...rest] = line;
|
|
762
|
+
first.x = 0;
|
|
763
|
+
return [first, ...rest];
|
|
764
|
+
}
|
|
765
|
+
const result = [];
|
|
766
|
+
const combinedBounds = getCombinedBounds(nonZeroWidthWords);
|
|
767
|
+
const w = combinedBounds.width;
|
|
768
|
+
const totalSpace = maxLineWidth - w;
|
|
769
|
+
const spacerWidth = totalSpace > 0 ? totalSpace / (countNonZeroWidthWords - 1) : 0;
|
|
770
|
+
let previousWord;
|
|
771
|
+
for (let i = 0; i < line.length; i++) {
|
|
772
|
+
const bounds = line[i];
|
|
773
|
+
if (bounds.width === 0) {
|
|
774
|
+
result[i] = {
|
|
775
|
+
...bounds
|
|
776
|
+
};
|
|
777
|
+
continue;
|
|
778
|
+
}
|
|
779
|
+
let x;
|
|
780
|
+
if (previousWord === undefined) {
|
|
781
|
+
x = 0;
|
|
782
|
+
} else {
|
|
783
|
+
x = previousWord.x + previousWord.width + spacerWidth;
|
|
784
|
+
}
|
|
785
|
+
if (isNaN(x)) {
|
|
786
|
+
throw new Error(`Something went wrong with the justified layout calculation. x is NaN.`);
|
|
787
|
+
}
|
|
788
|
+
const newWord = setBoundsX(x)(bounds);
|
|
789
|
+
previousWord = newWord;
|
|
790
|
+
result[i] = newWord;
|
|
791
|
+
}
|
|
792
|
+
return result;
|
|
793
|
+
};
|
|
794
|
+
const alignLines = (align, maxWidth, lines) => {
|
|
795
|
+
let alignFunction;
|
|
796
|
+
let lastAlignFunction;
|
|
797
|
+
switch (align) {
|
|
798
|
+
case "left":
|
|
799
|
+
alignFunction = alignLeft;
|
|
800
|
+
lastAlignFunction = alignFunction;
|
|
801
|
+
break;
|
|
802
|
+
case "right":
|
|
803
|
+
alignFunction = alignRight(maxWidth);
|
|
804
|
+
lastAlignFunction = alignFunction;
|
|
805
|
+
break;
|
|
806
|
+
case "center":
|
|
807
|
+
alignFunction = alignCenter(maxWidth);
|
|
808
|
+
lastAlignFunction = alignFunction;
|
|
809
|
+
break;
|
|
810
|
+
case "justify":
|
|
811
|
+
case "justify-left":
|
|
812
|
+
alignFunction = alignJustify(maxWidth);
|
|
813
|
+
lastAlignFunction = alignLeft;
|
|
814
|
+
break;
|
|
815
|
+
case "justify-right":
|
|
816
|
+
alignFunction = alignJustify(maxWidth);
|
|
817
|
+
lastAlignFunction = alignRight(maxWidth);
|
|
818
|
+
break;
|
|
819
|
+
case "justify-center":
|
|
820
|
+
alignFunction = alignJustify(maxWidth);
|
|
821
|
+
lastAlignFunction = alignCenter(maxWidth);
|
|
822
|
+
break;
|
|
823
|
+
case "justify-all":
|
|
824
|
+
alignFunction = alignJustify(maxWidth);
|
|
825
|
+
lastAlignFunction = alignFunction;
|
|
826
|
+
break;
|
|
827
|
+
default:
|
|
828
|
+
throw new Error(`Unsupported alignment type ${align}! Use one of : "left", "right", "center", "justify", "justify-left", "justify-right", justify-center", "justify-all"`);
|
|
829
|
+
}
|
|
830
|
+
for (const line of lines) {
|
|
831
|
+
const isLastLine = lines.indexOf(line) === lines.length - 1 || line.flat(2).filter(isNewlineToken).length > 0;
|
|
832
|
+
const wordBoundsForLine = [];
|
|
833
|
+
let alignedLine;
|
|
834
|
+
let isFirstWord = true;
|
|
835
|
+
let expectedX = 0;
|
|
836
|
+
for (const word of line) {
|
|
837
|
+
const wordBounds = getBoundsNested(word);
|
|
838
|
+
if (isFirstWord) {
|
|
839
|
+
expectedX = wordBounds.x;
|
|
840
|
+
wordBounds.x = 0;
|
|
841
|
+
isFirstWord = false;
|
|
842
|
+
} else {
|
|
843
|
+
wordBounds.x = wordBounds.x - expectedX;
|
|
844
|
+
}
|
|
845
|
+
wordBoundsForLine.push(wordBounds);
|
|
846
|
+
}
|
|
847
|
+
if (isLastLine) {
|
|
848
|
+
alignedLine = lastAlignFunction(wordBoundsForLine);
|
|
849
|
+
} else {
|
|
850
|
+
alignedLine = alignFunction(wordBoundsForLine);
|
|
851
|
+
}
|
|
852
|
+
for (let i = 0; i < line.length; i++) {
|
|
853
|
+
const word = line[i];
|
|
854
|
+
if (i < alignedLine.length) {
|
|
855
|
+
const bounds = alignedLine[i];
|
|
856
|
+
const updatedWord = positionWordX(bounds.x)(word);
|
|
857
|
+
line[i] = updatedWord;
|
|
858
|
+
}
|
|
859
|
+
}
|
|
860
|
+
}
|
|
861
|
+
return lines;
|
|
862
|
+
};
|
|
863
|
+
const getTallestToken = line => flatReduce((tallest, current) => {
|
|
864
|
+
let h = current.bounds.height ?? 0;
|
|
865
|
+
if (isSpriteToken(current)) {
|
|
866
|
+
h += current.fontProperties.descent;
|
|
867
|
+
}
|
|
868
|
+
const tallestH = tallest?.bounds.height ?? 0;
|
|
869
|
+
if (h > tallestH) {
|
|
870
|
+
return current;
|
|
871
|
+
}
|
|
872
|
+
return tallest;
|
|
873
|
+
}, createEmptySegmentToken())(line);
|
|
874
|
+
const verticalAlignInLines = (lines, lineSpacing, overrideValign) => {
|
|
875
|
+
let previousTallestToken = createEmptySegmentToken();
|
|
876
|
+
let previousLineBottom = 0;
|
|
877
|
+
let paragraphModifier = 0;
|
|
878
|
+
const newLines = [];
|
|
879
|
+
for (const line of lines) {
|
|
880
|
+
const newLine = [];
|
|
881
|
+
let tallestToken = getTallestToken(line);
|
|
882
|
+
let tallestHeight = (tallestToken.bounds?.height ?? 0) + paragraphModifier;
|
|
883
|
+
let tallestAscent = 0;
|
|
884
|
+
for (const word of line) {
|
|
885
|
+
for (const segment of word) {
|
|
886
|
+
const segAscent = segment.fontProperties?.ascent ?? 0;
|
|
887
|
+
if (segAscent > tallestAscent) {
|
|
888
|
+
tallestAscent = segAscent;
|
|
889
|
+
}
|
|
890
|
+
}
|
|
891
|
+
}
|
|
892
|
+
tallestAscent += paragraphModifier;
|
|
893
|
+
const valignParagraphModifier = paragraphModifier;
|
|
894
|
+
paragraphModifier = 0;
|
|
895
|
+
const lastToken = line[line.length - 1][0];
|
|
896
|
+
if (isNewlineToken(lastToken)) {
|
|
897
|
+
paragraphModifier = tallestToken.style.paragraphSpacing ?? 0;
|
|
898
|
+
}
|
|
899
|
+
if (isSpriteToken(tallestToken)) {
|
|
900
|
+
tallestHeight += tallestToken.fontProperties.descent;
|
|
901
|
+
tallestAscent = tallestToken.bounds.height;
|
|
902
|
+
}
|
|
903
|
+
if (tallestHeight === 0) {
|
|
904
|
+
tallestToken = previousTallestToken;
|
|
905
|
+
} else {
|
|
906
|
+
previousTallestToken = tallestToken;
|
|
907
|
+
}
|
|
908
|
+
for (const word of line) {
|
|
909
|
+
const newWord = [];
|
|
910
|
+
for (const segment of word) {
|
|
911
|
+
const {
|
|
912
|
+
bounds,
|
|
913
|
+
fontProperties,
|
|
914
|
+
style
|
|
915
|
+
} = segment;
|
|
916
|
+
const {
|
|
917
|
+
height
|
|
918
|
+
} = bounds;
|
|
919
|
+
const newBounds = {
|
|
920
|
+
...bounds
|
|
921
|
+
};
|
|
922
|
+
const valign = overrideValign ?? style.valign;
|
|
923
|
+
let {
|
|
924
|
+
ascent
|
|
925
|
+
} = fontProperties;
|
|
926
|
+
if (isSpriteToken(segment)) {
|
|
927
|
+
const imgDisplay = segment.style[IMG_DISPLAY_PROPERTY];
|
|
928
|
+
if (imgDisplay === 'icon') {
|
|
929
|
+
ascent = segment.bounds.height - 4;
|
|
930
|
+
} else {
|
|
931
|
+
ascent = segment.bounds.height;
|
|
932
|
+
}
|
|
933
|
+
}
|
|
934
|
+
if (isNewlineToken(segment)) {
|
|
935
|
+
const newToken = {
|
|
936
|
+
...segment
|
|
937
|
+
};
|
|
938
|
+
newToken.bounds.y = previousLineBottom + tallestAscent - ascent;
|
|
939
|
+
newWord.push(newToken);
|
|
940
|
+
continue;
|
|
941
|
+
}
|
|
942
|
+
let newY = previousLineBottom;
|
|
943
|
+
const hasStroke = style?.stroke && style.strokeThickness > 0;
|
|
944
|
+
const strokeThickness = hasStroke ? style.strokeThickness : 0;
|
|
945
|
+
switch (valign) {
|
|
946
|
+
case "bottom":
|
|
947
|
+
newY += tallestHeight - height;
|
|
948
|
+
if (hasStroke) {
|
|
949
|
+
newY -= strokeThickness / 2;
|
|
950
|
+
}
|
|
951
|
+
break;
|
|
952
|
+
case "middle":
|
|
953
|
+
newY += (tallestHeight + valignParagraphModifier - height) / 2;
|
|
954
|
+
if (hasStroke) {
|
|
955
|
+
newY -= strokeThickness / 2;
|
|
956
|
+
}
|
|
957
|
+
break;
|
|
958
|
+
case "top":
|
|
959
|
+
newY += valignParagraphModifier;
|
|
960
|
+
if (hasStroke) {
|
|
961
|
+
newY -= strokeThickness / 2;
|
|
962
|
+
}
|
|
963
|
+
break;
|
|
964
|
+
case "baseline":
|
|
965
|
+
default:
|
|
966
|
+
newY = previousLineBottom + tallestAscent - ascent;
|
|
967
|
+
if (hasStroke) {
|
|
968
|
+
newY -= strokeThickness / 2;
|
|
969
|
+
}
|
|
970
|
+
}
|
|
971
|
+
newBounds.y = newY;
|
|
972
|
+
const newToken = {
|
|
973
|
+
...segment,
|
|
974
|
+
bounds: newBounds
|
|
975
|
+
};
|
|
976
|
+
newWord.push(newToken);
|
|
977
|
+
}
|
|
978
|
+
newLine.push(newWord);
|
|
979
|
+
}
|
|
980
|
+
previousLineBottom += tallestHeight + lineSpacing;
|
|
981
|
+
newLines.push(newLine);
|
|
982
|
+
}
|
|
983
|
+
return newLines;
|
|
984
|
+
};
|
|
985
|
+
const collapseWhitespacesOnEndOfLines = lines => {
|
|
986
|
+
for (const line of lines) {
|
|
987
|
+
const l = line.length;
|
|
988
|
+
let i = l;
|
|
989
|
+
while (i >= 0) {
|
|
990
|
+
i -= 1;
|
|
991
|
+
const word = line[i];
|
|
992
|
+
if (isNotWhitespaceToken(word)) {
|
|
993
|
+
break;
|
|
994
|
+
} else {
|
|
995
|
+
for (const token of word) {
|
|
996
|
+
token.bounds.width = 0;
|
|
997
|
+
token.bounds.height = Math.min(token.bounds.height, token.fontProperties.fontSize);
|
|
998
|
+
}
|
|
999
|
+
}
|
|
1000
|
+
}
|
|
1001
|
+
}
|
|
1002
|
+
return lines;
|
|
1003
|
+
};
|
|
1004
|
+
const layout = (tokens, maxWidth, lineSpacing, align, _splitStyle) => {
|
|
1005
|
+
const cursor = {
|
|
1006
|
+
x: 0,
|
|
1007
|
+
y: 0
|
|
1008
|
+
};
|
|
1009
|
+
let wordWidth = 0;
|
|
1010
|
+
let word = [];
|
|
1011
|
+
let line = [];
|
|
1012
|
+
const allLines = [];
|
|
1013
|
+
let tallestHeightInLine = 0;
|
|
1014
|
+
let token;
|
|
1015
|
+
for (let i = 0; i < tokens.length; i++) {
|
|
1016
|
+
token = tokens[i];
|
|
1017
|
+
const normalLineBreaks = hasNormalLineBreaks(token);
|
|
1018
|
+
const isWhitespace = isWhitespaceToken(token);
|
|
1019
|
+
const isNewline = isNewlineToken(token);
|
|
1020
|
+
const isImage = isSpriteToken(token);
|
|
1021
|
+
const isWordEndingToken = isWhitespace || isImage;
|
|
1022
|
+
if (isWordEndingToken && normalLineBreaks || isNewline || token.style.breakWords) {
|
|
1023
|
+
positionWordBufferAndAddToLine();
|
|
1024
|
+
}
|
|
1025
|
+
addTokenToWordAndUpdateWordWidth(token);
|
|
1026
|
+
setTallestHeight(token);
|
|
1027
|
+
if (isWhitespace && normalLineBreaks || isNewline) {
|
|
1028
|
+
positionWordBufferAndAddToLine();
|
|
1029
|
+
}
|
|
1030
|
+
if (isNewline || isBlockImage(token)) {
|
|
1031
|
+
addLineToListOfLinesAndMoveCursorToNextLine(token);
|
|
1032
|
+
} else if (wordInBufferExceedsLineLength()) {
|
|
1033
|
+
if (line.length > 0) {
|
|
1034
|
+
addLineToListOfLinesAndMoveCursorToNextLine(token);
|
|
1035
|
+
}
|
|
1036
|
+
}
|
|
1037
|
+
}
|
|
1038
|
+
if (word.length > 0) {
|
|
1039
|
+
positionWordBufferAndAddToLine();
|
|
1040
|
+
}
|
|
1041
|
+
if (line.length > 0) {
|
|
1042
|
+
addLineToListOfLines();
|
|
1043
|
+
}
|
|
1044
|
+
const collapsedWhitespace = collapseWhitespacesOnEndOfLines(allLines);
|
|
1045
|
+
const alignedLines = alignLines(align, maxWidth, collapsedWhitespace);
|
|
1046
|
+
const valignedLines = verticalAlignInLines(alignedLines, lineSpacing);
|
|
1047
|
+
return valignedLines;
|
|
1048
|
+
function addWordBufferToLineBuffer() {
|
|
1049
|
+
if (word !== undefined && word.length > 0) {
|
|
1050
|
+
line.push(word);
|
|
1051
|
+
}
|
|
1052
|
+
word = [];
|
|
1053
|
+
wordWidth = 0;
|
|
1054
|
+
}
|
|
1055
|
+
function addLineToListOfLines() {
|
|
1056
|
+
allLines.push(line);
|
|
1057
|
+
line = [];
|
|
1058
|
+
}
|
|
1059
|
+
function addLineToListOfLinesAndMoveCursorToNextLine(token) {
|
|
1060
|
+
addLineToListOfLines();
|
|
1061
|
+
cursor.x = 0;
|
|
1062
|
+
cursor.y = cursor.y + tallestHeightInLine + lineSpacing;
|
|
1063
|
+
tallestHeightInLine = 0;
|
|
1064
|
+
setTallestHeight(token);
|
|
1065
|
+
}
|
|
1066
|
+
function setTallestHeight(token) {
|
|
1067
|
+
const fontSize = token?.fontProperties?.fontSize ?? 0;
|
|
1068
|
+
const height = token?.bounds?.height ?? 0;
|
|
1069
|
+
tallestHeightInLine = Math.max(tallestHeightInLine, fontSize);
|
|
1070
|
+
if (isNewlineToken(token) === false) {
|
|
1071
|
+
tallestHeightInLine = Math.max(tallestHeightInLine, height);
|
|
1072
|
+
}
|
|
1073
|
+
}
|
|
1074
|
+
function positionTokenAtCursorAndAdvanceCursor(token) {
|
|
1075
|
+
setTallestHeight(token);
|
|
1076
|
+
if (Math.abs(cursor.x) > 10000) {
|
|
1077
|
+
console.error('EXTREME CURSOR.X BEFORE ASSIGNMENT:', {
|
|
1078
|
+
cursorX: cursor.x,
|
|
1079
|
+
cursorY: cursor.y,
|
|
1080
|
+
tokenContent: token.content,
|
|
1081
|
+
tokenWidth: token.bounds.width,
|
|
1082
|
+
tokenBounds: {
|
|
1083
|
+
...token.bounds
|
|
1084
|
+
}
|
|
1085
|
+
});
|
|
1086
|
+
}
|
|
1087
|
+
token.bounds.x = cursor.x;
|
|
1088
|
+
token.bounds.y = cursor.y;
|
|
1089
|
+
cursor.x += token.bounds.width;
|
|
1090
|
+
if (Math.abs(cursor.x) > 10000) {
|
|
1091
|
+
console.error('EXTREME CURSOR.X AFTER ADVANCING:', {
|
|
1092
|
+
cursorX: cursor.x,
|
|
1093
|
+
tokenContent: token.content,
|
|
1094
|
+
tokenWidth: token.bounds.width
|
|
1095
|
+
});
|
|
1096
|
+
}
|
|
1097
|
+
}
|
|
1098
|
+
function positionWordBufferAtCursorAndAdvanceCursor() {
|
|
1099
|
+
word.forEach(positionTokenAtCursorAndAdvanceCursor);
|
|
1100
|
+
}
|
|
1101
|
+
function wordInBufferExceedsLineLength() {
|
|
1102
|
+
return cursor.x + wordWidth > maxWidth;
|
|
1103
|
+
}
|
|
1104
|
+
function isBlockImage(token) {
|
|
1105
|
+
return token.style[IMG_DISPLAY_PROPERTY] === "block";
|
|
1106
|
+
}
|
|
1107
|
+
function hasNormalLineBreaks(token) {
|
|
1108
|
+
return token.style.breakLines ?? true;
|
|
1109
|
+
}
|
|
1110
|
+
function addTokenToWordAndUpdateWordWidth(token) {
|
|
1111
|
+
word.push(token);
|
|
1112
|
+
wordWidth += token.bounds.width;
|
|
1113
|
+
}
|
|
1114
|
+
function positionWordBufferAndAddToLine() {
|
|
1115
|
+
positionWordBufferAtCursorAndAdvanceCursor();
|
|
1116
|
+
addWordBufferToLineBuffer();
|
|
1117
|
+
}
|
|
1118
|
+
};
|
|
1119
|
+
const notEmptyString = s => s !== "";
|
|
1120
|
+
const SPLIT_MARKER = `_🔪_`;
|
|
1121
|
+
const splitAroundWhitespace = s => s.replace(/\s/g, `${SPLIT_MARKER}$&${SPLIT_MARKER}`).split(SPLIT_MARKER).filter(s => s !== "");
|
|
1122
|
+
const splitText = (s, splitStyle) => {
|
|
1123
|
+
if (splitStyle === "words") {
|
|
1124
|
+
return [s].flatMap(splitAroundWhitespace).filter(notEmptyString);
|
|
1125
|
+
} else if (splitStyle === "characters") {
|
|
1126
|
+
return s.split("");
|
|
1127
|
+
} else {
|
|
1128
|
+
let suggestion = ` Supported styles are "words" and "characters"`;
|
|
1129
|
+
const badStyle = splitStyle.toLowerCase();
|
|
1130
|
+
if (badStyle.indexOf("char") === 0) {
|
|
1131
|
+
suggestion = `Did you mean "characters"?`;
|
|
1132
|
+
} else if (badStyle.indexOf("wor") === 0) {
|
|
1133
|
+
suggestion = `Did you mean "words"?`;
|
|
1134
|
+
}
|
|
1135
|
+
throw new Error(`Unsupported split style "${splitStyle}". ${suggestion}`);
|
|
1136
|
+
}
|
|
1137
|
+
};
|
|
1138
|
+
const calculateTokens = (styledTokens, splitStyle = "words", scaleIcons = true, adjustFontBaseline) => {
|
|
1139
|
+
const defaultStyle = styledTokens.style;
|
|
1140
|
+
let fontProperties;
|
|
1141
|
+
const generateTokensFormStyledToken = (style, tags) => token => {
|
|
1142
|
+
let output = [];
|
|
1143
|
+
const alignClassic = convertUnsupportedAlignment(style.align);
|
|
1144
|
+
tags && tags.includes("outline");
|
|
1145
|
+
const cleanedStyle = {
|
|
1146
|
+
...style
|
|
1147
|
+
};
|
|
1148
|
+
const sizerStyle = {
|
|
1149
|
+
...cleanedStyle,
|
|
1150
|
+
align: alignClassic,
|
|
1151
|
+
wordWrap: false,
|
|
1152
|
+
dropShadow: undefined,
|
|
1153
|
+
fontFamily: cleanedStyle.fontFamily || 'Arial',
|
|
1154
|
+
fontSize: cleanedStyle.fontSize || 24,
|
|
1155
|
+
fill: cleanedStyle.fill || 0x000000
|
|
1156
|
+
};
|
|
1157
|
+
const strokeThickness = sizerStyle.strokeThickness;
|
|
1158
|
+
delete sizerStyle.stroke;
|
|
1159
|
+
delete sizerStyle.strokeThickness;
|
|
1160
|
+
const localSizer = new PIXI__namespace.Text({
|
|
1161
|
+
text: "",
|
|
1162
|
+
style: sizerStyle
|
|
1163
|
+
});
|
|
1164
|
+
if (typeof token === "string") {
|
|
1165
|
+
const textSegments = splitText(token, splitStyle);
|
|
1166
|
+
const textTokens = textSegments.map(str => {
|
|
1167
|
+
switch (style.textTransform) {
|
|
1168
|
+
case "uppercase":
|
|
1169
|
+
localSizer.text = str.toUpperCase();
|
|
1170
|
+
break;
|
|
1171
|
+
case "lowercase":
|
|
1172
|
+
localSizer.text = str.toLowerCase();
|
|
1173
|
+
break;
|
|
1174
|
+
case "capitalize":
|
|
1175
|
+
localSizer.text = capitalize(str);
|
|
1176
|
+
break;
|
|
1177
|
+
default:
|
|
1178
|
+
localSizer.text = str;
|
|
1179
|
+
}
|
|
1180
|
+
fontProperties = {
|
|
1181
|
+
...getFontPropertiesOfText(localSizer, true)
|
|
1182
|
+
};
|
|
1183
|
+
if (isOnlyWhitespace(token) === false) {
|
|
1184
|
+
const stroke = localSizer.style.stroke ?? 0;
|
|
1185
|
+
if (stroke > 0) {
|
|
1186
|
+
fontProperties.descent += stroke / 2;
|
|
1187
|
+
fontProperties.ascent += stroke / 2;
|
|
1188
|
+
fontProperties.fontSize = fontProperties.ascent + fontProperties.descent;
|
|
1189
|
+
}
|
|
1190
|
+
}
|
|
1191
|
+
const sw = style.fontScaleWidth ?? 1.0;
|
|
1192
|
+
const sh = style.fontScaleHeight ?? 1.0;
|
|
1193
|
+
const scaleWidth = isNaN(sw) || sw < 0 ? 0.0 : sw;
|
|
1194
|
+
const scaleHeight = isNaN(sh) || sh < 0 ? 0.0 : sh;
|
|
1195
|
+
localSizer.scale.set(scaleWidth, scaleHeight);
|
|
1196
|
+
fontProperties.ascent *= scaleHeight;
|
|
1197
|
+
fontProperties.descent *= scaleHeight;
|
|
1198
|
+
fontProperties.fontSize *= scaleHeight;
|
|
1199
|
+
let bounds;
|
|
1200
|
+
if (isOnlyWhitespace(str)) {
|
|
1201
|
+
const fontSize = typeof localSizer.style.fontSize === 'string' ? parseInt(localSizer.style.fontSize) : localSizer.style.fontSize || 24;
|
|
1202
|
+
const spaceWidth = fontSize * 0.3 * str.length;
|
|
1203
|
+
const height = fontProperties.fontSize;
|
|
1204
|
+
bounds = new PIXI__namespace.Rectangle(0, 0, spaceWidth, height);
|
|
1205
|
+
} else {
|
|
1206
|
+
const measureSizer = new PIXI__namespace.Text({
|
|
1207
|
+
text: localSizer.text,
|
|
1208
|
+
style: {
|
|
1209
|
+
...localSizer.style
|
|
1210
|
+
}
|
|
1211
|
+
});
|
|
1212
|
+
measureSizer.scale.set(localSizer.scale.x, localSizer.scale.y);
|
|
1213
|
+
bounds = rectFromContainer(measureSizer);
|
|
1214
|
+
if (strokeThickness && strokeThickness > 0) {
|
|
1215
|
+
bounds.width += strokeThickness;
|
|
1216
|
+
bounds.height += strokeThickness;
|
|
1217
|
+
}
|
|
1218
|
+
}
|
|
1219
|
+
if (isNaN(bounds.width) || isNaN(bounds.height)) {
|
|
1220
|
+
console.error(`[ERROR] NaN bounds after all attempts for token "${str}" with tags="${tags}"`);
|
|
1221
|
+
if (isOnlyWhitespace(str)) {
|
|
1222
|
+
bounds = new PIXI__namespace.Rectangle(0, 0, fontProperties.fontSize * 0.3 * str.length, fontProperties.fontSize);
|
|
1223
|
+
} else {
|
|
1224
|
+
bounds = new PIXI__namespace.Rectangle(0, 0, 0, fontProperties.fontSize);
|
|
1225
|
+
}
|
|
1226
|
+
}
|
|
1227
|
+
const textDecorations = extractDecorations(style, bounds, fontProperties);
|
|
1228
|
+
const baselineAdjustment = getBaselineAdjustment(style, adjustFontBaseline, fontProperties.ascent);
|
|
1229
|
+
fontProperties.ascent += baselineAdjustment;
|
|
1230
|
+
const {
|
|
1231
|
+
letterSpacing
|
|
1232
|
+
} = style;
|
|
1233
|
+
if (letterSpacing) {
|
|
1234
|
+
bounds.width += letterSpacing;
|
|
1235
|
+
}
|
|
1236
|
+
const convertedToken = {
|
|
1237
|
+
content: str,
|
|
1238
|
+
style,
|
|
1239
|
+
tags,
|
|
1240
|
+
bounds,
|
|
1241
|
+
fontProperties,
|
|
1242
|
+
textDecorations
|
|
1243
|
+
};
|
|
1244
|
+
return convertedToken;
|
|
1245
|
+
});
|
|
1246
|
+
output = output.concat(textTokens);
|
|
1247
|
+
} else if (token instanceof PIXI__namespace.Sprite) {
|
|
1248
|
+
const sprite = token;
|
|
1249
|
+
const imgDisplay = style[IMG_DISPLAY_PROPERTY];
|
|
1250
|
+
const isIcon = imgDisplay === "icon";
|
|
1251
|
+
if (localSizer.text === "") {
|
|
1252
|
+
localSizer.text = "Mg";
|
|
1253
|
+
}
|
|
1254
|
+
fontProperties = {
|
|
1255
|
+
...getFontPropertiesOfText(localSizer, true)
|
|
1256
|
+
};
|
|
1257
|
+
if (strokeThickness && strokeThickness > 0) {
|
|
1258
|
+
fontProperties.ascent += strokeThickness / 2;
|
|
1259
|
+
fontProperties.descent += strokeThickness / 2;
|
|
1260
|
+
fontProperties.fontSize = fontProperties.ascent + fontProperties.descent;
|
|
1261
|
+
}
|
|
1262
|
+
if (isIcon) {
|
|
1263
|
+
const h = Math.max(sprite.height, 1);
|
|
1264
|
+
if (h > 1 && sprite.scale.y === 1) {
|
|
1265
|
+
const {
|
|
1266
|
+
iconScale = 1.0
|
|
1267
|
+
} = style;
|
|
1268
|
+
const effectiveFontSize = fontProperties.fontSize || 20;
|
|
1269
|
+
const ratio = effectiveFontSize / h * ICON_SCALE_BASE * iconScale;
|
|
1270
|
+
sprite.scale.set(ratio);
|
|
1271
|
+
}
|
|
1272
|
+
if (scaleIcons) {
|
|
1273
|
+
const {
|
|
1274
|
+
fontScaleWidth: scaleX = 1.0,
|
|
1275
|
+
fontScaleHeight: scaleY = 1.0
|
|
1276
|
+
} = style;
|
|
1277
|
+
sprite.scale.x *= scaleX;
|
|
1278
|
+
sprite.scale.y *= scaleY;
|
|
1279
|
+
}
|
|
1280
|
+
}
|
|
1281
|
+
const bounds = rectFromContainer(sprite);
|
|
1282
|
+
const {
|
|
1283
|
+
letterSpacing
|
|
1284
|
+
} = style;
|
|
1285
|
+
if (letterSpacing && isIcon) {
|
|
1286
|
+
bounds.width += letterSpacing;
|
|
1287
|
+
}
|
|
1288
|
+
output.push({
|
|
1289
|
+
content: sprite,
|
|
1290
|
+
style,
|
|
1291
|
+
tags,
|
|
1292
|
+
bounds,
|
|
1293
|
+
fontProperties,
|
|
1294
|
+
textDecorations: undefined
|
|
1295
|
+
});
|
|
1296
|
+
} else {
|
|
1297
|
+
const styledToken = token;
|
|
1298
|
+
const {
|
|
1299
|
+
children
|
|
1300
|
+
} = styledToken;
|
|
1301
|
+
const newStyle = styledToken.style;
|
|
1302
|
+
const newTags = styledToken.tags;
|
|
1303
|
+
if (newStyle === undefined) {
|
|
1304
|
+
throw new Error(`Expected to find a 'style' property on ${styledToken}`);
|
|
1305
|
+
}
|
|
1306
|
+
output = output.concat(children.flatMap(generateTokensFormStyledToken(newStyle, newTags)));
|
|
1307
|
+
}
|
|
1308
|
+
return output;
|
|
1309
|
+
};
|
|
1310
|
+
const tags = "";
|
|
1311
|
+
const style = defaultStyle;
|
|
1312
|
+
const finalTokens = styledTokens.children.flatMap(generateTokensFormStyledToken(style, tags));
|
|
1313
|
+
const {
|
|
1314
|
+
wordWrap: ww,
|
|
1315
|
+
wordWrapWidth: www
|
|
1316
|
+
} = defaultStyle;
|
|
1317
|
+
const hasWordWrapWidth = www !== undefined && isNaN(www) === false && www > 0;
|
|
1318
|
+
const maxWidth = ww && hasWordWrapWidth ? www : Number.POSITIVE_INFINITY;
|
|
1319
|
+
const lineSpacing = defaultStyle.lineSpacing ?? 0;
|
|
1320
|
+
const align = defaultStyle.align ?? "left";
|
|
1321
|
+
const lines = layout(finalTokens, maxWidth, lineSpacing, align);
|
|
1322
|
+
return lines;
|
|
1323
|
+
};
|
|
1324
|
+
const getBaselineAdjustment = (style, fontBaselineMap = {}, ascent) => {
|
|
1325
|
+
const fontFamily = style.fontFamily?.toString() ?? "";
|
|
1326
|
+
const adjustBaseline = style.adjustBaseline ?? 0;
|
|
1327
|
+
const adjustFontBaseline = fontBaselineMap[fontFamily] ?? null;
|
|
1328
|
+
let finalValue = adjustBaseline;
|
|
1329
|
+
if (typeof adjustFontBaseline === "string") {
|
|
1330
|
+
const percentPair = adjustFontBaseline.split("%");
|
|
1331
|
+
const isPercent = percentPair.length > 1;
|
|
1332
|
+
const value = Number(percentPair[0]);
|
|
1333
|
+
if (isPercent) {
|
|
1334
|
+
finalValue += ascent * (value / 100);
|
|
1335
|
+
} else {
|
|
1336
|
+
finalValue += value;
|
|
1337
|
+
}
|
|
1338
|
+
} else {
|
|
1339
|
+
finalValue += Number(adjustFontBaseline);
|
|
1340
|
+
}
|
|
1341
|
+
return finalValue;
|
|
1342
|
+
};
|
|
1343
|
+
|
|
1344
|
+
const DEFAULT_OPTIONS = {
|
|
1345
|
+
debug: false,
|
|
1346
|
+
debugConsole: false,
|
|
1347
|
+
splitStyle: "words",
|
|
1348
|
+
imgMap: {},
|
|
1349
|
+
scaleIcons: true,
|
|
1350
|
+
skipUpdates: false,
|
|
1351
|
+
skipDraw: false,
|
|
1352
|
+
drawWhitespace: false,
|
|
1353
|
+
wrapEmoji: true,
|
|
1354
|
+
errorHandler: undefined,
|
|
1355
|
+
supressConsole: false,
|
|
1356
|
+
overdrawDecorations: 0
|
|
1357
|
+
};
|
|
1358
|
+
|
|
1359
|
+
const DEBUG = {
|
|
1360
|
+
WORD_STROKE_COLOR: 0xffcccc,
|
|
1361
|
+
WORD_FILL_COLOR: 0xeeeeee,
|
|
1362
|
+
TEXT_FIELD_STROKE_COLOR: 0xff00ff,
|
|
1363
|
+
WHITESPACE_COLOR: 0xcccccc,
|
|
1364
|
+
WHITESPACE_STROKE_COLOR: 0xaaaaaa,
|
|
1365
|
+
BASELINE_COLOR: 0xffff99,
|
|
1366
|
+
LINE_COLOR: 0xffff00,
|
|
1367
|
+
OUTLINE_COLOR: 0xffcccc,
|
|
1368
|
+
OUTLINE_SHADOW_COLOR: 0x000000,
|
|
1369
|
+
TEXT_STYLE: {
|
|
1370
|
+
fontFamily: "courier",
|
|
1371
|
+
fontSize: 10,
|
|
1372
|
+
fill: 0xffffff,
|
|
1373
|
+
dropShadow: {
|
|
1374
|
+
color: 0x000000,
|
|
1375
|
+
blur: 2,
|
|
1376
|
+
distance: 2,
|
|
1377
|
+
alpha: 1
|
|
1378
|
+
}
|
|
1379
|
+
}
|
|
1380
|
+
};
|
|
1381
|
+
const DEFAULT_STYLE_SET = {
|
|
1382
|
+
default: DEFAULT_STYLE
|
|
1383
|
+
};
|
|
1384
|
+
Object.freeze(DEFAULT_STYLE_SET);
|
|
1385
|
+
Object.freeze(DEFAULT_STYLE);
|
|
1386
|
+
const DEFAULT_DESTROY_OPTIONS = {
|
|
1387
|
+
children: true,
|
|
1388
|
+
texture: true,
|
|
1389
|
+
textureSource: false,
|
|
1390
|
+
context: false
|
|
1391
|
+
};
|
|
1392
|
+
class Glyphs extends PIXI__namespace.Container {
|
|
1393
|
+
static get defaultStyles() {
|
|
1394
|
+
return DEFAULT_STYLE_SET;
|
|
1395
|
+
}
|
|
1396
|
+
static get defaultOptions() {
|
|
1397
|
+
return DEFAULT_OPTIONS;
|
|
1398
|
+
}
|
|
1399
|
+
get options() {
|
|
1400
|
+
return this._options;
|
|
1401
|
+
}
|
|
1402
|
+
get needsUpdate() {
|
|
1403
|
+
return this._needsUpdate;
|
|
1404
|
+
}
|
|
1405
|
+
get needsDraw() {
|
|
1406
|
+
return this._needsDraw;
|
|
1407
|
+
}
|
|
1408
|
+
get tokens() {
|
|
1409
|
+
return this._tokens;
|
|
1410
|
+
}
|
|
1411
|
+
get tokensFlat() {
|
|
1412
|
+
return this._tokens.flat(Infinity);
|
|
1413
|
+
}
|
|
1414
|
+
get text() {
|
|
1415
|
+
return this._text;
|
|
1416
|
+
}
|
|
1417
|
+
set text(text) {
|
|
1418
|
+
this.setText(text);
|
|
1419
|
+
}
|
|
1420
|
+
setText(text, skipUpdate) {
|
|
1421
|
+
if (text === this._text && this._needsUpdate === false) {
|
|
1422
|
+
return;
|
|
1423
|
+
}
|
|
1424
|
+
this._text = text;
|
|
1425
|
+
this._needsUpdate = true;
|
|
1426
|
+
this.updateIfShould(skipUpdate);
|
|
1427
|
+
}
|
|
1428
|
+
get untaggedText() {
|
|
1429
|
+
return removeTags(this.text);
|
|
1430
|
+
}
|
|
1431
|
+
get tagStyles() {
|
|
1432
|
+
return this._tagStyles;
|
|
1433
|
+
}
|
|
1434
|
+
set tagStyles(styles) {
|
|
1435
|
+
this.setTagStyles(styles);
|
|
1436
|
+
}
|
|
1437
|
+
setTagStyles(styles, skipUpdate) {
|
|
1438
|
+
Object.entries(styles).forEach(([tag, style]) => this.setStyleForTag(tag, style, true));
|
|
1439
|
+
this._needsUpdate = true;
|
|
1440
|
+
this.updateIfShould(skipUpdate);
|
|
1441
|
+
}
|
|
1442
|
+
getStyleForTag(tag, attributes = {}) {
|
|
1443
|
+
return getStyleForTag(tag, this.tagStyles, attributes);
|
|
1444
|
+
}
|
|
1445
|
+
getStyleForTags(tags) {
|
|
1446
|
+
const styles = tags.map(({
|
|
1447
|
+
tagName,
|
|
1448
|
+
attributes
|
|
1449
|
+
}) => this.getStyleForTag(tagName, attributes));
|
|
1450
|
+
return combineAllStyles(styles);
|
|
1451
|
+
}
|
|
1452
|
+
setStyleForTag(tag, styles, skipUpdate) {
|
|
1453
|
+
this.tagStyles[tag] = styles;
|
|
1454
|
+
if (tag === DEFAULT_KEY && this.defaultStyle[IMG_REFERENCE_PROPERTY]) {
|
|
1455
|
+
this.logWarning(`${IMG_REFERENCE_PROPERTY}-on-default`, `Style "${IMG_REFERENCE_PROPERTY}" can not be set on the "${DEFAULT_KEY}" style because it will add images to EVERY tag!`);
|
|
1456
|
+
this.defaultStyle[IMG_REFERENCE_PROPERTY] = undefined;
|
|
1457
|
+
}
|
|
1458
|
+
this._needsUpdate = true;
|
|
1459
|
+
this.updateIfShould(skipUpdate);
|
|
1460
|
+
return true;
|
|
1461
|
+
}
|
|
1462
|
+
removeStylesForTag(tag, skipUpdate) {
|
|
1463
|
+
if (tag in this.tagStyles) {
|
|
1464
|
+
delete this.tagStyles[tag];
|
|
1465
|
+
this._needsUpdate = true;
|
|
1466
|
+
this.updateIfShould(skipUpdate);
|
|
1467
|
+
return true;
|
|
1468
|
+
}
|
|
1469
|
+
return false;
|
|
1470
|
+
}
|
|
1471
|
+
get defaultStyle() {
|
|
1472
|
+
return this.tagStyles?.default;
|
|
1473
|
+
}
|
|
1474
|
+
set defaultStyle(defaultStyles) {
|
|
1475
|
+
this.setDefaultStyle(defaultStyles);
|
|
1476
|
+
}
|
|
1477
|
+
setDefaultStyle(defaultStyles, skipUpdate) {
|
|
1478
|
+
this.setStyleForTag(DEFAULT_KEY, defaultStyles, skipUpdate);
|
|
1479
|
+
}
|
|
1480
|
+
get textFields() {
|
|
1481
|
+
return this._textFields;
|
|
1482
|
+
}
|
|
1483
|
+
get sprites() {
|
|
1484
|
+
return this._sprites;
|
|
1485
|
+
}
|
|
1486
|
+
get decorations() {
|
|
1487
|
+
return this._decorations;
|
|
1488
|
+
}
|
|
1489
|
+
get spriteTemplates() {
|
|
1490
|
+
return this._spriteTemplates;
|
|
1491
|
+
}
|
|
1492
|
+
get textContainer() {
|
|
1493
|
+
return this._textContainer;
|
|
1494
|
+
}
|
|
1495
|
+
get decorationContainer() {
|
|
1496
|
+
return this._decorationContainer;
|
|
1497
|
+
}
|
|
1498
|
+
get spriteContainer() {
|
|
1499
|
+
return this._spriteContainer;
|
|
1500
|
+
}
|
|
1501
|
+
get debugContainer() {
|
|
1502
|
+
return this._debugContainer;
|
|
1503
|
+
}
|
|
1504
|
+
constructor(text = "", tagStyles = {}, options = {}) {
|
|
1505
|
+
super();
|
|
1506
|
+
this._options = void 0;
|
|
1507
|
+
this._needsUpdate = true;
|
|
1508
|
+
this._needsDraw = true;
|
|
1509
|
+
this._tokens = [];
|
|
1510
|
+
this._text = "";
|
|
1511
|
+
this._tagStyles = {};
|
|
1512
|
+
this._textFields = [];
|
|
1513
|
+
this._sprites = [];
|
|
1514
|
+
this._decorations = [];
|
|
1515
|
+
this._spriteTemplates = {};
|
|
1516
|
+
this._debugGraphics = void 0;
|
|
1517
|
+
this._textContainer = void 0;
|
|
1518
|
+
this._decorationContainer = void 0;
|
|
1519
|
+
this._spriteContainer = void 0;
|
|
1520
|
+
this._debugContainer = void 0;
|
|
1521
|
+
this.logWarning = (code, message) => logWarning(this.options.errorHandler, this.options.supressConsole, this)(code, message);
|
|
1522
|
+
this._textContainer = new PIXI__namespace.Container();
|
|
1523
|
+
this._spriteContainer = new PIXI__namespace.Container();
|
|
1524
|
+
this._decorationContainer = new PIXI__namespace.Container();
|
|
1525
|
+
this._debugContainer = new PIXI__namespace.Container();
|
|
1526
|
+
this._debugGraphics = new PIXI__namespace.Graphics();
|
|
1527
|
+
this.resetChildren();
|
|
1528
|
+
const mergedOptions = {
|
|
1529
|
+
...DEFAULT_OPTIONS,
|
|
1530
|
+
...options
|
|
1531
|
+
};
|
|
1532
|
+
this._options = mergedOptions;
|
|
1533
|
+
tagStyles = {
|
|
1534
|
+
default: {},
|
|
1535
|
+
...tagStyles
|
|
1536
|
+
};
|
|
1537
|
+
if (this.options.wrapEmoji) {
|
|
1538
|
+
const userStyles = tagStyles[EMOJI_TAG];
|
|
1539
|
+
tagStyles[EMOJI_TAG] = {
|
|
1540
|
+
fontFamily: "sans-serif",
|
|
1541
|
+
...userStyles
|
|
1542
|
+
};
|
|
1543
|
+
}
|
|
1544
|
+
const mergedDefaultStyles = {
|
|
1545
|
+
...DEFAULT_STYLE,
|
|
1546
|
+
...tagStyles.default
|
|
1547
|
+
};
|
|
1548
|
+
tagStyles.default = mergedDefaultStyles;
|
|
1549
|
+
this.tagStyles = tagStyles;
|
|
1550
|
+
if (this.options.imgMap) {
|
|
1551
|
+
this.createSpriteTemplatesFromSourceMap(this.options.imgMap);
|
|
1552
|
+
}
|
|
1553
|
+
this.text = text;
|
|
1554
|
+
}
|
|
1555
|
+
destroyImgMap() {
|
|
1556
|
+
if (this.destroyed) {
|
|
1557
|
+
throw new Error("destroyImgMap() was called after this object was already destroyed. You must call destroyImgMap() before destroy() because imgMap is cleared when the object is destroyed.");
|
|
1558
|
+
}
|
|
1559
|
+
this._spriteContainer.destroy({
|
|
1560
|
+
children: true,
|
|
1561
|
+
texture: true,
|
|
1562
|
+
textureSource: true
|
|
1563
|
+
});
|
|
1564
|
+
}
|
|
1565
|
+
destroy(options) {
|
|
1566
|
+
let destroyOptions;
|
|
1567
|
+
if (options === undefined || options === true) {
|
|
1568
|
+
destroyOptions = {};
|
|
1569
|
+
} else if (options === false) {
|
|
1570
|
+
destroyOptions = DEFAULT_DESTROY_OPTIONS;
|
|
1571
|
+
} else {
|
|
1572
|
+
destroyOptions = {
|
|
1573
|
+
...DEFAULT_DESTROY_OPTIONS,
|
|
1574
|
+
...options
|
|
1575
|
+
};
|
|
1576
|
+
}
|
|
1577
|
+
this._spriteContainer.destroy(false);
|
|
1578
|
+
super.destroy(destroyOptions);
|
|
1579
|
+
this._textFields = [];
|
|
1580
|
+
this._sprites = [];
|
|
1581
|
+
this._decorations = [];
|
|
1582
|
+
this._spriteTemplates = {};
|
|
1583
|
+
this._tokens = [];
|
|
1584
|
+
this._tagStyles = {};
|
|
1585
|
+
this._options.imgMap = {};
|
|
1586
|
+
this._options.skipUpdates = true;
|
|
1587
|
+
this._options.skipDraw = true;
|
|
1588
|
+
this._options = {};
|
|
1589
|
+
}
|
|
1590
|
+
resetChildren() {
|
|
1591
|
+
if (this._textContainer) {
|
|
1592
|
+
this._textContainer.removeChildren();
|
|
1593
|
+
this.removeChild(this._textContainer);
|
|
1594
|
+
}
|
|
1595
|
+
this._textContainer = new PIXI__namespace.Container();
|
|
1596
|
+
this.addChild(this._textContainer);
|
|
1597
|
+
if (this._spriteContainer) {
|
|
1598
|
+
this._spriteContainer.removeChildren();
|
|
1599
|
+
this.removeChild(this._spriteContainer);
|
|
1600
|
+
}
|
|
1601
|
+
this._spriteContainer = new PIXI__namespace.Container();
|
|
1602
|
+
this.addChild(this._spriteContainer);
|
|
1603
|
+
if (this._decorationContainer) {
|
|
1604
|
+
this._decorationContainer.removeChildren();
|
|
1605
|
+
this.removeChild(this._decorationContainer);
|
|
1606
|
+
}
|
|
1607
|
+
this._decorationContainer = new PIXI__namespace.Container();
|
|
1608
|
+
this.addChild(this._decorationContainer);
|
|
1609
|
+
if (this._debugContainer) {
|
|
1610
|
+
this._debugContainer.removeChildren();
|
|
1611
|
+
this.removeChild(this._debugContainer);
|
|
1612
|
+
}
|
|
1613
|
+
this._debugContainer = new PIXI__namespace.Container();
|
|
1614
|
+
this.addChild(this._debugContainer);
|
|
1615
|
+
this._textFields = [];
|
|
1616
|
+
this._sprites = [];
|
|
1617
|
+
this._decorations = [];
|
|
1618
|
+
}
|
|
1619
|
+
createSpriteTemplatesFromSourceMap(imgMap) {
|
|
1620
|
+
this._spriteTemplates = {};
|
|
1621
|
+
Object.entries(imgMap).forEach(([key, spriteSource]) => {
|
|
1622
|
+
const wrongFormatError = new TypeError(`The spriteSource provided for key ${key} was not in a valid format. Please use a Sprite, Texture, BaseTexture, string, HTMLImageElement, HTMLVideoElement, HTMLCanvasElement, or SVGElement`);
|
|
1623
|
+
const destroyedError = new Error(`The spriteSource provided for key ${key} appears to be a Sprite or Texture that has been destroyed or removed from PIXI.TextureCache probably using \`destroy()\` with aggressive options or \`destroyImgMap()\`.`);
|
|
1624
|
+
let error = null;
|
|
1625
|
+
let sprite = new PIXI__namespace.Sprite();
|
|
1626
|
+
try {
|
|
1627
|
+
if (spriteSource instanceof PIXI__namespace.Sprite) {
|
|
1628
|
+
sprite = spriteSource;
|
|
1629
|
+
} else if (isSpriteSource(spriteSource)) {
|
|
1630
|
+
const texture = spriteSource instanceof PIXI__namespace.Texture ? spriteSource : PIXI__namespace.Texture.from(spriteSource);
|
|
1631
|
+
sprite = new PIXI__namespace.Sprite(texture);
|
|
1632
|
+
} else if (isTextureSource(spriteSource)) {
|
|
1633
|
+
sprite = new PIXI__namespace.Sprite(PIXI__namespace.Texture.from(spriteSource));
|
|
1634
|
+
} else {
|
|
1635
|
+
error = wrongFormatError;
|
|
1636
|
+
console.log(error);
|
|
1637
|
+
}
|
|
1638
|
+
} catch (e) {
|
|
1639
|
+
error = e;
|
|
1640
|
+
console.log(error);
|
|
1641
|
+
}
|
|
1642
|
+
if (isSpriteSource(spriteSource) && spriteSource.baseTexture === null || sprite !== undefined && (sprite.destroyed || sprite.texture?.baseTexture === null)) {
|
|
1643
|
+
error = destroyedError;
|
|
1644
|
+
console.log(error);
|
|
1645
|
+
}
|
|
1646
|
+
if (error) {
|
|
1647
|
+
throw error;
|
|
1648
|
+
}
|
|
1649
|
+
const texture = sprite.texture;
|
|
1650
|
+
const onTextureUpdate = baseTexture => {
|
|
1651
|
+
this.onImageTextureUpdate(baseTexture);
|
|
1652
|
+
baseTexture.removeListener("update", onTextureUpdate);
|
|
1653
|
+
};
|
|
1654
|
+
texture.baseTexture.addListener("update", onTextureUpdate);
|
|
1655
|
+
this.spriteTemplates[key] = sprite;
|
|
1656
|
+
const existingStyle = this.getStyleForTag(key) ?? {};
|
|
1657
|
+
const style = {
|
|
1658
|
+
[IMG_REFERENCE_PROPERTY]: key,
|
|
1659
|
+
...existingStyle
|
|
1660
|
+
};
|
|
1661
|
+
this.setStyleForTag(key, style);
|
|
1662
|
+
});
|
|
1663
|
+
}
|
|
1664
|
+
onImageTextureUpdate(_baseTexture) {
|
|
1665
|
+
this._needsUpdate = true;
|
|
1666
|
+
this._needsDraw = true;
|
|
1667
|
+
this.updateIfShould();
|
|
1668
|
+
}
|
|
1669
|
+
updateIfShould(forcedSkipUpdate) {
|
|
1670
|
+
if (forcedSkipUpdate === false || forcedSkipUpdate === undefined && this.options.skipUpdates === false) {
|
|
1671
|
+
this.update();
|
|
1672
|
+
return true;
|
|
1673
|
+
}
|
|
1674
|
+
return false;
|
|
1675
|
+
}
|
|
1676
|
+
update(skipDraw) {
|
|
1677
|
+
const tagStyles = this.tagStyles;
|
|
1678
|
+
const {
|
|
1679
|
+
splitStyle,
|
|
1680
|
+
scaleIcons
|
|
1681
|
+
} = this.options;
|
|
1682
|
+
const spriteTemplates = this.options.imgMap && this.spriteTemplates;
|
|
1683
|
+
const tagTokensNew = parseTagsNew(this.text, Object.keys(this.tagStyles), this.options.wrapEmoji, this.logWarning);
|
|
1684
|
+
const styledTokens = mapTagsToStyles(tagTokensNew, tagStyles, spriteTemplates);
|
|
1685
|
+
const newFinalTokens = calculateTokens(styledTokens, splitStyle, scaleIcons, this.options.adjustFontBaseline);
|
|
1686
|
+
this._tokens = newFinalTokens;
|
|
1687
|
+
this._needsDraw = true;
|
|
1688
|
+
this.drawIfShould(skipDraw);
|
|
1689
|
+
if (this.options.debugConsole) {
|
|
1690
|
+
console.log(this.toDebugString());
|
|
1691
|
+
}
|
|
1692
|
+
this._needsUpdate = false;
|
|
1693
|
+
return newFinalTokens;
|
|
1694
|
+
}
|
|
1695
|
+
drawIfShould(forcedSkipDraw) {
|
|
1696
|
+
if (forcedSkipDraw === false || forcedSkipDraw === undefined && this.options.skipDraw === false) {
|
|
1697
|
+
this.draw();
|
|
1698
|
+
return true;
|
|
1699
|
+
}
|
|
1700
|
+
return false;
|
|
1701
|
+
}
|
|
1702
|
+
draw() {
|
|
1703
|
+
this.resetChildren();
|
|
1704
|
+
if (this.textContainer === null || this.spriteContainer === null) {
|
|
1705
|
+
throw new Error("Somehow the textContainer or spriteContainer is null. This shouldn't be possible. Perhaps you've destroyed this object?");
|
|
1706
|
+
}
|
|
1707
|
+
const textContainer = this.textContainer;
|
|
1708
|
+
const spriteContainer = this.spriteContainer;
|
|
1709
|
+
const {
|
|
1710
|
+
drawWhitespace
|
|
1711
|
+
} = this.options;
|
|
1712
|
+
const tokensFlat = this.tokensFlat;
|
|
1713
|
+
const tokens = drawWhitespace ? tokensFlat : tokensFlat.filter(isNotWhitespaceToken);
|
|
1714
|
+
let drewDecorations = false;
|
|
1715
|
+
let displayObject;
|
|
1716
|
+
tokens.forEach((t, index) => {
|
|
1717
|
+
if (isTextToken(t)) {
|
|
1718
|
+
displayObject = this.createTextFieldForToken(t);
|
|
1719
|
+
textContainer.addChild(displayObject);
|
|
1720
|
+
this.textFields.push(displayObject);
|
|
1721
|
+
if (t.textDecorations && t.textDecorations.length > 0) {
|
|
1722
|
+
for (const d of t.textDecorations) {
|
|
1723
|
+
const drawing = this.createDrawingForTextDecoration(d);
|
|
1724
|
+
displayObject.addChild(drawing);
|
|
1725
|
+
this._decorations.push(drawing);
|
|
1726
|
+
}
|
|
1727
|
+
drewDecorations = true;
|
|
1728
|
+
}
|
|
1729
|
+
}
|
|
1730
|
+
if (isSpriteToken(t)) {
|
|
1731
|
+
displayObject = t.content;
|
|
1732
|
+
this.sprites.push(displayObject);
|
|
1733
|
+
spriteContainer.addChild(displayObject);
|
|
1734
|
+
}
|
|
1735
|
+
const {
|
|
1736
|
+
bounds
|
|
1737
|
+
} = t;
|
|
1738
|
+
displayObject.x = bounds.x;
|
|
1739
|
+
displayObject.y = bounds.y;
|
|
1740
|
+
if (isSpriteToken(t)) {
|
|
1741
|
+
const imgDisplay = t.style['imgDisplay'];
|
|
1742
|
+
if (imgDisplay === 'icon') {
|
|
1743
|
+
let fontSize = t.style.fontSize || 20;
|
|
1744
|
+
if (typeof fontSize === 'string') {
|
|
1745
|
+
fontSize = parseInt(fontSize.replace('px', ''), 10);
|
|
1746
|
+
}
|
|
1747
|
+
const iconOffset = fontSize * 0.1;
|
|
1748
|
+
displayObject.y = bounds.y + iconOffset;
|
|
1749
|
+
}
|
|
1750
|
+
}
|
|
1751
|
+
});
|
|
1752
|
+
if (drawWhitespace === false && drewDecorations) {
|
|
1753
|
+
this.logWarning("text-decoration-and-whitespace", "Text decorations, such as underlines, will not appear under whitespace unless the `drawWhitespace` option is set to `true`.");
|
|
1754
|
+
}
|
|
1755
|
+
if (this.options.debug) {
|
|
1756
|
+
this.drawDebug();
|
|
1757
|
+
}
|
|
1758
|
+
this._needsDraw = false;
|
|
1759
|
+
}
|
|
1760
|
+
createDrawingForTextDecoration(textDecoration) {
|
|
1761
|
+
const {
|
|
1762
|
+
overdrawDecorations: overdraw = 0
|
|
1763
|
+
} = this.options;
|
|
1764
|
+
const {
|
|
1765
|
+
bounds
|
|
1766
|
+
} = textDecoration;
|
|
1767
|
+
let {
|
|
1768
|
+
color
|
|
1769
|
+
} = textDecoration;
|
|
1770
|
+
if (!bounds || bounds.width <= 0 && bounds.height <= 0) {
|
|
1771
|
+
return new PIXI__namespace.Graphics();
|
|
1772
|
+
}
|
|
1773
|
+
const drawing = new PIXI__namespace.Graphics();
|
|
1774
|
+
if (typeof color === "string") {
|
|
1775
|
+
if (color.indexOf("#") === 0) {
|
|
1776
|
+
color = "0x" + color.substring(1);
|
|
1777
|
+
color = parseInt(color, 16);
|
|
1778
|
+
} else {
|
|
1779
|
+
this.logWarning("invalid-color", "Sorry, at this point, only hex colors are supported for textDecorations like underlines. Please use either a hex number like 0x66FF33 or a string like '#66FF33'");
|
|
1780
|
+
color = 0x000000;
|
|
1781
|
+
}
|
|
1782
|
+
}
|
|
1783
|
+
if (color === undefined || color === null) {
|
|
1784
|
+
color = 0x000000;
|
|
1785
|
+
}
|
|
1786
|
+
const finalColor = typeof color === 'number' ? color : 0x000000;
|
|
1787
|
+
const {
|
|
1788
|
+
y,
|
|
1789
|
+
height
|
|
1790
|
+
} = bounds;
|
|
1791
|
+
const midpoint = bounds.x + bounds.width / 2;
|
|
1792
|
+
const x = Math.min(bounds.x - overdraw, midpoint);
|
|
1793
|
+
const width = Math.max(bounds.width + overdraw * 2, 0);
|
|
1794
|
+
drawing.setFillStyle({
|
|
1795
|
+
color: finalColor
|
|
1796
|
+
});
|
|
1797
|
+
drawing.rect(x, y, width, height);
|
|
1798
|
+
drawing.fill();
|
|
1799
|
+
return drawing;
|
|
1800
|
+
}
|
|
1801
|
+
createTextField(text, style) {
|
|
1802
|
+
const cleanedStyle = {
|
|
1803
|
+
...style
|
|
1804
|
+
};
|
|
1805
|
+
if (cleanedStyle.stroke && typeof cleanedStyle.stroke === 'string') {
|
|
1806
|
+
cleanedStyle.stroke = {
|
|
1807
|
+
color: cleanedStyle.stroke,
|
|
1808
|
+
width: cleanedStyle.strokeThickness || 0
|
|
1809
|
+
};
|
|
1810
|
+
}
|
|
1811
|
+
const textField = new PIXI__namespace.Text({
|
|
1812
|
+
text: text,
|
|
1813
|
+
style: cleanedStyle
|
|
1814
|
+
});
|
|
1815
|
+
return textField;
|
|
1816
|
+
}
|
|
1817
|
+
createTextFieldForToken(token) {
|
|
1818
|
+
const {
|
|
1819
|
+
textTransform = ""
|
|
1820
|
+
} = token.style;
|
|
1821
|
+
let text = token.content;
|
|
1822
|
+
switch (textTransform.toLowerCase()) {
|
|
1823
|
+
case "lowercase":
|
|
1824
|
+
text = text.toLowerCase();
|
|
1825
|
+
break;
|
|
1826
|
+
case "uppercase":
|
|
1827
|
+
text = text.toUpperCase();
|
|
1828
|
+
break;
|
|
1829
|
+
case "capitalize":
|
|
1830
|
+
text = capitalize(text);
|
|
1831
|
+
break;
|
|
1832
|
+
}
|
|
1833
|
+
const alignClassic = convertUnsupportedAlignment(token.style.align);
|
|
1834
|
+
const sanitizedStyle = {
|
|
1835
|
+
...token.style,
|
|
1836
|
+
align: alignClassic
|
|
1837
|
+
};
|
|
1838
|
+
const textField = this.createTextField(text, sanitizedStyle);
|
|
1839
|
+
let {
|
|
1840
|
+
fontScaleWidth = 1.0,
|
|
1841
|
+
fontScaleHeight = 1.0
|
|
1842
|
+
} = token.style;
|
|
1843
|
+
fontScaleWidth = isNaN(fontScaleWidth) || fontScaleWidth < 0 ? 0 : fontScaleWidth;
|
|
1844
|
+
fontScaleHeight = isNaN(fontScaleHeight) || fontScaleHeight < 0 ? 0 : fontScaleHeight;
|
|
1845
|
+
let finalScaleWidth = fontScaleWidth;
|
|
1846
|
+
let finalScaleHeight = fontScaleHeight;
|
|
1847
|
+
const largerScale = Math.max(fontScaleWidth, fontScaleHeight);
|
|
1848
|
+
if (largerScale > 1) {
|
|
1849
|
+
if (largerScale === fontScaleHeight) {
|
|
1850
|
+
finalScaleWidth /= largerScale;
|
|
1851
|
+
finalScaleHeight = 1.0;
|
|
1852
|
+
} else {
|
|
1853
|
+
finalScaleHeight /= largerScale;
|
|
1854
|
+
finalScaleWidth = 1.0;
|
|
1855
|
+
}
|
|
1856
|
+
const fs = textField.style.fontSize ?? 0;
|
|
1857
|
+
const fontSizePx = (typeof fs === "string" ? fontSizeStringToNumber(fs) : fs) * largerScale;
|
|
1858
|
+
textField.style.fontSize = fontSizePx;
|
|
1859
|
+
}
|
|
1860
|
+
textField.scale.set(finalScaleWidth, finalScaleHeight);
|
|
1861
|
+
return textField;
|
|
1862
|
+
}
|
|
1863
|
+
toDebugString() {
|
|
1864
|
+
const lines = this.tokens;
|
|
1865
|
+
let s = this.untaggedText + "\n=====\n";
|
|
1866
|
+
const nl = "\n ";
|
|
1867
|
+
if (lines !== undefined) {
|
|
1868
|
+
s += lines.map((line, lineNumber) => line.map((word, wordNumber) => word.map((token, tokenNumber) => {
|
|
1869
|
+
let text = "";
|
|
1870
|
+
if (isTextToken(token)) {
|
|
1871
|
+
if (isNewlineToken(token)) {
|
|
1872
|
+
text = `\\n`;
|
|
1873
|
+
} else {
|
|
1874
|
+
text = `"${token.content}"`;
|
|
1875
|
+
}
|
|
1876
|
+
} else if (isSpriteToken(token)) {
|
|
1877
|
+
text = `[Image]`;
|
|
1878
|
+
}
|
|
1879
|
+
let s = `\n${text}: (${lineNumber}/${wordNumber}/${tokenNumber})`;
|
|
1880
|
+
s += `${nl}tags: ${token.tags.length === 0 ? "<none>" : token.tags.split(",").map(tag => `<${tag}>`).join(", ")}`;
|
|
1881
|
+
s += `${nl}style: ${Object.entries(token.style).map(e => e.join(":")).join("; ")}`;
|
|
1882
|
+
s += `${nl}size: x:${token.bounds.x} y:${token.bounds.y} width:${token.bounds.width} height:${token.bounds.height} bottom:${token.bounds.height + token.bounds.y} right:${token.bounds.x + token.bounds.width}`;
|
|
1883
|
+
s += `${nl}font: fontSize:${token.fontProperties.fontSize} ascent:${token.fontProperties.ascent} descent:${token.fontProperties.descent}`;
|
|
1884
|
+
return s;
|
|
1885
|
+
}).join("\n")));
|
|
1886
|
+
}
|
|
1887
|
+
return s;
|
|
1888
|
+
}
|
|
1889
|
+
drawDebug() {
|
|
1890
|
+
const paragraph = this.tokens;
|
|
1891
|
+
this._debugGraphics = new PIXI__namespace.Graphics();
|
|
1892
|
+
if (this.debugContainer === null) {
|
|
1893
|
+
throw new Error("Somehow the debug container is null. This shouldn't be possible. Perhaps you've destroyed this object?");
|
|
1894
|
+
}
|
|
1895
|
+
const debugContainer = this.debugContainer;
|
|
1896
|
+
debugContainer.removeChildren();
|
|
1897
|
+
debugContainer.addChild(this._debugGraphics);
|
|
1898
|
+
const g = this._debugGraphics;
|
|
1899
|
+
g.clear();
|
|
1900
|
+
let lineHeightGraphics = new PIXI__namespace.Graphics();
|
|
1901
|
+
lineHeightGraphics.name = 'lineHeightGraphics';
|
|
1902
|
+
debugContainer.addChild(lineHeightGraphics);
|
|
1903
|
+
lineHeightGraphics.clear();
|
|
1904
|
+
let baselineGraphics = new PIXI__namespace.Graphics();
|
|
1905
|
+
baselineGraphics.name = 'baselineGraphics';
|
|
1906
|
+
baselineGraphics.visible = true;
|
|
1907
|
+
baselineGraphics.alpha = 1;
|
|
1908
|
+
debugContainer.addChild(baselineGraphics);
|
|
1909
|
+
baselineGraphics.clear();
|
|
1910
|
+
const baselines = [];
|
|
1911
|
+
function createInfoText(text, position) {
|
|
1912
|
+
const info = new PIXI__namespace.Text({
|
|
1913
|
+
text,
|
|
1914
|
+
style: DEBUG.TEXT_STYLE
|
|
1915
|
+
});
|
|
1916
|
+
info.x = position.x + 1;
|
|
1917
|
+
info.y = position.y + 1;
|
|
1918
|
+
return info;
|
|
1919
|
+
}
|
|
1920
|
+
for (let lineNumber = 0; lineNumber < paragraph.length; lineNumber++) {
|
|
1921
|
+
const line = paragraph[lineNumber];
|
|
1922
|
+
const lineBounds = getBoundsNested(line);
|
|
1923
|
+
let lineBoxY = lineBounds.y + 2;
|
|
1924
|
+
let lineBoxHeight = lineBounds.height;
|
|
1925
|
+
if (this.defaultStyle.wordWrap) {
|
|
1926
|
+
const w = this.defaultStyle.wordWrapWidth ?? this.width;
|
|
1927
|
+
g.lineStyle(0.5, DEBUG.LINE_COLOR, 0.2);
|
|
1928
|
+
g.drawRect(0, lineBoxY, w, lineBoxHeight);
|
|
1929
|
+
}
|
|
1930
|
+
for (let wordNumber = 0; wordNumber < line.length; wordNumber++) {
|
|
1931
|
+
const word = line[wordNumber];
|
|
1932
|
+
for (const segmentToken of word) {
|
|
1933
|
+
const isSprite = isSpriteToken(segmentToken);
|
|
1934
|
+
const {
|
|
1935
|
+
x,
|
|
1936
|
+
y,
|
|
1937
|
+
width
|
|
1938
|
+
} = segmentToken.bounds;
|
|
1939
|
+
let adjustedY = y;
|
|
1940
|
+
const fontSize = segmentToken.fontProperties?.fontSize || 24;
|
|
1941
|
+
let adjustment = 0;
|
|
1942
|
+
if (fontSize <= 24) {
|
|
1943
|
+
adjustment = -2;
|
|
1944
|
+
} else if (fontSize <= 28) {
|
|
1945
|
+
adjustment = -3;
|
|
1946
|
+
} else if (fontSize === 36) {
|
|
1947
|
+
adjustment = 5;
|
|
1948
|
+
} else if (fontSize > 36 && fontSize <= 47) {
|
|
1949
|
+
adjustment = 5 + (fontSize - 36) * 0.2;
|
|
1950
|
+
} else if (fontSize > 47) {
|
|
1951
|
+
adjustment = 7 + (fontSize - 47) * 0.1;
|
|
1952
|
+
}
|
|
1953
|
+
adjustedY -= adjustment;
|
|
1954
|
+
let baseline;
|
|
1955
|
+
if (isSprite) {
|
|
1956
|
+
baseline = adjustedY + segmentToken.bounds.height;
|
|
1957
|
+
} else {
|
|
1958
|
+
baseline = adjustedY + segmentToken.fontProperties.ascent;
|
|
1959
|
+
const hasStroke = segmentToken.style?.stroke && segmentToken.style.strokeThickness > 0;
|
|
1960
|
+
if (fontSize === 36) {
|
|
1961
|
+
baseline += 15;
|
|
1962
|
+
} else if (hasStroke && fontSize <= 28) {
|
|
1963
|
+
baseline += 6;
|
|
1964
|
+
} else if (fontSize <= 24) {
|
|
1965
|
+
baseline += 4;
|
|
1966
|
+
} else {
|
|
1967
|
+
baseline += 4;
|
|
1968
|
+
}
|
|
1969
|
+
}
|
|
1970
|
+
let boxY = adjustedY;
|
|
1971
|
+
let boxHeight = segmentToken.bounds.height;
|
|
1972
|
+
if (!isSprite) {
|
|
1973
|
+
const ascent = segmentToken.fontProperties.ascent;
|
|
1974
|
+
const descent = segmentToken.fontProperties.descent;
|
|
1975
|
+
boxY = baseline - ascent;
|
|
1976
|
+
boxHeight = ascent + descent;
|
|
1977
|
+
} else {
|
|
1978
|
+
boxHeight += segmentToken.fontProperties.descent;
|
|
1979
|
+
}
|
|
1980
|
+
const strokeColor = isWhitespaceToken(segmentToken) && this.options.drawWhitespace === false ? DEBUG.WHITESPACE_STROKE_COLOR : DEBUG.WORD_STROKE_COLOR;
|
|
1981
|
+
const fillColor = isWhitespaceToken(segmentToken) && this.options.drawWhitespace === false ? DEBUG.WHITESPACE_COLOR : DEBUG.WORD_FILL_COLOR;
|
|
1982
|
+
if (isNewlineToken(segmentToken)) {
|
|
1983
|
+
this.debugContainer.addChild(createInfoText("↩︎", {
|
|
1984
|
+
x,
|
|
1985
|
+
y: boxY + 10
|
|
1986
|
+
}));
|
|
1987
|
+
} else {
|
|
1988
|
+
g.rect(x, boxY, width, boxHeight).stroke({
|
|
1989
|
+
width: 0.5,
|
|
1990
|
+
color: DEBUG.LINE_COLOR,
|
|
1991
|
+
alpha: 0.2
|
|
1992
|
+
});
|
|
1993
|
+
g.rect(x, y, width, segmentToken.bounds.height).fill({
|
|
1994
|
+
color: fillColor,
|
|
1995
|
+
alpha: 0.2
|
|
1996
|
+
}).stroke({
|
|
1997
|
+
width: 0.5,
|
|
1998
|
+
color: strokeColor,
|
|
1999
|
+
alpha: 0.5
|
|
2000
|
+
});
|
|
2001
|
+
baselines.push({
|
|
2002
|
+
x,
|
|
2003
|
+
baseline,
|
|
2004
|
+
width
|
|
2005
|
+
});
|
|
2006
|
+
}
|
|
2007
|
+
let info;
|
|
2008
|
+
if (isTextToken(segmentToken)) {
|
|
2009
|
+
info = `${segmentToken.tags}`;
|
|
2010
|
+
this.debugContainer.addChild(createInfoText(info, {
|
|
2011
|
+
x,
|
|
2012
|
+
y: boxY
|
|
2013
|
+
}));
|
|
2014
|
+
}
|
|
2015
|
+
}
|
|
2016
|
+
}
|
|
2017
|
+
}
|
|
2018
|
+
if (baselines.length > 0) {
|
|
2019
|
+
baselineGraphics.beginFill(DEBUG.BASELINE_COLOR, 0.8);
|
|
2020
|
+
for (const {
|
|
2021
|
+
x,
|
|
2022
|
+
baseline,
|
|
2023
|
+
width
|
|
2024
|
+
} of baselines) {
|
|
2025
|
+
baselineGraphics.drawRect(x, baseline - 1, width, 2);
|
|
2026
|
+
}
|
|
2027
|
+
baselineGraphics.endFill();
|
|
2028
|
+
}
|
|
2029
|
+
}
|
|
2030
|
+
}
|
|
2031
|
+
|
|
2032
|
+
exports.Glyphs = Glyphs;
|
|
2033
|
+
exports["default"] = Glyphs;
|
|
2034
|
+
//# sourceMappingURL=pixi-glyphs.js.map
|