UNPKG

23 kBJavaScriptView Raw
1/**
2* Sutton SignWriting Core Module v1.4.2 (https://github.com/sutton-signwriting/core)
3* Author: Steve Slevinski (https://SteveSlevinski.me)
4* fsw.mjs is released under the MIT License.
5*/
6
7/**
8 * Object of regular expressions for FSW strings
9 *
10 * @alias fsw.re
11 * @property {string} symbol - regular expressions for a symbol
12 * @property {string} coord - regular expressions for a coordinate
13 * @property {string} sort - regular expressions for the sorting marker
14 * @property {string} box - regular expression for a signbox marker
15 * @property {string} prefix - regular expression for a sorting marker followed by one or more symbols
16 * @property {string} spatial - regular expression for a symbol followed by a coordinate
17 * @property {string} signbox - regular expression for a signbox marker, max coordinate and zero or more spatial symbols
18 * @property {string} sign - regular expression for an optional prefix followed by a signbox
19 * @property {string} sortable - regular expression for a mandatory prefix followed by a signbox
20 */
21let re$1 = {
22 'symbol': 'S[123][0-9a-f]{2}[0-5][0-9a-f]',
23 'coord': '[0-9]{3}x[0-9]{3}',
24 'sort': 'A',
25 'box': '[BLMR]'
26};
27re$1.prefix = `(?:${re$1.sort}(?:${re$1.symbol})+)`;
28re$1.spatial = `${re$1.symbol}${re$1.coord}`;
29re$1.signbox = `${re$1.box}${re$1.coord}(?:${re$1.spatial})*`;
30re$1.sign = `${re$1.prefix}?${re$1.signbox}`;
31re$1.sortable = `${re$1.prefix}${re$1.signbox}`;
32
33/**
34 * Object of regular expressions for style strings
35 *
36 * @alias style.re
37 * @type {object}
38 * @property {string} colorize - regular expression for colorize section
39 * @property {string} colorhex - regular expression for color hex values with 3 or 6 characters
40 * @property {string} colorname - regular expression for css color name
41 * @property {string} padding - regular expression for padding section
42 * @property {string} zoom - regular expression for zoom section
43 * @property {string} classbase - regular expression for class name definition
44 * @property {string} id - regular expression for id definition
45 * @property {string} colorbase - regular expression for color hex or color name
46 * @property {string} color - regular expression for single color entry
47 * @property {string} colors - regular expression for double color entry
48 * @property {string} background - regular expression for background section
49 * @property {string} detail - regular expression for color details for line and optional fill
50 * @property {string} detailsym - regular expression for color details for individual symbols
51 * @property {string} classes - regular expression for one or more class names
52 * @property {string} full - full regular expression for style string
53 */
54let re = {
55 'colorize': 'C',
56 'colorhex': '(?:[0-9a-fA-F]{3}){1,2}',
57 'colorname': '[a-zA-Z]+',
58 'padding': 'P[0-9]{2}',
59 'zoom': 'Z(?:[0-9]+(?:\\.[0-9]+)?|x)',
60 'classbase': '-?[_a-zA-Z][_a-zA-Z0-9-]{0,100}',
61 'id': '[a-zA-Z][_a-zA-Z0-9-]{0,100}'
62};
63re.colorbase = `(?:${re.colorhex}|${re.colorname})`;
64re.color = `_${re.colorbase}_`;
65re.colors = `_${re.colorbase}(?:,${re.colorbase})?_`;
66re.background = `G${re.color}`;
67re.detail = `D${re.colors}`;
68re.detailsym = `D[0-9]{2}${re.colors}`;
69re.classes = `${re.classbase}(?: ${re.classbase})*`;
70re.full = `-(${re.colorize})?(${re.padding})?(${re.background})?(${re.detail})?(${re.zoom})?(?:-((?:${re.detailsym})*))?(?:-(${re.classes})?!(?:(${re.id})!)?)?`;
71
72const prefixColor = color => {
73 const regex = new RegExp(`^${re.colorhex}$`);
74 return (regex.test(color) ? '#' : '') + color;
75};
76
77const definedProps = obj => Object.fromEntries(Object.entries(obj).filter(([k, v]) => v !== undefined));
78/**
79 * Function to parse style string to object
80 * @function style.parse
81 * @param {string} styleString - a style string
82 * @returns {StyleObject} elements of style string
83 * @example
84 * style.parse('-CP10G_blue_D_red,Cyan_')
85 *
86 * return {
87 * 'colorize': true,
88 * 'padding': 10,
89 * 'background': 'blue',
90 * 'detail': ['red', 'Cyan']
91 * }
92 */
93
94
95const parse$1 = styleString => {
96 const regex = `^${re.full}`;
97 const m = (typeof styleString === 'string' ? styleString.match(new RegExp(regex)) : []) || [];
98 return definedProps({
99 'colorize': !m[1] ? undefined : !!m[1],
100 'padding': !m[2] ? undefined : parseInt(m[2].slice(1)),
101 'background': !m[3] ? undefined : prefixColor(m[3].slice(2, -1)),
102 'detail': !m[4] ? undefined : m[4].slice(2, -1).split(',').map(prefixColor),
103 'zoom': !m[5] ? undefined : m[5] === 'Zx' ? 'x' : parseFloat(m[5].slice(1)),
104 'detailsym': !m[6] ? undefined : m[6].match(new RegExp(re.detailsym, 'g')).map(val => {
105 const parts = val.split('_');
106 const detail = parts[1].split(',').map(prefixColor);
107 return {
108 'index': parseInt(parts[0].slice(1)),
109 'detail': detail
110 };
111 }),
112 'classes': !m[7] ? undefined : m[7],
113 'id': !m[8] ? undefined : m[8]
114 });
115};
116
117/** The convert module contains functions to convert between Formal SignWriitng in ASCII (FSW) and SignWriting in Unicode (SWU) characters, along with other types of data.
118 * [Characters set definitions](https://tools.ietf.org/id/draft-slevinski-formal-signwriting-09.html#name-characters)
119 * @module convert
120 */
121/**
122 * Function to convert an FSW coordinate string to an array of x,y integers
123 * @function convert.fsw2coord
124 * @param {string} fswCoord - An FSW coordinate string
125 * @returns {number[]} Array of x,y integers
126 * @example
127 * convert.fsw2coord('500x500')
128 *
129 * return [500, 500]
130 */
131
132
133const fsw2coord = fswCoord => fswCoord.split('x').map(num => parseInt(num));
134
135const parse = {
136 /**
137 * Function to parse an fsw symbol with optional coordinate and style string
138 * @function fsw.parse.symbol
139 * @param {string} fswSym - an fsw symbol
140 * @returns {object} elements of fsw symbol
141 * @example
142 * fsw.parse.symbol('S10000500x500-C')
143 *
144 * return {
145 * 'symbol': 'S10000',
146 * 'coord': [500, 500],
147 * 'style': '-C'
148 * }
149 */
150 symbol: fswSym => {
151 const regex = `^(${re$1.symbol})(${re$1.coord})?(${re.full})?`;
152 const symbol = typeof fswSym === 'string' ? fswSym.match(new RegExp(regex)) : undefined;
153 return {
154 'symbol': symbol ? symbol[1] : undefined,
155 'coord': symbol && symbol[2] ? fsw2coord(symbol[2]) : undefined,
156 'style': symbol ? symbol[3] : undefined
157 };
158 },
159
160 /**
161 * Function to parse an fsw sign with style string
162 * @function fsw.parse.sign
163 * @param {string} fswSign - an fsw sign
164 * @returns {object} elements of fsw sign
165 * @example
166 * fsw.parse.sign('AS10011S10019S2e704S2e748M525x535S2e748483x510S10011501x466S2e704510x500S10019476x475-C')
167 *
168 * return {
169 * sequence: ['S10011', 'S10019', 'S2e704', 'S2e748'],
170 * box: 'M',
171 * max: [525, 535],
172 * spatials: [
173 * {
174 * symbol: 'S2e748',
175 * coord: [483, 510]
176 * },
177 * {
178 * symbol: 'S10011',
179 * coord: [501, 466]
180 * },
181 * {
182 * symbol: 'S2e704',
183 * coord: [510, 500]
184 * },
185 * {
186 * symbol: 'S10019',
187 * coord: [476, 475]
188 * }
189 * ],
190 * style: '-C'
191 * }
192 */
193 sign: fswSign => {
194 const regex = `^(${re$1.prefix})?(${re$1.signbox})(${re.full})?`;
195 const sign = typeof fswSign === 'string' ? fswSign.match(new RegExp(regex)) : undefined;
196
197 if (sign) {
198 return {
199 'sequence': sign[1] ? sign[1].slice(1).match(/.{6}/g) : undefined,
200 'box': sign[2][0],
201 'max': fsw2coord(sign[2].slice(1, 8)),
202 'spatials': sign[2].length < 9 ? undefined : sign[2].slice(8).match(/(.{13})/g).map(m => {
203 return {
204 symbol: m.slice(0, 6),
205 coord: [parseInt(m.slice(6, 9)), parseInt(m.slice(10, 13))]
206 };
207 }),
208 'style': sign[3]
209 };
210 } else {
211 return {};
212 }
213 },
214
215 /**
216 * Function to parse an fsw text
217 * @function fsw.parse.text
218 * @param {string} fswText - an fsw text
219 * @returns {array} fsw signs and punctuations
220 * @example
221 * fsw.parse.text('AS14c20S27106M518x529S14c20481x471S27106503x489 AS18701S1870aS2e734S20500M518x533S1870a489x515S18701482x490S20500508x496S2e734500x468 S38800464x496')
222 *
223 * return [
224 * 'AS14c20S27106M518x529S14c20481x471S27106503x489',
225 * 'AS18701S1870aS2e734S20500M518x533S1870a489x515S18701482x490S20500508x496S2e734500x468',
226 * 'S38800464x496'
227 * ]
228 */
229 text: fswText => {
230 if (typeof fswText !== 'string') return [];
231 const regex = `(${re$1.sign}(${re.full})?|${re$1.spatial}(${re.full})?)`;
232 const matches = fswText.match(new RegExp(regex, 'g'));
233 return matches ? [...matches] : [];
234 }
235};
236
237const compose = {
238 /**
239 * Function to compose an fsw symbol with optional coordinate and style string
240 * @function fsw.compose.symbol
241 * @param {object} fswSymObject - an fsw symbol object
242 * @param {string} fswSymObject.symbol - an fsw symbol key
243 * @param {number[]} fswSymObject.coord - top-left coordinate of symbol
244 * @param {string} fswSymObject.style - a style string for custom appearance
245 * @returns {string} an fsw symbol string
246 * @example
247 * fsw.compose.symbol({
248 * 'symbol': 'S10000',
249 * 'coord': [480, 480],
250 * 'style': '-C'
251 * })
252 *
253 * return 'S10000480x480-C'
254 */
255 symbol: fswSymObject => {
256 if (typeof fswSymObject.symbol === 'string') {
257 const symbol = (fswSymObject.symbol.match(re$1.symbol) || [''])[0];
258
259 if (symbol) {
260 const x = (fswSymObject.coord && fswSymObject.coord[0] || '').toString();
261 const y = (fswSymObject.coord && fswSymObject.coord[1] || '').toString();
262 const coord = ((x + 'x' + y).match(re$1.coord) || [''])[0] || '';
263 const styleStr = typeof fswSymObject.style === 'string' && (fswSymObject.style.match(re.full) || [''])[0] || '';
264 return symbol + coord + styleStr;
265 }
266 }
267
268 return undefined;
269 },
270
271 /**
272 * Function to compose an fsw sign with style string
273 * @function fsw.compose.sign
274 * @param {object} fswSymObject - an fsw sign object
275 * @param {string[]} fswSignObject.sequence - an ordered array of symbols
276 * @param {string} fswSignObject.box - a choice BLMR: horizontal Box, Left, Middle, and Right lane
277 * @param {number[]} fswSignObject.max - max bottom-right coordinate of the signbox space
278 * @param {{symbol:string,coord:number[]}[]} fswSignObject.spatials - array of symbols with top-left coordinate placement
279 * @param {string} fswSignObject.style - a style string for custom appearance
280 * @returns {string} an fsw sign string
281 * @example
282 * fsw.compose.sign({
283 * sequence: ['S10011', 'S10019', 'S2e704', 'S2e748'],
284 * box: 'M',
285 * max: [525, 535],
286 * spatials: [
287 * {
288 * symbol: 'S2e748',
289 * coord: [483, 510]
290 * },
291 * {
292 * symbol: 'S10011',
293 * coord: [501, 466]
294 * },
295 * {
296 * symbol: 'S2e704',
297 * coord: [510, 500]
298 * },
299 * {
300 * symbol: 'S10019',
301 * coord: [476, 475]
302 * }
303 * ],
304 * style: '-C'
305 * })
306 *
307 * return 'AS10011S10019S2e704S2e748M525x535S2e748483x510S10011501x466S2e704510x500S10019476x475-C'
308 */
309 sign: fswSignObject => {
310 let box = typeof fswSignObject.box !== 'string' ? 'M' : (fswSignObject.box + 'M').match(re$1.box);
311 const x = (fswSignObject.max && fswSignObject.max[0] || '').toString();
312 const y = (fswSignObject.max && fswSignObject.max[1] || '').toString();
313 const max = ((x + 'x' + y).match(re$1.coord) || [''])[0] || '';
314 if (!max) return undefined;
315 let prefix = '';
316
317 if (fswSignObject.sequence && Array.isArray(fswSignObject.sequence)) {
318 prefix = fswSignObject.sequence.map(key => (key.match(re$1.symbol) || [''])[0]).join('');
319 prefix = prefix ? 'A' + prefix : '';
320 }
321
322 let signbox = '';
323
324 if (fswSignObject.spatials && Array.isArray(fswSignObject.spatials)) {
325 signbox = fswSignObject.spatials.map(spatial => {
326 if (typeof spatial.symbol === 'string') {
327 const symbol = (spatial.symbol.match(re$1.symbol) || [''])[0];
328
329 if (symbol) {
330 const x = (spatial.coord && spatial.coord[0] || '').toString();
331 const y = (spatial.coord && spatial.coord[1] || '').toString();
332 const coord = ((x + 'x' + y).match(re$1.coord) || [''])[0] || '';
333
334 if (coord) {
335 return symbol + coord;
336 }
337 }
338 }
339
340 return '';
341 }).join('');
342 }
343
344 const styleStr = typeof fswSignObject.style === 'string' && (fswSignObject.style.match(re.full) || [''])[0] || '';
345 return prefix + box + max + signbox + styleStr;
346 }
347};
348
349/**
350 * Function to gather sizing information about an fsw sign or symbol
351 * @function fsw.info
352 * @param {string} fsw - an fsw sign or symbol
353 * @returns {object} information about the fsw string
354 * @example
355 * fsw.info('AS14c20S27106L518x529S14c20481x471S27106503x489-P10Z2')
356 *
357 * return {
358 * minX: 481,
359 * minY: 471,
360 * width: 37,
361 * height: 58,
362 * zoom: 2,
363 * padding: 10,
364 * segment: 'sign',
365 * lane: -1
366 * }
367 */
368
369const info = fsw => {
370 let lanes = {
371 "B": 0,
372 "L": -1,
373 "M": 0,
374 "R": 1
375 };
376 let parsed = parse.sign(fsw);
377 let width, height, segment, x1, x2, y1, y2, lane;
378
379 if (parsed.spatials) {
380 x1 = Math.min(...parsed.spatials.map(spatial => spatial.coord[0]));
381 x2 = parsed.max[0];
382 width = x2 - x1;
383 y1 = Math.min(...parsed.spatials.map(spatial => spatial.coord[1]));
384 y2 = parsed.max[1];
385 height = y2 - y1;
386 segment = 'sign';
387 lane = parsed.box;
388 } else {
389 parsed = parse.symbol(fsw);
390 lane = "M";
391
392 if (parsed.coord) {
393 x1 = parsed.coord[0];
394 width = (500 - x1) * 2;
395 y1 = parsed.coord[1];
396 height = (500 - y1) * 2;
397 segment = 'symbol';
398 } else {
399 x1 = 490;
400 width = 20;
401 y1 = 490;
402 height = 20;
403 segment = 'none';
404 }
405 }
406
407 let style = parse$1(parsed.style);
408 let zoom = style.zoom || 1;
409 let padding = style.padding || 0;
410 return {
411 minX: x1,
412 minY: y1,
413 width: width,
414 height: height,
415 segment: segment,
416 lane: lanes[lane],
417 padding: padding,
418 zoom: zoom
419 };
420};
421
422const columnDefaults = {
423 'height': 500,
424 'width': 150,
425 'offset': 50,
426 'pad': 20,
427 'margin': 5,
428 'dynamic': false,
429 'background': undefined,
430 'punctuation': {
431 'spacing': true,
432 'pad': 30,
433 'pull': true
434 },
435 'style': {
436 'detail': ['black', 'white'],
437 'zoom': 1
438 }
439};
440/**
441 * Function to an object of column options with default values
442 *
443 * @function fsw.columnDefaultsMerge
444 * @param {ColumnOptions} options - object of column options
445 * @returns {ColumnOptions} object of column options merged with column defaults
446 * @example
447 * fsw.columnDefaultsMerge({height: 500,width:150})
448 *
449 * return {
450 * "height": 500,
451 * "width": 150,
452 * "offset": 50,
453 * "pad": 20,
454 * "margin": 5,
455 * "dynamic": false,
456 * "punctuation": {
457 * "spacing": true,
458 * "pad": 30,
459 * "pull": true
460 * },
461 * "style": {
462 * "detail": [
463 * "black",
464 * "white"
465 * ],
466 * "zoom": 1
467 * }
468 * }
469 */
470
471const columnDefaultsMerge = options => {
472 if (typeof options !== 'object') options = {};
473 return { ...columnDefaults,
474 ...options,
475 punctuation: { ...columnDefaults.punctuation,
476 ...options.punctuation
477 },
478 style: { ...columnDefaults.style,
479 ...options.style
480 }
481 };
482};
483/**
484 * Function to transform an FSW text to an array of columns
485 *
486 * @function fsw.columns
487 * @param {string} fswText - FSW text of signs and punctuation
488 * @param {ColumnOptions} options - object of column options
489 * @returns {{options:ColumnOptions,widths:number[],columns:ColumnData}} object of column options, widths array, and column data
490 * @example
491 * fsw.columns('AS14c20S27106M518x529S14c20481x471S27106503x489 AS18701S1870aS2e734S20500M518x533S1870a489x515S18701482x490S20500508x496S2e734500x468 S38800464x496', {height: 500,width:150})
492 *
493 * return {
494 * "options": {
495 * "height": 500,
496 * "width": 150,
497 * "offset": 50,
498 * "pad": 20,
499 * "margin": 5,
500 * "dynamic": false,
501 * "punctuation": {
502 * "spacing": true,
503 * "pad": 30,
504 * "pull": true
505 * },
506 * "style": {
507 * "detail": [
508 * "black",
509 * "white"
510 * ],
511 * "zoom": 1
512 * }
513 * },
514 * "widths": [
515 * 150
516 * ],
517 * "columns": [
518 * [
519 * {
520 * "x": 56,
521 * "y": 20,
522 * "minX": 481,
523 * "minY": 471,
524 * "width": 37,
525 * "height": 58,
526 * "lane": 0,
527 * "padding": 0,
528 * "segment": "sign",
529 * "text": "AS14c20S27106M518x529S14c20481x471S27106503x489",
530 * "zoom": 1
531 * },
532 * {
533 * "x": 57,
534 * "y": 118,
535 * "minX": 482,
536 * "minY": 468,
537 * "width": 36,
538 * "height": 65,
539 * "lane": 0,
540 * "padding": 0,
541 * "segment": "sign",
542 * "text": "AS18701S1870aS2e734S20500M518x533S1870a489x515S18701482x490S20500508x496S2e734500x468",
543 * "zoom": 1
544 * },
545 * {
546 * "x": 39,
547 * "y": 203,
548 * "minX": 464,
549 * "minY": 496,
550 * "width": 72,
551 * "height": 8,
552 * "lane": 0,
553 * "padding": 0,
554 * "segment": "symbol",
555 * "text": "S38800464x496",
556 * "zoom": 1
557 * }
558 * ]
559 * ]
560 * }
561 */
562
563
564const columns = (fswText, options) => {
565 if (typeof fswText !== 'string') return {};
566 const values = columnDefaultsMerge(options);
567 let input = parse.text(fswText);
568 let cursor = 0;
569 let cols = [];
570 let col = [];
571 let plus = 0;
572 let center = parseInt(values.width / 2);
573 let maxHeight = values.height - values.margin;
574 let pullable = true;
575 let finalize = false;
576
577 for (let val of input) {
578 let informed = info(val);
579 cursor += plus;
580
581 if (values.punctuation.spacing) {
582 cursor += informed.segment == 'sign' ? values.pad : 0;
583 } else {
584 cursor += values.pad;
585 }
586
587 finalize = cursor + informed.height > maxHeight;
588
589 if (finalize && informed.segment == 'symbol' && values.punctuation.pull && pullable) {
590 finalize = false;
591 pullable = false;
592 }
593
594 if (col.length == 0) {
595 finalize = false;
596 }
597
598 if (finalize) {
599 cursor = values.pad;
600 cols.push(col);
601 col = [];
602 pullable = true;
603 }
604
605 col.push(Object.assign(informed, {
606 x: center + values.offset * informed.lane - (500 - informed.minX) * informed.zoom * values.style.zoom,
607 y: cursor,
608 text: val
609 }));
610 cursor += informed.height * informed.zoom * values.style.zoom;
611
612 if (values.punctuation.spacing) {
613 plus = informed.segment == 'sign' ? values.pad : values.punctuation.pad;
614 } else {
615 plus = values.pad;
616 }
617 }
618
619 if (col.length) {
620 cols.push(col);
621 } // over height issue when pulling punctuation
622
623
624 if (values.punctuation.pull) {
625 for (let col of cols) {
626 let last = col[col.length - 1];
627 let diff = last.y + last.height - (values.height - values.margin);
628
629 if (diff > 0) {
630 let adj = parseInt(diff / col.length) + 1;
631
632 for (let i in col) {
633 col[i].y -= adj * i + adj;
634 }
635 }
636 }
637 } // contract, expand, adjust
638
639
640 let widths = [];
641
642 for (let col of cols) {
643 let min = [center - values.offset - values.pad];
644 let max = [center + values.offset + values.pad];
645
646 for (let item of col) {
647 min.push(item.x - values.pad);
648 max.push(item.x + item.width + values.pad);
649 }
650
651 min = Math.min(...min);
652 max = Math.max(...max);
653 let width = values.width;
654 let adj = 0;
655
656 if (!values.dynamic) {
657 adj = center - parseInt((min + max) / 2);
658 } else {
659 width = max - min;
660 adj = -min;
661 }
662
663 for (let item of col) {
664 item.x += adj;
665 }
666
667 widths.push(width);
668 }
669
670 return {
671 'options': values,
672 'widths': widths,
673 'columns': cols
674 };
675};
676
677/**
678 * Array of numbers for kinds of symbols: writing, location, and punctuation.
679 * @alias fsw.kind
680 * @type {array}
681 */
682
683const kind = [0x100, 0x37f, 0x387];
684/**
685 * Array of numbers for categories of symbols: hand, movement, dynamics, head, trunk & limb, location, and punctuation.
686 * @alias fsw.category
687 * @type {array}
688 */
689
690const category = [0x100, 0x205, 0x2f7, 0x2ff, 0x36d, 0x37f, 0x387];
691/**
692 * Array of numbers for the 30 symbol groups.
693 * @alias fsw.group
694 * @type {array}
695 */
696
697const group = [0x100, 0x10e, 0x11e, 0x144, 0x14c, 0x186, 0x1a4, 0x1ba, 0x1cd, 0x1f5, 0x205, 0x216, 0x22a, 0x255, 0x265, 0x288, 0x2a6, 0x2b7, 0x2d5, 0x2e3, 0x2f7, 0x2ff, 0x30a, 0x32a, 0x33b, 0x359, 0x36d, 0x376, 0x37f, 0x387];
698/**
699 * Object of symbol ranges with starting and ending numbers.
700 *
701 * { all, writing, hand, movement, dynamic, head, hcenter, vcenter, trunk, limb, location, punctuation }
702 * @alias fsw.ranges
703 * @type {object}
704 */
705
706const ranges = {
707 'all': [0x100, 0x38b],
708 'writing': [0x100, 0x37e],
709 'hand': [0x100, 0x204],
710 'movement': [0x205, 0x2f6],
711 'dynamic': [0x2f7, 0x2fe],
712 'head': [0x2ff, 0x36c],
713 'hcenter': [0x2ff, 0x36c],
714 'vcenter': [0x2ff, 0x375],
715 'trunk': [0x36d, 0x375],
716 'limb': [0x376, 0x37e],
717 'location': [0x37f, 0x386],
718 'punctuation': [0x387, 0x38b]
719};
720/**
721 * Function to test if symbol is of a certain type.
722 * @function fsw.isType
723 * @param {string} key - an FSW symbol key
724 * @param {string} type - the name of a symbol range
725 * @returns {boolean} is symbol of specified type
726 * @example
727 * fsw.isType('S10000', 'hand')
728 *
729 * return true
730 */
731
732const isType = (key, type) => {
733 const parsed = parse.symbol(key);
734
735 if (parsed.symbol) {
736 const dec = parseInt(parsed.symbol.slice(1, 4), 16);
737 const range = ranges[type];
738
739 if (range) {
740 return range[0] <= dec && range[1] >= dec;
741 }
742 }
743
744 return false;
745};
746
747/**
748 * Array of colors associated with the seven symbol categories.
749 * @alias fsw.colors
750 * @type {array}
751 */
752
753const colors = ['#0000CC', '#CC0000', '#FF0099', '#006600', '#000000', '#884411', '#FF9900'];
754/**
755 * Function that returns the standardized color for a symbol.
756 * @function fsw.colorize
757 * @param {string} key - an FSW symbol key
758 * @returns {string} name of standardized color for symbol
759 * @example
760 * fsw.colorize('S10000')
761 *
762 * return '#0000CC'
763 */
764
765const colorize = key => {
766 const parsed = parse.symbol(key);
767 let color = '#000000';
768
769 if (parsed.symbol) {
770 const dec = parseInt(parsed.symbol.slice(1, 4), 16);
771 const index = category.findIndex(val => val > dec);
772 color = colors[index < 0 ? 6 : index - 1];
773 }
774
775 return color;
776};
777
778export { category, colorize, colors, columnDefaults, columnDefaultsMerge, columns, compose, group, info, isType, kind, parse, ranges, re$1 as re };
779
780/* support ongoing development on https://patreon.com/signwriting */