element-symbol-format 1.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 ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 Paul Puhnaty
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
package/README.md ADDED
@@ -0,0 +1,99 @@
1
+ # Element Symbol Format
2
+
3
+ Capitalizes a string so the letters match the chemical element symbols found
4
+ on the [Periodic Table](https://wikipedia.org/wiki/Periodic_table). For example, the string "Case one" will be formatted
5
+ as "CaSe ONe":
6
+
7
+ - `Ca` > [Calcium](https://wikipedia.org/wiki/Calcium)
8
+ - `Se` > [Selenium](https://wikipedia.org/wiki/Selenium)
9
+ - `O` > [Oxygen](https://wikipedia.org/wiki/Oxygen)
10
+ - `Ne` > [Neon](https://wikipedia.org/wiki/Neon)
11
+
12
+
13
+ ## Installation
14
+
15
+ Install using npm:
16
+
17
+ `$ npm i element-symbol-format`
18
+
19
+ ## Usage
20
+
21
+ ### Import
22
+
23
+ Import the `element-symbol-format` package in your code.
24
+
25
+ ```javascript
26
+ // ESM
27
+ import { elementSymbolFormat } from 'element-symbol-format';
28
+
29
+ // CJS
30
+ const { elementSymbolFormat } = require('element-symbol-format');
31
+ ```
32
+
33
+ ### Format Strings
34
+
35
+ Pass your strings to the `elementSymbolFormat` function.
36
+
37
+ ```javascript
38
+ const result = elementSymbolFormat('I snack on chocolate');
39
+
40
+ // The result is an array of formatted words.
41
+
42
+ console.log(result.length) // 4
43
+
44
+ // You can get the entire result at once...
45
+
46
+ console.log(result.toString()); // I SnAcK ON CHoCoLaTe
47
+ console.log(result.toSymbols()); // [ 'I', ' ', 'Sn', 'Ac', 'K', ' ', 'O', 'N', ... ]
48
+
49
+ // ...or access each word individually
50
+
51
+ console.log(result[3].toString()); // CHoCoLaTe
52
+ console.log(result[3].toSymbols()); // [ 'C', 'Ho', 'Co', 'La', 'Te' ]
53
+ ```
54
+
55
+ ### Handling Formatting Failures
56
+
57
+ Unfortunately, many common words can't be represented completely with element symbols.
58
+ Make sure to check the `isFormatted` field before using the result.
59
+
60
+ ```javascript
61
+ const result = elementSymbolFormat('Learn how to fail intelligently ~ Charles Kettering')
62
+
63
+ console.log(result.isFormatted) // false
64
+
65
+ console.log(result[2].isFormatted) // true
66
+ console.log(result[4].isFormatted) // false
67
+
68
+ console.log(result[4].formatted.toString()) // InTe
69
+ console.log(result[4].unformatted) // lligently
70
+ ```
71
+
72
+ ### Options
73
+
74
+ The behavior of the formatter can be customized by passing an object as
75
+ a parameter to `elementSymbolFormat`.
76
+
77
+ ```javascript
78
+ const defaultOptions = {
79
+ // The input string will be split on every occurrence of "split".
80
+ // If "split" is an empty string, the input string will not be split.
81
+ "split": ' ',
82
+
83
+ // The input string will be split at every non-letter.
84
+ // If set to true, this overrides "split".
85
+ "strictSplit": false,
86
+
87
+ // Prefer single-letter element symbols when possible.
88
+ "preferSingleSymbols": false,
89
+
90
+ // Remove non-letters from the input string.
91
+ "stripNonLetters": true,
92
+
93
+ // The string used to join the formatted words together into a string.
94
+ // If not defined, then "split" will be used.
95
+ "join": undefined,
96
+ }
97
+
98
+ const result = elementSymbolFormat('Pass options here ->', defaultOptions)
99
+ ```
@@ -0,0 +1,28 @@
1
+ interface FormattedCheckable {
2
+ readonly isFormatted: boolean;
3
+ }
4
+ interface SymbolDecomposable {
5
+ toString(): string;
6
+ toSymbols(): string[];
7
+ }
8
+ interface PartiallyFormattable extends SymbolDecomposable, FormattedCheckable {
9
+ get formatted(): SymbolDecomposable;
10
+ get unformatted(): string;
11
+ }
12
+ interface FormattedResult extends Array<PartiallyFormattable>, SymbolDecomposable, FormattedCheckable {
13
+ }
14
+ interface FormatOptions {
15
+ split?: string;
16
+ strictSplit?: boolean;
17
+ preferSingleSymbols?: boolean;
18
+ stripNonLetters?: boolean;
19
+ join?: string;
20
+ }
21
+
22
+ declare function elementSymbolFormat(input: string, options?: FormatOptions): FormattedResult;
23
+
24
+ declare const _default: {
25
+ elementSymbolFormat(input: string, options?: FormatOptions): FormattedResult;
26
+ };
27
+
28
+ export { type FormatOptions, type FormattedCheckable, type FormattedResult, type PartiallyFormattable, type SymbolDecomposable, _default as default, elementSymbolFormat };
@@ -0,0 +1,28 @@
1
+ interface FormattedCheckable {
2
+ readonly isFormatted: boolean;
3
+ }
4
+ interface SymbolDecomposable {
5
+ toString(): string;
6
+ toSymbols(): string[];
7
+ }
8
+ interface PartiallyFormattable extends SymbolDecomposable, FormattedCheckable {
9
+ get formatted(): SymbolDecomposable;
10
+ get unformatted(): string;
11
+ }
12
+ interface FormattedResult extends Array<PartiallyFormattable>, SymbolDecomposable, FormattedCheckable {
13
+ }
14
+ interface FormatOptions {
15
+ split?: string;
16
+ strictSplit?: boolean;
17
+ preferSingleSymbols?: boolean;
18
+ stripNonLetters?: boolean;
19
+ join?: string;
20
+ }
21
+
22
+ declare function elementSymbolFormat(input: string, options?: FormatOptions): FormattedResult;
23
+
24
+ declare const _default: {
25
+ elementSymbolFormat(input: string, options?: FormatOptions): FormattedResult;
26
+ };
27
+
28
+ export { type FormatOptions, type FormattedCheckable, type FormattedResult, type PartiallyFormattable, type SymbolDecomposable, _default as default, elementSymbolFormat };
package/dist/index.js ADDED
@@ -0,0 +1,430 @@
1
+ "use strict";
2
+ var __defProp = Object.defineProperty;
3
+ var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
4
+ var __getOwnPropNames = Object.getOwnPropertyNames;
5
+ var __hasOwnProp = Object.prototype.hasOwnProperty;
6
+ var __export = (target, all) => {
7
+ for (var name in all)
8
+ __defProp(target, name, { get: all[name], enumerable: true });
9
+ };
10
+ var __copyProps = (to, from, except, desc) => {
11
+ if (from && typeof from === "object" || typeof from === "function") {
12
+ for (let key of __getOwnPropNames(from))
13
+ if (!__hasOwnProp.call(to, key) && key !== except)
14
+ __defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
15
+ }
16
+ return to;
17
+ };
18
+ var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
19
+
20
+ // src/index.ts
21
+ var index_exports = {};
22
+ __export(index_exports, {
23
+ default: () => index_default,
24
+ elementSymbolFormat: () => elementSymbolFormat
25
+ });
26
+ module.exports = __toCommonJS(index_exports);
27
+
28
+ // src/interfaces.ts
29
+ var interfaces_exports = {};
30
+
31
+ // src/api.ts
32
+ var api_exports = {};
33
+ __export(api_exports, {
34
+ elementSymbolFormat: () => elementSymbolFormat
35
+ });
36
+
37
+ // src/helpers.ts
38
+ function doSplit(input, split) {
39
+ if (input === "") return [];
40
+ if (split === "") return [input];
41
+ return input.split(split);
42
+ }
43
+ function doStrictSplit(input) {
44
+ const output = [];
45
+ if (input.length === 0) return output;
46
+ let iStart = 0;
47
+ let wasLetter = isLetter(input[iStart]);
48
+ for (let iEnd = 1; iEnd < input.length; iEnd++) {
49
+ const nowLetter = isLetter(input[iEnd]);
50
+ if (nowLetter !== wasLetter) {
51
+ output.push(input.slice(iStart, iEnd));
52
+ wasLetter = nowLetter;
53
+ iStart = iEnd;
54
+ }
55
+ }
56
+ output.push(input.slice(iStart));
57
+ return output;
58
+ }
59
+ function doStripNonLetters(input) {
60
+ for (let i = 0; i < input.length; i++) {
61
+ input[i] = input[i].split("").filter((char) => isLetter(char)).join("");
62
+ }
63
+ return input.filter((s) => s.length !== 0);
64
+ }
65
+ function isLetter(ch) {
66
+ return ch >= "a" && ch <= "z" || ch >= "A" && ch <= "Z";
67
+ }
68
+ function isLowerCase(ch) {
69
+ return ch >= "a" && ch <= "z";
70
+ }
71
+ function isUpperCase(ch) {
72
+ return ch >= "A" && ch <= "Z";
73
+ }
74
+
75
+ // src/types.ts
76
+ var SegmentSymbols = class {
77
+ value;
78
+ constructor(value) {
79
+ this.value = value;
80
+ }
81
+ toString() {
82
+ return this.value;
83
+ }
84
+ toSymbols() {
85
+ const symbols = [];
86
+ const chars = this.value.split("").filter((char) => isLetter(char));
87
+ for (let i = 0; i < chars.length; i++) {
88
+ if (isUpperCase(chars[i])) {
89
+ const iNext = i + 1;
90
+ if (iNext < chars.length && isLowerCase(chars[iNext])) {
91
+ symbols.push(chars[i] + chars[iNext]);
92
+ } else {
93
+ symbols.push(chars[i]);
94
+ }
95
+ }
96
+ }
97
+ return symbols;
98
+ }
99
+ };
100
+ var ResultSegment = class {
101
+ isFormatted;
102
+ value;
103
+ firstMiss;
104
+ constructor(value, firstMiss) {
105
+ this.firstMiss = firstMiss;
106
+ this.value = value;
107
+ this.isFormatted = firstMiss >= value.length;
108
+ }
109
+ get formatted() {
110
+ const formattedPart = this.value.slice(0, this.firstMiss);
111
+ return new SegmentSymbols(formattedPart, true);
112
+ }
113
+ get unformatted() {
114
+ if (this.isFormatted) {
115
+ return "";
116
+ } else {
117
+ return this.value.slice(this.firstMiss);
118
+ }
119
+ }
120
+ toString() {
121
+ return this.value;
122
+ }
123
+ toSymbols() {
124
+ const symbols = this.formatted.toSymbols();
125
+ if (!this.isFormatted) {
126
+ const stripped = this.unformatted.split("").filter((char) => isLetter(char)).join("");
127
+ symbols.push(stripped);
128
+ }
129
+ return symbols;
130
+ }
131
+ };
132
+ var ResultArray = class extends Array {
133
+ isFormatted;
134
+ joinStr;
135
+ constructor(join, ...segments) {
136
+ super(...segments);
137
+ this.isFormatted = true;
138
+ this.joinStr = join;
139
+ for (const segment of segments) {
140
+ if (!segment.isFormatted) {
141
+ this.isFormatted = false;
142
+ break;
143
+ }
144
+ }
145
+ }
146
+ toString() {
147
+ const segments = [];
148
+ for (const segment of this) {
149
+ segments.push(segment.toString());
150
+ }
151
+ return segments.join(this.joinStr);
152
+ }
153
+ toSymbols() {
154
+ const symbols = [];
155
+ this.forEach((segment, index) => {
156
+ const segmentSymbols = segment.toSymbols();
157
+ symbols.push(...segmentSymbols);
158
+ if (index != this.length - 1) {
159
+ symbols.push(this.joinStr);
160
+ }
161
+ }, this);
162
+ return symbols;
163
+ }
164
+ };
165
+
166
+ // src/elements.ts
167
+ var elementSymbols = [
168
+ "H",
169
+ "He",
170
+ "Li",
171
+ "Be",
172
+ "B",
173
+ "C",
174
+ "N",
175
+ "O",
176
+ "F",
177
+ "Ne",
178
+ "Na",
179
+ "Mg",
180
+ "Al",
181
+ "Si",
182
+ "P",
183
+ "S",
184
+ "Cl",
185
+ "Ar",
186
+ "K",
187
+ "Ca",
188
+ "Sc",
189
+ "Ti",
190
+ "V",
191
+ "Cr",
192
+ "Mn",
193
+ "Fe",
194
+ "Co",
195
+ "Ni",
196
+ "Cu",
197
+ "Zn",
198
+ "Ga",
199
+ "Ge",
200
+ "As",
201
+ "Se",
202
+ "Br",
203
+ "Kr",
204
+ "Rb",
205
+ "Sr",
206
+ "Y",
207
+ "Zr",
208
+ "Nb",
209
+ "Mo",
210
+ "Tc",
211
+ "Ru",
212
+ "Rh",
213
+ "Pd",
214
+ "Ag",
215
+ "Cd",
216
+ "In",
217
+ "Sn",
218
+ "Sb",
219
+ "Te",
220
+ "I",
221
+ "Xe",
222
+ "Cs",
223
+ "Ba",
224
+ "La",
225
+ "Ce",
226
+ "Pr",
227
+ "Nd",
228
+ "Pm",
229
+ "Sm",
230
+ "Eu",
231
+ "Gd",
232
+ "Tb",
233
+ "Dy",
234
+ "Ho",
235
+ "Er",
236
+ "Tm",
237
+ "Yb",
238
+ "Lu",
239
+ "Hf",
240
+ "Ta",
241
+ "W",
242
+ "Re",
243
+ "Os",
244
+ "Ir",
245
+ "Pt",
246
+ "Au",
247
+ "Hg",
248
+ "Tl",
249
+ "Pb",
250
+ "Bi",
251
+ "Po",
252
+ "At",
253
+ "Rn",
254
+ "Fr",
255
+ "Ra",
256
+ "Ac",
257
+ "Th",
258
+ "Pa",
259
+ "U",
260
+ "Np",
261
+ "Pu",
262
+ "Am",
263
+ "Cm",
264
+ "Bk",
265
+ "Cf",
266
+ "Es",
267
+ "Fm",
268
+ "Md",
269
+ "No",
270
+ "Lr",
271
+ "Rf",
272
+ "Db",
273
+ "Sg",
274
+ "Bh",
275
+ "Hs",
276
+ "Mt",
277
+ "Ds",
278
+ "Rg",
279
+ "Cn",
280
+ "Nh",
281
+ "Fl",
282
+ "Mc",
283
+ "Lv",
284
+ "Ts",
285
+ "Og"
286
+ ];
287
+ var elementSet = new Set(elementSymbols.map((s) => s.toLowerCase()));
288
+ function elementLookup(input) {
289
+ return elementSet.has(input.toLowerCase());
290
+ }
291
+
292
+ // src/formatter.ts
293
+ var SmartIndex = class {
294
+ _index = 1;
295
+ get mark() {
296
+ return this._index;
297
+ }
298
+ get char() {
299
+ return this._index - 1;
300
+ }
301
+ set mark(i) {
302
+ this._index = i;
303
+ }
304
+ set char(i) {
305
+ this._index = i + 1;
306
+ }
307
+ };
308
+ function format(input, preferSingleSymbols) {
309
+ input = input.toLowerCase();
310
+ const markers = findFormatMarkers(input);
311
+ let firstMiss;
312
+ for (let i = input.length; i >= 0; i--) {
313
+ if (markers[i] !== 0 /* Unmarked */) {
314
+ firstMiss = i;
315
+ break;
316
+ }
317
+ }
318
+ const output = applyFormatMarkers(input, markers, preferSingleSymbols);
319
+ return new ResultSegment(output, firstMiss);
320
+ }
321
+ function findFormatMarkers(input) {
322
+ const markers = new Array(input.length + 1).fill(0 /* Unmarked */);
323
+ markers[0] = 1 /* Start */;
324
+ const i = new SmartIndex();
325
+ for (i.char = 0; i.char < input.length; i.char++) {
326
+ if (markers[i.mark - 1] === 0 /* Unmarked */) continue;
327
+ if (markers[i.mark - 1] === 6 /* FirstHalf */) continue;
328
+ if (!isLetter(input[i.char])) {
329
+ markers[i.mark] = 5 /* Skip */;
330
+ continue;
331
+ }
332
+ if (elementLookup(input[i.char])) {
333
+ if (markers[i.mark] === 3 /* TwoLetter */) {
334
+ markers[i.mark] = 4 /* OneOrTwoLetter */;
335
+ } else {
336
+ markers[i.mark] = 2 /* OneLetter */;
337
+ }
338
+ }
339
+ let offset = 1;
340
+ while (i.char + offset < input.length) {
341
+ if (!isLetter(input[i.char + offset])) {
342
+ offset += 1;
343
+ continue;
344
+ }
345
+ const digram = input[i.char] + input[i.char + offset];
346
+ if (elementLookup(digram)) {
347
+ markers[i.mark + offset] = 3 /* TwoLetter */;
348
+ if (markers[i.mark] === 0 /* Unmarked */) {
349
+ markers[i.mark] = 6 /* FirstHalf */;
350
+ }
351
+ }
352
+ break;
353
+ }
354
+ }
355
+ return markers;
356
+ }
357
+ function applyFormatMarkers(input, markers, preferSingleSymbols) {
358
+ const array = input.split("");
359
+ const i = new SmartIndex();
360
+ i.mark = input.length;
361
+ while (i.mark > 0) {
362
+ if (markers[i.mark] === 0 /* Unmarked */ || markers[i.mark] === 5 /* Skip */) {
363
+ i.mark -= 1;
364
+ continue;
365
+ }
366
+ if (markers[i.mark] === 4 /* OneOrTwoLetter */) {
367
+ if (preferSingleSymbols) {
368
+ markers[i.mark] = 2 /* OneLetter */;
369
+ } else {
370
+ markers[i.mark] = 3 /* TwoLetter */;
371
+ }
372
+ }
373
+ if (markers[i.mark] === 2 /* OneLetter */) {
374
+ array[i.char] = array[i.char].toUpperCase();
375
+ } else if (markers[i.mark] === 3 /* TwoLetter */) {
376
+ let offset = 1;
377
+ while (markers[i.mark - offset] === 5 /* Skip */ || markers[i.mark - offset] === 0 /* Unmarked */) {
378
+ offset += 1;
379
+ }
380
+ array[i.char - offset] = array[i.char - offset].toUpperCase();
381
+ i.mark -= offset;
382
+ }
383
+ i.mark -= 1;
384
+ }
385
+ return array.join("");
386
+ }
387
+
388
+ // src/api.ts
389
+ function valueOrDefault(value, defaultValue) {
390
+ return value !== void 0 ? value : defaultValue;
391
+ }
392
+ function elementSymbolFormat(input, options) {
393
+ const split = valueOrDefault(options?.split, " ");
394
+ const strictSplit = valueOrDefault(options?.strictSplit, false);
395
+ const preferSingleSymbols = valueOrDefault(options?.preferSingleSymbols, false);
396
+ const stripNonLetters = valueOrDefault(options?.stripNonLetters, true);
397
+ const join = valueOrDefault(options?.join, split);
398
+ const splitLetter = split.split("").find((char) => isLetter(char));
399
+ if (splitLetter !== void 0) {
400
+ throw new Error(
401
+ `elementSymbolFormat() will not split on a string with a letter "${splitLetter}"`
402
+ );
403
+ }
404
+ let inputSegments;
405
+ if (strictSplit) {
406
+ inputSegments = doStrictSplit(input);
407
+ } else {
408
+ inputSegments = doSplit(input, split);
409
+ }
410
+ if (stripNonLetters) {
411
+ inputSegments = doStripNonLetters(inputSegments);
412
+ }
413
+ const outputSegments = [];
414
+ for (const segment of inputSegments) {
415
+ const result = format(segment, preferSingleSymbols);
416
+ outputSegments.push(result);
417
+ }
418
+ return new ResultArray(join, ...outputSegments);
419
+ }
420
+
421
+ // src/index.ts
422
+ var index_default = {
423
+ ...interfaces_exports,
424
+ ...api_exports
425
+ };
426
+ // Annotate the CommonJS export names for ESM import in node:
427
+ 0 && (module.exports = {
428
+ elementSymbolFormat
429
+ });
430
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/index.ts","../src/interfaces.ts","../src/api.ts","../src/helpers.ts","../src/types.ts","../src/elements.ts","../src/formatter.ts"],"sourcesContent":["// SPDX-License-Identifier: MIT\n// Copyright © 2026 Paul Puhnaty\n\nexport * from \"./interfaces\";\nexport * from \"./api\";\n\nimport * as interfaces from \"./interfaces\";\nimport * as api from \"./api\";\n\nexport default {\n ...interfaces,\n ...api,\n};\n","// SPDX-License-Identifier: MIT\n// Copyright © 2026 Paul Puhnaty\n\nexport interface FormattedCheckable {\n readonly isFormatted: boolean;\n}\n\nexport interface SymbolDecomposable {\n toString(): string;\n toSymbols(): string[];\n}\n\nexport interface PartiallyFormattable extends SymbolDecomposable, FormattedCheckable {\n get formatted(): SymbolDecomposable;\n get unformatted(): string;\n}\n\nexport interface FormattedResult\n extends Array<PartiallyFormattable>, SymbolDecomposable, FormattedCheckable {}\n\nexport interface FormatOptions {\n split?: string;\n strictSplit?: boolean;\n preferSingleSymbols?: boolean;\n stripNonLetters?: boolean;\n join?: string;\n}\n","// SPDX-License-Identifier: MIT\n// Copyright © 2026 Paul Puhnaty\n\nimport * as interfaces from \"./interfaces\";\nimport * as types from \"./types\";\nimport * as helpers from \"./helpers\";\nimport { format } from \"./formatter\";\n\nfunction valueOrDefault<T>(value: T | undefined, defaultValue: T): T {\n return value !== undefined ? value : defaultValue;\n}\n\nexport function elementSymbolFormat(\n input: string,\n options?: interfaces.FormatOptions,\n): interfaces.FormattedResult {\n const split = valueOrDefault(options?.split, \" \");\n const strictSplit = valueOrDefault(options?.strictSplit, false);\n const preferSingleSymbols = valueOrDefault(options?.preferSingleSymbols, false);\n const stripNonLetters = valueOrDefault(options?.stripNonLetters, true);\n const join = valueOrDefault(options?.join, split);\n\n // Verify \"split\"\n\n const splitLetter = split.split(\"\").find((char) => helpers.isLetter(char));\n\n if (splitLetter !== undefined) {\n throw new Error(\n `elementSymbolFormat() will not split on a string with a letter \"${splitLetter}\"`,\n );\n }\n\n // Split input\n\n let inputSegments: string[];\n\n if (strictSplit) {\n inputSegments = helpers.doStrictSplit(input);\n } else {\n inputSegments = helpers.doSplit(input, split);\n }\n\n // Strip input\n\n if (stripNonLetters) {\n inputSegments = helpers.doStripNonLetters(inputSegments);\n }\n\n // Format input\n\n const outputSegments: types.ResultSegment[] = [];\n\n for (const segment of inputSegments) {\n const result = format(segment, preferSingleSymbols);\n outputSegments.push(result);\n }\n\n // Return results\n\n return new types.ResultArray(join, ...outputSegments);\n}\n","// SPDX-License-Identifier: MIT\n// Copyright © 2026 Paul Puhnaty\n\nexport function doSplit(input: string, split: string): string[] {\n if (input === \"\") return [];\n\n if (split === \"\") return [input];\n\n return input.split(split);\n}\n\nexport function doStrictSplit(input: string): string[] {\n const output: string[] = [];\n\n if (input.length === 0) return output;\n\n let iStart = 0;\n let wasLetter: boolean = isLetter(input[iStart]);\n\n for (let iEnd = 1; iEnd < input.length; iEnd++) {\n const nowLetter: boolean = isLetter(input[iEnd]);\n\n if (nowLetter !== wasLetter) {\n output.push(input.slice(iStart, iEnd));\n\n wasLetter = nowLetter;\n iStart = iEnd;\n }\n }\n\n // Add the remaining bit to the output\n output.push(input.slice(iStart));\n\n return output;\n}\n\nexport function doStripNonLetters(input: string[]): string[] {\n for (let i = 0; i < input.length; i++) {\n input[i] = input[i]\n .split(\"\")\n .filter((char) => isLetter(char))\n .join(\"\");\n }\n\n return input.filter((s) => s.length !== 0);\n}\n\nexport function isLetter(ch: string): boolean {\n return (ch >= \"a\" && ch <= \"z\") || (ch >= \"A\" && ch <= \"Z\");\n}\n\nexport function isLowerCase(ch: string): boolean {\n return ch >= \"a\" && ch <= \"z\";\n}\n\nexport function isUpperCase(ch: string): boolean {\n return ch >= \"A\" && ch <= \"Z\";\n}\n","// SPDX-License-Identifier: MIT\n// Copyright © 2026 Paul Puhnaty\n\nimport * as interfaces from \"./interfaces\";\nimport { isLetter, isLowerCase, isUpperCase } from \"./helpers\";\n\nexport class SegmentSymbols implements interfaces.SymbolDecomposable {\n private value: string;\n\n constructor(value: string) {\n this.value = value;\n }\n\n toString(): string {\n return this.value;\n }\n\n toSymbols(): string[] {\n const symbols: string[] = [];\n\n const chars = this.value.split(\"\").filter((char) => isLetter(char));\n\n for (let i = 0; i < chars.length; i++) {\n if (isUpperCase(chars[i])) {\n const iNext = i + 1;\n\n if (iNext < chars.length && isLowerCase(chars[iNext])) {\n symbols.push(chars[i] + chars[iNext]);\n } else {\n symbols.push(chars[i]);\n }\n }\n }\n\n return symbols;\n }\n}\n\nexport class ResultSegment implements interfaces.PartiallyFormattable {\n readonly isFormatted: boolean;\n private value: string;\n private firstMiss: number;\n\n constructor(value: string, firstMiss: number) {\n this.firstMiss = firstMiss;\n this.value = value;\n\n this.isFormatted = firstMiss >= value.length;\n }\n\n get formatted(): SegmentSymbols {\n const formattedPart = this.value.slice(0, this.firstMiss);\n\n return new SegmentSymbols(formattedPart, true);\n }\n\n get unformatted(): string {\n if (this.isFormatted) {\n return \"\";\n } else {\n return this.value.slice(this.firstMiss);\n }\n }\n\n toString(): string {\n return this.value;\n }\n\n toSymbols(): string[] {\n const symbols = this.formatted.toSymbols();\n\n if (!this.isFormatted) {\n const stripped = this.unformatted\n .split(\"\")\n .filter((char) => isLetter(char))\n .join(\"\");\n\n symbols.push(stripped);\n }\n\n return symbols;\n }\n}\n\nexport class ResultArray extends Array<ResultSegment> implements interfaces.FormattedResult {\n readonly isFormatted: boolean;\n private joinStr: string;\n\n constructor(join: string, ...segments: ResultSegment[]) {\n super(...segments);\n\n this.isFormatted = true; // Assume true\n this.joinStr = join;\n\n // Check for unformatted segments\n for (const segment of segments) {\n if (!segment.isFormatted) {\n this.isFormatted = false;\n break;\n }\n }\n }\n\n toString(): string {\n const segments: string[] = [];\n\n for (const segment of this) {\n segments.push(segment.toString());\n }\n\n return segments.join(this.joinStr);\n }\n\n toSymbols(): string[] {\n const symbols: string[] = [];\n\n this.forEach((segment, index) => {\n const segmentSymbols = segment.toSymbols();\n\n symbols.push(...segmentSymbols);\n\n if (index != this.length - 1) {\n symbols.push(this.joinStr);\n }\n }, this);\n\n return symbols;\n }\n}\n","// SPDX-License-Identifier: MIT\n// Copyright © 2026 Paul Puhnaty\n\nconst elementSymbols = [\n \"H\",\n \"He\",\n \"Li\",\n \"Be\",\n \"B\",\n \"C\",\n \"N\",\n \"O\",\n \"F\",\n \"Ne\",\n \"Na\",\n \"Mg\",\n \"Al\",\n \"Si\",\n \"P\",\n \"S\",\n \"Cl\",\n \"Ar\",\n \"K\",\n \"Ca\",\n \"Sc\",\n \"Ti\",\n \"V\",\n \"Cr\",\n \"Mn\",\n \"Fe\",\n \"Co\",\n \"Ni\",\n \"Cu\",\n \"Zn\",\n \"Ga\",\n \"Ge\",\n \"As\",\n \"Se\",\n \"Br\",\n \"Kr\",\n \"Rb\",\n \"Sr\",\n \"Y\",\n \"Zr\",\n \"Nb\",\n \"Mo\",\n \"Tc\",\n \"Ru\",\n \"Rh\",\n \"Pd\",\n \"Ag\",\n \"Cd\",\n \"In\",\n \"Sn\",\n \"Sb\",\n \"Te\",\n \"I\",\n \"Xe\",\n \"Cs\",\n \"Ba\",\n \"La\",\n \"Ce\",\n \"Pr\",\n \"Nd\",\n \"Pm\",\n \"Sm\",\n \"Eu\",\n \"Gd\",\n \"Tb\",\n \"Dy\",\n \"Ho\",\n \"Er\",\n \"Tm\",\n \"Yb\",\n \"Lu\",\n \"Hf\",\n \"Ta\",\n \"W\",\n \"Re\",\n \"Os\",\n \"Ir\",\n \"Pt\",\n \"Au\",\n \"Hg\",\n \"Tl\",\n \"Pb\",\n \"Bi\",\n \"Po\",\n \"At\",\n \"Rn\",\n \"Fr\",\n \"Ra\",\n \"Ac\",\n \"Th\",\n \"Pa\",\n \"U\",\n \"Np\",\n \"Pu\",\n \"Am\",\n \"Cm\",\n \"Bk\",\n \"Cf\",\n \"Es\",\n \"Fm\",\n \"Md\",\n \"No\",\n \"Lr\",\n \"Rf\",\n \"Db\",\n \"Sg\",\n \"Bh\",\n \"Hs\",\n \"Mt\",\n \"Ds\",\n \"Rg\",\n \"Cn\",\n \"Nh\",\n \"Fl\",\n \"Mc\",\n \"Lv\",\n \"Ts\",\n \"Og\",\n];\n\nconst elementSet = new Set(elementSymbols.map((s) => s.toLowerCase()));\n\nexport function elementLookup(input: string): boolean {\n return elementSet.has(input.toLowerCase());\n}\n","// SPDX-License-Identifier: MIT\n// Copyright © 2026 Paul Puhnaty\n\nimport * as types from \"./types\";\nimport { isLetter } from \"./helpers\";\nimport { elementLookup } from \"./elements\";\n\nenum FormatMarker {\n Unmarked,\n Start,\n OneLetter,\n TwoLetter,\n OneOrTwoLetter,\n Skip,\n FirstHalf,\n}\n\n// The marker array and input string are offset by one\n// since the marker array contains a \"start\" symbol in\n// its first index. The SmartIndex will handle the conversion.\nclass SmartIndex {\n private _index: number = 1;\n\n get mark(): number {\n return this._index;\n }\n\n get char(): number {\n return this._index - 1;\n }\n\n set mark(i: number) {\n this._index = i;\n }\n\n set char(i: number) {\n this._index = i + 1;\n }\n}\n\nexport function format(input: string, preferSingleSymbols: boolean): types.ResultSegment {\n input = input.toLowerCase();\n\n // Analyze the input string\n const markers: FormatMarker[] = findFormatMarkers(input);\n\n let firstMiss: number;\n\n // \"firstMiss\" is guaranteed to be set by this next loop\n // because the first value in \"markers\" is always FormatMarker.Start\n\n for (let i = input.length; i >= 0; i--) {\n if (markers[i] !== FormatMarker.Unmarked) {\n firstMiss = i;\n break;\n }\n }\n\n // Apply formatting to the input string\n const output = applyFormatMarkers(input, markers, preferSingleSymbols);\n\n return new types.ResultSegment(output, firstMiss);\n}\n\nfunction findFormatMarkers(input: string): FormatMarker[] {\n const markers: FormatMarker[] = new Array(input.length + 1).fill(FormatMarker.Unmarked);\n markers[0] = FormatMarker.Start;\n\n const i = new SmartIndex();\n\n for (i.char = 0; i.char < input.length; i.char++) {\n // Check the previous marker; skip if this letter is not reachable\n if (markers[i.mark - 1] === FormatMarker.Unmarked) continue;\n if (markers[i.mark - 1] === FormatMarker.FirstHalf) continue;\n\n // Skip non-letters\n if (!isLetter(input[i.char])) {\n markers[i.mark] = FormatMarker.Skip;\n continue;\n }\n\n // Check for single letter symbol\n if (elementLookup(input[i.char])) {\n if (markers[i.mark] === FormatMarker.TwoLetter) {\n markers[i.mark] = FormatMarker.OneOrTwoLetter;\n } else {\n markers[i.mark] = FormatMarker.OneLetter;\n }\n }\n\n // Check for two letter symbol\n let offset = 1;\n while (i.char + offset < input.length) {\n // Skip if this is not a letter\n if (!isLetter(input[i.char + offset])) {\n offset += 1;\n continue;\n }\n\n const digram = input[i.char] + input[i.char + offset];\n\n if (elementLookup(digram)) {\n markers[i.mark + offset] = FormatMarker.TwoLetter;\n\n // Mark the current letter if it isn't marked\n // already, so it can be found when formatting.\n if (markers[i.mark] === FormatMarker.Unmarked) {\n markers[i.mark] = FormatMarker.FirstHalf;\n }\n }\n\n break;\n }\n }\n\n return markers;\n}\n\nfunction applyFormatMarkers(\n input: string,\n markers: FormatMarker[],\n preferSingleSymbols: boolean,\n): string {\n const array = input.split(\"\");\n\n const i = new SmartIndex();\n\n i.mark = input.length;\n\n while (i.mark > 0) {\n if (markers[i.mark] === FormatMarker.Unmarked || markers[i.mark] === FormatMarker.Skip) {\n i.mark -= 1;\n continue;\n }\n\n // Choose between OneLetter or TwoLetter\n if (markers[i.mark] === FormatMarker.OneOrTwoLetter) {\n if (preferSingleSymbols) {\n markers[i.mark] = FormatMarker.OneLetter;\n } else {\n markers[i.mark] = FormatMarker.TwoLetter;\n }\n }\n\n // Case OneLetter\n if (markers[i.mark] === FormatMarker.OneLetter) {\n array[i.char] = array[i.char].toUpperCase();\n }\n // Case TwoLetter\n else if (markers[i.mark] === FormatMarker.TwoLetter) {\n let offset = 1;\n while (\n markers[i.mark - offset] === FormatMarker.Skip ||\n markers[i.mark - offset] === FormatMarker.Unmarked\n ) {\n offset += 1;\n }\n array[i.char - offset] = array[i.char - offset].toUpperCase();\n i.mark -= offset;\n }\n\n i.mark -= 1;\n }\n\n return array.join(\"\");\n}\n"],"mappings":"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","names":[]}
package/dist/index.mjs ADDED
@@ -0,0 +1,409 @@
1
+ var __defProp = Object.defineProperty;
2
+ var __export = (target, all) => {
3
+ for (var name in all)
4
+ __defProp(target, name, { get: all[name], enumerable: true });
5
+ };
6
+
7
+ // src/interfaces.ts
8
+ var interfaces_exports = {};
9
+
10
+ // src/api.ts
11
+ var api_exports = {};
12
+ __export(api_exports, {
13
+ elementSymbolFormat: () => elementSymbolFormat
14
+ });
15
+
16
+ // src/helpers.ts
17
+ function doSplit(input, split) {
18
+ if (input === "") return [];
19
+ if (split === "") return [input];
20
+ return input.split(split);
21
+ }
22
+ function doStrictSplit(input) {
23
+ const output = [];
24
+ if (input.length === 0) return output;
25
+ let iStart = 0;
26
+ let wasLetter = isLetter(input[iStart]);
27
+ for (let iEnd = 1; iEnd < input.length; iEnd++) {
28
+ const nowLetter = isLetter(input[iEnd]);
29
+ if (nowLetter !== wasLetter) {
30
+ output.push(input.slice(iStart, iEnd));
31
+ wasLetter = nowLetter;
32
+ iStart = iEnd;
33
+ }
34
+ }
35
+ output.push(input.slice(iStart));
36
+ return output;
37
+ }
38
+ function doStripNonLetters(input) {
39
+ for (let i = 0; i < input.length; i++) {
40
+ input[i] = input[i].split("").filter((char) => isLetter(char)).join("");
41
+ }
42
+ return input.filter((s) => s.length !== 0);
43
+ }
44
+ function isLetter(ch) {
45
+ return ch >= "a" && ch <= "z" || ch >= "A" && ch <= "Z";
46
+ }
47
+ function isLowerCase(ch) {
48
+ return ch >= "a" && ch <= "z";
49
+ }
50
+ function isUpperCase(ch) {
51
+ return ch >= "A" && ch <= "Z";
52
+ }
53
+
54
+ // src/types.ts
55
+ var SegmentSymbols = class {
56
+ value;
57
+ constructor(value) {
58
+ this.value = value;
59
+ }
60
+ toString() {
61
+ return this.value;
62
+ }
63
+ toSymbols() {
64
+ const symbols = [];
65
+ const chars = this.value.split("").filter((char) => isLetter(char));
66
+ for (let i = 0; i < chars.length; i++) {
67
+ if (isUpperCase(chars[i])) {
68
+ const iNext = i + 1;
69
+ if (iNext < chars.length && isLowerCase(chars[iNext])) {
70
+ symbols.push(chars[i] + chars[iNext]);
71
+ } else {
72
+ symbols.push(chars[i]);
73
+ }
74
+ }
75
+ }
76
+ return symbols;
77
+ }
78
+ };
79
+ var ResultSegment = class {
80
+ isFormatted;
81
+ value;
82
+ firstMiss;
83
+ constructor(value, firstMiss) {
84
+ this.firstMiss = firstMiss;
85
+ this.value = value;
86
+ this.isFormatted = firstMiss >= value.length;
87
+ }
88
+ get formatted() {
89
+ const formattedPart = this.value.slice(0, this.firstMiss);
90
+ return new SegmentSymbols(formattedPart, true);
91
+ }
92
+ get unformatted() {
93
+ if (this.isFormatted) {
94
+ return "";
95
+ } else {
96
+ return this.value.slice(this.firstMiss);
97
+ }
98
+ }
99
+ toString() {
100
+ return this.value;
101
+ }
102
+ toSymbols() {
103
+ const symbols = this.formatted.toSymbols();
104
+ if (!this.isFormatted) {
105
+ const stripped = this.unformatted.split("").filter((char) => isLetter(char)).join("");
106
+ symbols.push(stripped);
107
+ }
108
+ return symbols;
109
+ }
110
+ };
111
+ var ResultArray = class extends Array {
112
+ isFormatted;
113
+ joinStr;
114
+ constructor(join, ...segments) {
115
+ super(...segments);
116
+ this.isFormatted = true;
117
+ this.joinStr = join;
118
+ for (const segment of segments) {
119
+ if (!segment.isFormatted) {
120
+ this.isFormatted = false;
121
+ break;
122
+ }
123
+ }
124
+ }
125
+ toString() {
126
+ const segments = [];
127
+ for (const segment of this) {
128
+ segments.push(segment.toString());
129
+ }
130
+ return segments.join(this.joinStr);
131
+ }
132
+ toSymbols() {
133
+ const symbols = [];
134
+ this.forEach((segment, index) => {
135
+ const segmentSymbols = segment.toSymbols();
136
+ symbols.push(...segmentSymbols);
137
+ if (index != this.length - 1) {
138
+ symbols.push(this.joinStr);
139
+ }
140
+ }, this);
141
+ return symbols;
142
+ }
143
+ };
144
+
145
+ // src/elements.ts
146
+ var elementSymbols = [
147
+ "H",
148
+ "He",
149
+ "Li",
150
+ "Be",
151
+ "B",
152
+ "C",
153
+ "N",
154
+ "O",
155
+ "F",
156
+ "Ne",
157
+ "Na",
158
+ "Mg",
159
+ "Al",
160
+ "Si",
161
+ "P",
162
+ "S",
163
+ "Cl",
164
+ "Ar",
165
+ "K",
166
+ "Ca",
167
+ "Sc",
168
+ "Ti",
169
+ "V",
170
+ "Cr",
171
+ "Mn",
172
+ "Fe",
173
+ "Co",
174
+ "Ni",
175
+ "Cu",
176
+ "Zn",
177
+ "Ga",
178
+ "Ge",
179
+ "As",
180
+ "Se",
181
+ "Br",
182
+ "Kr",
183
+ "Rb",
184
+ "Sr",
185
+ "Y",
186
+ "Zr",
187
+ "Nb",
188
+ "Mo",
189
+ "Tc",
190
+ "Ru",
191
+ "Rh",
192
+ "Pd",
193
+ "Ag",
194
+ "Cd",
195
+ "In",
196
+ "Sn",
197
+ "Sb",
198
+ "Te",
199
+ "I",
200
+ "Xe",
201
+ "Cs",
202
+ "Ba",
203
+ "La",
204
+ "Ce",
205
+ "Pr",
206
+ "Nd",
207
+ "Pm",
208
+ "Sm",
209
+ "Eu",
210
+ "Gd",
211
+ "Tb",
212
+ "Dy",
213
+ "Ho",
214
+ "Er",
215
+ "Tm",
216
+ "Yb",
217
+ "Lu",
218
+ "Hf",
219
+ "Ta",
220
+ "W",
221
+ "Re",
222
+ "Os",
223
+ "Ir",
224
+ "Pt",
225
+ "Au",
226
+ "Hg",
227
+ "Tl",
228
+ "Pb",
229
+ "Bi",
230
+ "Po",
231
+ "At",
232
+ "Rn",
233
+ "Fr",
234
+ "Ra",
235
+ "Ac",
236
+ "Th",
237
+ "Pa",
238
+ "U",
239
+ "Np",
240
+ "Pu",
241
+ "Am",
242
+ "Cm",
243
+ "Bk",
244
+ "Cf",
245
+ "Es",
246
+ "Fm",
247
+ "Md",
248
+ "No",
249
+ "Lr",
250
+ "Rf",
251
+ "Db",
252
+ "Sg",
253
+ "Bh",
254
+ "Hs",
255
+ "Mt",
256
+ "Ds",
257
+ "Rg",
258
+ "Cn",
259
+ "Nh",
260
+ "Fl",
261
+ "Mc",
262
+ "Lv",
263
+ "Ts",
264
+ "Og"
265
+ ];
266
+ var elementSet = new Set(elementSymbols.map((s) => s.toLowerCase()));
267
+ function elementLookup(input) {
268
+ return elementSet.has(input.toLowerCase());
269
+ }
270
+
271
+ // src/formatter.ts
272
+ var SmartIndex = class {
273
+ _index = 1;
274
+ get mark() {
275
+ return this._index;
276
+ }
277
+ get char() {
278
+ return this._index - 1;
279
+ }
280
+ set mark(i) {
281
+ this._index = i;
282
+ }
283
+ set char(i) {
284
+ this._index = i + 1;
285
+ }
286
+ };
287
+ function format(input, preferSingleSymbols) {
288
+ input = input.toLowerCase();
289
+ const markers = findFormatMarkers(input);
290
+ let firstMiss;
291
+ for (let i = input.length; i >= 0; i--) {
292
+ if (markers[i] !== 0 /* Unmarked */) {
293
+ firstMiss = i;
294
+ break;
295
+ }
296
+ }
297
+ const output = applyFormatMarkers(input, markers, preferSingleSymbols);
298
+ return new ResultSegment(output, firstMiss);
299
+ }
300
+ function findFormatMarkers(input) {
301
+ const markers = new Array(input.length + 1).fill(0 /* Unmarked */);
302
+ markers[0] = 1 /* Start */;
303
+ const i = new SmartIndex();
304
+ for (i.char = 0; i.char < input.length; i.char++) {
305
+ if (markers[i.mark - 1] === 0 /* Unmarked */) continue;
306
+ if (markers[i.mark - 1] === 6 /* FirstHalf */) continue;
307
+ if (!isLetter(input[i.char])) {
308
+ markers[i.mark] = 5 /* Skip */;
309
+ continue;
310
+ }
311
+ if (elementLookup(input[i.char])) {
312
+ if (markers[i.mark] === 3 /* TwoLetter */) {
313
+ markers[i.mark] = 4 /* OneOrTwoLetter */;
314
+ } else {
315
+ markers[i.mark] = 2 /* OneLetter */;
316
+ }
317
+ }
318
+ let offset = 1;
319
+ while (i.char + offset < input.length) {
320
+ if (!isLetter(input[i.char + offset])) {
321
+ offset += 1;
322
+ continue;
323
+ }
324
+ const digram = input[i.char] + input[i.char + offset];
325
+ if (elementLookup(digram)) {
326
+ markers[i.mark + offset] = 3 /* TwoLetter */;
327
+ if (markers[i.mark] === 0 /* Unmarked */) {
328
+ markers[i.mark] = 6 /* FirstHalf */;
329
+ }
330
+ }
331
+ break;
332
+ }
333
+ }
334
+ return markers;
335
+ }
336
+ function applyFormatMarkers(input, markers, preferSingleSymbols) {
337
+ const array = input.split("");
338
+ const i = new SmartIndex();
339
+ i.mark = input.length;
340
+ while (i.mark > 0) {
341
+ if (markers[i.mark] === 0 /* Unmarked */ || markers[i.mark] === 5 /* Skip */) {
342
+ i.mark -= 1;
343
+ continue;
344
+ }
345
+ if (markers[i.mark] === 4 /* OneOrTwoLetter */) {
346
+ if (preferSingleSymbols) {
347
+ markers[i.mark] = 2 /* OneLetter */;
348
+ } else {
349
+ markers[i.mark] = 3 /* TwoLetter */;
350
+ }
351
+ }
352
+ if (markers[i.mark] === 2 /* OneLetter */) {
353
+ array[i.char] = array[i.char].toUpperCase();
354
+ } else if (markers[i.mark] === 3 /* TwoLetter */) {
355
+ let offset = 1;
356
+ while (markers[i.mark - offset] === 5 /* Skip */ || markers[i.mark - offset] === 0 /* Unmarked */) {
357
+ offset += 1;
358
+ }
359
+ array[i.char - offset] = array[i.char - offset].toUpperCase();
360
+ i.mark -= offset;
361
+ }
362
+ i.mark -= 1;
363
+ }
364
+ return array.join("");
365
+ }
366
+
367
+ // src/api.ts
368
+ function valueOrDefault(value, defaultValue) {
369
+ return value !== void 0 ? value : defaultValue;
370
+ }
371
+ function elementSymbolFormat(input, options) {
372
+ const split = valueOrDefault(options?.split, " ");
373
+ const strictSplit = valueOrDefault(options?.strictSplit, false);
374
+ const preferSingleSymbols = valueOrDefault(options?.preferSingleSymbols, false);
375
+ const stripNonLetters = valueOrDefault(options?.stripNonLetters, true);
376
+ const join = valueOrDefault(options?.join, split);
377
+ const splitLetter = split.split("").find((char) => isLetter(char));
378
+ if (splitLetter !== void 0) {
379
+ throw new Error(
380
+ `elementSymbolFormat() will not split on a string with a letter "${splitLetter}"`
381
+ );
382
+ }
383
+ let inputSegments;
384
+ if (strictSplit) {
385
+ inputSegments = doStrictSplit(input);
386
+ } else {
387
+ inputSegments = doSplit(input, split);
388
+ }
389
+ if (stripNonLetters) {
390
+ inputSegments = doStripNonLetters(inputSegments);
391
+ }
392
+ const outputSegments = [];
393
+ for (const segment of inputSegments) {
394
+ const result = format(segment, preferSingleSymbols);
395
+ outputSegments.push(result);
396
+ }
397
+ return new ResultArray(join, ...outputSegments);
398
+ }
399
+
400
+ // src/index.ts
401
+ var index_default = {
402
+ ...interfaces_exports,
403
+ ...api_exports
404
+ };
405
+ export {
406
+ index_default as default,
407
+ elementSymbolFormat
408
+ };
409
+ //# sourceMappingURL=index.mjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/interfaces.ts","../src/api.ts","../src/helpers.ts","../src/types.ts","../src/elements.ts","../src/formatter.ts","../src/index.ts"],"sourcesContent":["// SPDX-License-Identifier: MIT\n// Copyright © 2026 Paul Puhnaty\n\nexport interface FormattedCheckable {\n readonly isFormatted: boolean;\n}\n\nexport interface SymbolDecomposable {\n toString(): string;\n toSymbols(): string[];\n}\n\nexport interface PartiallyFormattable extends SymbolDecomposable, FormattedCheckable {\n get formatted(): SymbolDecomposable;\n get unformatted(): string;\n}\n\nexport interface FormattedResult\n extends Array<PartiallyFormattable>, SymbolDecomposable, FormattedCheckable {}\n\nexport interface FormatOptions {\n split?: string;\n strictSplit?: boolean;\n preferSingleSymbols?: boolean;\n stripNonLetters?: boolean;\n join?: string;\n}\n","// SPDX-License-Identifier: MIT\n// Copyright © 2026 Paul Puhnaty\n\nimport * as interfaces from \"./interfaces\";\nimport * as types from \"./types\";\nimport * as helpers from \"./helpers\";\nimport { format } from \"./formatter\";\n\nfunction valueOrDefault<T>(value: T | undefined, defaultValue: T): T {\n return value !== undefined ? value : defaultValue;\n}\n\nexport function elementSymbolFormat(\n input: string,\n options?: interfaces.FormatOptions,\n): interfaces.FormattedResult {\n const split = valueOrDefault(options?.split, \" \");\n const strictSplit = valueOrDefault(options?.strictSplit, false);\n const preferSingleSymbols = valueOrDefault(options?.preferSingleSymbols, false);\n const stripNonLetters = valueOrDefault(options?.stripNonLetters, true);\n const join = valueOrDefault(options?.join, split);\n\n // Verify \"split\"\n\n const splitLetter = split.split(\"\").find((char) => helpers.isLetter(char));\n\n if (splitLetter !== undefined) {\n throw new Error(\n `elementSymbolFormat() will not split on a string with a letter \"${splitLetter}\"`,\n );\n }\n\n // Split input\n\n let inputSegments: string[];\n\n if (strictSplit) {\n inputSegments = helpers.doStrictSplit(input);\n } else {\n inputSegments = helpers.doSplit(input, split);\n }\n\n // Strip input\n\n if (stripNonLetters) {\n inputSegments = helpers.doStripNonLetters(inputSegments);\n }\n\n // Format input\n\n const outputSegments: types.ResultSegment[] = [];\n\n for (const segment of inputSegments) {\n const result = format(segment, preferSingleSymbols);\n outputSegments.push(result);\n }\n\n // Return results\n\n return new types.ResultArray(join, ...outputSegments);\n}\n","// SPDX-License-Identifier: MIT\n// Copyright © 2026 Paul Puhnaty\n\nexport function doSplit(input: string, split: string): string[] {\n if (input === \"\") return [];\n\n if (split === \"\") return [input];\n\n return input.split(split);\n}\n\nexport function doStrictSplit(input: string): string[] {\n const output: string[] = [];\n\n if (input.length === 0) return output;\n\n let iStart = 0;\n let wasLetter: boolean = isLetter(input[iStart]);\n\n for (let iEnd = 1; iEnd < input.length; iEnd++) {\n const nowLetter: boolean = isLetter(input[iEnd]);\n\n if (nowLetter !== wasLetter) {\n output.push(input.slice(iStart, iEnd));\n\n wasLetter = nowLetter;\n iStart = iEnd;\n }\n }\n\n // Add the remaining bit to the output\n output.push(input.slice(iStart));\n\n return output;\n}\n\nexport function doStripNonLetters(input: string[]): string[] {\n for (let i = 0; i < input.length; i++) {\n input[i] = input[i]\n .split(\"\")\n .filter((char) => isLetter(char))\n .join(\"\");\n }\n\n return input.filter((s) => s.length !== 0);\n}\n\nexport function isLetter(ch: string): boolean {\n return (ch >= \"a\" && ch <= \"z\") || (ch >= \"A\" && ch <= \"Z\");\n}\n\nexport function isLowerCase(ch: string): boolean {\n return ch >= \"a\" && ch <= \"z\";\n}\n\nexport function isUpperCase(ch: string): boolean {\n return ch >= \"A\" && ch <= \"Z\";\n}\n","// SPDX-License-Identifier: MIT\n// Copyright © 2026 Paul Puhnaty\n\nimport * as interfaces from \"./interfaces\";\nimport { isLetter, isLowerCase, isUpperCase } from \"./helpers\";\n\nexport class SegmentSymbols implements interfaces.SymbolDecomposable {\n private value: string;\n\n constructor(value: string) {\n this.value = value;\n }\n\n toString(): string {\n return this.value;\n }\n\n toSymbols(): string[] {\n const symbols: string[] = [];\n\n const chars = this.value.split(\"\").filter((char) => isLetter(char));\n\n for (let i = 0; i < chars.length; i++) {\n if (isUpperCase(chars[i])) {\n const iNext = i + 1;\n\n if (iNext < chars.length && isLowerCase(chars[iNext])) {\n symbols.push(chars[i] + chars[iNext]);\n } else {\n symbols.push(chars[i]);\n }\n }\n }\n\n return symbols;\n }\n}\n\nexport class ResultSegment implements interfaces.PartiallyFormattable {\n readonly isFormatted: boolean;\n private value: string;\n private firstMiss: number;\n\n constructor(value: string, firstMiss: number) {\n this.firstMiss = firstMiss;\n this.value = value;\n\n this.isFormatted = firstMiss >= value.length;\n }\n\n get formatted(): SegmentSymbols {\n const formattedPart = this.value.slice(0, this.firstMiss);\n\n return new SegmentSymbols(formattedPart, true);\n }\n\n get unformatted(): string {\n if (this.isFormatted) {\n return \"\";\n } else {\n return this.value.slice(this.firstMiss);\n }\n }\n\n toString(): string {\n return this.value;\n }\n\n toSymbols(): string[] {\n const symbols = this.formatted.toSymbols();\n\n if (!this.isFormatted) {\n const stripped = this.unformatted\n .split(\"\")\n .filter((char) => isLetter(char))\n .join(\"\");\n\n symbols.push(stripped);\n }\n\n return symbols;\n }\n}\n\nexport class ResultArray extends Array<ResultSegment> implements interfaces.FormattedResult {\n readonly isFormatted: boolean;\n private joinStr: string;\n\n constructor(join: string, ...segments: ResultSegment[]) {\n super(...segments);\n\n this.isFormatted = true; // Assume true\n this.joinStr = join;\n\n // Check for unformatted segments\n for (const segment of segments) {\n if (!segment.isFormatted) {\n this.isFormatted = false;\n break;\n }\n }\n }\n\n toString(): string {\n const segments: string[] = [];\n\n for (const segment of this) {\n segments.push(segment.toString());\n }\n\n return segments.join(this.joinStr);\n }\n\n toSymbols(): string[] {\n const symbols: string[] = [];\n\n this.forEach((segment, index) => {\n const segmentSymbols = segment.toSymbols();\n\n symbols.push(...segmentSymbols);\n\n if (index != this.length - 1) {\n symbols.push(this.joinStr);\n }\n }, this);\n\n return symbols;\n }\n}\n","// SPDX-License-Identifier: MIT\n// Copyright © 2026 Paul Puhnaty\n\nconst elementSymbols = [\n \"H\",\n \"He\",\n \"Li\",\n \"Be\",\n \"B\",\n \"C\",\n \"N\",\n \"O\",\n \"F\",\n \"Ne\",\n \"Na\",\n \"Mg\",\n \"Al\",\n \"Si\",\n \"P\",\n \"S\",\n \"Cl\",\n \"Ar\",\n \"K\",\n \"Ca\",\n \"Sc\",\n \"Ti\",\n \"V\",\n \"Cr\",\n \"Mn\",\n \"Fe\",\n \"Co\",\n \"Ni\",\n \"Cu\",\n \"Zn\",\n \"Ga\",\n \"Ge\",\n \"As\",\n \"Se\",\n \"Br\",\n \"Kr\",\n \"Rb\",\n \"Sr\",\n \"Y\",\n \"Zr\",\n \"Nb\",\n \"Mo\",\n \"Tc\",\n \"Ru\",\n \"Rh\",\n \"Pd\",\n \"Ag\",\n \"Cd\",\n \"In\",\n \"Sn\",\n \"Sb\",\n \"Te\",\n \"I\",\n \"Xe\",\n \"Cs\",\n \"Ba\",\n \"La\",\n \"Ce\",\n \"Pr\",\n \"Nd\",\n \"Pm\",\n \"Sm\",\n \"Eu\",\n \"Gd\",\n \"Tb\",\n \"Dy\",\n \"Ho\",\n \"Er\",\n \"Tm\",\n \"Yb\",\n \"Lu\",\n \"Hf\",\n \"Ta\",\n \"W\",\n \"Re\",\n \"Os\",\n \"Ir\",\n \"Pt\",\n \"Au\",\n \"Hg\",\n \"Tl\",\n \"Pb\",\n \"Bi\",\n \"Po\",\n \"At\",\n \"Rn\",\n \"Fr\",\n \"Ra\",\n \"Ac\",\n \"Th\",\n \"Pa\",\n \"U\",\n \"Np\",\n \"Pu\",\n \"Am\",\n \"Cm\",\n \"Bk\",\n \"Cf\",\n \"Es\",\n \"Fm\",\n \"Md\",\n \"No\",\n \"Lr\",\n \"Rf\",\n \"Db\",\n \"Sg\",\n \"Bh\",\n \"Hs\",\n \"Mt\",\n \"Ds\",\n \"Rg\",\n \"Cn\",\n \"Nh\",\n \"Fl\",\n \"Mc\",\n \"Lv\",\n \"Ts\",\n \"Og\",\n];\n\nconst elementSet = new Set(elementSymbols.map((s) => s.toLowerCase()));\n\nexport function elementLookup(input: string): boolean {\n return elementSet.has(input.toLowerCase());\n}\n","// SPDX-License-Identifier: MIT\n// Copyright © 2026 Paul Puhnaty\n\nimport * as types from \"./types\";\nimport { isLetter } from \"./helpers\";\nimport { elementLookup } from \"./elements\";\n\nenum FormatMarker {\n Unmarked,\n Start,\n OneLetter,\n TwoLetter,\n OneOrTwoLetter,\n Skip,\n FirstHalf,\n}\n\n// The marker array and input string are offset by one\n// since the marker array contains a \"start\" symbol in\n// its first index. The SmartIndex will handle the conversion.\nclass SmartIndex {\n private _index: number = 1;\n\n get mark(): number {\n return this._index;\n }\n\n get char(): number {\n return this._index - 1;\n }\n\n set mark(i: number) {\n this._index = i;\n }\n\n set char(i: number) {\n this._index = i + 1;\n }\n}\n\nexport function format(input: string, preferSingleSymbols: boolean): types.ResultSegment {\n input = input.toLowerCase();\n\n // Analyze the input string\n const markers: FormatMarker[] = findFormatMarkers(input);\n\n let firstMiss: number;\n\n // \"firstMiss\" is guaranteed to be set by this next loop\n // because the first value in \"markers\" is always FormatMarker.Start\n\n for (let i = input.length; i >= 0; i--) {\n if (markers[i] !== FormatMarker.Unmarked) {\n firstMiss = i;\n break;\n }\n }\n\n // Apply formatting to the input string\n const output = applyFormatMarkers(input, markers, preferSingleSymbols);\n\n return new types.ResultSegment(output, firstMiss);\n}\n\nfunction findFormatMarkers(input: string): FormatMarker[] {\n const markers: FormatMarker[] = new Array(input.length + 1).fill(FormatMarker.Unmarked);\n markers[0] = FormatMarker.Start;\n\n const i = new SmartIndex();\n\n for (i.char = 0; i.char < input.length; i.char++) {\n // Check the previous marker; skip if this letter is not reachable\n if (markers[i.mark - 1] === FormatMarker.Unmarked) continue;\n if (markers[i.mark - 1] === FormatMarker.FirstHalf) continue;\n\n // Skip non-letters\n if (!isLetter(input[i.char])) {\n markers[i.mark] = FormatMarker.Skip;\n continue;\n }\n\n // Check for single letter symbol\n if (elementLookup(input[i.char])) {\n if (markers[i.mark] === FormatMarker.TwoLetter) {\n markers[i.mark] = FormatMarker.OneOrTwoLetter;\n } else {\n markers[i.mark] = FormatMarker.OneLetter;\n }\n }\n\n // Check for two letter symbol\n let offset = 1;\n while (i.char + offset < input.length) {\n // Skip if this is not a letter\n if (!isLetter(input[i.char + offset])) {\n offset += 1;\n continue;\n }\n\n const digram = input[i.char] + input[i.char + offset];\n\n if (elementLookup(digram)) {\n markers[i.mark + offset] = FormatMarker.TwoLetter;\n\n // Mark the current letter if it isn't marked\n // already, so it can be found when formatting.\n if (markers[i.mark] === FormatMarker.Unmarked) {\n markers[i.mark] = FormatMarker.FirstHalf;\n }\n }\n\n break;\n }\n }\n\n return markers;\n}\n\nfunction applyFormatMarkers(\n input: string,\n markers: FormatMarker[],\n preferSingleSymbols: boolean,\n): string {\n const array = input.split(\"\");\n\n const i = new SmartIndex();\n\n i.mark = input.length;\n\n while (i.mark > 0) {\n if (markers[i.mark] === FormatMarker.Unmarked || markers[i.mark] === FormatMarker.Skip) {\n i.mark -= 1;\n continue;\n }\n\n // Choose between OneLetter or TwoLetter\n if (markers[i.mark] === FormatMarker.OneOrTwoLetter) {\n if (preferSingleSymbols) {\n markers[i.mark] = FormatMarker.OneLetter;\n } else {\n markers[i.mark] = FormatMarker.TwoLetter;\n }\n }\n\n // Case OneLetter\n if (markers[i.mark] === FormatMarker.OneLetter) {\n array[i.char] = array[i.char].toUpperCase();\n }\n // Case TwoLetter\n else if (markers[i.mark] === FormatMarker.TwoLetter) {\n let offset = 1;\n while (\n markers[i.mark - offset] === FormatMarker.Skip ||\n markers[i.mark - offset] === FormatMarker.Unmarked\n ) {\n offset += 1;\n }\n array[i.char - offset] = array[i.char - offset].toUpperCase();\n i.mark -= offset;\n }\n\n i.mark -= 1;\n }\n\n return array.join(\"\");\n}\n","// SPDX-License-Identifier: MIT\n// Copyright © 2026 Paul Puhnaty\n\nexport * from \"./interfaces\";\nexport * from \"./api\";\n\nimport * as interfaces from \"./interfaces\";\nimport * as api from \"./api\";\n\nexport default {\n ...interfaces,\n ...api,\n};\n"],"mappings":";;;;;;;AAAA;;;ACAA;AAAA;AAAA;AAAA;;;ACGO,SAAS,QAAQ,OAAe,OAAyB;AAC9D,MAAI,UAAU,GAAI,QAAO,CAAC;AAE1B,MAAI,UAAU,GAAI,QAAO,CAAC,KAAK;AAE/B,SAAO,MAAM,MAAM,KAAK;AAC1B;AAEO,SAAS,cAAc,OAAyB;AACrD,QAAM,SAAmB,CAAC;AAE1B,MAAI,MAAM,WAAW,EAAG,QAAO;AAE/B,MAAI,SAAS;AACb,MAAI,YAAqB,SAAS,MAAM,MAAM,CAAC;AAE/C,WAAS,OAAO,GAAG,OAAO,MAAM,QAAQ,QAAQ;AAC9C,UAAM,YAAqB,SAAS,MAAM,IAAI,CAAC;AAE/C,QAAI,cAAc,WAAW;AAC3B,aAAO,KAAK,MAAM,MAAM,QAAQ,IAAI,CAAC;AAErC,kBAAY;AACZ,eAAS;AAAA,IACX;AAAA,EACF;AAGA,SAAO,KAAK,MAAM,MAAM,MAAM,CAAC;AAE/B,SAAO;AACT;AAEO,SAAS,kBAAkB,OAA2B;AAC3D,WAAS,IAAI,GAAG,IAAI,MAAM,QAAQ,KAAK;AACrC,UAAM,CAAC,IAAI,MAAM,CAAC,EACf,MAAM,EAAE,EACR,OAAO,CAAC,SAAS,SAAS,IAAI,CAAC,EAC/B,KAAK,EAAE;AAAA,EACZ;AAEA,SAAO,MAAM,OAAO,CAAC,MAAM,EAAE,WAAW,CAAC;AAC3C;AAEO,SAAS,SAAS,IAAqB;AAC5C,SAAQ,MAAM,OAAO,MAAM,OAAS,MAAM,OAAO,MAAM;AACzD;AAEO,SAAS,YAAY,IAAqB;AAC/C,SAAO,MAAM,OAAO,MAAM;AAC5B;AAEO,SAAS,YAAY,IAAqB;AAC/C,SAAO,MAAM,OAAO,MAAM;AAC5B;;;ACnDO,IAAM,iBAAN,MAA8D;AAAA,EAC3D;AAAA,EAER,YAAY,OAAe;AACzB,SAAK,QAAQ;AAAA,EACf;AAAA,EAEA,WAAmB;AACjB,WAAO,KAAK;AAAA,EACd;AAAA,EAEA,YAAsB;AACpB,UAAM,UAAoB,CAAC;AAE3B,UAAM,QAAQ,KAAK,MAAM,MAAM,EAAE,EAAE,OAAO,CAAC,SAAS,SAAS,IAAI,CAAC;AAElE,aAAS,IAAI,GAAG,IAAI,MAAM,QAAQ,KAAK;AACrC,UAAI,YAAY,MAAM,CAAC,CAAC,GAAG;AACzB,cAAM,QAAQ,IAAI;AAElB,YAAI,QAAQ,MAAM,UAAU,YAAY,MAAM,KAAK,CAAC,GAAG;AACrD,kBAAQ,KAAK,MAAM,CAAC,IAAI,MAAM,KAAK,CAAC;AAAA,QACtC,OAAO;AACL,kBAAQ,KAAK,MAAM,CAAC,CAAC;AAAA,QACvB;AAAA,MACF;AAAA,IACF;AAEA,WAAO;AAAA,EACT;AACF;AAEO,IAAM,gBAAN,MAA+D;AAAA,EAC3D;AAAA,EACD;AAAA,EACA;AAAA,EAER,YAAY,OAAe,WAAmB;AAC5C,SAAK,YAAY;AACjB,SAAK,QAAQ;AAEb,SAAK,cAAc,aAAa,MAAM;AAAA,EACxC;AAAA,EAEA,IAAI,YAA4B;AAC9B,UAAM,gBAAgB,KAAK,MAAM,MAAM,GAAG,KAAK,SAAS;AAExD,WAAO,IAAI,eAAe,eAAe,IAAI;AAAA,EAC/C;AAAA,EAEA,IAAI,cAAsB;AACxB,QAAI,KAAK,aAAa;AACpB,aAAO;AAAA,IACT,OAAO;AACL,aAAO,KAAK,MAAM,MAAM,KAAK,SAAS;AAAA,IACxC;AAAA,EACF;AAAA,EAEA,WAAmB;AACjB,WAAO,KAAK;AAAA,EACd;AAAA,EAEA,YAAsB;AACpB,UAAM,UAAU,KAAK,UAAU,UAAU;AAEzC,QAAI,CAAC,KAAK,aAAa;AACrB,YAAM,WAAW,KAAK,YACnB,MAAM,EAAE,EACR,OAAO,CAAC,SAAS,SAAS,IAAI,CAAC,EAC/B,KAAK,EAAE;AAEV,cAAQ,KAAK,QAAQ;AAAA,IACvB;AAEA,WAAO;AAAA,EACT;AACF;AAEO,IAAM,cAAN,cAA0B,MAA2D;AAAA,EACjF;AAAA,EACD;AAAA,EAER,YAAY,SAAiB,UAA2B;AACtD,UAAM,GAAG,QAAQ;AAEjB,SAAK,cAAc;AACnB,SAAK,UAAU;AAGf,eAAW,WAAW,UAAU;AAC9B,UAAI,CAAC,QAAQ,aAAa;AACxB,aAAK,cAAc;AACnB;AAAA,MACF;AAAA,IACF;AAAA,EACF;AAAA,EAEA,WAAmB;AACjB,UAAM,WAAqB,CAAC;AAE5B,eAAW,WAAW,MAAM;AAC1B,eAAS,KAAK,QAAQ,SAAS,CAAC;AAAA,IAClC;AAEA,WAAO,SAAS,KAAK,KAAK,OAAO;AAAA,EACnC;AAAA,EAEA,YAAsB;AACpB,UAAM,UAAoB,CAAC;AAE3B,SAAK,QAAQ,CAAC,SAAS,UAAU;AAC/B,YAAM,iBAAiB,QAAQ,UAAU;AAEzC,cAAQ,KAAK,GAAG,cAAc;AAE9B,UAAI,SAAS,KAAK,SAAS,GAAG;AAC5B,gBAAQ,KAAK,KAAK,OAAO;AAAA,MAC3B;AAAA,IACF,GAAG,IAAI;AAEP,WAAO;AAAA,EACT;AACF;;;AC7HA,IAAM,iBAAiB;AAAA,EACrB;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AACF;AAEA,IAAM,aAAa,IAAI,IAAI,eAAe,IAAI,CAAC,MAAM,EAAE,YAAY,CAAC,CAAC;AAE9D,SAAS,cAAc,OAAwB;AACpD,SAAO,WAAW,IAAI,MAAM,YAAY,CAAC;AAC3C;;;AC5GA,IAAM,aAAN,MAAiB;AAAA,EACP,SAAiB;AAAA,EAEzB,IAAI,OAAe;AACjB,WAAO,KAAK;AAAA,EACd;AAAA,EAEA,IAAI,OAAe;AACjB,WAAO,KAAK,SAAS;AAAA,EACvB;AAAA,EAEA,IAAI,KAAK,GAAW;AAClB,SAAK,SAAS;AAAA,EAChB;AAAA,EAEA,IAAI,KAAK,GAAW;AAClB,SAAK,SAAS,IAAI;AAAA,EACpB;AACF;AAEO,SAAS,OAAO,OAAe,qBAAmD;AACvF,UAAQ,MAAM,YAAY;AAG1B,QAAM,UAA0B,kBAAkB,KAAK;AAEvD,MAAI;AAKJ,WAAS,IAAI,MAAM,QAAQ,KAAK,GAAG,KAAK;AACtC,QAAI,QAAQ,CAAC,MAAM,kBAAuB;AACxC,kBAAY;AACZ;AAAA,IACF;AAAA,EACF;AAGA,QAAM,SAAS,mBAAmB,OAAO,SAAS,mBAAmB;AAErE,SAAO,IAAU,cAAc,QAAQ,SAAS;AAClD;AAEA,SAAS,kBAAkB,OAA+B;AACxD,QAAM,UAA0B,IAAI,MAAM,MAAM,SAAS,CAAC,EAAE,KAAK,gBAAqB;AACtF,UAAQ,CAAC,IAAI;AAEb,QAAM,IAAI,IAAI,WAAW;AAEzB,OAAK,EAAE,OAAO,GAAG,EAAE,OAAO,MAAM,QAAQ,EAAE,QAAQ;AAEhD,QAAI,QAAQ,EAAE,OAAO,CAAC,MAAM,iBAAuB;AACnD,QAAI,QAAQ,EAAE,OAAO,CAAC,MAAM,kBAAwB;AAGpD,QAAI,CAAC,SAAS,MAAM,EAAE,IAAI,CAAC,GAAG;AAC5B,cAAQ,EAAE,IAAI,IAAI;AAClB;AAAA,IACF;AAGA,QAAI,cAAc,MAAM,EAAE,IAAI,CAAC,GAAG;AAChC,UAAI,QAAQ,EAAE,IAAI,MAAM,mBAAwB;AAC9C,gBAAQ,EAAE,IAAI,IAAI;AAAA,MACpB,OAAO;AACL,gBAAQ,EAAE,IAAI,IAAI;AAAA,MACpB;AAAA,IACF;AAGA,QAAI,SAAS;AACb,WAAO,EAAE,OAAO,SAAS,MAAM,QAAQ;AAErC,UAAI,CAAC,SAAS,MAAM,EAAE,OAAO,MAAM,CAAC,GAAG;AACrC,kBAAU;AACV;AAAA,MACF;AAEA,YAAM,SAAS,MAAM,EAAE,IAAI,IAAI,MAAM,EAAE,OAAO,MAAM;AAEpD,UAAI,cAAc,MAAM,GAAG;AACzB,gBAAQ,EAAE,OAAO,MAAM,IAAI;AAI3B,YAAI,QAAQ,EAAE,IAAI,MAAM,kBAAuB;AAC7C,kBAAQ,EAAE,IAAI,IAAI;AAAA,QACpB;AAAA,MACF;AAEA;AAAA,IACF;AAAA,EACF;AAEA,SAAO;AACT;AAEA,SAAS,mBACP,OACA,SACA,qBACQ;AACR,QAAM,QAAQ,MAAM,MAAM,EAAE;AAE5B,QAAM,IAAI,IAAI,WAAW;AAEzB,IAAE,OAAO,MAAM;AAEf,SAAO,EAAE,OAAO,GAAG;AACjB,QAAI,QAAQ,EAAE,IAAI,MAAM,oBAAyB,QAAQ,EAAE,IAAI,MAAM,cAAmB;AACtF,QAAE,QAAQ;AACV;AAAA,IACF;AAGA,QAAI,QAAQ,EAAE,IAAI,MAAM,wBAA6B;AACnD,UAAI,qBAAqB;AACvB,gBAAQ,EAAE,IAAI,IAAI;AAAA,MACpB,OAAO;AACL,gBAAQ,EAAE,IAAI,IAAI;AAAA,MACpB;AAAA,IACF;AAGA,QAAI,QAAQ,EAAE,IAAI,MAAM,mBAAwB;AAC9C,YAAM,EAAE,IAAI,IAAI,MAAM,EAAE,IAAI,EAAE,YAAY;AAAA,IAC5C,WAES,QAAQ,EAAE,IAAI,MAAM,mBAAwB;AACnD,UAAI,SAAS;AACb,aACE,QAAQ,EAAE,OAAO,MAAM,MAAM,gBAC7B,QAAQ,EAAE,OAAO,MAAM,MAAM,kBAC7B;AACA,kBAAU;AAAA,MACZ;AACA,YAAM,EAAE,OAAO,MAAM,IAAI,MAAM,EAAE,OAAO,MAAM,EAAE,YAAY;AAC5D,QAAE,QAAQ;AAAA,IACZ;AAEA,MAAE,QAAQ;AAAA,EACZ;AAEA,SAAO,MAAM,KAAK,EAAE;AACtB;;;AJ7JA,SAAS,eAAkB,OAAsB,cAAoB;AACnE,SAAO,UAAU,SAAY,QAAQ;AACvC;AAEO,SAAS,oBACd,OACA,SAC4B;AAC5B,QAAM,QAAQ,eAAe,SAAS,OAAO,GAAG;AAChD,QAAM,cAAc,eAAe,SAAS,aAAa,KAAK;AAC9D,QAAM,sBAAsB,eAAe,SAAS,qBAAqB,KAAK;AAC9E,QAAM,kBAAkB,eAAe,SAAS,iBAAiB,IAAI;AACrE,QAAM,OAAO,eAAe,SAAS,MAAM,KAAK;AAIhD,QAAM,cAAc,MAAM,MAAM,EAAE,EAAE,KAAK,CAAC,SAAiB,SAAS,IAAI,CAAC;AAEzE,MAAI,gBAAgB,QAAW;AAC7B,UAAM,IAAI;AAAA,MACR,mEAAmE,WAAW;AAAA,IAChF;AAAA,EACF;AAIA,MAAI;AAEJ,MAAI,aAAa;AACf,oBAAwB,cAAc,KAAK;AAAA,EAC7C,OAAO;AACL,oBAAwB,QAAQ,OAAO,KAAK;AAAA,EAC9C;AAIA,MAAI,iBAAiB;AACnB,oBAAwB,kBAAkB,aAAa;AAAA,EACzD;AAIA,QAAM,iBAAwC,CAAC;AAE/C,aAAW,WAAW,eAAe;AACnC,UAAM,SAAS,OAAO,SAAS,mBAAmB;AAClD,mBAAe,KAAK,MAAM;AAAA,EAC5B;AAIA,SAAO,IAAU,YAAY,MAAM,GAAG,cAAc;AACtD;;;AKnDA,IAAO,gBAAQ;AAAA,EACb,GAAG;AAAA,EACH,GAAG;AACL;","names":[]}
package/package.json ADDED
@@ -0,0 +1,61 @@
1
+ {
2
+ "name": "element-symbol-format",
3
+ "version": "1.0.0",
4
+ "license": "MIT",
5
+ "author": "Paul Puhnaty",
6
+ "description": "Format text as chemical element symbols",
7
+ "keywords": [
8
+ "periodic",
9
+ "table",
10
+ "element",
11
+ "symbol",
12
+ "format",
13
+ "string",
14
+ "capitalize"
15
+ ],
16
+ "homepage": "https://github.com/happysnail2357/element-symbol-format#readme",
17
+ "bugs": {
18
+ "url": "https://github.com/happysnail2357/element-symbol-format/issues"
19
+ },
20
+ "repository": {
21
+ "type": "git",
22
+ "url": "git+https://github.com/happysnail2357/element-symbol-format.git"
23
+ },
24
+ "files": [
25
+ "dist"
26
+ ],
27
+ "main": "dist/index.cjs",
28
+ "module": "dist/index.js",
29
+ "types": "dist/index.d.ts",
30
+ "exports": {
31
+ ".": {
32
+ "require": {
33
+ "types": "./dist/index.d.ts",
34
+ "default": "./dist/index.js"
35
+ },
36
+ "import": {
37
+ "types": "./dist/index.d.mts",
38
+ "default": "./dist/index.mjs"
39
+ }
40
+ }
41
+ },
42
+ "scripts": {
43
+ "lint": "eslint .",
44
+ "lint:fix": "eslint . --fix",
45
+ "format": "prettier . --check",
46
+ "format:write": "prettier . --write",
47
+ "build": "tsup",
48
+ "prebuild": "npm run lint",
49
+ "test": "jest --verbose"
50
+ },
51
+ "devDependencies": {
52
+ "@eslint/js": "^10.0.1",
53
+ "eslint": "^10.0.2",
54
+ "eslint-config-prettier": "^10.1.8",
55
+ "prettier": "^3.8.1",
56
+ "ts-jest": "^29.4.6",
57
+ "tsup": "^8.5.1",
58
+ "typescript": "^5.9.3",
59
+ "typescript-eslint": "^8.56.1"
60
+ }
61
+ }