UNPKG

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