UNPKG

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