UNPKG

91.9 kBJavaScriptView Raw
1/**
2* Sutton SignWriting Core Module v1.3.1 (https://github.com/sutton-signwriting/core)
3* Author: Steve Slevinski (https://SteveSlevinski.me)
4* core.js is released under the MIT License.
5*/
6
7(function (global, factory) {
8 typeof exports === 'object' && typeof module !== 'undefined' ? factory(exports) :
9 typeof define === 'function' && define.amd ? define(['exports'], factory) :
10 (global = typeof globalThis !== 'undefined' ? globalThis : global || self, factory((global.ssw = global.ssw || {}, global.ssw.core = {})));
11})(this, (function (exports) { 'use strict';
12
13 /**
14 * Object of regular expressions for FSW strings
15 *
16 * { symbol, coord, sort, box, prefix, spatial, signbox, sign, sortable }
17 * @alias fsw.re
18 * @type {object}
19 */
20 let re$4 = {
21 'symbol': 'S[123][0-9a-f]{2}[0-5][0-9a-f]',
22 'coord': '[0-9]{3}x[0-9]{3}',
23 'sort': 'A',
24 'box': '[BLMR]'
25 };
26 re$4.prefix = `(?:${re$4.sort}(?:${re$4.symbol})+)`;
27 re$4.spatial = `${re$4.symbol}${re$4.coord}`;
28 re$4.signbox = `${re$4.box}${re$4.coord}(?:${re$4.spatial})*`;
29 re$4.sign = `${re$4.prefix}?${re$4.signbox}`;
30 re$4.sortable = `${re$4.prefix}${re$4.signbox}`;
31
32 /**
33 * Object of regular expressions for style strings
34 *
35 * { colorize, colorhex, colorname, padding, zoom, zoomsym, classbase, id, colorbase, color, colors, background, detail, detailsym, classes, full }
36 * @alias style.re
37 * @type {object}
38 */
39 let re$3 = {
40 'colorize': 'C',
41 'colorhex': '(?:[0-9a-fA-F]{3}){1,2}',
42 'colorname': '[a-zA-Z]+',
43 'padding': 'P[0-9]{2}',
44 'zoom': 'Z(?:[0-9]+(?:\\.[0-9]+)?|x)',
45 'zoomsym': 'Z[0-9]{2},[0-9]+(?:\\.[0-9]+)?(?:,[0-9]{3}x[0-9]{3})?',
46 'classbase': '-?[_a-zA-Z][_a-zA-Z0-9-]{0,100}',
47 'id': '[a-zA-Z][_a-zA-Z0-9-]{0,100}'
48 };
49 re$3.colorbase = `(?:${re$3.colorhex}|${re$3.colorname})`;
50 re$3.color = `_${re$3.colorbase}_`;
51 re$3.colors = `_${re$3.colorbase}(?:,${re$3.colorbase})?_`;
52 re$3.background = `G${re$3.color}`;
53 re$3.detail = `D${re$3.colors}`;
54 re$3.detailsym = `D[0-9]{2}${re$3.colors}`;
55 re$3.classes = `${re$3.classbase}(?: ${re$3.classbase})*`;
56 re$3.full = `-(${re$3.colorize})?(${re$3.padding})?(${re$3.background})?(${re$3.detail})?(${re$3.zoom})?(?:-((?:${re$3.detailsym})*)((?:${re$3.zoomsym})*))?(?:-(${re$3.classes})?!(?:(${re$3.id})!)?)?`;
57
58 const prefixColor = color => {
59 const regex = new RegExp(`^${re$3.colorhex}$`);
60 return (regex.test(color) ? '#' : '') + color;
61 };
62 /**
63 * Function to parse style string to object
64 * @function style.parse
65 * @param {string} styleString - a style string
66 * @returns {object} elements of style string
67 * @example
68 * style.parse('-CP10G_blue_D_red,Cyan_')
69 *
70 * return {
71 * 'colorize': true,
72 * 'padding': 10,
73 * 'background': 'blue',
74 * 'detail': ['red', 'Cyan']
75 * }
76 */
77
78
79 const parse$4 = styleString => {
80 const regex = `^${re$3.full}`;
81 const m = (typeof styleString === 'string' ? styleString.match(new RegExp(regex)) : []) || [];
82 return {
83 'colorize': !m[1] ? undefined : !!m[1],
84 'padding': !m[2] ? undefined : parseInt(m[2].slice(1)),
85 'background': !m[3] ? undefined : prefixColor(m[3].slice(2, -1)),
86 'detail': !m[4] ? undefined : m[4].slice(2, -1).split(',').map(prefixColor),
87 'zoom': !m[5] ? undefined : m[5] === 'Zx' ? 'x' : parseFloat(m[5].slice(1)),
88 'detailsym': !m[6] ? undefined : m[6].match(new RegExp(re$3.detailsym, 'g')).map(val => {
89 const parts = val.split('_');
90 const detail = parts[1].split(',').map(prefixColor);
91 return {
92 'index': parseInt(parts[0].slice(1)),
93 'detail': detail
94 };
95 }),
96 'zoomsym': !m[7] ? undefined : m[7].match(new RegExp(re$3.zoomsym, 'g')).map(val => {
97 const parts = val.split(',');
98 return {
99 'index': parseInt(parts[0].slice(1)),
100 'zoom': parseFloat(parts[1]),
101 'offset': !parts[2] ? undefined : parts[2].split('x').map(val => parseInt(val) - 500)
102 };
103 }),
104 'classes': !m[8] ? undefined : m[8],
105 'id': !m[9] ? undefined : m[9]
106 };
107 };
108
109 /**
110 * Function to compose style string from object
111 * @function style.compose
112 * @param {object} styleObject - an object of style options
113 * @param {boolean} styleObject.colorize - boolean to use standardized colors for symbol groups
114 * @param {number} styleObject.padding - integer value for padding around symbol or sign
115 * @param {string} styleObject.background - css name or hex color for background
116 * @param {string[]} styleObject.detail - css name or hex color for line and optional fill
117 * @param {number} styleObject.zoom - decimal value for zoom level
118 * @param {{index:number,detail:string[]}[]} styleObject.detailsym - array of symbol indexes and detail color array
119 * @param {{index:number,zoom:number,offset:number[]}[]} styleObject.zoomsym - array of symbol indexes and zoom levels with optional x,y offset
120 * @param {string} styleObject.classes - list of class names separated with spaces used for SVG
121 * @param {string} styleObject.id - id name used for SVG
122 * @returns {string} style string
123 * @example
124 * style.compose({
125 * 'colorize': true,
126 * 'padding': 10,
127 * 'background': 'blue',
128 * 'detail': ['red', 'Cyan'],
129 * 'zoom': 1.1,
130 * 'detailsym': [
131 * {
132 * 'index': 1,
133 * 'detail': ['#ff00ff']
134 * },
135 * {
136 * 'index': 2,
137 * 'detail': ['yellow', 'green']
138 * }
139 * ],
140 * 'zoomsym': [
141 * {
142 * 'index': 1,
143 * 'zoom': 10,
144 * 'offset': [0, 0]
145 * },
146 * {
147 * 'index': 2,
148 * 'zoom': 5.5
149 * }
150 * ],
151 * 'classes': 'primary blinking',
152 * 'id': 'cursor'
153 * })
154 *
155 * return '-CP10G_blue_D_red,Cyan_Z1.1-D01_ff00ff_D02_yellow,green_Z01,10,500x500Z02,5.5-primary blinking!cursor!'
156 */
157
158 const compose$4 = styleObject => {
159 if (typeof styleObject !== 'object' || styleObject === null) return undefined; // three sections
160
161 let style1 = '-';
162 style1 += !styleObject.colorize ? '' : 'C';
163 const padding = parseInt(styleObject.padding);
164 style1 += !padding || padding <= 0 || padding > 99 ? '' : 'P' + (padding > 9 ? padding : '0' + padding);
165 const background = !styleObject.background || !(typeof styleObject.background === 'string') ? undefined : styleObject.background.match(re$3.colorbase)[0];
166 style1 += !background ? '' : 'G_' + background + '_';
167 const detail1 = !styleObject.detail || !styleObject.detail[0] || !(typeof styleObject.detail[0] === 'string') ? undefined : styleObject.detail[0].match(re$3.colorbase)[0];
168 const detail2 = !styleObject.detail || !styleObject.detail[1] || !(typeof styleObject.detail[1] === 'string') ? undefined : styleObject.detail[1].match(re$3.colorbase)[0];
169
170 if (detail1) {
171 style1 += 'D_' + detail1;
172
173 if (detail2) {
174 style1 += ',' + detail2;
175 }
176
177 style1 += '_';
178 }
179
180 const zoom = styleObject.zoom === 'x' ? 'x' : parseFloat(styleObject.zoom);
181 style1 += !zoom || zoom <= 0 ? '' : 'Z' + zoom;
182 let style2 = '';
183 const detailsym = !styleObject.detailsym || !Array.isArray(styleObject.detailsym) ? [] : styleObject.detailsym.map(styleObject => {
184 const index = parseInt(styleObject.index);
185 if (!index || index <= 0 || index > 99) return '';
186 let style = 'D' + (index > 9 ? index : '0' + index);
187 const detail1 = !styleObject.detail || !styleObject.detail[0] ? undefined : styleObject.detail[0].match(re$3.colorbase)[0];
188 const detail2 = !styleObject.detail || !styleObject.detail[1] ? undefined : styleObject.detail[1].match(re$3.colorbase)[0];
189
190 if (detail1) {
191 style += '_' + detail1;
192
193 if (detail2) {
194 style += ',' + detail2;
195 }
196
197 style += '_';
198 }
199
200 return style;
201 });
202 style2 += detailsym.join('');
203 const zoomsym = !styleObject.zoomsym || !Array.isArray(styleObject.zoomsym) ? [] : styleObject.zoomsym.map(styleObject => {
204 const index = parseInt(styleObject.index);
205 if (!index || index <= 0 || index > 99) return '';
206 let style = 'Z' + (index > 9 ? index : '0' + index);
207 const zoom = parseFloat(styleObject.zoom);
208 style += !zoom || zoom <= 0 ? '' : ',' + zoom;
209
210 if (styleObject.offset && 0 in styleObject.offset && 1 in styleObject.offset) {
211 const x = parseInt(styleObject.offset[0]) + 500;
212 const y = parseInt(styleObject.offset[1]) + 500;
213
214 if (x >= 250 && x < 750 && y >= 250 && y < 750) {
215 style += ',' + x + 'x' + y;
216 }
217 }
218
219 return style;
220 });
221 style2 += zoomsym.join('');
222 let style3 = '';
223 const classes = !styleObject.classes || !(typeof styleObject.classes === 'string') ? undefined : styleObject.classes.match(re$3.classes)[0];
224 style3 += !classes ? '' : classes;
225 const id = !styleObject.id || !(typeof styleObject.id === 'string') ? undefined : styleObject.id.match(re$3.id)[0];
226 style3 += classes || id ? '!' : '';
227 style3 += !id ? '' : id + '!';
228 return style1 + (style2 || style3 ? '-' + style2 : '') + (style3 ? '-' + style3 : '');
229 };
230
231 /** The style module contains regular expressions and functions for parsing and composing style strings.
232 * [Style string definition](https://tools.ietf.org/id/draft-slevinski-formal-signwriting-07.html#rfc.section.2.5)
233 * @module style
234 */
235
236 var index$5 = /*#__PURE__*/Object.freeze({
237 __proto__: null,
238 re: re$3,
239 parse: parse$4,
240 compose: compose$4
241 });
242
243 /**
244 * Object of regular expressions for SWU strings in UTF-16
245 *
246 * { symbol, coord, sort, box, prefix, spatial, signbox, sign, sortable }
247 * @alias swu.re
248 * @type {object}
249 */
250 let re$2 = {
251 'symbol': '(?:(?:\uD8C0[\uDC01-\uDFFF])|(?:[\uD8C1-\uD8FC][\uDC00-\uDFFF])|(?:\uD8FD[\uDC00-\uDC80]))',
252 'coord': '(?:\uD836[\uDC0C-\uDDFF]){2}',
253 'sort': '\uD836\uDC00',
254 'box': '\uD836[\uDC01-\uDC04]'
255 };
256 re$2.prefix = `(?:${re$2.sort}(?:${re$2.symbol})+)`;
257 re$2.spatial = `${re$2.symbol}${re$2.coord}`;
258 re$2.signbox = `${re$2.box}${re$2.coord}(?:${re$2.spatial})*`;
259 re$2.sign = `${re$2.prefix}?${re$2.signbox}`;
260 re$2.sortable = `${re$2.prefix}${re$2.signbox}`;
261
262 /** 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.
263 * [Characters set definitions](https://tools.ietf.org/id/draft-slevinski-formal-signwriting-07.html#rfc.section.2.2)
264 * @module convert
265 */
266 /**
267 * Function to convert an SWU structural marker to FSW equivalent
268 * @function convert.swu2mark
269 * @param {string} swuMark - character for SWU structural marker
270 * @returns {string} FSW structural marker
271 * @example
272 * convert.swu2mark('𝠀')
273 *
274 * return 'A'
275 */
276
277 const swu2mark = swuMark => {
278 return {
279 '𝠀': 'A',
280 '𝠁': 'B',
281 '𝠂': 'L',
282 '𝠃': 'M',
283 '𝠄': 'R'
284 }[swuMark];
285 };
286 /**
287 * Function to convert an FSW structural marker to SWU equivalent
288 * @function convert.mark2swu
289 * @param {string} fswMark - character for FSW structural marker
290 * @returns {string} SWU structural marker
291 * @example
292 * convert.mark2swu('A')
293 *
294 * return '𝠀'
295 */
296
297
298 const mark2swu = fswMark => {
299 return {
300 'A': '𝠀',
301 'B': '𝠁',
302 'L': '𝠂',
303 'M': '𝠃',
304 'R': '𝠄'
305 }[fswMark];
306 };
307 /**
308 * Function to convert an SWU number character to an integer
309 * @function convert.swu2num
310 * @param {string} swuNum - SWU number character
311 * @returns {number} Integer value for number
312 * @example
313 * convert.swu2num('𝤆')
314 *
315 * return 500
316 */
317
318
319 const swu2num = swuNum => parseInt(swuNum.codePointAt(0)) - 0x1D80C + 250;
320 /**
321 * Function to convert a number to an SWU number character
322 * @function convert.num2swu
323 * @param {number} num - Integer value for number
324 * @returns {string} SWU number character
325 * @example
326 * convert.num2swu(500)
327 *
328 * return '𝤆'
329 */
330
331
332 const num2swu = num => String.fromCodePoint(0x1D80C + parseInt(num) - 250);
333 /**
334 * Function to convert two SWU number characters to an array of x,y integers
335 * @function convert.swu2coord
336 * @param {string} swuCoord - Two SWU number character
337 * @returns {number[]} Array of x,y integers
338 * @example
339 * convert.swu2coord('𝤆𝤆')
340 *
341 * return [500, 500]
342 */
343
344
345 const swu2coord = swuCoord => [swu2num(swuCoord.slice(0, 2)), swu2num(swuCoord.slice(2, 4))];
346 /**
347 * Function to convert an array of x,y integers to two SWU number characters
348 * @function convert.coord2swu
349 * @param {number[]} coord - Array of x,y integers
350 * @returns {string} Two SWU number character
351 * @example
352 * convert.coord2swu([500, 500])
353 *
354 * return '𝤆𝤆'
355 */
356
357
358 const coord2swu = coord => coord.map(num => num2swu(num)).join('');
359 /**
360 * Function to convert an FSW coordinate string to an array of x,y integers
361 * @function convert.fsw2coord
362 * @param {string} fswCoord - An FSW coordinate string
363 * @returns {number[]} Array of x,y integers
364 * @example
365 * convert.fsw2coord('500x500')
366 *
367 * return [500, 500]
368 */
369
370
371 const fsw2coord = fswCoord => fswCoord.split('x').map(num => parseInt(num));
372 /**
373 * Function to convert an array of x,y integers to an FSW coordinate string
374 * @function convert.coord2fsw
375 * @param {number[]} coord - Array of x,y integers
376 * @returns {string} An FSW coordinate string
377 * @example
378 * convert.coord2fsw([500, 500])
379 *
380 * return '500x500'
381 */
382
383
384 const coord2fsw = coord => coord.join('x');
385 /**
386 * Function to convert an SWU symbol character to a code point on plane 4
387 * @function convert.swu2code
388 * @param {string} swuSym - SWU symbol character
389 * @returns {number} Code point on plane 4
390 * @example
391 * convert.swu2code('񀀁')
392 *
393 * return 0x40001
394 */
395
396
397 const swu2code = swuSym => parseInt(swuSym.codePointAt(0));
398 /**
399 * Function to convert a code point on plane 4 to an SWU symbol character
400 * @function convert.code2swu
401 * @param {number} code - Code point on plane 4
402 * @returns {string} SWU symbol character
403 * @example
404 * convert.code2swu(0x40001)
405 *
406 * return '񀀁'
407 */
408
409
410 const code2swu = code => String.fromCodePoint(code);
411 /**
412 * Function to convert an SWU symbol character to a 16-bit ID
413 * @function convert.swu2id
414 * @param {string} swuSym - SWU symbol character
415 * @returns {number} 16-bit ID
416 * @example
417 * convert.swu2id('񀀁')
418 *
419 * return 1
420 */
421
422
423 const swu2id = swuSym => swu2code(swuSym) - 0x40000;
424 /**
425 * Function to convert a 16-bit ID to an SWU symbol character
426 * @function convert.id2swu
427 * @param {number} id - 16-bit ID
428 * @returns {string} SWU symbol character
429 * @example
430 * convert.id2swu(1)
431 *
432 * return '񀀁'
433 */
434
435
436 const id2swu = id => code2swu(id + 0x40000);
437 /**
438 * Function to convert an FSW symbol key to a 16-bit ID
439 * @function convert.key2id
440 * @param {string} key - FSW symbol key
441 * @returns {number} 16-bit ID
442 * @example
443 * convert.key2id('S10000')
444 *
445 * return 1
446 */
447
448
449 const key2id = key => 1 + (parseInt(key.slice(1, 4), 16) - 256) * 96 + parseInt(key.slice(4, 5), 16) * 16 + parseInt(key.slice(5, 6), 16);
450 /**
451 * Function to convert a 16-bit ID to an FSW symbol key
452 * @function convert.id2key
453 * @param {number} id - 16-bit ID
454 * @returns {string} FSW symbol key
455 * @example
456 * convert.id2key(1)
457 *
458 * return 'S10000'
459 */
460
461
462 const id2key = id => {
463 const symcode = id - 1;
464 const base = parseInt(symcode / 96);
465 const fill = parseInt((symcode - base * 96) / 16);
466 const rotation = parseInt(symcode - base * 96 - fill * 16);
467 return 'S' + (base + 0x100).toString(16) + fill.toString(16) + rotation.toString(16);
468 };
469 /**
470 * Function to convert an SWU symbol character to an FSW symbol key
471 * @function convert.swu2key
472 * @param {string} swuSym - SWU symbol character
473 * @returns {string} FSW symbol key
474 * @example
475 * convert.swu2key('񀀁')
476 *
477 * return 'S10000'
478 */
479
480
481 const swu2key = swuSym => {
482 const symcode = swu2code(swuSym) - 0x40001;
483 const base = parseInt(symcode / 96);
484 const fill = parseInt((symcode - base * 96) / 16);
485 const rotation = parseInt(symcode - base * 96 - fill * 16);
486 return 'S' + (base + 0x100).toString(16) + fill.toString(16) + rotation.toString(16);
487 };
488 /**
489 * Function to convert an FSW symbol key to an SWU symbol character
490 * @function convert.key2swu
491 * @param {string} key - FSW symbol key
492 * @returns {string} SWU symbol character
493 * @example
494 * convert.key2swu('S10000')
495 *
496 * return '񀀁'
497 */
498
499
500 const key2swu = key => code2swu(0x40001 + (parseInt(key.slice(1, 4), 16) - 256) * 96 + parseInt(key.slice(4, 5), 16) * 16 + parseInt(key.slice(5, 6), 16));
501 /**
502 * Function to convert SWU text to FSW text
503 * @function convert.swu2fsw
504 * @param {string} swuText - SWU text
505 * @returns {string} FSW text
506 * @example
507 * convert.swu2fsw('𝠀񀀒񀀚񋚥񋛩𝠃𝤟𝤩񋛩𝣵𝤐񀀒𝤇𝣤񋚥𝤐𝤆񀀚𝣮𝣭')
508 *
509 * return 'AS10011S10019S2e704S2e748M525x535S2e748483x510S10011501x466S2e704510x500S10019476x475'
510 */
511
512
513 const swu2fsw = swuText => {
514 if (!swuText) return '';
515 let fsw = swuText.replace(/𝠀/g, "A").replace(/𝠁/g, "B").replace(/𝠂/g, "L").replace(/𝠃/g, "M").replace(/𝠄/g, "R");
516 const syms = fsw.match(new RegExp(re$2.symbol, 'g'));
517
518 if (syms) {
519 syms.forEach(function (sym) {
520 fsw = fsw.replace(sym, swu2key(sym));
521 });
522 }
523
524 const coords = fsw.match(new RegExp(re$2.coord, 'g'));
525
526 if (coords) {
527 coords.forEach(function (coord) {
528 fsw = fsw.replace(coord, swu2coord(coord).join('x'));
529 });
530 }
531
532 return fsw;
533 };
534 /**
535 * Function to convert FSW text to SWU text
536 * @function convert.fsw2swu
537 * @param {string} fswText - FSW text
538 * @returns {string} SWU text
539 * @example
540 * convert.fsw2swu('AS10011S10019S2e704S2e748M525x535S2e748483x510S10011501x466S2e704510x500S10019476x475')
541 *
542 * return '𝠀񀀒񀀚񋚥񋛩𝠃𝤟𝤩񋛩𝣵𝤐񀀒𝤇𝣤񋚥𝤐𝤆񀀚𝣮𝣭'
543 */
544
545
546 const fsw2swu = fswText => {
547 if (!fswText) return '';
548 const prefixes = fswText.match(new RegExp(re$4.prefix, 'g'));
549
550 if (prefixes) {
551 prefixes.forEach(function (prefix) {
552 fswText = fswText.replace(prefix, '𝠀' + prefix.slice(1).match(/.{6}/g).map(key => key2swu(key)).join(''));
553 });
554 }
555
556 const boxes = fswText.match(new RegExp(re$4.box + re$4.coord, 'g'));
557
558 if (boxes) {
559 boxes.forEach(function (boxes) {
560 fswText = fswText.replace(boxes, mark2swu(boxes.slice(0, 1)) + coord2swu(fsw2coord(boxes.slice(1, 8))));
561 });
562 }
563
564 const spatials = fswText.match(new RegExp(re$4.spatial, 'g'));
565
566 if (spatials) {
567 spatials.forEach(function (spatial) {
568 fswText = fswText.replace(spatial, key2swu(spatial.slice(0, 6)) + coord2swu(fsw2coord(spatial.slice(6, 13))));
569 });
570 }
571
572 return fswText;
573 };
574
575 var index$4 = /*#__PURE__*/Object.freeze({
576 __proto__: null,
577 swu2mark: swu2mark,
578 mark2swu: mark2swu,
579 swu2num: swu2num,
580 num2swu: num2swu,
581 swu2coord: swu2coord,
582 coord2swu: coord2swu,
583 fsw2coord: fsw2coord,
584 coord2fsw: coord2fsw,
585 swu2code: swu2code,
586 code2swu: code2swu,
587 swu2id: swu2id,
588 id2swu: id2swu,
589 key2id: key2id,
590 id2key: id2key,
591 swu2key: swu2key,
592 key2swu: key2swu,
593 swu2fsw: swu2fsw,
594 fsw2swu: fsw2swu
595 });
596
597 const parse$3 = {
598 /**
599 * Function to parse an fsw symbol with optional coordinate and style string
600 * @function fsw.parse.symbol
601 * @param {string} fswSym - an fsw symbol
602 * @returns {object} elements of fsw symbol
603 * @example
604 * fsw.parse.symbol('S10000500x500-C')
605 *
606 * return {
607 * 'symbol': 'S10000',
608 * 'coord': [500, 500],
609 * 'style': '-C'
610 * }
611 */
612 symbol: fswSym => {
613 const regex = `^(${re$4.symbol})(${re$4.coord})?(${re$3.full})?`;
614 const symbol = typeof fswSym === 'string' ? fswSym.match(new RegExp(regex)) : undefined;
615 return {
616 'symbol': symbol ? symbol[1] : undefined,
617 'coord': symbol && symbol[2] ? fsw2coord(symbol[2]) : undefined,
618 'style': symbol ? symbol[3] : undefined
619 };
620 },
621
622 /**
623 * Function to parse an fsw sign with style string
624 * @function fsw.parse.sign
625 * @param {string} fswSign - an fsw sign
626 * @returns {object} elements of fsw sign
627 * @example
628 * fsw.parse.sign('AS10011S10019S2e704S2e748M525x535S2e748483x510S10011501x466S2e704510x500S10019476x475-C')
629 *
630 * return {
631 * sequence: ['S10011', 'S10019', 'S2e704', 'S2e748'],
632 * box: 'M',
633 * max: [525, 535],
634 * spatials: [
635 * {
636 * symbol: 'S2e748',
637 * coord: [483, 510]
638 * },
639 * {
640 * symbol: 'S10011',
641 * coord: [501, 466]
642 * },
643 * {
644 * symbol: 'S2e704',
645 * coord: [510, 500]
646 * },
647 * {
648 * symbol: 'S10019',
649 * coord: [476, 475]
650 * }
651 * ],
652 * style: '-C'
653 * }
654 */
655 sign: fswSign => {
656 const regex = `^(${re$4.prefix})?(${re$4.signbox})(${re$3.full})?`;
657 const sign = typeof fswSign === 'string' ? fswSign.match(new RegExp(regex)) : undefined;
658
659 if (sign) {
660 return {
661 'sequence': sign[1] ? sign[1].slice(1).match(/.{6}/g) : undefined,
662 'box': sign[2][0],
663 'max': fsw2coord(sign[2].slice(1, 8)),
664 'spatials': sign[2].length < 9 ? undefined : sign[2].slice(8).match(/(.{13})/g).map(m => {
665 return {
666 symbol: m.slice(0, 6),
667 coord: [parseInt(m.slice(6, 9)), parseInt(m.slice(10, 13))]
668 };
669 }),
670 'style': sign[3]
671 };
672 } else {
673 return {};
674 }
675 }
676 };
677
678 const compose$3 = {
679 /**
680 * Function to compose an fsw symbol with optional coordinate and style string
681 * @function fsw.compose.symbol
682 * @param {object} fswSymObject - an fsw symbol object
683 * @param {string} fswSymObject.symbol - an fsw symbol key
684 * @param {number[]} fswSymObject.coord - top-left coordinate of symbol with 500,500 center
685 * @param {string} fswSymObject.style - a style string for custom appearance
686 * @returns {string} an fsw symbol string
687 * @example
688 * fsw.compose.symbol({
689 * 'symbol': 'S10000',
690 * 'coord': [480, 480],
691 * 'style': '-C'
692 * })
693 *
694 * return 'S10000480x480-C'
695 */
696 symbol: fswSymObject => {
697 if (typeof fswSymObject.symbol === 'string') {
698 const symbol = (fswSymObject.symbol.match(re$4.symbol) || [''])[0];
699
700 if (symbol) {
701 const x = (fswSymObject.coord && fswSymObject.coord[0] || '').toString();
702 const y = (fswSymObject.coord && fswSymObject.coord[1] || '').toString();
703 const coord = ((x + 'x' + y).match(re$4.coord) || [''])[0] || '';
704 const styleStr = typeof fswSymObject.style === 'string' && (fswSymObject.style.match(re$3.full) || [''])[0] || '';
705 return symbol + coord + styleStr;
706 }
707 }
708
709 return undefined;
710 },
711
712 /**
713 * Function to compose an fsw sign with style string
714 * @function fsw.compose.sign
715 * @param {string[]} fswSignObject.sequence - an ordered array of symbols
716 * @param {string} fswSignObject.box - a choice BLMR: horizontal Box, Left, Middle, and Right lane
717 * @param {number[]} fswSymObject.max - max bottom left coordinate of the signbox space
718 * @param {{symbol:string,coord:number[]}[]} fswSymObject.spatials - array of symbols with top-left coordinate placement
719 * @param {string} fswSymObject.style - a style string for custom appearance
720 * @returns {string} an fsw sign string
721 * @example
722 * fsw.compose.sign({
723 * sequence: ['S10011', 'S10019', 'S2e704', 'S2e748'],
724 * box: 'M',
725 * max: [525, 535],
726 * spatials: [
727 * {
728 * symbol: 'S2e748',
729 * coord: [483, 510]
730 * },
731 * {
732 * symbol: 'S10011',
733 * coord: [501, 466]
734 * },
735 * {
736 * symbol: 'S2e704',
737 * coord: [510, 500]
738 * },
739 * {
740 * symbol: 'S10019',
741 * coord: [476, 475]
742 * }
743 * ],
744 * style: '-C'
745 * })
746 *
747 * return 'AS10011S10019S2e704S2e748M525x535S2e748483x510S10011501x466S2e704510x500S10019476x475-C'
748 */
749 sign: fswSignObject => {
750 let box = typeof fswSignObject.box !== 'string' ? 'M' : (fswSignObject.box + 'M').match(re$4.box);
751 const x = (fswSignObject.max && fswSignObject.max[0] || '').toString();
752 const y = (fswSignObject.max && fswSignObject.max[1] || '').toString();
753 const max = ((x + 'x' + y).match(re$4.coord) || [''])[0] || '';
754 if (!max) return undefined;
755 let prefix = '';
756
757 if (fswSignObject.sequence && Array.isArray(fswSignObject.sequence)) {
758 prefix = fswSignObject.sequence.map(key => (key.match(re$4.symbol) || [''])[0]).join('');
759 prefix = prefix ? 'A' + prefix : '';
760 }
761
762 let signbox = '';
763
764 if (fswSignObject.spatials && Array.isArray(fswSignObject.spatials)) {
765 signbox = fswSignObject.spatials.map(spatial => {
766 if (typeof spatial.symbol === 'string') {
767 const symbol = (spatial.symbol.match(re$4.symbol) || [''])[0];
768
769 if (symbol) {
770 const x = (spatial.coord && spatial.coord[0] || '').toString();
771 const y = (spatial.coord && spatial.coord[1] || '').toString();
772 const coord = ((x + 'x' + y).match(re$4.coord) || [''])[0] || '';
773
774 if (coord) {
775 return symbol + coord;
776 }
777 }
778 }
779
780 return '';
781 }).join('');
782 }
783
784 const styleStr = typeof fswSignObject.style === 'string' && (fswSignObject.style.match(re$3.full) || [''])[0] || '';
785 return prefix + box + max + signbox + styleStr;
786 }
787 };
788
789 /**
790 * Array of numbers for kinds of symbols: writing, location, and punctuation.
791 * @alias fsw.kind
792 * @type {array}
793 */
794
795 const kind$1 = [0x100, 0x37f, 0x387];
796 /**
797 * Array of numbers for categories of symbols: hand, movement, dynamics, head, trunk & limb, location, and punctuation.
798 * @alias fsw.category
799 * @type {array}
800 */
801
802 const category$1 = [0x100, 0x205, 0x2f7, 0x2ff, 0x36d, 0x37f, 0x387];
803 /**
804 * Array of numbers for the 30 symbol groups.
805 * @alias fsw.group
806 * @type {array}
807 */
808
809 const group$1 = [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];
810 /**
811 * Object of symbol ranges with starting and ending numbers.
812 *
813 * { all, writing, hand, movement, dynamic, head, hcenter, vcenter, trunk, limb, location, punctuation }
814 * @alias fsw.ranges
815 * @type {object}
816 */
817
818 const ranges$1 = {
819 'all': [0x100, 0x38b],
820 'writing': [0x100, 0x37e],
821 'hand': [0x100, 0x204],
822 'movement': [0x205, 0x2f6],
823 'dynamic': [0x2f7, 0x2fe],
824 'head': [0x2ff, 0x36c],
825 'hcenter': [0x2ff, 0x36c],
826 'vcenter': [0x2ff, 0x375],
827 'trunk': [0x36d, 0x375],
828 'limb': [0x376, 0x37e],
829 'location': [0x37f, 0x386],
830 'punctuation': [0x387, 0x38b]
831 };
832 /**
833 * Function to test if symbol is of a certain type.
834 * @function fsw.isType
835 * @param {string} key - an FSW symbol key
836 * @param {string} type - the name of a symbol range
837 * @returns {boolean} is symbol of specified type
838 * @example
839 * fsw.isType('S10000', 'hand')
840 *
841 * return true
842 */
843
844 const isType$1 = (key, type) => {
845 const parsed = parse$3.symbol(key);
846
847 if (parsed.symbol) {
848 const dec = parseInt(parsed.symbol.slice(1, 4), 16);
849 const range = ranges$1[type];
850
851 if (range) {
852 return range[0] <= dec && range[1] >= dec;
853 }
854 }
855
856 return false;
857 };
858
859 /**
860 * Array of colors associated with the seven symbol categories.
861 * @alias fsw.colors
862 * @type {array}
863 */
864
865 const colors$1 = ['#0000CC', '#CC0000', '#FF0099', '#006600', '#000000', '#884411', '#FF9900'];
866 /**
867 * Function that returns the standardized color for a symbol.
868 * @function fsw.colorize
869 * @param {string} key - an FSW symbol key
870 * @returns {string} name of standardized color for symbol
871 * @example
872 * fsw.colorize('S10000')
873 *
874 * return '#0000CC'
875 */
876
877 const colorize$1 = key => {
878 const parsed = parse$3.symbol(key);
879 let color = '#000000';
880
881 if (parsed.symbol) {
882 const dec = parseInt(parsed.symbol.slice(1, 4), 16);
883 const index = category$1.findIndex(val => val > dec);
884 color = colors$1[index < 0 ? 6 : index - 1];
885 }
886
887 return color;
888 };
889
890 /** The fsw module contains functions for handling Formal SignWriitng in ASCII (FSW) characters.
891 * [FSW characters definition](https://tools.ietf.org/id/draft-slevinski-formal-signwriting-07.html#rfc.section.2.2.1)
892 * @module fsw
893 */
894
895 var index$3 = /*#__PURE__*/Object.freeze({
896 __proto__: null,
897 re: re$4,
898 parse: parse$3,
899 compose: compose$3,
900 kind: kind$1,
901 category: category$1,
902 group: group$1,
903 ranges: ranges$1,
904 isType: isType$1,
905 colors: colors$1,
906 colorize: colorize$1
907 });
908
909 /**
910 * Object of regular expressions for FSW query strings
911 *
912 * { base, coord, var, symbol, range, item, list, prefix, signbox, full }
913 * @alias fswquery.re
914 * @type {object}
915 */
916 let re$1 = {
917 'base': '[123][0-9a-f]{2}',
918 'coord': '(?:[0-9]{3}x[0-9]{3})?',
919 'var': 'V[0-9]+'
920 };
921 re$1.symbol = `S${re$1.base}[0-5u][0-9a-fu]`;
922 re$1.range = `R${re$1.base}t${re$1.base}`;
923 re$1.item = `(?:${re$1.symbol}|${re$1.range})`;
924 re$1.list = `${re$1.item}(?:o${re$1.item})*`;
925 re$1.prefix = `(?:A(?:${re$1.list})+)?T`;
926 re$1.signbox = `(?:${re$1.list}${re$1.coord})*`;
927 re$1.full = `Q(${re$1.prefix})?(${re$1.signbox})?(${re$1.var})?(-?)`;
928
929 const parsePrefix$1 = text => {
930 return {
931 required: true,
932 parts: text == 'T' ? undefined : text.match(new RegExp(`${re$1.list}`, 'g')).map(part => {
933 if (part.includes('o')) {
934 return ['or'].concat(part.match(new RegExp(`(${re$1.item})`, 'g')).map(part => part[0] == 'S' ? part : part.slice(1).split('t')));
935 } else {
936 return part[0] == 'S' ? part : part.slice(1).split('t');
937 }
938 })
939 };
940 };
941
942 const parseSignbox$1 = text => {
943 return text.match(new RegExp(`(${re$1.list}${re$1.coord})`, 'g')).map(part => {
944 let coord, front;
945
946 if (part.includes('x')) {
947 coord = fsw2coord(part.slice(-7));
948 front = part.slice(0, -7);
949 } else {
950 front = part;
951 }
952
953 if (front.includes('o')) {
954 return {
955 or: front.split('o').map(part => {
956 if (part.includes('S')) {
957 return part;
958 } else {
959 return part.slice(1).split('t');
960 }
961 }),
962 coord,
963 coord
964 };
965 } else if (front.includes('S')) {
966 return {
967 symbol: front,
968 coord: coord
969 };
970 } else {
971 return {
972 range: front.slice(1).split('t'),
973 coord: coord
974 };
975 }
976 });
977 };
978 /**
979 * Function to parse FSW query string to object
980 * @function fswquery.parse
981 * @param {string} fswQueryString - an FSW query string
982 * @returns {object} elements of an FSW query string
983 * @example
984 * fswquery.parse('QAS10000S10500oS20500oR2fft304TS100uuR205t206oS207uu510x510V5-')
985 *
986 * return {
987 * "query": true,
988 * "prefix": {
989 * "required": true,
990 * "parts": [
991 * "S10000",
992 * [
993 * "or",
994 * "S10500",
995 * "S20500",
996 * [
997 * "2ff",
998 * "304"
999 * ]
1000 * ]
1001 * ]
1002 * },
1003 * "signbox": [
1004 * {
1005 * "symbol": "S100uu"
1006 * },
1007 * {
1008 * "or": [
1009 * [
1010 * "205",
1011 * "206"
1012 * ],
1013 * "S207uu"
1014 * ],
1015 * "coord": [
1016 * 510,
1017 * 510
1018 * ]
1019 * }
1020 * ],
1021 * "variance": 5,
1022 * "style": true
1023 * }
1024 */
1025
1026
1027 const parse$2 = fswQueryString => {
1028 const query = typeof fswQueryString === 'string' ? fswQueryString.match(new RegExp(`^${re$1.full}`)) : undefined;
1029 return {
1030 'query': query ? true : undefined,
1031 'prefix': query && query[1] ? parsePrefix$1(query[1]) : undefined,
1032 'signbox': query && query[2] ? parseSignbox$1(query[2]) : undefined,
1033 'variance': query && query[3] ? parseInt(query[3].slice(1)) : undefined,
1034 'style': query && query[4] ? true : undefined
1035 };
1036 };
1037
1038 /**
1039 * Function to compose FSW query string from object
1040 * @function fswquery.compose
1041 * @param {object} fswQueryObject - an object of style options
1042 * @param {boolean} fswQueryObject.query - required true for FSW query object
1043 * @param {object} fswQueryObject.prefix - an object for prefix elements
1044 * @param {boolean} fswQueryObject.prefix.required - true if sorting prefix is required
1045 * @param {(string|string[]|(string|string[])[])[]} fswQueryObject.prefix.parts - array of symbol strings, range arrays, and OR arrays of strings and range arrays
1046 * @param {({symbol:string,coord:number[]}|{range:string[],coord:number[]}|{or:(string|string[])[],coord:number[]})[]} fswQueryObject.signbox - array of objects for symbols, ranges, and list of symbols or ranges, with optional coordinates
1047 * @param {number} fswQueryObject.variance - amount that x or y coordinates can vary and find a match, defaults to 20
1048 * @param {boolean} fswQueryObject.style - boolean value for including style string in matches
1049 * @returns {string} FSW query string
1050 * @example
1051 * fswquery.compose({
1052 * query: true,
1053 * prefix: {
1054 * required: true,
1055 * parts: [
1056 * 'S10000',
1057 * ['100', '204'],
1058 * 'S20500'
1059 * ]
1060 * },
1061 * signbox: [
1062 * { symbol: 'S20000' },
1063 * {
1064 * range: ['100', '105'],
1065 * coord: [500, 500]
1066 * }
1067 * ],
1068 * variance: 5,
1069 * style: true
1070 * })
1071 *
1072 * return 'QAS10000R100t204S20500TS20000R100t105500x500V5-'
1073 */
1074
1075 const compose$2 = fswQueryObject => {
1076 if (!fswQueryObject || !fswQueryObject.query) {
1077 return undefined;
1078 }
1079
1080 let query = 'Q';
1081
1082 if (fswQueryObject.prefix && fswQueryObject.prefix.required) {
1083 if (Array.isArray(fswQueryObject.prefix.parts)) {
1084 query += 'A';
1085 query += fswQueryObject.prefix.parts.map(part => {
1086 if (typeof part === 'string') {
1087 return part;
1088 } else {
1089 if (Array.isArray(part) && part.length == 2) {
1090 return `R${part[0]}t${part[1]}`;
1091 } else if (Array.isArray(part) && part.length > 2 && part[0] == 'or') {
1092 part.shift();
1093 return part.map(part => {
1094 if (typeof part === 'string') {
1095 return part;
1096 } else {
1097 if (Array.isArray(part) && part.length == 2) {
1098 return `R${part[0]}t${part[1]}`;
1099 }
1100 }
1101 }).join('o');
1102 }
1103 }
1104 }).join('');
1105 }
1106
1107 query += 'T';
1108 }
1109
1110 if (Array.isArray(fswQueryObject.signbox)) {
1111 query += fswQueryObject.signbox.map(part => {
1112 let out;
1113
1114 if (part.or) {
1115 out = part.or.map(item => {
1116 if (typeof item === 'string') {
1117 return item;
1118 } else {
1119 if (Array.isArray(item) && item.length == 2) {
1120 return `R${item[0]}t${item[1]}`;
1121 }
1122 }
1123 }).join('o');
1124 } else if (part.symbol) {
1125 out = part.symbol;
1126 } else {
1127 if (part.range && Array.isArray(part.range) && part.range.length == 2) {
1128 out = `R${part.range[0]}t${part.range[1]}`;
1129 }
1130 }
1131
1132 return out + (Array.isArray(part.coord) && part.coord.length == 2 ? part.coord.join('x') : '');
1133 }).join('');
1134 }
1135
1136 query += fswQueryObject.style ? '-' : '';
1137 query = query.match(new RegExp(`^${re$1.full}`))[0];
1138 return query;
1139 };
1140
1141 /**
1142 * Function to convert an FSW sign to a query string
1143 *
1144 * For the flags parameter, use one or more of the following.
1145 * - A: exact symbol in temporal prefix
1146 * - a: general symbol in temporal prefix
1147 * - S: exact symbol in spatial signbox
1148 * - s: general symbol in spatial signbox
1149 * - L: spatial signbox symbol at location
1150 * @function fswquery.fsw2query
1151 * @param {string} fswSign - FSW sign
1152 * @param {string} flags - flags for query string creation
1153 * @returns {string} FSW query string
1154 * @example
1155 * fswquery.fsw2query('AS10011S10019S2e704S2e748M525x535S2e748483x510S10011501x466S2e704510x500S10019476x475', 'ASL')
1156 *
1157 * return 'QAS10011S10019S2e704S2e748TS2e748483x510S10011501x466S2e704510x500S10019476x475'
1158 */
1159
1160 const fsw2query = (fswSign, flags) => {
1161 let query = '';
1162 const parsed = parse$3.sign(fswSign);
1163
1164 if (parsed.box) {
1165 const A_flag = flags.indexOf('A') > -1;
1166 const a_flag = flags.indexOf('a') > -1;
1167 const S_flag = flags.indexOf('S') > -1;
1168 const s_flag = flags.indexOf('s') > -1;
1169 const L_flag = flags.indexOf('L') > -1;
1170
1171 if (A_flag || a_flag || S_flag || s_flag) {
1172 if ((A_flag || a_flag) && parsed.sequence) {
1173 query += 'A';
1174 query += parsed.sequence.map(sym => sym.slice(0, 4) + (a_flag ? 'uu' : sym.slice(4, 6))).join('');
1175 query += 'T';
1176 }
1177
1178 if ((S_flag || s_flag) && parsed.spatials) {
1179 query += parsed.spatials.map(spatial => spatial.symbol.slice(0, 4) + (s_flag ? 'uu' : spatial.symbol.slice(4, 6)) + (L_flag ? spatial.coord.join('x') : '')).join('');
1180 }
1181 }
1182
1183 return query ? "Q" + query : undefined;
1184 } else {
1185 return undefined;
1186 }
1187 };
1188
1189 //needs rewritten, but it works
1190
1191 /**
1192 * Function to transform a range to a regular expression
1193 * @function fswquery.range
1194 * @param {(number|string)} min - either a decimal number or hexidecimal string
1195 * @param {(number|string)} max - either a decimal number or hexidecimal string
1196 * @param {boolean?} hex - if true, the regular expression will match a hexidecimal range
1197 * @returns {string} a regular expression that matches a range
1198 * @example
1199 * fswquery.range(500,750)
1200 *
1201 * return '(([56][0-9][0-9])|(7[0-4][0-9])|(750))'
1202 * @example
1203 * fswquery.range('100','10e',true)
1204 *
1205 * return '10[0-9a-e]'
1206 */
1207 const range$1 = (min, max, hex) => {
1208 let pattern;
1209 let re;
1210 let diff;
1211 let tmax;
1212 let cnt;
1213 let minV;
1214 let maxV;
1215
1216 if (!hex) {
1217 hex = '';
1218 }
1219
1220 min = ("000" + min).slice(-3);
1221 max = '' + max;
1222 pattern = '';
1223
1224 if (min === max) {
1225 return min;
1226 } //ending pattern will be series of connected OR ranges
1227
1228
1229 re = []; //first pattern+ 10's don't match and the min 1's are not zero
1230 //odd number to 9
1231
1232 if (!(min[0] == max[0] && min[1] == max[1])) {
1233 if (min[2] != '0') {
1234 pattern = min[0] + min[1];
1235
1236 if (hex) {
1237 //switch for dex
1238 switch (min[2]) {
1239 case "f":
1240 pattern += 'f';
1241 break;
1242
1243 case "e":
1244 pattern += '[ef]';
1245 break;
1246
1247 case "d":
1248 case "c":
1249 case "b":
1250 case "a":
1251 pattern += '[' + min[2] + '-f]';
1252 break;
1253
1254 default:
1255 switch (min[2]) {
1256 case "9":
1257 pattern += '[9a-f]';
1258 break;
1259
1260 case "8":
1261 pattern += '[89a-f]';
1262 break;
1263
1264 default:
1265 pattern += '[' + min[2] + '-9a-f]';
1266 break;
1267 }
1268
1269 break;
1270 }
1271
1272 diff = 15 - parseInt(min[2], 16) + 1;
1273 min = '' + (parseInt(min, 16) + diff).toString(16);
1274 re.push(pattern);
1275 } else {
1276 //switch for dex
1277 switch (min[2]) {
1278 case "9":
1279 pattern += '9';
1280 break;
1281
1282 case "8":
1283 pattern += '[89]';
1284 break;
1285
1286 default:
1287 pattern += '[' + min[2] + '-9]';
1288 break;
1289 }
1290
1291 diff = 9 - min[2] + 1;
1292 min = '' + (min * 1 + diff);
1293 re.push(pattern);
1294 }
1295 }
1296 }
1297
1298 pattern = ''; //if hundreds are different, get odd to 99 or ff
1299
1300 if (min[0] != max[0]) {
1301 if (min[1] != '0') {
1302 if (hex) {
1303 //scrape to ff
1304 pattern = min[0];
1305
1306 switch (min[1]) {
1307 case "f":
1308 pattern += 'f';
1309 break;
1310
1311 case "e":
1312 pattern += '[ef]';
1313 break;
1314
1315 case "d":
1316 case "c":
1317 case "b":
1318 case "a":
1319 pattern += '[' + min[1] + '-f]';
1320 break;
1321
1322 case "9":
1323 pattern += '[9a-f]';
1324 break;
1325
1326 case "8":
1327 pattern += '[89a-f]';
1328 break;
1329
1330 default:
1331 pattern += '[' + min[1] + '-9a-f]';
1332 break;
1333 }
1334
1335 pattern += '[0-9a-f]';
1336 diff = 15 - parseInt(min[1], 16) + 1;
1337 min = '' + (parseInt(min, 16) + diff * 16).toString(16);
1338 re.push(pattern);
1339 } else {
1340 //scrape to 99
1341 pattern = min[0];
1342 diff = 9 - min[1] + 1;
1343
1344 switch (min[1]) {
1345 case "9":
1346 pattern += '9';
1347 break;
1348
1349 case "8":
1350 pattern += '[89]';
1351 break;
1352
1353 default:
1354 pattern += '[' + min[1] + '-9]';
1355 break;
1356 }
1357
1358 pattern += '[0-9]';
1359 diff = 9 - min[1] + 1;
1360 min = '' + (min * 1 + diff * 10);
1361 re.push(pattern);
1362 }
1363 }
1364 }
1365
1366 pattern = ''; //if hundreds are different, get to same
1367
1368 if (min[0] != max[0]) {
1369 if (hex) {
1370 diff = parseInt(max[0], 16) - parseInt(min[0], 16);
1371 tmax = (parseInt(min[0], 16) + diff - 1).toString(16);
1372
1373 switch (diff) {
1374 case 1:
1375 pattern = min[0];
1376 break;
1377
1378 case 2:
1379 pattern = '[' + min[0] + tmax + ']';
1380 break;
1381
1382 default:
1383 if (parseInt(min[0], 16) > 9) {
1384 minV = 'h';
1385 } else {
1386 minV = 'd';
1387 }
1388
1389 if (parseInt(tmax, 16) > 9) {
1390 maxV = 'h';
1391 } else {
1392 maxV = 'd';
1393 }
1394
1395 switch (minV + maxV) {
1396 case "dd":
1397 pattern += '[' + min[0] + '-' + tmax + ']';
1398 break;
1399
1400 case "dh":
1401 diff = 9 - min[0]; //firs get up to 9
1402
1403 switch (diff) {
1404 case 0:
1405 pattern += '[9';
1406 break;
1407
1408 case 1:
1409 pattern += '[89';
1410 break;
1411
1412 default:
1413 pattern += '[' + min[0] + '-9';
1414 break;
1415 }
1416
1417 switch (tmax[0]) {
1418 case 'a':
1419 pattern += 'a]';
1420 break;
1421
1422 case 'b':
1423 pattern += 'ab]';
1424 break;
1425
1426 default:
1427 pattern += 'a-' + tmax + ']';
1428 break;
1429 }
1430
1431 break;
1432
1433 case "hh":
1434 pattern += '[' + min[0] + '-' + tmax + ']';
1435 break;
1436 }
1437
1438 }
1439
1440 pattern += '[0-9a-f][0-9a-f]';
1441 diff = parseInt(max[0], 16) - parseInt(min[0], 16);
1442 min = '' + (parseInt(min, 16) + diff * 256).toString(16);
1443 re.push(pattern);
1444 } else {
1445 diff = max[0] - min[0];
1446 tmax = min[0] * 1 + diff - 1;
1447
1448 switch (diff) {
1449 case 1:
1450 pattern = min[0];
1451 break;
1452
1453 case 2:
1454 pattern = '[' + min[0] + tmax + ']';
1455 break;
1456
1457 default:
1458 pattern = '[' + min[0] + '-' + tmax + ']';
1459 break;
1460 }
1461
1462 pattern += '[0-9][0-9]';
1463 min = '' + (min * 1 + diff * 100);
1464 re.push(pattern);
1465 }
1466 }
1467
1468 pattern = ''; //if tens are different, get to same
1469
1470 if (min[1] != max[1]) {
1471 if (hex) {
1472 diff = parseInt(max[1], 16) - parseInt(min[1], 16);
1473 tmax = (parseInt(min[1], 16) + diff - 1).toString(16);
1474 pattern = min[0];
1475
1476 switch (diff) {
1477 case 1:
1478 pattern += min[1];
1479 break;
1480
1481 case 2:
1482 pattern += '[' + min[1] + tmax + ']';
1483 break;
1484
1485 default:
1486 if (parseInt(min[1], 16) > 9) {
1487 minV = 'h';
1488 } else {
1489 minV = 'd';
1490 }
1491
1492 if (parseInt(tmax, 16) > 9) {
1493 maxV = 'h';
1494 } else {
1495 maxV = 'd';
1496 }
1497
1498 switch (minV + maxV) {
1499 case "dd":
1500 pattern += '[' + min[1];
1501
1502 if (diff > 1) {
1503 pattern += '-';
1504 }
1505
1506 pattern += tmax + ']';
1507 break;
1508
1509 case "dh":
1510 diff = 9 - min[1]; //firs get up to 9
1511
1512 switch (diff) {
1513 case 0:
1514 pattern += '[9';
1515 break;
1516
1517 case 1:
1518 pattern += '[89';
1519 break;
1520
1521 default:
1522 pattern += '[' + min[1] + '-9';
1523 break;
1524 }
1525
1526 switch (max[1]) {
1527 case 'a':
1528 pattern += ']';
1529 break;
1530
1531 case 'b':
1532 pattern += 'a]';
1533 break;
1534
1535 default:
1536 pattern += 'a-' + (parseInt(max[1], 16) - 1).toString(16) + ']';
1537 break;
1538 }
1539
1540 break;
1541
1542 case "hh":
1543 pattern += '[' + min[1];
1544
1545 if (diff > 1) {
1546 pattern += '-';
1547 }
1548
1549 pattern += (parseInt(max[1], 16) - 1).toString(16) + ']';
1550 break;
1551 }
1552
1553 break;
1554 }
1555
1556 pattern += '[0-9a-f]';
1557 diff = parseInt(max[1], 16) - parseInt(min[1], 16);
1558 min = '' + (parseInt(min, 16) + diff * 16).toString(16);
1559 re.push(pattern);
1560 } else {
1561 diff = max[1] - min[1];
1562 tmax = min[1] * 1 + diff - 1;
1563 pattern = min[0];
1564
1565 switch (diff) {
1566 case 1:
1567 pattern += min[1];
1568 break;
1569
1570 case 2:
1571 pattern += '[' + min[1] + tmax + ']';
1572 break;
1573
1574 default:
1575 pattern += '[' + min[1] + '-' + tmax + ']';
1576 break;
1577 }
1578
1579 pattern += '[0-9]';
1580 min = '' + (min * 1 + diff * 10);
1581 re.push(pattern);
1582 }
1583 }
1584
1585 pattern = ''; //if digits are different, get to same
1586
1587 if (min[2] != max[2]) {
1588 if (hex) {
1589 pattern = min[0] + min[1];
1590 diff = parseInt(max[2], 16) - parseInt(min[2], 16);
1591
1592 if (parseInt(min[2], 16) > 9) {
1593 minV = 'h';
1594 } else {
1595 minV = 'd';
1596 }
1597
1598 if (parseInt(max[2], 16) > 9) {
1599 maxV = 'h';
1600 } else {
1601 maxV = 'd';
1602 }
1603
1604 switch (minV + maxV) {
1605 case "dd":
1606 pattern += '[' + min[2];
1607
1608 if (diff > 1) {
1609 pattern += '-';
1610 }
1611
1612 pattern += max[2] + ']';
1613 break;
1614
1615 case "dh":
1616 diff = 9 - min[2]; //firs get up to 9
1617
1618 switch (diff) {
1619 case 0:
1620 pattern += '[9';
1621 break;
1622
1623 case 1:
1624 pattern += '[89';
1625 break;
1626
1627 default:
1628 pattern += '[' + min[2] + '-9';
1629 break;
1630 }
1631
1632 switch (max[2]) {
1633 case 'a':
1634 pattern += 'a]';
1635 break;
1636
1637 case 'b':
1638 pattern += 'ab]';
1639 break;
1640
1641 default:
1642 pattern += 'a-' + max[2] + ']';
1643 break;
1644 }
1645
1646 break;
1647
1648 case "hh":
1649 pattern += '[' + min[2];
1650
1651 if (diff > 1) {
1652 pattern += '-';
1653 }
1654
1655 pattern += max[2] + ']';
1656 break;
1657 }
1658
1659 diff = parseInt(max[2], 16) - parseInt(min[2], 16);
1660 min = '' + (parseInt(min, 16) + diff).toString(16);
1661 re.push(pattern);
1662 } else {
1663 diff = max[2] - min[2];
1664 pattern = min[0] + min[1];
1665
1666 switch (diff) {
1667 case 0:
1668 pattern += min[2];
1669 break;
1670
1671 case 1:
1672 pattern += '[' + min[2] + max[2] + ']';
1673 break;
1674
1675 default:
1676 pattern += '[' + min[2] + '-' + max[2] + ']';
1677 break;
1678 }
1679
1680 min = '' + (min * 1 + diff);
1681 re.push(pattern);
1682 }
1683 }
1684
1685 pattern = ''; //last place is whole hundred
1686
1687 if (min[2] == '0' && max[2] == '0') {
1688 pattern = max;
1689 re.push(pattern);
1690 }
1691
1692 pattern = '';
1693 cnt = re.length;
1694
1695 if (cnt == 1) {
1696 pattern = re[0];
1697 } else {
1698 pattern = re.join(')|(');
1699 pattern = '((' + pattern + '))';
1700 }
1701
1702 return pattern;
1703 };
1704
1705 const regexSymbol = sym => {
1706 let segment = sym.slice(0, 4);
1707 let fill = sym.slice(4, 5);
1708
1709 if (fill == 'u') {
1710 segment += '[0-5]';
1711 } else {
1712 segment += fill;
1713 }
1714
1715 let rotate = sym.slice(5, 6);
1716
1717 if (rotate == 'u') {
1718 segment += '[0-9a-f]';
1719 } else {
1720 segment += rotate;
1721 }
1722
1723 return segment;
1724 };
1725
1726 const regexRange$1 = symRange => {
1727 let from = symRange.slice(1, 4);
1728 let to = symRange.slice(5, 8);
1729 return 'S' + range$1(from, to, 'hex') + '[0-5][0-9a-f]';
1730 }; //needs rewritten, but it works
1731
1732 /**
1733 * Function to transform an FSW query string to one or more regular expressions
1734 * @function fswquery.regex
1735 * @param {string} query - an FSW query string
1736 * @returns {string[]} an array of one or more regular expressions
1737 * @example
1738 * fswquery.regex('QS100uuS20500480x520')
1739 *
1740 * return [
1741 * '(?:A(?:S[123][0-9a-f]{2}[0-5][0-9a-f])+)?[BLMR]([0-9]{3}x[0-9]{3})(S[123][0-9a-f]{2}[0-5][0-9a-f][0-9]{3}x[0-9]{3})*S100[0-5][0-9a-f][0-9]{3}x[0-9]{3}(S[123][0-9a-f]{2}[0-5][0-9a-f][0-9]{3}x[0-9]{3})*',
1742 * '(?:A(?:S[123][0-9a-f]{2}[0-5][0-9a-f])+)?[BLMR]([0-9]{3}x[0-9]{3})(S[123][0-9a-f]{2}[0-5][0-9a-f][0-9]{3}x[0-9]{3})*S20500((4[6-9][0-9])|(500))x((5[0-3][0-9])|(540))(S[123][0-9a-f]{2}[0-5][0-9a-f][0-9]{3}x[0-9]{3})*'
1743 * ]
1744 */
1745
1746
1747 const regex$1 = query => {
1748 query = query.match(new RegExp(`^${re$1.full}`))[0];
1749
1750 if (!query) {
1751 return '';
1752 }
1753
1754 var matches;
1755 var matchesOr;
1756 var matched;
1757 var orList;
1758 var i;
1759 var j;
1760 var segment;
1761 var coord;
1762 var x;
1763 var y;
1764 var fuzz = 20;
1765 var q_style = '(' + re$3.full + ')?';
1766 var q_sortable;
1767
1768 if (query == 'Q') {
1769 return [re$4.prefix + "?" + re$4.signbox];
1770 }
1771
1772 if (query == 'Q-') {
1773 return [re$4.prefix + "?" + re$4.signbox + q_style];
1774 }
1775
1776 if (query == 'QT') {
1777 return [re$4.prefix + re$4.signbox];
1778 }
1779
1780 if (query == 'QT-') {
1781 return [re$4.prefix + re$4.signbox + q_style];
1782 }
1783
1784 var segments = [];
1785 var sortable = query.indexOf('T') + 1;
1786
1787 if (sortable) {
1788 q_sortable = '(A';
1789 var qat = query.slice(0, sortable);
1790 query = query.replace(qat, '');
1791
1792 if (qat == 'QT') {
1793 q_sortable += '(' + re$4.symbol + ')+)';
1794 } else {
1795 matches = qat.match(new RegExp('(' + re$1.list + ')', 'g'));
1796
1797 if (matches) {
1798 for (i = 0; i < matches.length; i += 1) {
1799 orList = [];
1800 matchesOr = matches[i].match(new RegExp('(' + re$1.symbol + '|' + re$1.range + ')', 'g'));
1801
1802 if (matchesOr) {
1803 for (j = 0; j < matchesOr.length; j += 1) {
1804 matched = matchesOr[j].match(new RegExp(re$1.symbol));
1805
1806 if (matched) {
1807 orList.push(regexSymbol(matched[0]));
1808 } else {
1809 orList.push(regexRange$1(matchesOr[j]));
1810 }
1811 }
1812
1813 if (orList.length == 1) {
1814 q_sortable += orList[0];
1815 } else {
1816 q_sortable += '(' + orList.join('|') + ')';
1817 }
1818 }
1819 }
1820
1821 q_sortable += '(' + re$4.symbol + ')*)';
1822 }
1823 }
1824 } //get the variance
1825
1826
1827 matches = query.match(new RegExp(re$1.var, 'g'));
1828
1829 if (matches) {
1830 fuzz = matches.toString().slice(1) * 1;
1831 } //this gets all symbols and ranges with or without location
1832
1833
1834 matches = query.match(new RegExp(re$1.list + re$1.coord, 'g'));
1835
1836 if (matches) {
1837 for (i = 0; i < matches.length; i += 1) {
1838 orList = [];
1839 matchesOr = matches[i].match(new RegExp('(' + re$1.symbol + '|' + re$1.range + ')', 'g'));
1840
1841 if (matchesOr) {
1842 for (j = 0; j < matchesOr.length; j += 1) {
1843 matched = matchesOr[j].match(new RegExp(re$1.symbol));
1844
1845 if (matched) {
1846 orList.push(regexSymbol(matched[0]));
1847 } else {
1848 orList.push(regexRange$1(matchesOr[j]));
1849 }
1850 }
1851
1852 if (orList.length == 1) {
1853 segment = orList[0];
1854 } else {
1855 segment = '(' + orList.join('|') + ')';
1856 }
1857 }
1858
1859 if (matches[i].includes('x')) {
1860 coord = fsw2coord(matches[i].slice(-7));
1861 x = coord[0];
1862 y = coord[1];
1863 segment += range$1(x - fuzz, x + fuzz);
1864 segment += 'x';
1865 segment += range$1(y - fuzz, y + fuzz);
1866 } else {
1867 segment += re$4.coord;
1868 } // add to general fsw word
1869
1870
1871 segment = re$4.signbox + segment + '(' + re$4.symbol + re$4.coord + ')*';
1872
1873 if (sortable) {
1874 segment = q_sortable + segment;
1875 } else {
1876 segment = re$4.prefix + "?" + segment;
1877 }
1878
1879 if (query.indexOf('-') > 0) {
1880 segment += q_style;
1881 }
1882
1883 segments.push(segment);
1884 }
1885 }
1886
1887 if (!segments.length) {
1888 if (query.indexOf('-') > 0) {
1889 segment += q_style;
1890 }
1891
1892 segments.push(q_sortable + re$4.signbox);
1893 }
1894
1895 return segments;
1896 };
1897
1898 /**
1899 * Function that uses a query string to match signs from a string of text.
1900 * @function fswquery.results
1901 * @param {string} query - an FSW query string
1902 * @param {string} text - a string of text containing multiple signs
1903 * @returns {string[]} an array of FSW signs
1904 * @example
1905 * fswquery.results('QAS10011T','AS10011S10019S2e704S2e748M525x535S2e748483x510S10011501x466S2e704510x500S10019476x475 AS15a21S15a07S21100S2df04S2df14M521x538S15a07494x488S15a21498x489S2df04498x517S2df14497x461S21100479x486 AS1f010S10018S20600M519x524S10018485x494S1f010490x494S20600481x476')
1906 *
1907 * return [
1908 * 'AS10011S10019S2e704S2e748M525x535S2e748483x510S10011501x466S2e704510x500S10019476x475'
1909 * ]
1910 */
1911
1912 const results$1 = (query, text) => {
1913 if (!text) {
1914 return [];
1915 }
1916
1917 let pattern;
1918 let matches;
1919 let parts;
1920 let words;
1921 let re = regex$1(query);
1922
1923 if (!re) {
1924 return [];
1925 }
1926
1927 let i;
1928
1929 for (i = 0; i < re.length; i += 1) {
1930 pattern = re[i];
1931 matches = text.match(new RegExp(pattern, 'g'));
1932
1933 if (matches) {
1934 text = matches.join(' ');
1935 } else {
1936 text = '';
1937 }
1938 }
1939
1940 if (text) {
1941 parts = text.split(' ');
1942 words = parts.filter(function (element) {
1943 return element in parts ? false : parts[element] = true;
1944 }, {});
1945 } else {
1946 words = [];
1947 }
1948
1949 return words;
1950 }; //needs rewritten, but it works
1951
1952 /**
1953 * Function that uses an FSW query string to match signs from multiple lines of text.
1954 * @function fswquery.lines
1955 * @param {string} query - an FSW query string
1956 * @param {string} text - multiple lines of text, each starting with an FSW sign
1957 * @returns {string[]} an array of lines of text, each starting with an FSW sign
1958 * @example
1959 * fswquery.lines('QAS10011T',`AS10011S10019S2e704S2e748M525x535S2e748483x510S10011501x466S2e704510x500S10019476x475 line one
1960 * AS15a21S15a07S21100S2df04S2df14M521x538S15a07494x488S15a21498x489S2df04498x517S2df14497x461S21100479x486 line two
1961 * AS1f010S10018S20600M519x524S10018485x494S1f010490x494S20600481x476 line three`)
1962 *
1963 * return [
1964 * 'AS10011S10019S2e704S2e748M525x535S2e748483x510S10011501x466S2e704510x500S10019476x475 line one'
1965 * ]
1966 */
1967
1968
1969 const lines$1 = (query, text) => {
1970 if (!text) {
1971 return [];
1972 }
1973
1974 let pattern;
1975 let matches;
1976 let parts;
1977 let words;
1978 let re = regex$1(query);
1979
1980 if (!re) {
1981 return [];
1982 }
1983
1984 let i;
1985
1986 for (i = 0; i < re.length; i += 1) {
1987 pattern = re[i];
1988 pattern = '^' + pattern + '.*';
1989 matches = text.match(new RegExp(pattern, 'mg'));
1990
1991 if (matches) {
1992 text = matches.join("\n");
1993 } else {
1994 text = '';
1995 }
1996 }
1997
1998 if (text) {
1999 parts = text.split("\n");
2000 words = parts.filter(function (element) {
2001 return element in parts ? false : parts[element] = true;
2002 }, {});
2003 } else {
2004 words = [];
2005 }
2006
2007 return words;
2008 };
2009
2010 /** The fswquery module contains functions for handling the FSW query language.
2011 * [Query Language definition](https://tools.ietf.org/id/draft-slevinski-formal-signwriting-07.html#rfc.section.2.6)
2012 * @module fswquery
2013 */
2014
2015 var index$2 = /*#__PURE__*/Object.freeze({
2016 __proto__: null,
2017 re: re$1,
2018 parse: parse$2,
2019 compose: compose$2,
2020 fsw2query: fsw2query,
2021 range: range$1,
2022 regex: regex$1,
2023 results: results$1,
2024 lines: lines$1
2025 });
2026
2027 const parse$1 = {
2028 /**
2029 * Function to parse an swu symbol with optional coordinate and style string
2030 * @function swu.parse.symbol
2031 * @param {string} swuSym - an swu symbol
2032 * @returns {object} elements of swu symbol
2033 * @example
2034 * swu.parse.symbol('񀀁𝤆𝤆-C')
2035 *
2036 * return {
2037 * 'symbol': '񀀁',
2038 * 'coord': [500, 500],
2039 * 'style': '-C'
2040 * }
2041 */
2042 symbol: swuSym => {
2043 const regex = `^(${re$2.symbol})(${re$2.coord})?(${re$3.full})?`;
2044 const symbol = typeof swuSym === 'string' ? swuSym.match(new RegExp(regex)) : undefined;
2045 return {
2046 'symbol': symbol ? symbol[1] : undefined,
2047 'coord': symbol && symbol[2] ? swu2coord(symbol[2]) : undefined,
2048 'style': symbol ? symbol[3] : undefined
2049 };
2050 },
2051
2052 /**
2053 * Function to parse an swu sign with style string
2054 * @function swu.parse.sign
2055 * @param {string} swuSign - an swu sign
2056 * @returns {object} elements of swu sign
2057 * @example
2058 * swu.parse.sign('𝠀񀀒񀀚񋚥񋛩𝠃𝤟𝤩񋛩𝣵𝤐񀀒𝤇𝣤񋚥𝤐𝤆񀀚𝣮𝣭-C')
2059 *
2060 * return {
2061 * sequence: ['񀀒','񀀚','񋚥','񋛩'],
2062 * box: '𝠃',
2063 * max: [525, 535],
2064 * spatials: [
2065 * {
2066 * symbol: '񋛩',
2067 * coord: [483, 510]
2068 * },
2069 * {
2070 * symbol: '񀀒',
2071 * coord: [501, 466]
2072 * },
2073 * {
2074 * symbol: '񋚥',
2075 * coord: [510, 500]
2076 * },
2077 * {
2078 * symbol: '񀀚',
2079 * coord: [476, 475]
2080 * }
2081 * ],
2082 * style: '-C'
2083 * }
2084 */
2085 sign: swuSign => {
2086 const regex = `^(${re$2.prefix})?(${re$2.signbox})(${re$3.full})?`;
2087 const sign = typeof swuSign === 'string' ? swuSign.match(new RegExp(regex)) : undefined;
2088
2089 if (sign) {
2090 return {
2091 'sequence': sign[1] ? sign[1].slice(2).match(/.{2}/g) : undefined,
2092 'box': sign[2].slice(0, 2),
2093 'max': swu2coord(sign[2].slice(2, 6)),
2094 'spatials': sign[2].length < 7 ? undefined : sign[2].slice(6).match(/(.{6})/g).map(m => {
2095 return {
2096 symbol: m.slice(0, 2),
2097 coord: swu2coord(m.slice(2))
2098 };
2099 }),
2100 'style': sign[3]
2101 };
2102 } else {
2103 return {};
2104 }
2105 }
2106 };
2107 /**
2108 * Function to encode SWU characters using the UTF-16 escape format.
2109 * @function swu.encode
2110 * @param {string} swu - SWU characters
2111 * @returns {string} UTF-16 escape format
2112 * @example
2113 * swu.encode('񀀁𝤆𝤆')
2114 *
2115 * return '\\uD8C0\\uDC01\\uD836\\uDD06\\uD836\\uDD06'
2116 */
2117
2118 const encode = text => text.replace(/[\u007F-\uFFFF]/g, function (chr) {
2119 return "\\u" + ("0000" + chr.charCodeAt(0).toString(16)).substr(-4).toUpperCase();
2120 });
2121 /**
2122 * Function to decode UTF-16 escape format to SWU characters.
2123 * @function swu.decode
2124 * @param {string} encoded - UTF-16 escape format
2125 * @returns {string} SWU characters
2126 * @example
2127 * swu.decode('\\uD8C0\\uDC01\\uD836\\uDD06\\uD836\\uDD06')
2128 *
2129 * return '񀀁𝤆𝤆'
2130 */
2131
2132
2133 const decode = encoded => encoded.replace(/\\u([0-9A-F]{4})/g, function (match, chr) {
2134 return String.fromCharCode(parseInt(chr, 16));
2135 });
2136 /**
2137 * Function to decompose an SWU character into UTF-16 surrogate pairs.
2138 * @function swu.pair
2139 * @param {string} swuChar - an SWU character
2140 * @returns {string[]} an array of UTF-16 surrogate pairs
2141 * @example
2142 * swu.pair('񀀁')
2143 *
2144 * return ['D8C0', 'DC01']
2145 */
2146
2147
2148 const pair = swuChar => [swuChar.charCodeAt(0).toString(16).toUpperCase(), swuChar.charCodeAt(1).toString(16).toUpperCase()];
2149
2150 const compose$1 = {
2151 /**
2152 * Function to compose an swu symbol with optional coordinate and style string
2153 * @function swu.compose.symbol
2154 * @param {object} swuSymObject - an swu symbol object
2155 * @param {string} swuSymObject.symbol - an swu symbol key
2156 * @param {number[]} swuSymObject.coord - top-left coordinate of symbol with 500,500 center
2157 * @param {string} swuSymObject.style - a style string for custom appearance
2158 * @returns {string} an swu symbol string
2159 * @example
2160 * swu.compose.symbol({
2161 * 'symbol': '񀀁',
2162 * 'coord': [500, 500],
2163 * 'style': '-C'
2164 * })
2165 *
2166 * return '񀀁𝤆𝤆-C'
2167 */
2168 symbol: swuSymObject => {
2169 if (typeof swuSymObject !== 'object' || swuSymObject === null) return undefined;
2170
2171 if (typeof swuSymObject.symbol === 'string') {
2172 const symbol = (swuSymObject.symbol.match(re$2.symbol) || [''])[0];
2173
2174 if (symbol) {
2175 const x = swuSymObject.coord && swuSymObject.coord[0] || '';
2176 const y = swuSymObject.coord && swuSymObject.coord[1] || '';
2177 const coord = x && y ? coord2swu([x, y]) : '';
2178 const styleStr = typeof swuSymObject.style === 'string' && (swuSymObject.style.match(re$3.full) || [''])[0] || '';
2179 return symbol + coord + styleStr;
2180 }
2181 }
2182
2183 return undefined;
2184 },
2185
2186 /**
2187 * Function to compose an swu sign with style string
2188 * @function swu.compose.sign
2189 * @param {string[]} swuSignObject.sequence - an ordered array of symbols
2190 * @param {string} swuSignObject.box - a choice BLMR: horizontal Box, Left, Middle, and Right lane
2191 * @param {number[]} swuSymObject.max - max bottom left coordinate of the signbox space
2192 * @param {{symbol:string,coord:number[]}[]} swuSymObject.spatials - array of symbols with top-left coordinate placement
2193 * @param {string} swuSymObject.style - a style string for custom appearance
2194 * @returns {string} an swu sign string
2195 * @example
2196 * swu.compose.sign({
2197 * sequence: ['񀀒','񀀚','񋚥','񋛩'],
2198 * box: '𝠃',
2199 * max: [525, 535],
2200 * spatials: [
2201 * {
2202 * symbol: '񋛩',
2203 * coord: [483, 510]
2204 * },
2205 * {
2206 * symbol: '񀀒',
2207 * coord: [501, 466]
2208 * },
2209 * {
2210 * symbol: '񋚥',
2211 * coord: [510, 500]
2212 * },
2213 * {
2214 * symbol: '񀀚',
2215 * coord: [476, 475]
2216 * }
2217 * ],
2218 * style: '-C'
2219 * })
2220 *
2221 * return '𝠀񀀒񀀚񋚥񋛩𝠃𝤟𝤩񋛩𝣵𝤐񀀒𝤇𝣤񋚥𝤐𝤆񀀚𝣮𝣭-C'
2222 */
2223 sign: swuSignObject => {
2224 if (typeof swuSignObject !== 'object' || swuSignObject === null) return undefined;
2225 let box = typeof swuSignObject.box !== 'string' ? '𝠃' : (swuSignObject.box + '𝠃').match(re$2.box);
2226 const x = swuSignObject.max && swuSignObject.max[0] || '';
2227 const y = swuSignObject.max && swuSignObject.max[1] || '';
2228 const max = x && y ? coord2swu([x, y]) : undefined;
2229 if (!max) return undefined;
2230 let prefix = '';
2231
2232 if (swuSignObject.sequence && Array.isArray(swuSignObject.sequence)) {
2233 prefix = swuSignObject.sequence.map(key => (key.match(re$2.symbol) || [''])[0]).join('');
2234 prefix = prefix ? '𝠀' + prefix : '';
2235 }
2236
2237 let signbox = '';
2238
2239 if (swuSignObject.spatials && Array.isArray(swuSignObject.spatials)) {
2240 signbox = swuSignObject.spatials.map(spatial => {
2241 if (typeof spatial.symbol === 'string') {
2242 const symbol = (spatial.symbol.match(re$2.symbol) || [''])[0];
2243
2244 if (symbol) {
2245 const x = spatial.coord && spatial.coord[0] || '';
2246 const y = spatial.coord && spatial.coord[1] || '';
2247 const coord = x && y ? coord2swu([x, y]) : '';
2248
2249 if (coord) {
2250 return symbol + coord;
2251 }
2252 }
2253 }
2254
2255 return '';
2256 }).join('');
2257 }
2258
2259 const styleStr = typeof swuSignObject.style === 'string' && (swuSignObject.style.match(re$3.full) || [''])[0] || '';
2260 return prefix + box + max + signbox + styleStr;
2261 }
2262 };
2263
2264 /**
2265 * Array of plane 4 code points for kinds of symbols: writing, location, and punctuation.
2266 * @alias swu.kind
2267 * @type {array}
2268 */
2269
2270 const kind = [0x40001, 0x4efa1, 0x4f2a1];
2271 /**
2272 * Array of plane 4 code points for categories of symbols: hand, movement, dynamics, head, trunk & limb, location, and punctuation.
2273 * @alias swu.category
2274 * @type {array}
2275 */
2276
2277 const category = [0x40001, 0x461e1, 0x4bca1, 0x4bfa1, 0x4e8e1, 0x4efa1, 0x4f2a1];
2278 /**
2279 * Array of plane 4 code points for the 30 symbol groups.
2280 * @alias swu.group
2281 * @type {array}
2282 */
2283
2284 const group = [0x40001, 0x40541, 0x40b41, 0x41981, 0x41c81, 0x43241, 0x43d81, 0x445c1, 0x44ce1, 0x45be1, 0x461e1, 0x46841, 0x46fc1, 0x47fe1, 0x485e1, 0x49301, 0x49e41, 0x4a4a1, 0x4afe1, 0x4b521, 0x4bca1, 0x4bfa1, 0x4c3c1, 0x4cfc1, 0x4d621, 0x4e161, 0x4e8e1, 0x4ec41, 0x4efa1, 0x4f2a1];
2285 /**
2286 * Object of symbol ranges with starting and ending code points on plane 4.
2287 *
2288 * { all, writing, hand, movement, dynamic, head, hcenter, vcenter, trunk, limb, location, punctuation }
2289 * @alias swu.ranges
2290 * @type {object}
2291 */
2292
2293 const ranges = {
2294 'all': [0x40001, 0x4f480],
2295 'writing': [0x40001, 0x4efa0],
2296 'hand': [0x40001, 0x461e0],
2297 'movement': [0x461e1, 0x4bca0],
2298 'dynamic': [0x4bca1, 0x4bfa0],
2299 'head': [0x4bfa1, 0x4e8e0],
2300 'hcenter': [0x4bfa1, 0x4e8e0],
2301 'vcenter': [0x4bfa1, 0x4ec40],
2302 'trunk': [0x4e8e1, 0x4ec40],
2303 'limb': [0x4ec41, 0x4efa0],
2304 'location': [0x4efa1, 0x4f2a0],
2305 'punctuation': [0x4f2a1, 0x4f480]
2306 };
2307 /**
2308 * Function to test if symbol is of a certain type.
2309 * @function swu.isType
2310 * @param {string} swuSym - an SWU symbol character
2311 * @param {string} type - the name of a symbol range
2312 * @returns {boolean} is symbol of specified type
2313 * @example
2314 * swu.isType('񀀁', 'hand')
2315 *
2316 * return true
2317 */
2318
2319 const isType = (swuSym, type) => {
2320 const parsed = parse$1.symbol(swuSym);
2321
2322 if (parsed.symbol) {
2323 const code = swu2code(parsed.symbol);
2324 const range = ranges[type];
2325
2326 if (range) {
2327 return range[0] <= code && range[1] >= code;
2328 }
2329 }
2330
2331 return false;
2332 };
2333
2334 /**
2335 * Array of colors associated with the seven symbol categories.
2336 * @alias swu.colors
2337 * @type {array}
2338 */
2339
2340 const colors = ['#0000CC', '#CC0000', '#FF0099', '#006600', '#000000', '#884411', '#FF9900'];
2341 /**
2342 * Function that returns the standardized color for a symbol.
2343 * @function swu.colorize
2344 * @param {string} swuSym - an SWU symbol character
2345 * @returns {string} name of standardized color for symbol
2346 * @example
2347 * swu.colorize('񀀁')
2348 *
2349 * return '#0000CC'
2350 */
2351
2352 const colorize = swuSym => {
2353 const parsed = parse$1.symbol(swuSym);
2354 let color = '#000000';
2355
2356 if (parsed.symbol) {
2357 const code = swu2code(parsed.symbol);
2358 const index = category.findIndex(val => val > code);
2359 color = colors[index < 0 ? 6 : index - 1];
2360 }
2361
2362 return color;
2363 };
2364
2365 /** The swu module contains functions for handling SignWriitng in Unicode (SWu) characters.
2366 * [SWU characters definition](https://tools.ietf.org/id/draft-slevinski-formal-signwriting-07.html#rfc.section.2.2.2)
2367 * @module swu
2368 */
2369
2370 var index$1 = /*#__PURE__*/Object.freeze({
2371 __proto__: null,
2372 re: re$2,
2373 parse: parse$1,
2374 encode: encode,
2375 decode: decode,
2376 pair: pair,
2377 compose: compose$1,
2378 kind: kind,
2379 category: category,
2380 group: group,
2381 ranges: ranges,
2382 isType: isType,
2383 colors: colors,
2384 colorize: colorize
2385 });
2386
2387 /**
2388 * Object of regular expressions for SWU query strings
2389 *
2390 * { base, coord, var, symbol, range, item, list, prefix, signbox, full }
2391 * @alias swuquery.re
2392 * @type {object}
2393 */
2394 let re = {
2395 'base': '(?:(?:\uD8C0[\uDC01-\uDFFF])|(?:[\uD8C1-\uD8FC][\uDC00-\uDFFF])|(?:\uD8FD[\uDC00-\uDC80]))',
2396 'coord': '(?:(?:\uD836[\uDC0C-\uDDFF]){2})?',
2397 'var': 'V[0-9]+'
2398 };
2399 re.symbol = `${re.base}f?r?`;
2400 re.range = `R${re.base}${re.base}`;
2401 re.item = `(?:${re.symbol}|${re.range})`;
2402 re.list = `${re.item}(?:o${re.item})*`;
2403 re.prefix = `(?:A(?:${re.list})+)?T`;
2404 re.signbox = `(?:${re.list}${re.coord})*`;
2405 re.full = `Q(${re.prefix})?(${re.signbox})?(${re.var})?(-?)`;
2406
2407 const parsePrefix = text => {
2408 return {
2409 required: true,
2410 parts: text == 'T' ? undefined : text.match(new RegExp(`(${re.list})`, 'g')).map(part => {
2411 if (part.includes('o')) {
2412 return ['or'].concat(part.match(new RegExp(`(${re.item})`, 'g')).map(part => part[0] != 'R' ? part : [part.slice(1, 3), part.slice(3, 5)]));
2413 } else {
2414 return part[0] != 'R' ? part : [part.slice(1, 3), part.slice(3, 5)];
2415 }
2416 })
2417 };
2418 };
2419
2420 const parseSignbox = text => {
2421 return text.match(new RegExp(`(${re.list}${re.coord})`, 'g')).map(part => {
2422 let coord, front;
2423 coord = part.match(new RegExp(`${re$2.coord}`));
2424
2425 if (coord) {
2426 coord = swu2coord(coord[0]);
2427 front = part.slice(0, -4);
2428 } else {
2429 coord = undefined;
2430 front = part;
2431 }
2432
2433 if (front.includes('o')) {
2434 return {
2435 or: front.split('o').map(part => {
2436 if (!part.includes('R')) {
2437 return part;
2438 } else {
2439 return [part.slice(1, 3), part.slice(3, 5)];
2440 }
2441 }),
2442 coord,
2443 coord
2444 };
2445 } else if (!front.includes('R')) {
2446 return {
2447 symbol: front,
2448 coord: coord
2449 };
2450 } else {
2451 return {
2452 range: [front.slice(1, 3), front.slice(3, 5)],
2453 coord: coord
2454 };
2455 }
2456 });
2457 };
2458 /**
2459 * Function to parse SWU query string to object
2460 * @function swuquery.parse
2461 * @param {string} swuQueryString - an SWU query string
2462 * @returns {object} elements of an SWU query string
2463 * @example
2464 * swuquery.parse('QA񀀁R񀀁񆆑񆇡T񆀁R񀀁񀇱𝤆𝤆V5-')
2465 *
2466 * return {
2467 * query: true,
2468 * prefix: {
2469 * required: true,
2470 * parts: [
2471 * '񀀁',
2472 * ['񀀁', '񆆑'],
2473 * '񆇡'
2474 * ]
2475 * },
2476 * signbox: [
2477 * { symbol: '񆀁' },
2478 * {
2479 * range: ['񀀁', '񀇱'],
2480 * coord: [500, 500]
2481 * }
2482 * ],
2483 * variance: 5,
2484 * style: true
2485 * }
2486 */
2487
2488
2489 const parse = swuQueryString => {
2490 const query = typeof swuQueryString === 'string' ? swuQueryString.match(new RegExp(`^${re.full}`)) : undefined;
2491 return {
2492 'query': query ? true : undefined,
2493 'prefix': query && query[1] ? parsePrefix(query[1]) : undefined,
2494 'signbox': query && query[2] ? parseSignbox(query[2]) : undefined,
2495 'variance': query && query[3] ? parseInt(query[3].slice(1)) : undefined,
2496 'style': query && query[4] ? true : undefined
2497 };
2498 };
2499
2500 /**
2501 * Function to compose SWU query string from object
2502 * @function swuquery.compose
2503 * @param {object} swuQueryObject - an object of style options
2504 * @param {boolean} swuQueryObject.query - required true for SWU query object
2505 * @param {object} swuQueryObject.prefix - an object for prefix elements
2506 * @param {boolean} swuQueryObject.prefix.required - true if sorting prefix is required
2507 * @param {(string|string[]|(string|string[])[])[]} swuQueryObject.prefix.parts - array of symbol strings, range arrays, and OR arrays of strings and range arrays
2508 * @param {({symbol:string,coord:number[]}|{range:string[],coord:number[]}|{or:(string|string[])[],coord:number[]})[]} swuQueryObject.signbox - array of objects for symbols, ranges, and list of symbols or ranges, with optional coordinates
2509 * @param {number} swuQueryObject.variance - amount that x or y coordinates can vary and find a match, defaults to 20
2510 * @param {boolean} swuQueryObject.style - boolean value for including style string in matches
2511 * @returns {string} SWU query string
2512 * @example
2513 * swuquery.compose({
2514 * query: true,
2515 * prefix: {
2516 * required: true,
2517 * parts: [
2518 * '񀀁',
2519 * ['񀀁', '񆆑'],
2520 * '񆇡'
2521 * ]
2522 * },
2523 * signbox: [
2524 * { symbol: '񆀁' },
2525 * {
2526 * range: ['񀀁', '񀇱'],
2527 * coord: [500, 500]
2528 * }
2529 * ],
2530 * variance: 5,
2531 * style: true
2532 * })
2533 *
2534 * return 'QA񀀁R񀀁񆆑񆇡T񆀁R񀀁񀇱𝤆𝤆V5-'
2535 */
2536
2537 const compose = swuQueryObject => {
2538 if (!swuQueryObject || !swuQueryObject.query) {
2539 return undefined;
2540 }
2541
2542 let query = 'Q';
2543
2544 if (swuQueryObject.prefix && swuQueryObject.prefix.required) {
2545 if (Array.isArray(swuQueryObject.prefix.parts)) {
2546 query += 'A';
2547 query += swuQueryObject.prefix.parts.map(part => {
2548 if (typeof part === 'string') {
2549 return part;
2550 } else {
2551 if (Array.isArray(part) && part.length == 2) {
2552 return `R${part[0]}${part[1]}`;
2553 } else if (Array.isArray(part) && part.length > 2 && part[0] == 'or') {
2554 part.shift();
2555 return part.map(part => {
2556 if (typeof part === 'string') {
2557 return part;
2558 } else {
2559 if (Array.isArray(part) && part.length == 2) {
2560 return `R${part[0]}${part[1]}`;
2561 }
2562 }
2563 }).join('o');
2564 }
2565 }
2566 }).join('');
2567 }
2568
2569 query += 'T';
2570 }
2571
2572 if (Array.isArray(swuQueryObject.signbox)) {
2573 query += swuQueryObject.signbox.map(part => {
2574 let out;
2575
2576 if (part.or) {
2577 out = part.or.map(item => {
2578 if (typeof item === 'string') {
2579 return item;
2580 } else {
2581 if (Array.isArray(item) && item.length == 2) {
2582 return `R${item[0]}${item[1]}`;
2583 }
2584 }
2585 }).join('o');
2586 } else if (part.symbol) {
2587 out = part.symbol;
2588 } else {
2589 if (part.range && Array.isArray(part.range) && part.range.length == 2) {
2590 out = `R${part.range[0]}${part.range[1]}`;
2591 }
2592 }
2593
2594 return out + (Array.isArray(part.coord) && part.coord.length == 2 ? coord2swu(part.coord) : '');
2595 }).join('');
2596 }
2597
2598 query += swuQueryObject.style ? '-' : '';
2599 query = query.match(new RegExp(`^${re.full}`))[0];
2600 return query;
2601 };
2602
2603 /**
2604 * Function to convert an SWU sign to a query string
2605 *
2606 * For the flags parameter, use one or more of the following.
2607 * - A: exact symbol in temporal prefix
2608 * - a: general symbol in temporal prefix
2609 * - S: exact symbol in spatial signbox
2610 * - s: general symbol in spatial signbox
2611 * - L: spatial signbox symbol at location
2612 * @function swuquery.swu2query
2613 * @param {string} swuSign - SWU sign
2614 * @param {string} flags - flags for query string creation
2615 * @returns {string} SWU query string
2616 * @example
2617 * swuquery.swu2query('𝠀񀀒񀀚񋚥񋛩𝠃𝤟𝤩񋛩𝣵𝤐񀀒𝤇𝣤񋚥𝤐𝤆񀀚𝣮𝣭', 'ASL')
2618 *
2619 * return 'QA񀀒񀀚񋚥񋛩T񋛩𝣵𝤐񀀒𝤇𝣤񋚥𝤐𝤆񀀚𝣮𝣭'
2620 */
2621
2622 const swu2query = (swuSign, flags) => {
2623 let query = '';
2624 const parsed = parse$1.sign(swuSign);
2625
2626 if (parsed.box) {
2627 const A_flag = flags.indexOf('A') > -1;
2628 const a_flag = flags.indexOf('a') > -1;
2629 const S_flag = flags.indexOf('S') > -1;
2630 const s_flag = flags.indexOf('s') > -1;
2631 const L_flag = flags.indexOf('L') > -1;
2632
2633 if (A_flag || a_flag || S_flag || s_flag) {
2634 if ((A_flag || a_flag) && parsed.sequence) {
2635 query += 'A';
2636 query += parsed.sequence.map(sym => sym + (a_flag ? 'fr' : '')).join('');
2637 query += 'T';
2638 }
2639
2640 if ((S_flag || s_flag) && parsed.spatials) {
2641 query += parsed.spatials.map(spatial => spatial.symbol + (s_flag ? 'fr' : '') + (L_flag ? coord2swu(spatial.coord) : '')).join('');
2642 }
2643 }
2644
2645 return query ? "Q" + query : undefined;
2646 } else {
2647 return undefined;
2648 }
2649 };
2650
2651 /**
2652 * Function to transform a range of SWU characters to a regular expression
2653 * @function swuquery.range
2654 * @param {string} min - an SWU character
2655 * @param {string} max - an SWU character
2656 * @returns {string} a regular expression that matches a range of SWU characters
2657 * @example
2658 * swuquery.range('񀀁', '񀇡')
2659 *
2660 * return '\uD8C0[\uDC01-\uDDE1]'
2661 * @example
2662 * swuquery.range('𝣔', '𝤸')
2663 *
2664 * return '\uD836[\uDCD4-\uDD38]'
2665 */
2666
2667 const range = (min, max) => {
2668 if (min > max) return '';
2669 let pattern = '';
2670 let cnt;
2671 let re = [];
2672 min = pair(min);
2673 max = pair(max);
2674 if (min.length != 2 && max.length != 2) return ''; // HEAD // min[0] with range of min[1] to (DFFF or max[1])
2675
2676 if (min[0] == max[0]) {
2677 if (min[1] == max[1]) {
2678 pattern = '\\u' + min[0] + '\\u' + min[1];
2679 re.push(pattern);
2680 } else {
2681 pattern = '\\u' + min[0] + '[\\u' + min[1] + '-\\u' + max[1] + ']';
2682 re.push(pattern);
2683 }
2684 } else {
2685 if (min[1] == "DFFF") {
2686 pattern = '\\u' + min[0] + '\\uDFFF';
2687 } else {
2688 pattern = '\\u' + min[0] + '[\\u' + min[1] + '-\\uDFFF]';
2689 }
2690
2691 re.push(pattern); // BODY // range of (min[0] +1) to (max[0] -1) with all DC00-DFFF
2692
2693 let diff = parseInt(max[0], 16) - parseInt(min[0], 16);
2694
2695 if (diff == 2) {
2696 pattern = '\\u' + (parseInt(min[0], 16) + 1).toString(16).toUpperCase();
2697 pattern += '[\\uDC00-\\uDFFF]';
2698 re.push(pattern);
2699 }
2700
2701 if (diff > 2) {
2702 pattern = '[';
2703 pattern += '\\u' + (parseInt(min[0], 16) + 1).toString(16).toUpperCase();
2704 pattern += '-\\u' + (parseInt(max[0], 16) - 1).toString(16).toUpperCase();
2705 pattern += '][\\uDC00-\\uDFFF]';
2706 re.push(pattern);
2707 } // TAIL // max[0] with range of DC00 to max[1]
2708
2709
2710 if (max[1] == "DC00") {
2711 pattern = '\\u' + max[0] + '\\uDC00';
2712 } else {
2713 pattern = '\\u' + max[0] + '[\\uDC00-\\u' + max[1] + ']';
2714 }
2715
2716 re.push(pattern);
2717 }
2718
2719 cnt = re.length;
2720
2721 if (cnt == 1) {
2722 pattern = re[0];
2723 } else {
2724 pattern = re.join(')|(');
2725 pattern = '((' + pattern + '))';
2726 }
2727
2728 return decode(pattern);
2729 };
2730
2731 /**
2732 * Function to transform an SWU symbol with fill and rotation flags to a regular expression
2733 * @function swuquery.symbolRanges
2734 * @param {string} symbolFR - an SWU character with optional flags of 'f' for any fill and 'r' for any rotation
2735 * @returns {string} a regular expression that matches one or more ranges of SWU symbols
2736 * @example <caption>Match an exact symbol</caption>
2737 * swuquery.symbolRanges('񀀁')
2738 *
2739 * return '\uD8C0\uDC01');
2740 * @example <caption>Match a symbol with any fill</caption>
2741 * swuquery.symbolRanges('񀀁f')
2742 *
2743 * return '(\uD8C0\uDC01|\uD8C0\uDC11|\uD8C0\uDC21|\uD8C0\uDC31|\uD8C0\uDC41|\uD8C0\uDC51)'
2744 * @example <caption>Match a symbol with any rotation</caption>
2745 * swuquery.symbolRanges('񀀁r')
2746 *
2747 * return '\uD8C0[\uDC01-\uDC10]'
2748 * @example <caption>Match a symbol with any fill or rotation</caption>
2749 * swuquery.symbolRanges('񀀁fr')
2750 *
2751 * return '\uD8C0[\uDC01-\uDC60]'
2752 */
2753
2754 const symbolRanges = symbolFR => {
2755 let match = symbolFR.match(new RegExp(re.symbol));
2756
2757 if (match) {
2758 let sym = match[0].slice(0, 2);
2759 let key = swu2key(sym);
2760 let base = key.slice(0, 4);
2761 let start, end;
2762
2763 if (match[0].slice(-2) == 'fr') {
2764 start = key2swu(base + "00");
2765 end = key2swu(base + "5f");
2766 return range(start, end);
2767 } else if (match[0].slice(-1) == 'r') {
2768 start = key2swu(key.slice(0, 5) + '0');
2769 end = key2swu(key.slice(0, 5) + 'f');
2770 return range(start, end);
2771 } else if (match[0].slice(-1) == 'f') {
2772 let list = [0, 1, 2, 3, 4, 5].map(function (f) {
2773 return key2swu(base + f + key.slice(-1));
2774 });
2775 return "(" + list.join("|") + ")";
2776 } else {
2777 return sym;
2778 }
2779 } else {
2780 return '';
2781 }
2782 };
2783
2784 const regexRange = symRange => {
2785 from = swu2key(symRange.slice(1, 3));
2786 to = swu2key(symRange.slice(-2));
2787 from = key2swu(from.slice(0, 4) + '00');
2788 to = key2swu(to.slice(0, 4) + '5f');
2789 return range(from, to);
2790 }; //needs rewritten, but it works
2791
2792 /**
2793 * Function to transform an SWU query string to one or more regular expressions
2794 * @function swuquery.regex
2795 * @param {string} query - an SWU query string
2796 * @returns {string[]} an array of one or more regular expressions
2797 * @example
2798 * swuquery.regex('QA񀀒T')
2799 *
2800 * return [
2801 * '(\uD836\uDC00\uD8C0\uDC12((?:(?:\uD8C0[\uDC01-\uDFFF])|(?:[\uD8C1-\uD8FC][\uDC00-\uDFFF])|(?:\uD8FD[\uDC00-\uDC80])))*)\uD836[\uDC01-\uDC04](?:\uD836[\uDC0C-\uDDFF]){2}((?:(?:\uD8C0[\uDC01-\uDFFF])|(?:[\uD8C1-\uD8FC][\uDC00-\uDFFF])|(?:\uD8FD[\uDC00-\uDC80]))(?:\uD836[\uDC0C-\uDDFF]){2})*'
2802 * ]
2803 */
2804
2805
2806 const regex = query => {
2807 query = query.match(new RegExp(`^${re.full}`))[0];
2808
2809 if (!query) {
2810 return '';
2811 }
2812
2813 let matches;
2814 let matchesOr;
2815 let matched;
2816 let orList;
2817 let i;
2818 let j;
2819 let coord;
2820 let segment;
2821 let x;
2822 let y;
2823 let fuzz = 20;
2824 let re_sym = re$2.symbol;
2825 let re_coord = re$2.coord;
2826 let re_signbox = re$2.box;
2827 let re_seq = re$2.sort;
2828 let re_word = re_signbox + re_coord + '(' + re_sym + re_coord + ')*';
2829 let re_sortable = '(' + re_seq + '(' + re_sym + ')+)';
2830 let q_var = '(V[0-9]+)';
2831 let q_style = '(' + re$3.full + ')?';
2832 let q_sortable;
2833
2834 if (query == 'Q') {
2835 return [re$2.sign];
2836 }
2837
2838 if (query == 'Q-') {
2839 return [re$2.sign + "(" + re$3.full + ")?"];
2840 }
2841
2842 if (query == 'QT') {
2843 return [re$2.sortable];
2844 }
2845
2846 if (query == 'QT-') {
2847 return [re$2.sortable + "(" + re$3.full + ")?"];
2848 }
2849
2850 let segments = [];
2851 let sortable = query.indexOf('T') + 1;
2852
2853 if (sortable) {
2854 q_sortable = '(' + re$2.sort;
2855 let qat = query.slice(0, sortable);
2856 query = query.replace(qat, '');
2857
2858 if (qat == 'QT') {
2859 q_sortable += '(' + re_sym + ')+)';
2860 } else {
2861 matches = qat.match(new RegExp('(' + re.list + ')', 'g'));
2862
2863 if (matches) {
2864 for (i = 0; i < matches.length; i += 1) {
2865 orList = [];
2866 matchesOr = matches[i].match(new RegExp('(' + re.symbol + '|' + re.range + ')', 'g'));
2867
2868 if (matchesOr) {
2869 for (j = 0; j < matchesOr.length; j += 1) {
2870 matched = matchesOr[j].match(new RegExp(re.symbol));
2871
2872 if (matched) {
2873 orList.push(symbolRanges(matched[0]));
2874 } else {
2875 orList.push(regexRange(matchesOr[j]));
2876 }
2877 }
2878
2879 if (orList.length == 1) {
2880 q_sortable += orList[0];
2881 } else {
2882 q_sortable += '(' + orList.join('|') + ')';
2883 }
2884 }
2885 }
2886
2887 q_sortable += '(' + re$2.symbol + ')*)';
2888 }
2889 }
2890 } //get the variance
2891
2892
2893 matches = query.match(new RegExp(q_var, 'g'));
2894
2895 if (matches) {
2896 fuzz = matches.toString().slice(1) * 1;
2897 } //this gets all symbols and ranges with or without location
2898
2899
2900 matches = query.match(new RegExp(re.list + re.coord, 'g'));
2901
2902 if (matches) {
2903 for (i = 0; i < matches.length; i += 1) {
2904 orList = [];
2905 matchesOr = matches[i].match(new RegExp('(' + re.symbol + '|' + re.range + ')', 'g'));
2906
2907 if (matchesOr) {
2908 for (j = 0; j < matchesOr.length; j += 1) {
2909 matched = matchesOr[j].match(new RegExp(re.symbol));
2910
2911 if (matched) {
2912 orList.push(symbolRanges(matched[0]));
2913 } else {
2914 orList.push(regexRange(matchesOr[j]));
2915 }
2916 }
2917
2918 if (orList.length == 1) {
2919 segment = orList[0];
2920 } else {
2921 segment = '(' + orList.join('|') + ')';
2922 }
2923 }
2924
2925 coord = matches[i].match(new RegExp(`${re$2.coord}`));
2926
2927 if (coord) {
2928 coord = swu2coord(coord[0]);
2929 x = coord[0];
2930 y = coord[1];
2931 segment += range(num2swu(x - fuzz), num2swu(x + fuzz));
2932 segment += range(num2swu(y - fuzz), num2swu(y + fuzz));
2933 } else {
2934 segment += re$2.coord;
2935 } // add to general swu word
2936
2937
2938 segment = re_word + segment + '(' + re_sym + re_coord + ')*';
2939
2940 if (sortable) {
2941 segment = q_sortable + segment;
2942 } else {
2943 segment = re_sortable + "?" + segment;
2944 }
2945
2946 if (query.indexOf('-') > 0) {
2947 segment += q_style;
2948 }
2949
2950 segments.push(segment);
2951 }
2952 }
2953
2954 if (!segments.length) {
2955 if (query.indexOf('-') > 0) {
2956 segment += q_style;
2957 }
2958
2959 segments.push(q_sortable + re_word);
2960 }
2961
2962 return segments;
2963 };
2964
2965 /**
2966 * Function that uses a query string to match signs from a string of text.
2967 * @function swuquery.results
2968 * @param {string} query - an SWU query string
2969 * @param {string} text - a string of text containing multiple signs
2970 * @returns {string[]} an array of SWU signs
2971 * @example
2972 * swuquery.results('QA񀀒T','𝠀񀀒񀀚񋚥񋛩𝠃𝤟𝤩񋛩𝣵𝤐񀀒𝤇𝣤񋚥𝤐𝤆񀀚𝣮𝣭 𝠀񂇢񂇈񆙡񋎥񋎵𝠃𝤛𝤬񂇈𝤀𝣺񂇢𝤄𝣻񋎥𝤄𝤗񋎵𝤃𝣟񆙡𝣱𝣸 𝠀񅨑񀀙񆉁𝠃𝤙𝤞񀀙𝣷𝤀񅨑𝣼𝤀񆉁𝣳𝣮')
2973 *
2974 * return [
2975 * '𝠀񀀒񀀚񋚥񋛩𝠃𝤟𝤩񋛩𝣵𝤐񀀒𝤇𝣤񋚥𝤐𝤆񀀚𝣮𝣭'
2976 * ]
2977 */
2978
2979 const results = (query, text) => {
2980 if (!text) {
2981 return [];
2982 }
2983
2984 let pattern;
2985 let matches;
2986 let parts;
2987 let words;
2988 let res = regex(query);
2989
2990 if (!res) {
2991 return [];
2992 }
2993
2994 let i;
2995
2996 for (i = 0; i < res.length; i += 1) {
2997 pattern = res[i];
2998 matches = text.match(new RegExp(pattern, 'g'));
2999
3000 if (matches) {
3001 text = matches.join(' ');
3002 } else {
3003 text = '';
3004 }
3005 }
3006
3007 if (text) {
3008 parts = text.split(' ');
3009 words = parts.filter(function (element) {
3010 return element in parts ? false : parts[element] = true;
3011 }, {});
3012 } else {
3013 words = [];
3014 }
3015
3016 return words;
3017 }; //needs rewritten, but it works
3018
3019 /**
3020 * Function that uses an SWU query string to match signs from multiple lines of text.
3021 * @function swuquery.lines
3022 * @param {string} query - an SWU query string
3023 * @param {string} text - multiple lines of text, each starting with an SWU sign
3024 * @returns {string[]} an array of lines of text, each starting with an SWU sign
3025 * @example
3026 * swuquery.lines('QA񀀒T',`𝠀񀀒񀀚񋚥񋛩𝠃𝤟𝤩񋛩𝣵𝤐񀀒𝤇𝣤񋚥𝤐𝤆񀀚𝣮𝣭 line one
3027 * 𝠀񂇢񂇈񆙡񋎥񋎵𝠃𝤛𝤬񂇈𝤀𝣺񂇢𝤄𝣻񋎥𝤄𝤗񋎵𝤃𝣟񆙡𝣱𝣸 line two
3028 * 𝠀񅨑񀀙񆉁𝠃𝤙𝤞񀀙𝣷𝤀񅨑𝣼𝤀񆉁𝣳𝣮 line three`)
3029 *
3030 * return [
3031 * '𝠀񀀒񀀚񋚥񋛩𝠃𝤟𝤩񋛩𝣵𝤐񀀒𝤇𝣤񋚥𝤐𝤆񀀚𝣮𝣭 line one'
3032 * ]
3033 */
3034
3035
3036 const lines = (query, text) => {
3037 if (!text) {
3038 return [];
3039 }
3040
3041 let pattern;
3042 let matches;
3043 let parts;
3044 let words;
3045 let res = regex(query);
3046
3047 if (!res) {
3048 return [];
3049 }
3050
3051 let i;
3052
3053 for (i = 0; i < res.length; i += 1) {
3054 pattern = res[i];
3055 pattern = '^' + pattern + '.*';
3056 matches = text.match(new RegExp(pattern, 'mg'));
3057
3058 if (matches) {
3059 text = matches.join("\n");
3060 } else {
3061 text = '';
3062 }
3063 }
3064
3065 if (text) {
3066 parts = text.split("\n");
3067 words = parts.filter(function (element) {
3068 return element in parts ? false : parts[element] = true;
3069 }, {});
3070 } else {
3071 words = [];
3072 }
3073
3074 return words;
3075 };
3076
3077 /** The swuquery module contains functions for handling the SWU query language.
3078 * [Query Language definition](https://tools.ietf.org/id/draft-slevinski-formal-signwriting-07.html#rfc.section.2.6)
3079 * @module swuquery
3080 */
3081
3082 var index = /*#__PURE__*/Object.freeze({
3083 __proto__: null,
3084 re: re,
3085 parse: parse,
3086 compose: compose,
3087 swu2query: swu2query,
3088 range: range,
3089 symbolRanges: symbolRanges,
3090 regex: regex,
3091 results: results,
3092 lines: lines
3093 });
3094
3095 exports.convert = index$4;
3096 exports.fsw = index$3;
3097 exports.fswquery = index$2;
3098 exports.style = index$5;
3099 exports.swu = index$1;
3100 exports.swuquery = index;
3101
3102 Object.defineProperty(exports, '__esModule', { value: true });
3103
3104}));
3105
3106/* support ongoing development on https://patreon.com/signwriting */