UNPKG

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