UNPKG

26.8 kBJavaScriptView Raw
1/**
2* Sutton SignWriting Core Module v1.2.0 (https://github.com/sutton-signwriting/core)
3* Author: Steve Slevinski (https://SteveSlevinski.me)
4* swuquery.mjs is released under the MIT License.
5*/
6
7/**
8 * Object of regular expressions for SWU query strings
9 *
10 * { base, coord, var, symbol, range, prefix, signbox, full }
11 * @alias swuquery.re
12 * @type {object}
13 */
14let re = {
15 'base': '(?:(?:\uD8C0[\uDC01-\uDFFF])|(?:[\uD8C1-\uD8FC][\uDC00-\uDFFF])|(?:\uD8FD[\uDC00-\uDC80]))',
16 'coord': '(?:(?:\uD836[\uDC0C-\uDDFF]){2})?',
17 'var': 'V[0-9]+'
18};
19re.symbol = `${re.base}f?r?`;
20re.range = `R${re.base}${re.base}`;
21re.prefix = `(?:A(?:${re.symbol}|${re.range})+)?T`;
22re.signbox = `(?:${re.symbol}${re.coord}|${re.range}${re.coord})*`;
23re.full = `Q(${re.prefix})?(${re.signbox})?(${re.var})?(-?)`;
24
25/**
26 * Object of regular expressions for SWU strings in UTF-16
27 *
28 * { symbol, coord, sort, box, prefix, spatial, signbox, sign, sortable }
29 * @alias swu.re
30 * @type {object}
31 */
32let re$1 = {
33 'symbol': '(?:(?:\uD8C0[\uDC01-\uDFFF])|(?:[\uD8C1-\uD8FC][\uDC00-\uDFFF])|(?:\uD8FD[\uDC00-\uDC80]))',
34 'coord': '(?:\uD836[\uDC0C-\uDDFF]){2}',
35 'sort': '\uD836\uDC00',
36 'box': '\uD836[\uDC01-\uDC04]'
37};
38re$1.prefix = `(?:${re$1.sort}(?:${re$1.symbol})+)`;
39re$1.spatial = `${re$1.symbol}${re$1.coord}`;
40re$1.signbox = `${re$1.box}${re$1.coord}(?:${re$1.spatial})*`;
41re$1.sign = `${re$1.prefix}?${re$1.signbox}`;
42re$1.sortable = `${re$1.prefix}${re$1.signbox}`;
43
44/** 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.
45 * [Characters set definitions](https://tools.ietf.org/id/draft-slevinski-formal-signwriting-07.html#rfc.section.2.2)
46 * @module convert
47 */
48/**
49 * Function to convert an SWU number character to an integer
50 * @function convert.swu2num
51 * @param {string} swuNum - SWU number character
52 * @returns {number} Integer value for number
53 * @example
54 * convert.swu2num('𝤆')
55 *
56 * return 500
57 */
58
59
60const swu2num = swuNum => parseInt(swuNum.codePointAt(0)) - 0x1D80C + 250;
61/**
62 * Function to convert a number to an SWU number character
63 * @function convert.num2swu
64 * @param {number} num - Integer value for number
65 * @returns {string} SWU number character
66 * @example
67 * convert.num2swu(500)
68 *
69 * return '𝤆'
70 */
71
72
73const num2swu = num => String.fromCodePoint(0x1D80C + parseInt(num) - 250);
74/**
75 * Function to convert two SWU number characters to an array of x,y integers
76 * @function convert.swu2coord
77 * @param {string} swuCoord - Two SWU number character
78 * @returns {number[]} Array of x,y integers
79 * @example
80 * convert.swu2coord('𝤆𝤆')
81 *
82 * return [500, 500]
83 */
84
85
86const swu2coord = swuCoord => [swu2num(swuCoord.slice(0, 2)), swu2num(swuCoord.slice(2, 4))];
87/**
88 * Function to convert an array of x,y integers to two SWU number characters
89 * @function convert.coord2swu
90 * @param {number[]} coord - Array of x,y integers
91 * @returns {string} Two SWU number character
92 * @example
93 * convert.coord2swu([500, 500])
94 *
95 * return '𝤆𝤆'
96 */
97
98
99const coord2swu = coord => coord.map(num => num2swu(num)).join('');
100/**
101 * Function to convert an SWU symbol character to a code point on plane 4
102 * @function convert.swu2code
103 * @param {string} swuSym - SWU symbol character
104 * @returns {number} Code point on plane 4
105 * @example
106 * convert.swu2code('񀀁')
107 *
108 * return 0x40001
109 */
110
111
112const swu2code = swuSym => parseInt(swuSym.codePointAt(0));
113/**
114 * Function to convert a code point on plane 4 to an SWU symbol character
115 * @function convert.code2swu
116 * @param {number} code - Code point on plane 4
117 * @returns {string} SWU symbol character
118 * @example
119 * convert.code2swu(0x40001)
120 *
121 * return '񀀁'
122 */
123
124
125const code2swu = code => String.fromCodePoint(code);
126/**
127 * Function to convert an SWU symbol character to an FSW symbol key
128 * @function convert.swu2key
129 * @param {string} swuSym - SWU symbol character
130 * @returns {string} FSW symbol key
131 * @example
132 * convert.swu2key('񀀁')
133 *
134 * return 'S10000'
135 */
136
137
138const swu2key = swuSym => {
139 const symcode = swu2code(swuSym) - 0x40001;
140 const base = parseInt(symcode / 96);
141 const fill = parseInt((symcode - base * 96) / 16);
142 const rotation = parseInt(symcode - base * 96 - fill * 16);
143 return 'S' + (base + 0x100).toString(16) + fill.toString(16) + rotation.toString(16);
144};
145/**
146 * Function to convert an FSW symbol key to an SWU symbol character
147 * @function convert.key2swu
148 * @param {string} key - FSW symbol key
149 * @returns {string} SWU symbol character
150 * @example
151 * convert.key2swu('S10000')
152 *
153 * return '񀀁'
154 */
155
156
157const 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));
158
159const parsePrefix = text => {
160 return {
161 required: true,
162 parts: text == 'T' ? undefined : text.match(new RegExp(`(${re.symbol}|${re.range})`, 'g')).map(part => part[0] != 'R' ? part : [part.slice(1, 3), part.slice(3, 5)])
163 };
164};
165
166const parseSignbox = text => {
167 return text.match(new RegExp(`(${re.symbol}${re.coord}|${re.range}${re.coord})`, 'g')).map(part => {
168 let coord, front;
169
170 if (part.length > 5) {
171 coord = swu2coord(part.slice(-4));
172 front = part.slice(0, -4);
173 } else {
174 front = part;
175 }
176
177 if (!front.includes('R')) {
178 return {
179 symbol: front,
180 coord: coord
181 };
182 } else {
183 return {
184 range: [front.slice(1, 3), front.slice(3, 5)],
185 coord: coord
186 };
187 }
188 });
189};
190/**
191 * Function to parse SWU query string to object
192 * @function swuquery.parse
193 * @param {string} swuQueryString - an SWU query string
194 * @returns {object} elements of an SWU query string
195 * @example
196 * swuquery.parse('QA񀀁R񀀁񆆑񆇡T񆀁R񀀁񀇱𝤆𝤆V5-')
197 *
198 * return {
199 * query: true,
200 * prefix: {
201 * required: true,
202 * parts: [
203 * '񀀁',
204 * ['񀀁', '񆆑'],
205 * '񆇡'
206 * ]
207 * },
208 * signbox: [
209 * { symbol: '񆀁' },
210 * {
211 * range: ['񀀁', '񀇱'],
212 * coord: [500, 500]
213 * }
214 * ],
215 * variance: 5,
216 * style: true
217 * }
218 */
219
220
221const parse = swuQueryString => {
222 const query = typeof swuQueryString === 'string' ? swuQueryString.match(new RegExp(`^${re.full}`)) : undefined;
223 return {
224 'query': query ? true : undefined,
225 'prefix': query && query[1] ? parsePrefix(query[1]) : undefined,
226 'signbox': query && query[2] ? parseSignbox(query[2]) : undefined,
227 'variance': query && query[3] ? parseInt(query[3].slice(1)) : undefined,
228 'style': query && query[4] ? true : undefined
229 };
230};
231
232/**
233 * Function to compose SWU query string from object
234 * @function swuquery.compose
235 * @param {object} swuQueryObject - an object of style options
236 * @param {boolean} swuQueryObject.query - required true for SWU query object
237 * @param {object} swuQueryObject.prefix - an object for prefix elements
238 * @param {boolean} swuQueryObject.prefix.required - true if sorting prefix is required
239 * @param {(string|string[])[]} swuQueryObject.prefix.parts - array of symbol strings and range arrays
240 * @param {({symbol:string,coord:number[]}|{range:string[],coord:number[]})[]} swuQueryObject.signbox - array of objects for symbols and ranges with optional coordinates
241 * @param {number} swuQueryObject.variance - amount that x or y coordinates can vary and find a match, defaults to 20
242 * @param {boolean} swuQueryObject.style - boolean value for including style string in matches
243 * @returns {string} SWU query string
244 * @example
245 * swuquery.compose({
246 * query: true,
247 * prefix: {
248 * required: true,
249 * parts: [
250 * '񀀁',
251 * ['񀀁', '񆆑'],
252 * '񆇡'
253 * ]
254 * },
255 * signbox: [
256 * { symbol: '񆀁' },
257 * {
258 * range: ['񀀁', '񀇱'],
259 * coord: [500, 500]
260 * }
261 * ],
262 * variance: 5,
263 * style: true
264 * })
265 *
266 * return 'QA񀀁R񀀁񆆑񆇡T񆀁R񀀁񀇱𝤆𝤆V5-'
267 */
268
269const compose = swuQueryObject => {
270 if (!swuQueryObject || !swuQueryObject.query) {
271 return undefined;
272 }
273
274 let query = 'Q';
275
276 if (swuQueryObject.prefix && swuQueryObject.prefix.required) {
277 if (Array.isArray(swuQueryObject.prefix.parts)) {
278 query += 'A';
279 query += swuQueryObject.prefix.parts.map(part => {
280 if (typeof part === 'string') {
281 return part;
282 } else {
283 if (Array.isArray(part) && part.length == 2) {
284 return `R${part[0]}${part[1]}`;
285 }
286 }
287 }).join('');
288 }
289
290 query += 'T';
291 }
292
293 if (Array.isArray(swuQueryObject.signbox)) {
294 query += swuQueryObject.signbox.map(part => {
295 let out;
296
297 if (part.symbol) {
298 out = part.symbol;
299 } else {
300 if (part.range && Array.isArray(part.range) && part.range.length == 2) {
301 out = `R${part.range[0]}${part.range[1]}`;
302 }
303 }
304
305 return out + (Array.isArray(part.coord) && part.coord.length == 2 ? coord2swu(part.coord) : '');
306 }).join('');
307 }
308
309 query += swuQueryObject.style ? '-' : '';
310 query = query.match(new RegExp(`^${re.full}`))[0];
311 return query;
312};
313
314/**
315 * Object of regular expressions for style strings
316 *
317 * { colorize, colorhex, colorname, padding, zoom, zoomsym, classbase, id, colorbase, color, colors, background, detail, detailsym, classes, full }
318 * @alias style.re
319 * @type {object}
320 */
321let re$2 = {
322 'colorize': 'C',
323 'colorhex': '(?:[0-9a-fA-F]{3}){1,2}',
324 'colorname': '[a-zA-Z]+',
325 'padding': 'P[0-9]{2}',
326 'zoom': 'Z(?:[0-9]+(?:\\.[0-9]+)?|x)',
327 'zoomsym': 'Z[0-9]{2},[0-9]+(?:\\.[0-9]+)?(?:,[0-9]{3}x[0-9]{3})?',
328 'classbase': '-?[_a-zA-Z][_a-zA-Z0-9-]{0,100}',
329 'id': '[a-zA-Z][_a-zA-Z0-9-]{0,100}'
330};
331re$2.colorbase = `(?:${re$2.colorhex}|${re$2.colorname})`;
332re$2.color = `_${re$2.colorbase}_`;
333re$2.colors = `_${re$2.colorbase}(?:,${re$2.colorbase})?_`;
334re$2.background = `G${re$2.color}`;
335re$2.detail = `D${re$2.colors}`;
336re$2.detailsym = `D[0-9]{2}${re$2.colors}`;
337re$2.classes = `${re$2.classbase}(?: ${re$2.classbase})*`;
338re$2.full = `-(${re$2.colorize})?(${re$2.padding})?(${re$2.background})?(${re$2.detail})?(${re$2.zoom})?(?:-((?:${re$2.detailsym})*)((?:${re$2.zoomsym})*))?(?:-(${re$2.classes})?!(?:(${re$2.id})!)?)?`;
339
340const parse$1 = {
341 /**
342 * Function to parse an swu symbol with optional coordinate and style string
343 * @function swu.parse.symbol
344 * @param {string} swuSym - an swu symbol
345 * @returns {object} elements of swu symbol
346 * @example
347 * swu.parse.symbol('񀀁𝤆𝤆-C')
348 *
349 * return {
350 * 'symbol': '񀀁',
351 * 'coord': [500, 500],
352 * 'style': '-C'
353 * }
354 */
355 symbol: swuSym => {
356 const regex = `^(${re$1.symbol})(${re$1.coord})?(${re$2.full})?`;
357 const symbol = typeof swuSym === 'string' ? swuSym.match(new RegExp(regex)) : undefined;
358 return {
359 'symbol': symbol ? symbol[1] : undefined,
360 'coord': symbol && symbol[2] ? swu2coord(symbol[2]) : undefined,
361 'style': symbol ? symbol[3] : undefined
362 };
363 },
364
365 /**
366 * Function to parse an swu sign with style string
367 * @function swu.parse.sign
368 * @param {string} swuSign - an swu sign
369 * @returns {object} elements of swu sign
370 * @example
371 * swu.parse.sign('𝠀񀀒񀀚񋚥񋛩𝠃𝤟𝤩񋛩𝣵𝤐񀀒𝤇𝣤񋚥𝤐𝤆񀀚𝣮𝣭-C')
372 *
373 * return {
374 * sequence: ['񀀒','񀀚','񋚥','񋛩''],
375 * box: '𝠃',
376 * max: [525, 535],
377 * spatials: [
378 * {
379 * symbol: '񋛩',
380 * coord: [483, 510]
381 * },
382 * {
383 * symbol: '񀀒',
384 * coord: [501, 466]
385 * },
386 * {
387 * symbol: '񋚥',
388 * coord: [510, 500]
389 * },
390 * {
391 * symbol: '񀀚',
392 * coord: [476, 475]
393 * }
394 * ],
395 * style: '-C'
396 * }
397 */
398 sign: swuSign => {
399 const regex = `^(${re$1.prefix})?(${re$1.signbox})(${re$2.full})?`;
400 const sign = typeof swuSign === 'string' ? swuSign.match(new RegExp(regex)) : undefined;
401
402 if (sign) {
403 return {
404 'sequence': sign[1] ? sign[1].slice(2).match(/.{2}/g) : undefined,
405 'box': sign[2].slice(0, 2),
406 'max': swu2coord(sign[2].slice(2, 6)),
407 'spatials': sign[2].length < 7 ? undefined : sign[2].slice(6).match(/(.{6})/g).map(m => {
408 return {
409 symbol: m.slice(0, 2),
410 coord: swu2coord(m.slice(2))
411 };
412 }),
413 'style': sign[3]
414 };
415 } else {
416 return {};
417 }
418 }
419};
420/**
421 * Function to decode UTF-16 escape format to SWU characters.
422 * @function swu.decode
423 * @param {string} encoded - UTF-16 escape format
424 * @returns {string} SWU characters
425 * @example
426 * swu.decode('\\uD8C0\\uDC01\\uD836\\uDD06\\uD836\\uDD06')
427 *
428 * return '񀀁𝤆𝤆'
429 */
430
431
432const decode = encoded => encoded.replace(/\\u([0-9A-F]{4})/g, function (match, chr) {
433 return String.fromCharCode(parseInt(chr, 16));
434});
435/**
436 * Function to decompose an SWU character into UTF-16 surrogate pairs.
437 * @function swu.pair
438 * @param {string} swuChar - an SWU character
439 * @returns {string[]} an array of UTF-16 surrogate pairs
440 * @example
441 * swu.pair('񀀁')
442 *
443 * return ['D8C0', 'DC01']
444 */
445
446
447const pair = swuChar => [swuChar.charCodeAt(0).toString(16).toUpperCase(), swuChar.charCodeAt(1).toString(16).toUpperCase()];
448
449/**
450 * Function to convert an SWU sign to a query string
451 *
452 * For the flags parameter, use one or more of the following.
453 * - A: exact symbol in temporal prefix
454 * - a: general symbol in temporal prefix
455 * - S: exact symbol in spatial signbox
456 * - s: general symbol in spatial signbox
457 * - L: spatial signbox symbol at location
458 * @function swuquery.swu2query
459 * @param {string} swuSign - SWU sign
460 * @param {string} flags - flags for query string creation
461 * @returns {string} SWU query string
462 * @example
463 * swuquery.swu2query('𝠀񀀒񀀚񋚥񋛩𝠃𝤟𝤩񋛩𝣵𝤐񀀒𝤇𝣤񋚥𝤐𝤆񀀚𝣮𝣭', 'ASL')
464 *
465 * return 'QA񀀒񀀚񋚥񋛩T񋛩𝣵𝤐񀀒𝤇𝣤񋚥𝤐𝤆񀀚𝣮𝣭'
466 */
467
468const swu2query = (swuSign, flags) => {
469 let query = '';
470 const parsed = parse$1.sign(swuSign);
471
472 if (parsed.box) {
473 const A_flag = flags.indexOf('A') > -1;
474 const a_flag = flags.indexOf('a') > -1;
475 const S_flag = flags.indexOf('S') > -1;
476 const s_flag = flags.indexOf('s') > -1;
477 const L_flag = flags.indexOf('L') > -1;
478
479 if (A_flag || a_flag || S_flag || s_flag) {
480 if ((A_flag || a_flag) && parsed.sequence) {
481 query += 'A';
482 query += parsed.sequence.map(sym => sym + (a_flag ? 'fr' : '')).join('');
483 query += 'T';
484 }
485
486 if ((S_flag || s_flag) && parsed.spatials) {
487 query += parsed.spatials.map(spatial => spatial.symbol + (s_flag ? 'fr' : '') + (L_flag ? coord2swu(spatial.coord) : '')).join('');
488 }
489 }
490
491 return query ? "Q" + query : undefined;
492 } else {
493 return undefined;
494 }
495};
496
497/**
498 * Function to transform a range of SWU characters to a regular expression
499 * @function swuquery.range
500 * @param {string} min - an SWU character
501 * @param {string} max - an SWU character
502 * @returns {string} a regular expression that matches a range of SWU characters
503 * @example
504 * swuquery.range('񀀁', '񀇡')
505 *
506 * return '\uD8C0[\uDC01-\uDDE1]'
507 * @example
508 * swuquery.range('𝣔', '𝤸')
509 *
510 * return '\uD836[\uDCD4-\uDD38]'
511 */
512
513const range = (min, max) => {
514 if (min > max) return '';
515 let pattern = '';
516 let cnt;
517 let re = [];
518 min = pair(min);
519 max = pair(max);
520 if (min.length != 2 && max.length != 2) return ''; // HEAD // min[0] with range of min[1] to (DFFF or max[1])
521
522 if (min[0] == max[0]) {
523 if (min[1] == max[1]) {
524 pattern = '\\u' + min[0] + '\\u' + min[1];
525 re.push(pattern);
526 } else {
527 pattern = '\\u' + min[0] + '[\\u' + min[1] + '-\\u' + max[1] + ']';
528 re.push(pattern);
529 }
530 } else {
531 if (min[1] == "DFFF") {
532 pattern = '\\u' + min[0] + '\\uDFFF';
533 } else {
534 pattern = '\\u' + min[0] + '[\\u' + min[1] + '-\\uDFFF]';
535 }
536
537 re.push(pattern); // BODY // range of (min[0] +1) to (max[0] -1) with all DC00-DFFF
538
539 let diff = parseInt(max[0], 16) - parseInt(min[0], 16);
540
541 if (diff == 2) {
542 pattern = '\\u' + (parseInt(min[0], 16) + 1).toString(16).toUpperCase();
543 pattern += '[\\uDC00-\\uDFFF]';
544 re.push(pattern);
545 }
546
547 if (diff > 2) {
548 pattern = '[';
549 pattern += '\\u' + (parseInt(min[0], 16) + 1).toString(16).toUpperCase();
550 pattern += '-\\u' + (parseInt(max[0], 16) - 1).toString(16).toUpperCase();
551 pattern += '][\\uDC00-\\uDFFF]';
552 re.push(pattern);
553 } // TAIL // max[0] with range of DC00 to max[1]
554
555
556 if (max[1] == "DC00") {
557 pattern = '\\u' + max[0] + '\\uDC00';
558 } else {
559 pattern = '\\u' + max[0] + '[\\uDC00-\\u' + max[1] + ']';
560 }
561
562 re.push(pattern);
563 }
564
565 cnt = re.length;
566
567 if (cnt == 1) {
568 pattern = re[0];
569 } else {
570 pattern = re.join(')|(');
571 pattern = '((' + pattern + '))';
572 }
573
574 return decode(pattern);
575};
576
577/**
578 * Function to transform an SWU symbol with fill and rotation flags to a regular expression
579 * @function swuquery.symbolRanges
580 * @param {string} symbolFR - an SWU character with optional flags of 'f' for any fill and 'r' for any rotation
581 * @returns {string} a regular expression that matches one or more ranges of SWU symbols
582 * @example <caption>Match an exact symbol</caption>
583 * swuquery.symbolRanges('񀀁')
584 *
585 * return '\uD8C0\uDC01');
586 * @example <caption>Match a symbol with any fill</caption>
587 * swuquery.symbolRanges('񀀁f')
588 *
589 * return '(\uD8C0\uDC01|\uD8C0\uDC11|\uD8C0\uDC21|\uD8C0\uDC31|\uD8C0\uDC41|\uD8C0\uDC51)'
590 * @example <caption>Match a symbol with any rotation</caption>
591 * swuquery.symbolRanges('񀀁r')
592 *
593 * return '\uD8C0[\uDC01-\uDC10]'
594 * @example <caption>Match a symbol with any fill or rotation</caption>
595 * swuquery.symbolRanges('񀀁fr')
596 *
597 * return '\uD8C0[\uDC01-\uDC60]'
598 */
599
600const symbolRanges = symbolFR => {
601 let match = symbolFR.match(new RegExp(re.symbol));
602
603 if (match) {
604 let sym = match[0].slice(0, 2);
605 let key = swu2key(sym);
606 let base = key.slice(0, 4);
607 let start, end;
608
609 if (match[0].slice(-2) == 'fr') {
610 start = key2swu(base + "00");
611 end = key2swu(base + "5f");
612 return range(start, end);
613 } else if (match[0].slice(-1) == 'r') {
614 start = key2swu(key.slice(0, 5) + '0');
615 end = key2swu(key.slice(0, 5) + 'f');
616 return range(start, end);
617 } else if (match[0].slice(-1) == 'f') {
618 let list = [0, 1, 2, 3, 4, 5].map(function (f) {
619 return key2swu(base + f + key.slice(-1));
620 });
621 return "(" + list.join("|") + ")";
622 } else {
623 return sym;
624 }
625 } else {
626 return '';
627 }
628};
629
630/**
631 * Function to transform an SWU query string to one or more regular expressions
632 * @function swuquery.regex
633 * @param {string} query - an SWU query string
634 * @returns {string[]} an array of one or more regular expressions
635 * @example
636 * swuquery.regex('QA񀀒T')
637 *
638 * return [
639 * '(\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})*'
640 * ]
641 */
642
643const regex = query => {
644 query = query.match(new RegExp(`^${re.full}`))[0];
645
646 if (!query) {
647 return '';
648 }
649
650 let matches;
651 let i;
652 let part;
653 let from;
654 let to;
655 let coord;
656 let segment;
657 let x;
658 let y;
659 let fuzz = 20;
660 let re_sym = re$1.symbol;
661 let re_coord = re$1.coord;
662 let re_signbox = re$1.box;
663 let re_seq = re$1.sort;
664 let re_word = re_signbox + re_coord + '(' + re_sym + re_coord + ')*';
665 let re_sortable = '(' + re_seq + '(' + re_sym + ')+)';
666 let q_range = 'R' + re_sym + re_sym;
667 let q_sym = re_sym + 'f?r?';
668 let q_coord = '(' + re_coord + ')?';
669 let q_var = '(V[0-9]+)';
670 let q_style = '(' + re$2.full + ')?';
671 let q_sortable;
672
673 if (query == 'Q') {
674 return [re$1.sign];
675 }
676
677 if (query == 'Q-') {
678 return [re$1.sign + "(" + re$2.full + ")?"];
679 }
680
681 if (query == 'QT') {
682 return [re$1.sortable];
683 }
684
685 if (query == 'QT-') {
686 return [re$1.sortable + "(" + re$2.full + ")?"];
687 }
688
689 let segments = [];
690 let sym;
691 let sortable = query.indexOf('T') + 1;
692
693 if (sortable) {
694 q_sortable = '(' + re$1.sort;
695 let qat = query.slice(0, sortable);
696 query = query.replace(qat, '');
697
698 if (qat == 'QT') {
699 q_sortable += '(' + re_sym + ')+)';
700 } else {
701 matches = qat.match(new RegExp('(' + q_sym + '|' + q_range + ')', 'g'));
702
703 if (matches) {
704 let matched;
705
706 for (i = 0; i < matches.length; i += 1) {
707 matched = matches[i].match(new RegExp('^' + q_sym));
708
709 if (matched) {
710 q_sortable += symbolRanges(matched[0]);
711 } else {
712 from = swu2key(matches[i].slice(1, 3));
713 to = swu2key(matches[i].slice(-2));
714 from = key2swu(from.slice(0, 4) + '00');
715 to = key2swu(to.slice(0, 4) + '5f');
716 q_sortable += range(from, to);
717 }
718 }
719
720 q_sortable += '(' + re_sym + ')*)';
721 }
722 }
723 } //get the variance
724
725
726 matches = query.match(new RegExp(q_var, 'g'));
727
728 if (matches) {
729 fuzz = matches.toString().slice(1) * 1;
730 } //this gets all symbols with or without location
731 matches = query.match(new RegExp("(" + q_range + q_coord + "|" + q_sym + q_coord + ")", 'g'));
732
733 if (matches) {
734 for (i = 0; i < matches.length; i += 1) {
735 part = matches[i].toString();
736
737 if (part[0] != "R") {
738 sym = part.match(new RegExp(q_sym))[0];
739 segment = symbolRanges(sym);
740
741 if (sym.length > part.length) {
742 coord = swu2coord(part.slice(-4));
743 x = coord[0];
744 y = coord[1]; //now get the x segment range+++
745
746 segment += range(coord2swu([x - fuzz, x + fuzz]));
747 segment += range(coord2swu([y - fuzz, y + fuzz]));
748 } else {
749 segment += re_coord;
750 } //now I have the specific search symbol
751 // add to general swu word
752
753
754 segment = re_word + segment + '(' + re_sym + re_coord + ')*';
755
756 if (sortable) {
757 segment = q_sortable + segment;
758 } else {
759 segment = re_sortable + "?" + segment;
760 }
761
762 if (query.indexOf('-') > 0) {
763 segment += q_style;
764 }
765
766 segments.push(segment);
767 } else {
768 //ranges
769 part = matches[i].toString();
770 from = swu2key(part.slice(1, 3));
771 to = swu2key(part.slice(3, 5));
772 from = key2swu(from.slice(0, 4) + '00');
773 to = key2swu(to.slice(0, 4) + '5f');
774 segment = range(from, to);
775
776 if (part.length > 5) {
777 coord = swu2coord(part.slice(5, 9));
778 x = coord[0];
779 y = coord[1]; //now get the x segment range+++
780
781 segment += range(coord2swu([x - fuzz, x + fuzz]));
782 segment += range(coord2swu([y - fuzz, y + fuzz]));
783 } else {
784 segment += re_coord;
785 } // add to general swu word
786
787
788 segment = re_word + segment + '(' + re_sym + re_coord + ')*';
789
790 if (sortable) {
791 segment = q_sortable + segment;
792 } else {
793 segment = re_sortable + "?" + segment;
794 }
795
796 if (query.indexOf('-') > 0) {
797 segment += q_style;
798 }
799
800 segments.push(segment);
801 }
802 }
803 }
804
805 if (!segments.length) {
806 if (query.indexOf('-') > 0) {
807 segment += q_style;
808 }
809
810 segments.push(q_sortable + re_word);
811 }
812
813 return segments;
814};
815
816/**
817 * Function that uses a query string to match signs from a string of text.
818 * @function swuquery.results
819 * @param {string} query - an SWU query string
820 * @param {string} text - a string of text containing multiple signs
821 * @returns {string[]} an array of SWU signs
822 * @example
823 * swuquery.results('QA񀀒T','𝠀񀀒񀀚񋚥񋛩𝠃𝤟𝤩񋛩𝣵𝤐񀀒𝤇𝣤񋚥𝤐𝤆񀀚𝣮𝣭 𝠀񂇢񂇈񆙡񋎥񋎵𝠃𝤛𝤬񂇈𝤀𝣺񂇢𝤄𝣻񋎥𝤄𝤗񋎵𝤃𝣟񆙡𝣱𝣸 𝠀񅨑񀀙񆉁𝠃𝤙𝤞񀀙𝣷𝤀񅨑𝣼𝤀񆉁𝣳𝣮')
824 *
825 * return [
826 * '𝠀񀀒񀀚񋚥񋛩𝠃𝤟𝤩񋛩𝣵𝤐񀀒𝤇𝣤񋚥𝤐𝤆񀀚𝣮𝣭'
827 * ]
828 */
829
830const results = (query, text) => {
831 if (!text) {
832 return [];
833 }
834
835 let pattern;
836 let matches;
837 let parts;
838 let words;
839 let res = regex(query);
840
841 if (!res) {
842 return [];
843 }
844
845 let i;
846
847 for (i = 0; i < res.length; i += 1) {
848 pattern = res[i];
849 matches = text.match(new RegExp(pattern, 'g'));
850
851 if (matches) {
852 text = matches.join(' ');
853 } else {
854 text = '';
855 }
856 }
857
858 if (text) {
859 parts = text.split(' ');
860 words = parts.filter(function (element) {
861 return element in parts ? false : parts[element] = true;
862 }, {});
863 } else {
864 words = [];
865 }
866
867 return words;
868}; //needs rewritten, but it works
869
870/**
871 * Function that uses an SWU query string to match signs from multiple lines of text.
872 * @function swuquery.lines
873 * @param {string} query - an SWU query string
874 * @param {string} text - multiple lines of text, each starting with an SWU sign
875 * @returns {string[]} an array of lines of text, each starting with an SWU sign
876 * @example
877 * swuquery.lines('QA񀀒T',`𝠀񀀒񀀚񋚥񋛩𝠃𝤟𝤩񋛩𝣵𝤐񀀒𝤇𝣤񋚥𝤐𝤆񀀚𝣮𝣭 line one
878 * 𝠀񂇢񂇈񆙡񋎥񋎵𝠃𝤛𝤬񂇈𝤀𝣺񂇢𝤄𝣻񋎥𝤄𝤗񋎵𝤃𝣟񆙡𝣱𝣸 line two
879 * 𝠀񅨑񀀙񆉁𝠃𝤙𝤞񀀙𝣷𝤀񅨑𝣼𝤀񆉁𝣳𝣮 line three`)
880 *
881 * return [
882 * '𝠀񀀒񀀚񋚥񋛩𝠃𝤟𝤩񋛩𝣵𝤐񀀒𝤇𝣤񋚥𝤐𝤆񀀚𝣮𝣭 line one'
883 * ]
884 */
885
886
887const lines = (query, text) => {
888 if (!text) {
889 return [];
890 }
891
892 let pattern;
893 let matches;
894 let parts;
895 let words;
896 let res = regex(query);
897
898 if (!res) {
899 return [];
900 }
901
902 let i;
903
904 for (i = 0; i < res.length; i += 1) {
905 pattern = res[i];
906 pattern = '^' + pattern + '.*';
907 matches = text.match(new RegExp(pattern, 'mg'));
908
909 if (matches) {
910 text = matches.join("\n");
911 } else {
912 text = '';
913 }
914 }
915
916 if (text) {
917 parts = text.split("\n");
918 words = parts.filter(function (element) {
919 return element in parts ? false : parts[element] = true;
920 }, {});
921 } else {
922 words = [];
923 }
924
925 return words;
926};
927
928export { compose, lines, parse, range, re, regex, results, swu2query, symbolRanges };
929
930/* support ongoing development on https://patreon.com/signwriting */