UNPKG

121 kBJavaScriptView Raw
1/**
2* Sutton SignWriting Core Module v1.6.0 (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 const definedProps = obj => Object.fromEntries(Object.entries(obj).filter(([k, v]) => v !== undefined));
83
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 const parse$4 = styleString => {
100 const regex = `^${re$3.full}`;
101 const m = (typeof styleString === 'string' ? styleString.match(new RegExp(regex)) : []) || [];
102 return definedProps({
103 'colorize': !m[1] ? undefined : !!m[1],
104 'padding': !m[2] ? undefined : parseInt(m[2].slice(1)),
105 'background': !m[3] ? undefined : prefixColor(m[3].slice(2, -1)),
106 'detail': !m[4] ? undefined : m[4].slice(2, -1).split(',').map(prefixColor),
107 'zoom': !m[5] ? undefined : m[5] === 'Zx' ? 'x' : parseFloat(m[5].slice(1)),
108 'detailsym': !m[6] ? undefined : m[6].match(new RegExp(re$3.detailsym, 'g')).map(val => {
109 const parts = val.split('_');
110 const detail = parts[1].split(',').map(prefixColor);
111 return {
112 'index': parseInt(parts[0].slice(1)),
113 'detail': detail
114 };
115 }),
116 'classes': !m[7] ? undefined : m[7],
117 'id': !m[8] ? undefined : m[8]
118 });
119 };
120
121 /**
122 * Function to compose style string from object
123 * @function style.compose
124 * @param {StyleObject} styleObject - an object of style options
125 * @returns {string} style string
126 * @example
127 * style.compose({
128 * 'colorize': true,
129 * 'padding': 10,
130 * 'background': 'blue',
131 * 'detail': ['red', 'Cyan'],
132 * 'zoom': 1.1,
133 * 'detailsym': [
134 * {
135 * 'index': 1,
136 * 'detail': ['#ff00ff']
137 * },
138 * {
139 * 'index': 2,
140 * 'detail': ['yellow', 'green']
141 * }
142 * ],
143 * 'classes': 'primary blinking',
144 * 'id': 'cursor'
145 * })
146 *
147 * return '-CP10G_blue_D_red,Cyan_Z1.1-D01_ff00ff_D02_yellow,green_-primary blinking!cursor!'
148 */
149 const compose$4 = styleObject => {
150 if (typeof styleObject !== 'object' || styleObject === null) return undefined;
151
152 // three sections
153 let style1 = '-';
154 style1 += !styleObject.colorize ? '' : 'C';
155 const padding = parseInt(styleObject.padding);
156 style1 += !padding || padding <= 0 || padding > 99 ? '' : 'P' + (padding > 9 ? padding : '0' + padding);
157 const background = !styleObject.background || !(typeof styleObject.background === 'string') ? undefined : styleObject.background.match(re$3.colorbase)[0];
158 style1 += !background ? '' : 'G_' + background + '_';
159 const detail1 = !styleObject.detail || !styleObject.detail[0] || !(typeof styleObject.detail[0] === 'string') ? undefined : styleObject.detail[0].match(re$3.colorbase)[0];
160 const detail2 = !styleObject.detail || !styleObject.detail[1] || !(typeof styleObject.detail[1] === 'string') ? undefined : styleObject.detail[1].match(re$3.colorbase)[0];
161 if (detail1) {
162 style1 += 'D_' + detail1;
163 if (detail2) {
164 style1 += ',' + detail2;
165 }
166 style1 += '_';
167 }
168 const zoom = styleObject.zoom === 'x' ? 'x' : parseFloat(styleObject.zoom);
169 style1 += !zoom || zoom <= 0 ? '' : 'Z' + zoom;
170 let style2 = '';
171 const detailsym = !styleObject.detailsym || !Array.isArray(styleObject.detailsym) ? [] : styleObject.detailsym.map(styleObject => {
172 const index = parseInt(styleObject.index);
173 if (!index || index <= 0 || index > 99) return '';
174 let style = 'D' + (index > 9 ? index : '0' + index);
175 const detail1 = !styleObject.detail || !styleObject.detail[0] ? undefined : styleObject.detail[0].match(re$3.colorbase)[0];
176 const detail2 = !styleObject.detail || !styleObject.detail[1] ? undefined : styleObject.detail[1].match(re$3.colorbase)[0];
177 if (detail1) {
178 style += '_' + detail1;
179 if (detail2) {
180 style += ',' + detail2;
181 }
182 style += '_';
183 }
184 return style;
185 });
186 style2 += detailsym.join('');
187 let style3 = '';
188 const classes = !styleObject.classes || !(typeof styleObject.classes === 'string') ? undefined : styleObject.classes.match(re$3.classes)[0];
189 style3 += !classes ? '' : classes;
190 const id = !styleObject.id || !(typeof styleObject.id === 'string') ? undefined : styleObject.id.match(re$3.id)[0];
191 style3 += classes || id ? '!' : '';
192 style3 += !id ? '' : id + '!';
193 return style1 + (style2 || style3 ? '-' + style2 : '') + (style3 ? '-' + style3 : '');
194 };
195
196 /**
197 * Function to merge style objects
198 * @function style.merge
199 * @param {StyleObject} style1 - a style object
200 * @param {StyleObject} style2 - a style object
201 * @returns {StyleObject} a style object
202 * @example
203 * style.merge({'colorize': true},{zoom:2})
204 *
205 * return {
206 * 'colorize': true,
207 * 'zoom': 2
208 * }
209 */
210 const merge = (style1, style2) => {
211 if (typeof style1 !== 'object') style1 = {};
212 if (typeof style2 !== 'object') style2 = {};
213 const zoom1 = 'zoom' in style1 ? style1['zoom'] : 1;
214 const zoom2 = 'zoom' in style2 ? style2['zoom'] : 1;
215 return {
216 ...style1,
217 ...style2,
218 ...{
219 zoom: zoom1 * zoom2
220 }
221 };
222 };
223
224 const rgb2arr = rgb => {
225 if (typeof rgb !== 'string') return [0, 0, 0];
226 return rgb.replace(/rgba?\((.+?)\)/ig, (_, values) => {
227 return values;
228 }).split(',').map(Number);
229 };
230 const arr2hex = arr => {
231 return arr.slice(0, 3).map(num => num.toString(16).padStart(2, '0')).join('');
232 };
233
234 /**
235 * Function to convert rgb color to hex or "transparent" if below tolerance
236 * @function style.rgb2hex
237 * @param {string} rgb - an rgb color
238 * @param {number} [tolerance=0] - max alpha for full transparency
239 * @returns {string} a hex color or "transparent"
240 * @example
241 * style.rgb2hex("rgb(255,255,255)")
242 * return "ffffff"
243 *
244 * style.rgb2hex("rgba(255,255,255,0.5)",0.5)
245 * return "transparent"
246 */
247 const rgb2hex = (rgb, tolerance = 0) => {
248 const arr = rgb2arr(rgb);
249 if (arr.length == 4 && arr[3] <= tolerance) {
250 return 'transparent';
251 } else {
252 return arr2hex(arr);
253 }
254 };
255
256 /**
257 * Function to merge color with background based on alpha transparency
258 * @function style.rgba2hex
259 * @param {string} color - an rgba color
260 * @param {string} background - an rgba background color
261 * @returns {string} a hex color or "transparent"
262 * @example
263 * style.rgba2hex("rgba(255,255,255,0.5)","rgb(0,0,0)")
264 *
265 * return "7f7f7f"
266 */
267 const rgba2hex = (color, background) => {
268 const bArr = rgb2arr(background);
269 const cArr = rgb2arr(color);
270 const alpha = cArr.length == 4 ? cArr[3] : 1;
271 if (alpha == 0) {
272 return 'transparent';
273 } else {
274 return arr2hex(cArr.map((v, i) => parseInt((1 - alpha) * bArr[i] + alpha * v)));
275 }
276 };
277
278 /** The style module contains regular expressions and functions for parsing and composing style strings.
279 * [Style string definition](https://tools.ietf.org/id/draft-slevinski-formal-signwriting-09.html#name-styling-string)
280 * @module style
281 */
282
283 var index$5 = /*#__PURE__*/Object.freeze({
284 __proto__: null,
285 re: re$3,
286 parse: parse$4,
287 compose: compose$4,
288 merge: merge,
289 rgb2hex: rgb2hex,
290 rgba2hex: rgba2hex
291 });
292
293 /**
294 * Object of regular expressions for SWU strings in UTF-16
295 *
296 * @alias swu.re
297 * @property {string} symbol - regular expressions for a symbol
298 * @property {string} coord - regular expressions for a coordinate
299 * @property {string} sort - regular expressions for the sorting marker
300 * @property {string} box - regular expression for a signbox marker
301 * @property {string} prefix - regular expression for a sorting marker followed by one or more symbols
302 * @property {string} spatial - regular expression for a symbol followed by a coordinate
303 * @property {string} signbox - regular expression for a signbox marker, max coordinate and zero or more spatial symbols
304 * @property {string} sign - regular expression for an optional prefix followed by a signbox
305 * @property {string} sortable - regular expression for a mandatory prefix followed by a signbox
306 */
307 let re$2 = {
308 'symbol': '(?:(?:\uD8C0[\uDC01-\uDFFF])|(?:[\uD8C1-\uD8FC][\uDC00-\uDFFF])|(?:\uD8FD[\uDC00-\uDC80]))',
309 'coord': '(?:\uD836[\uDC0C-\uDDFF]){2}',
310 'sort': '\uD836\uDC00',
311 'box': '\uD836[\uDC01-\uDC04]'
312 };
313 re$2.prefix = `(?:${re$2.sort}(?:${re$2.symbol})+)`;
314 re$2.spatial = `${re$2.symbol}${re$2.coord}`;
315 re$2.signbox = `${re$2.box}${re$2.coord}(?:${re$2.spatial})*`;
316 re$2.sign = `${re$2.prefix}?${re$2.signbox}`;
317 re$2.sortable = `${re$2.prefix}${re$2.signbox}`;
318
319 /**
320 * An array of symbol IDs in minimized format such as "101011"
321 *
322 * @alias convert.symidArr
323 * @type {string[]}
324 */
325 const symidArr = ["101011", "101021", "101031", "101041", "101051", "101061", "101071", "101081", "101091", "101101", "101111", "101121", "101131", "101141", "102011", "102021", "102031", "102041", "102051", "102061", "102071", "102081", "102091", "102101", "102111", "102121", "102131", "102141", "102151", "102161", "103011", "103021", "103031", "103041", "103051", "103061", "103071", "103081", "103091", "103101", "103111", "103121", "103131", "103141", "103151", "103161", "103171", "103181", "103191", "103201", "103211", "103221", "103231", "103241", "103251", "103261", "103271", "103281", "103291", "103301", "103311", "103321", "103331", "103341", "103351", "103361", "103371", "103381", "104011", "104021", "104031", "104041", "104051", "104061", "104071", "104081", "105011", "105021", "105031", "105041", "105051", "105061", "105071", "105081", "105091", "105101", "105111", "105121", "105131", "105141", "105151", "105161", "105171", "105181", "105191", "105201", "105211", "105221", "105231", "105241", "105251", "105261", "105271", "105281", "105291", "105301", "105311", "105321", "105331", "105341", "105351", "105361", "105371", "105381", "105391", "105401", "105411", "105421", "105431", "105441", "105451", "105461", "105471", "105481", "105491", "105501", "105511", "105521", "105531", "105541", "105551", "105561", "105571", "105581", "106011", "106021", "106031", "106041", "106051", "106061", "106071", "106081", "106091", "106101", "106111", "106121", "106131", "106141", "106151", "106161", "106171", "106181", "106191", "106201", "106211", "106221", "106231", "106241", "106251", "106261", "106271", "106281", "106291", "106301", "107011", "107021", "107031", "107041", "107051", "107061", "107071", "107081", "107091", "107101", "107111", "107121", "107131", "107141", "107151", "107161", "107171", "107181", "107191", "107201", "107211", "107221", "108011", "108021", "108031", "108041", "108051", "108061", "108071", "108081", "108091", "108101", "108111", "108121", "108131", "108141", "108151", "108161", "108171", "108181", "108191", "109011", "109021", "109031", "109041", "109051", "109061", "109071", "109081", "109091", "109101", "109111", "109121", "109131", "109141", "109151", "109161", "109171", "109181", "109191", "109201", "109211", "109221", "109231", "109241", "109251", "109261", "109271", "109281", "109291", "109301", "109311", "109321", "109331", "109341", "109351", "109361", "109371", "109381", "109391", "109401", "110011", "110021", "110031", "110041", "110051", "110061", "110071", "110081", "110091", "110101", "110111", "110121", "110131", "110141", "110151", "110161", "201011", "201021", "201031", "201041", "201051", "201061", "201071", "201081", "201091", "201101", "201111", "201121", "201131", "201141", "201151", "201161", "201171", "202011", "202012", "202021", "202022", "202031", "202041", "202042", "202051", "202052", "202061", "202071", "202081", "202082", "202091", "202092", "202101", "202102", "202111", "202121", "202131", "203011", "203012", "203013", "203014", "203015", "203021", "203022", "203031", "203032", "203041", "203051", "203052", "203061", "203062", "203071", "203072", "203073", "203081", "203082", "203083", "203084", "203091", "203092", "203093", "203101", "203102", "203103", "203111", "203112", "203113", "203121", "203122", "203123", "203131", "203141", "203151", "203161", "203171", "203181", "203191", "203201", "203202", "203203", "204011", "204012", "204013", "204014", "204021", "204022", "204023", "204024", "204031", "204032", "204033", "204034", "204041", "204042", "204043", "204044", "205011", "205012", "205013", "205014", "205015", "205021", "205022", "205031", "205032", "205041", "205051", "205052", "205061", "205062", "205071", "205081", "205082", "205083", "205091", "205101", "205102", "205103", "205111", "205112", "205113", "205121", "205122", "205123", "205131", "205141", "205151", "205161", "205171", "205181", "205191", "206011", "206012", "206013", "206014", "206021", "206022", "206023", "206024", "206031", "206032", "206041", "206042", "206043", "206051", "206052", "206053", "206054", "206061", "206062", "206063", "206064", "206065", "206066", "206071", "206072", "206073", "206081", "206091", "206101", "206111", "207011", "207021", "207031", "207041", "207051", "207061", "207071", "207091", "207101", "207111", "207121", "207131", "207141", "207151", "207161", "207162", "207163", "208011", "208012", "208021", "208022", "208023", "208024", "208031", "208032", "208033", "208034", "208041", "208042", "208051", "208061", "208071", "208081", "208082", "208091", "208092", "208093", "208094", "208101", "208102", "208103", "208104", "208111", "208112", "208121", "208131", "208141", "209011", "209012", "209013", "209014", "209015", "209021", "209031", "209041", "209042", "209043", "209051", "209061", "209071", "209081", "210011", "210012", "210021", "210022", "210031", "210032", "210033", "210041", "210042", "210043", "210051", "210052", "210061", "210062", "210071", "210072", "210073", "210074", "210081", "210082", "301011", "301021", "301031", "301032", "301041", "301042", "301043", "301044", "401011", "401021", "401031", "401041", "401051", "401061", "401071", "401081", "401091", "401101", "401102", "402011", "402012", "402013", "402021", "402022", "402023", "402024", "402031", "402032", "402033", "402041", "402042", "402043", "402044", "402045", "402051", "402052", "402053", "402054", "402055", "402061", "402062", "402063", "402071", "402072", "402073", "402081", "402082", "402083", "402091", "402101", "402111", "403011", "403012", "403013", "403021", "403022", "403023", "403031", "403041", "403042", "403043", "403044", "403051", "403052", "403061", "403062", "403071", "403072", "404011", "404012", "404013", "404021", "404022", "404023", "404031", "404032", "404033", "404041", "404042", "404043", "404051", "404052", "404053", "404061", "404062", "404063", "404071", "404072", "404073", "404081", "404082", "404083", "404091", "404092", "404093", "404101", "404111", "404112", "405011", "405012", "405013", "405014", "405015", "405021", "405031", "405041", "405051", "405052", "405061", "405062", "405071", "405072", "405081", "405091", "405101", "405111", "405121", "405131", "501011", "501021", "501031", "501041", "501051", "501061", "501071", "501081", "501091", "502011", "502021", "502022", "502023", "502024", "502031", "502032", "502033", "502041", "601011", "601012", "601021", "601031", "601041", "601051", "601061", "601071", "701011", "701012", "701021", "701022", "701031"];
326
327 /** 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.
328 * [Characters set definitions](https://tools.ietf.org/id/draft-slevinski-formal-signwriting-09.html#name-characters)
329 * @module convert
330 */
331
332 /**
333 * Function to convert an SWU structural marker to FSW equivalent
334 * @function convert.swu2mark
335 * @param {string} swuMark - character for SWU structural marker
336 * @returns {string} FSW structural marker
337 * @example
338 * convert.swu2mark('𝠀')
339 *
340 * return 'A'
341 */
342 const swu2mark = swuMark => {
343 return {
344 '𝠀': 'A',
345 '𝠁': 'B',
346 '𝠂': 'L',
347 '𝠃': 'M',
348 '𝠄': 'R'
349 }[swuMark];
350 };
351
352 /**
353 * Function to convert an FSW structural marker to SWU equivalent
354 * @function convert.mark2swu
355 * @param {string} fswMark - character for FSW structural marker
356 * @returns {string} SWU structural marker
357 * @example
358 * convert.mark2swu('A')
359 *
360 * return '𝠀'
361 */
362 const mark2swu = fswMark => {
363 return {
364 'A': '𝠀',
365 'B': '𝠁',
366 'L': '𝠂',
367 'M': '𝠃',
368 'R': '𝠄'
369 }[fswMark];
370 };
371
372 /**
373 * Function to convert an SWU number character to an integer
374 * @function convert.swu2num
375 * @param {string} swuNum - SWU number character
376 * @returns {number} Integer value for number
377 * @example
378 * convert.swu2num('𝤆')
379 *
380 * return 500
381 */
382 const swu2num = swuNum => parseInt(swuNum.codePointAt(0)) - 0x1D80C + 250;
383
384 /**
385 * Function to convert a number to an SWU number character
386 * @function convert.num2swu
387 * @param {number} num - Integer value for number
388 * @returns {string} SWU number character
389 * @example
390 * convert.num2swu(500)
391 *
392 * return '𝤆'
393 */
394 const num2swu = num => String.fromCodePoint(0x1D80C + parseInt(num) - 250);
395
396 /**
397 * Function to convert two SWU number characters to an array of x,y integers
398 * @function convert.swu2coord
399 * @param {string} swuCoord - Two SWU number character
400 * @returns {number[]} Array of x,y integers
401 * @example
402 * convert.swu2coord('𝤆𝤆')
403 *
404 * return [500, 500]
405 */
406 const swu2coord = swuCoord => [swu2num(swuCoord.slice(0, 2)), swu2num(swuCoord.slice(2, 4))];
407
408 /**
409 * Function to convert an array of x,y integers to two SWU number characters
410 * @function convert.coord2swu
411 * @param {number[]} coord - Array of x,y integers
412 * @returns {string} Two SWU number character
413 * @example
414 * convert.coord2swu([500, 500])
415 *
416 * return '𝤆𝤆'
417 */
418 const coord2swu = coord => coord.map(num => num2swu(num)).join('');
419
420 /**
421 * Function to convert an FSW coordinate string to an array of x,y integers
422 * @function convert.fsw2coord
423 * @param {string} fswCoord - An FSW coordinate string
424 * @returns {number[]} Array of x,y integers
425 * @example
426 * convert.fsw2coord('500x500')
427 *
428 * return [500, 500]
429 */
430 const fsw2coord = fswCoord => fswCoord.split('x').map(num => parseInt(num));
431
432 /**
433 * Function to convert an array of x,y integers to an FSW coordinate string
434 * @function convert.coord2fsw
435 * @param {number[]} coord - Array of x,y integers
436 * @returns {string} An FSW coordinate string
437 * @example
438 * convert.coord2fsw([500, 500])
439 *
440 * return '500x500'
441 */
442 const coord2fsw = coord => coord.join('x');
443
444 /**
445 * Function to convert an SWU symbol character to a code point on plane 4
446 * @function convert.swu2code
447 * @param {string} swuSym - SWU symbol character
448 * @returns {number} Code point on plane 4
449 * @example
450 * convert.swu2code('񀀁')
451 *
452 * return 0x40001
453 */
454 const swu2code = swuSym => parseInt(swuSym.codePointAt(0));
455
456 /**
457 * Function to convert a code point on plane 4 to an SWU symbol character
458 * @function convert.code2swu
459 * @param {number} code - Code point on plane 4
460 * @returns {string} SWU symbol character
461 * @example
462 * convert.code2swu(0x40001)
463 *
464 * return '񀀁'
465 */
466 const code2swu = code => String.fromCodePoint(code);
467
468 /**
469 * Function to convert an SWU symbol character to a 16-bit ID
470 * @function convert.swu2id
471 * @param {string} swuSym - SWU symbol character
472 * @returns {number} 16-bit ID
473 * @example
474 * convert.swu2id('񀀁')
475 *
476 * return 1
477 */
478 const swu2id = swuSym => swu2code(swuSym) - 0x40000;
479
480 /**
481 * Function to convert a 16-bit ID to an SWU symbol character
482 * @function convert.id2swu
483 * @param {number} id - 16-bit ID
484 * @returns {string} SWU symbol character
485 * @example
486 * convert.id2swu(1)
487 *
488 * return '񀀁'
489 */
490 const id2swu = id => code2swu(id + 0x40000);
491
492 /**
493 * Function to convert an FSW symbol key to a 16-bit ID
494 * @function convert.key2id
495 * @param {string} key - FSW symbol key
496 * @returns {number} 16-bit ID
497 * @example
498 * convert.key2id('S10000')
499 *
500 * return 1
501 */
502 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);
503
504 /**
505 * Function to convert a 16-bit ID to an FSW symbol key
506 * @function convert.id2key
507 * @param {number} id - 16-bit ID
508 * @returns {string} FSW symbol key
509 * @example
510 * convert.id2key(1)
511 *
512 * return 'S10000'
513 */
514 const id2key = id => {
515 const symcode = id - 1;
516 const base = parseInt(symcode / 96);
517 const fill = parseInt((symcode - base * 96) / 16);
518 const rotation = parseInt(symcode - base * 96 - fill * 16);
519 return 'S' + (base + 0x100).toString(16) + fill.toString(16) + rotation.toString(16);
520 };
521
522 /**
523 * Function to convert an SWU symbol character to an FSW symbol key
524 * @function convert.swu2key
525 * @param {string} swuSym - SWU symbol character
526 * @returns {string} FSW symbol key
527 * @example
528 * convert.swu2key('񀀁')
529 *
530 * return 'S10000'
531 */
532 const swu2key = swuSym => {
533 const symcode = swu2code(swuSym) - 0x40001;
534 const base = parseInt(symcode / 96);
535 const fill = parseInt((symcode - base * 96) / 16);
536 const rotation = parseInt(symcode - base * 96 - fill * 16);
537 return 'S' + (base + 0x100).toString(16) + fill.toString(16) + rotation.toString(16);
538 };
539
540 /**
541 * Function to convert an FSW symbol key to an SWU symbol character
542 * @function convert.key2swu
543 * @param {string} key - FSW symbol key
544 * @returns {string} SWU symbol character
545 * @example
546 * convert.key2swu('S10000')
547 *
548 * return '񀀁'
549 */
550 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));
551
552 /**
553 * Function to convert SWU text to FSW text
554 * @function convert.swu2fsw
555 * @param {string} swuText - SWU text
556 * @returns {string} FSW text
557 * @example
558 * convert.swu2fsw('𝠀񀀒񀀚񋚥񋛩𝠃𝤟𝤩񋛩𝣵𝤐񀀒𝤇𝣤񋚥𝤐𝤆񀀚𝣮𝣭')
559 *
560 * return 'AS10011S10019S2e704S2e748M525x535S2e748483x510S10011501x466S2e704510x500S10019476x475'
561 */
562 const swu2fsw = swuText => {
563 if (!swuText) return '';
564 let fsw = swuText.replace(/𝠀/g, "A").replace(/𝠁/g, "B").replace(/𝠂/g, "L").replace(/𝠃/g, "M").replace(/𝠄/g, "R");
565 const syms = fsw.match(new RegExp(re$2.symbol, 'g'));
566 if (syms) {
567 syms.forEach(function (sym) {
568 fsw = fsw.replace(sym, swu2key(sym));
569 });
570 }
571 const coords = fsw.match(new RegExp(re$2.coord, 'g'));
572 if (coords) {
573 coords.forEach(function (coord) {
574 fsw = fsw.replace(coord, swu2coord(coord).join('x'));
575 });
576 }
577 return fsw;
578 };
579
580 /**
581 * Function to convert FSW text to SWU text
582 * @function convert.fsw2swu
583 * @param {string} fswText - FSW text
584 * @returns {string} SWU text
585 * @example
586 * convert.fsw2swu('AS10011S10019S2e704S2e748M525x535S2e748483x510S10011501x466S2e704510x500S10019476x475')
587 *
588 * return '𝠀񀀒񀀚񋚥񋛩𝠃𝤟𝤩񋛩𝣵𝤐񀀒𝤇𝣤񋚥𝤐𝤆񀀚𝣮𝣭'
589 */
590 const fsw2swu = fswText => {
591 if (!fswText) return '';
592 const prefixes = fswText.match(new RegExp(re$4.prefix, 'g'));
593 if (prefixes) {
594 prefixes.forEach(function (prefix) {
595 fswText = fswText.replace(prefix, '𝠀' + prefix.slice(1).match(/.{6}/g).map(key => key2swu(key)).join(''));
596 });
597 }
598 const boxes = fswText.match(new RegExp(re$4.box + re$4.coord, 'g'));
599 if (boxes) {
600 boxes.forEach(function (boxes) {
601 fswText = fswText.replace(boxes, mark2swu(boxes.slice(0, 1)) + coord2swu(fsw2coord(boxes.slice(1, 8))));
602 });
603 }
604 const spatials = fswText.match(new RegExp(re$4.spatial, 'g'));
605 if (spatials) {
606 spatials.forEach(function (spatial) {
607 fswText = fswText.replace(spatial, key2swu(spatial.slice(0, 6)) + coord2swu(fsw2coord(spatial.slice(6, 13))));
608 });
609 }
610 return fswText;
611 };
612
613 /**
614 * Function to convert base or full symid Min to symid Max
615 * @function convert.symidMax
616 * @param {string} symidMin - Symbol ID minimized
617 * @returns {string} Symbol ID maximized
618 * @example
619 * convert.symidMax('101011')
620 *
621 * return '01-01-001-01'
622 * @example
623 * convert.symidMax('101011616')
624 *
625 * return '01-01-001-01-06-16'
626 */
627 const symidMax = symidMin => {
628 if (!/^\d{6}(?:\d{3})?$/.test(symidMin)) {
629 return '';
630 }
631 let max = `0${symidMin.charAt(0)}-${symidMin.charAt(1)}${symidMin.charAt(2)}-0${symidMin.charAt(3)}${symidMin.charAt(4)}-0${symidMin.charAt(5)}`;
632 if (symidMin.length > 6) {
633 max += `-0${symidMin.charAt(6)}-${symidMin.charAt(7)}${symidMin.charAt(8)}`;
634 }
635 return max;
636 };
637
638 /**
639 * Function to convert base or full symid Max to symid Min
640 * @function convert.symidMin
641 * @param {string} symidMax - Symbol ID maximized
642 * @returns {string} Symbol ID minimized
643 * @example
644 * convert.symidMin('01-01-001-01')
645 *
646 * return '101011'
647 * @example
648 * convert.symidMin('01-01-001-01-06-16')
649 *
650 * return '101011616'
651 */
652 const symidMin = symidMax => {
653 const matches = symidMax.match(/^0(\d)-(\d{2})-0(\d{2})-0(\d)(?:-0(\d)-(\d{2}))?$/);
654 if (!matches) {
655 return '';
656 }
657 if (matches[5]) {
658 return matches[1] + matches[2] + matches[3] + matches[4] + matches[5] + matches[6];
659 } else {
660 return matches[1] + matches[2] + matches[3] + matches[4];
661 }
662 };
663
664 /**
665 * Function to convert base or full symid to key
666 * @function convert.symid2key
667 * @param {string} symid - Symbol ID
668 * @returns {string} Symbol key
669 * @example
670 * convert.symid2key('01-01-001-01')
671 *
672 * return 'S100'
673 * @example
674 * convert.symid2key('01-01-001-01-06-16')
675 *
676 * return 'S1005f'
677 */
678 const symid2key = symid => {
679 const matches = symid.match(/^0(\d)-(\d{2})-0(\d{2})-0(\d)(?:-0(\d)-(\d{2}))?$/);
680 if (!matches) {
681 return '';
682 }
683 const symidMin = matches[1] + matches[2] + matches[3] + matches[4];
684 const i = symidArr.indexOf(symidMin);
685 if (i === -1) {
686 return '';
687 }
688 if (matches[5]) {
689 return 'S' + (256 + i).toString(16) + (parseInt(matches[5], 10) - 1) + (parseInt(matches[6], 10) - 1).toString(16);
690 } else {
691 return 'S' + (256 + i).toString(16);
692 }
693 };
694
695 /**
696 * Function to convert base or full key to symid
697 * @function convert.key2symid
698 * @param {string} key - Symbol key
699 * @returns {string} Symbol ID
700 * @example
701 * convert.key2symid('S100')
702 *
703 * return '01-01-001-01'
704 * @example
705 * convert.key2symid('S1005f')
706 *
707 * return '01-01-001-01-06-16'
708 */
709 const key2symid = key => {
710 const matches = key.match(/^S([1-3][0-9a-f]{2})(?:([0-5])([0-9a-f]))?$/);
711 if (!matches) {
712 return '';
713 }
714 const i = parseInt(matches[1], 16) - 256;
715 if (i >= symidArr.length) {
716 return '';
717 }
718 if (matches[3]) {
719 return symidMax(symidArr[i]) + '-0' + (1 + parseInt(matches[2])) + '-' + (parseInt(matches[3], 16) + 1).toString().padStart(2, '0');
720 } else {
721 return symidMax(symidArr[i]);
722 }
723 };
724
725 var index$4 = /*#__PURE__*/Object.freeze({
726 __proto__: null,
727 swu2mark: swu2mark,
728 mark2swu: mark2swu,
729 swu2num: swu2num,
730 num2swu: num2swu,
731 swu2coord: swu2coord,
732 coord2swu: coord2swu,
733 fsw2coord: fsw2coord,
734 coord2fsw: coord2fsw,
735 swu2code: swu2code,
736 code2swu: code2swu,
737 swu2id: swu2id,
738 id2swu: id2swu,
739 key2id: key2id,
740 id2key: id2key,
741 swu2key: swu2key,
742 key2swu: key2swu,
743 swu2fsw: swu2fsw,
744 fsw2swu: fsw2swu,
745 symidArr: symidArr,
746 symidMax: symidMax,
747 symidMin: symidMin,
748 symid2key: symid2key,
749 key2symid: key2symid
750 });
751
752 const parse$3 = {
753 /**
754 * Function to parse an fsw symbol with optional coordinate and style string
755 * @function fsw.parse.symbol
756 * @param {string} fswSym - an fsw symbol
757 * @returns {SymbolObject} elements of fsw symbol
758 * @example
759 * fsw.parse.symbol('S10000500x500-C')
760 *
761 * return {
762 * 'symbol': 'S10000',
763 * 'coord': [500, 500],
764 * 'style': '-C'
765 * }
766 */
767 symbol: fswSym => {
768 const regex = `^(${re$4.symbol})(${re$4.coord})?(${re$3.full})?`;
769 const symbol = typeof fswSym === 'string' ? fswSym.match(new RegExp(regex)) : undefined;
770 return {
771 'symbol': symbol ? symbol[1] : undefined,
772 'coord': symbol && symbol[2] ? fsw2coord(symbol[2]) : undefined,
773 'style': symbol ? symbol[3] : undefined
774 };
775 },
776 /**
777 * Function to parse an fsw sign with style string
778 * @function fsw.parse.sign
779 * @param {string} fswSign - an fsw sign
780 * @returns { SignObject } elements of fsw sign
781 * @example
782 * fsw.parse.sign('AS10011S10019S2e704S2e748M525x535S2e748483x510S10011501x466S2e704510x500S10019476x475-C')
783 *
784 * return {
785 * sequence: ['S10011', 'S10019', 'S2e704', 'S2e748'],
786 * box: 'M',
787 * max: [525, 535],
788 * spatials: [
789 * {
790 * symbol: 'S2e748',
791 * coord: [483, 510]
792 * },
793 * {
794 * symbol: 'S10011',
795 * coord: [501, 466]
796 * },
797 * {
798 * symbol: 'S2e704',
799 * coord: [510, 500]
800 * },
801 * {
802 * symbol: 'S10019',
803 * coord: [476, 475]
804 * }
805 * ],
806 * style: '-C'
807 * }
808 */
809 sign: fswSign => {
810 const regex = `^(${re$4.prefix})?(${re$4.signbox})(${re$3.full})?`;
811 const sign = typeof fswSign === 'string' ? fswSign.match(new RegExp(regex)) : undefined;
812 if (sign) {
813 return {
814 'sequence': sign[1] ? sign[1].slice(1).match(/.{6}/g) : undefined,
815 'box': sign[2][0],
816 'max': fsw2coord(sign[2].slice(1, 8)),
817 'spatials': sign[2].length < 9 ? undefined : sign[2].slice(8).match(/(.{13})/g).map(m => {
818 return {
819 symbol: m.slice(0, 6),
820 coord: [parseInt(m.slice(6, 9)), parseInt(m.slice(10, 13))]
821 };
822 }),
823 'style': sign[3]
824 };
825 } else {
826 return {};
827 }
828 },
829 /**
830 * Function to parse an fsw text
831 * @function fsw.parse.text
832 * @param {string} fswText - an fsw text
833 * @returns {string[]} fsw signs and punctuations
834 * @example
835 * fsw.parse.text('AS14c20S27106M518x529S14c20481x471S27106503x489 AS18701S1870aS2e734S20500M518x533S1870a489x515S18701482x490S20500508x496S2e734500x468 S38800464x496')
836 *
837 * return [
838 * 'AS14c20S27106M518x529S14c20481x471S27106503x489',
839 * 'AS18701S1870aS2e734S20500M518x533S1870a489x515S18701482x490S20500508x496S2e734500x468',
840 * 'S38800464x496'
841 * ]
842 */
843 text: fswText => {
844 if (typeof fswText !== 'string') return [];
845 const regex = `(${re$4.sign}(${re$3.full})?|${re$4.spatial}(${re$3.full})?)`;
846 const matches = fswText.match(new RegExp(regex, 'g'));
847 return matches ? [...matches] : [];
848 }
849 };
850
851 const compose$3 = {
852 /**
853 * Function to compose an fsw symbol with optional coordinate and style string
854 * @function fsw.compose.symbol
855 * @param {SymbolObject} fswSymObject - an fsw symbol object
856 * @returns {string} an fsw symbol string
857 * @example
858 * fsw.compose.symbol({
859 * 'symbol': 'S10000',
860 * 'coord': [480, 480],
861 * 'style': '-C'
862 * })
863 *
864 * return 'S10000480x480-C'
865 */
866 symbol: fswSymObject => {
867 if (typeof fswSymObject.symbol === 'string') {
868 const symbol = (fswSymObject.symbol.match(re$4.symbol) || [''])[0];
869 if (symbol) {
870 const x = (fswSymObject.coord && fswSymObject.coord[0] || '').toString();
871 const y = (fswSymObject.coord && fswSymObject.coord[1] || '').toString();
872 const coord = ((x + 'x' + y).match(re$4.coord) || [''])[0] || '';
873 const styleStr = typeof fswSymObject.style === 'string' && (fswSymObject.style.match(re$3.full) || [''])[0] || '';
874 return symbol + coord + styleStr;
875 }
876 }
877 return undefined;
878 },
879 /**
880 * Function to compose an fsw sign with style string
881 * @function fsw.compose.sign
882 * @param {SignObject} fswSignObject - an fsw symbol object
883 * @returns {string} an fsw sign string
884 * @example
885 * fsw.compose.sign({
886 * sequence: ['S10011', 'S10019', 'S2e704', 'S2e748'],
887 * box: 'M',
888 * max: [525, 535],
889 * spatials: [
890 * {
891 * symbol: 'S2e748',
892 * coord: [483, 510]
893 * },
894 * {
895 * symbol: 'S10011',
896 * coord: [501, 466]
897 * },
898 * {
899 * symbol: 'S2e704',
900 * coord: [510, 500]
901 * },
902 * {
903 * symbol: 'S10019',
904 * coord: [476, 475]
905 * }
906 * ],
907 * style: '-C'
908 * })
909 *
910 * return 'AS10011S10019S2e704S2e748M525x535S2e748483x510S10011501x466S2e704510x500S10019476x475-C'
911 */
912 sign: fswSignObject => {
913 let box = typeof fswSignObject.box !== 'string' ? 'M' : (fswSignObject.box + 'M').match(re$4.box);
914 const x = (fswSignObject.max && fswSignObject.max[0] || '').toString();
915 const y = (fswSignObject.max && fswSignObject.max[1] || '').toString();
916 const max = ((x + 'x' + y).match(re$4.coord) || [''])[0] || '';
917 if (!max) return undefined;
918 let prefix = '';
919 if (fswSignObject.sequence && Array.isArray(fswSignObject.sequence)) {
920 prefix = fswSignObject.sequence.map(key => (key.match(re$4.symbol) || [''])[0]).join('');
921 prefix = prefix ? 'A' + prefix : '';
922 }
923 let signbox = '';
924 if (fswSignObject.spatials && Array.isArray(fswSignObject.spatials)) {
925 signbox = fswSignObject.spatials.map(spatial => {
926 if (typeof spatial.symbol === 'string') {
927 const symbol = (spatial.symbol.match(re$4.symbol) || [''])[0];
928 if (symbol) {
929 const x = (spatial.coord && spatial.coord[0] || '').toString();
930 const y = (spatial.coord && spatial.coord[1] || '').toString();
931 const coord = ((x + 'x' + y).match(re$4.coord) || [''])[0] || '';
932 if (coord) {
933 return symbol + coord;
934 }
935 }
936 }
937 return '';
938 }).join('');
939 }
940 const styleStr = typeof fswSignObject.style === 'string' && (fswSignObject.style.match(re$3.full) || [''])[0] || '';
941 return prefix + box + max + signbox + styleStr;
942 }
943 };
944
945 /**
946 * Function to gather sizing information about an fsw sign or symbol
947 * @function fsw.info
948 * @param {string} fsw - an fsw sign or symbol
949 * @returns {SegmentInfo} information about the fsw string
950 * @example
951 * fsw.info('AS14c20S27106L518x529S14c20481x471S27106503x489-P10Z2')
952 *
953 * return {
954 * minX: 481,
955 * minY: 471,
956 * width: 37,
957 * height: 58,
958 * lane: -1,
959 * padding: 10,
960 * segment: 'sign',
961 * zoom: 2
962 * }
963 */
964 const info$1 = fsw => {
965 let lanes = {
966 "B": 0,
967 "L": -1,
968 "M": 0,
969 "R": 1
970 };
971 let parsed = parse$3.sign(fsw);
972 let width, height, segment, x1, x2, y1, y2, lane;
973 if (parsed.spatials) {
974 x1 = Math.min(...parsed.spatials.map(spatial => spatial.coord[0]));
975 x2 = parsed.max[0];
976 width = x2 - x1;
977 y1 = Math.min(...parsed.spatials.map(spatial => spatial.coord[1]));
978 y2 = parsed.max[1];
979 height = y2 - y1;
980 segment = 'sign';
981 lane = parsed.box;
982 } else {
983 parsed = parse$3.symbol(fsw);
984 lane = "M";
985 if (parsed.coord) {
986 x1 = parsed.coord[0];
987 width = (500 - x1) * 2;
988 y1 = parsed.coord[1];
989 height = (500 - y1) * 2;
990 segment = 'symbol';
991 } else {
992 x1 = 490;
993 width = 20;
994 y1 = 490;
995 height = 20;
996 segment = 'none';
997 }
998 }
999 let style = parse$4(parsed.style);
1000 let zoom = style.zoom || 1;
1001 let padding = style.padding || 0;
1002 return {
1003 minX: x1,
1004 minY: y1,
1005 width: width,
1006 height: height,
1007 segment: segment,
1008 lane: lanes[lane],
1009 padding: padding,
1010 zoom: zoom
1011 };
1012 };
1013
1014 const columnDefaults$1 = {
1015 'height': 500,
1016 'width': 150,
1017 'offset': 50,
1018 'pad': 20,
1019 'margin': 5,
1020 'dynamic': false,
1021 'background': undefined,
1022 'punctuation': {
1023 'spacing': true,
1024 'pad': 30,
1025 'pull': true
1026 },
1027 'style': {
1028 'detail': ['black', 'white'],
1029 'zoom': 1
1030 }
1031 };
1032
1033 /**
1034 * Function to an object of column options with default values
1035 *
1036 * @function fsw.columnDefaultsMerge
1037 * @param {ColumnOptions} options - object of column options
1038 * @returns {ColumnOptions} object of column options merged with column defaults
1039 * @example
1040 * fsw.columnDefaultsMerge({height: 500,width:150})
1041 *
1042 * return {
1043 * "height": 500,
1044 * "width": 150,
1045 * "offset": 50,
1046 * "pad": 20,
1047 * "margin": 5,
1048 * "dynamic": false,
1049 * "punctuation": {
1050 * "spacing": true,
1051 * "pad": 30,
1052 * "pull": true
1053 * },
1054 * "style": {
1055 * "detail": [
1056 * "black",
1057 * "white"
1058 * ],
1059 * "zoom": 1
1060 * }
1061 * }
1062 */
1063 const columnDefaultsMerge$1 = options => {
1064 if (typeof options !== 'object') options = {};
1065 return {
1066 ...columnDefaults$1,
1067 ...options,
1068 punctuation: {
1069 ...columnDefaults$1.punctuation,
1070 ...options.punctuation
1071 },
1072 style: {
1073 ...columnDefaults$1.style,
1074 ...options.style
1075 }
1076 };
1077 };
1078
1079 /**
1080 * Function to transform an FSW text to an array of columns
1081 *
1082 * @function fsw.columns
1083 * @param {string} fswText - FSW text of signs and punctuation
1084 * @param {ColumnOptions} options - object of column options
1085 * @returns {{options:ColumnOptions,widths:number[],columns:ColumnData}} object of column options, widths array, and column data
1086 * @example
1087 * fsw.columns('AS14c20S27106M518x529S14c20481x471S27106503x489 AS18701S1870aS2e734S20500M518x533S1870a489x515S18701482x490S20500508x496S2e734500x468 S38800464x496', {height: 500,width:150})
1088 *
1089 * return {
1090 * "options": {
1091 * "height": 500,
1092 * "width": 150,
1093 * "offset": 50,
1094 * "pad": 20,
1095 * "margin": 5,
1096 * "dynamic": false,
1097 * "punctuation": {
1098 * "spacing": true,
1099 * "pad": 30,
1100 * "pull": true
1101 * },
1102 * "style": {
1103 * "detail": [
1104 * "black",
1105 * "white"
1106 * ],
1107 * "zoom": 1
1108 * }
1109 * },
1110 * "widths": [
1111 * 150
1112 * ],
1113 * "columns": [
1114 * [
1115 * {
1116 * "x": 56,
1117 * "y": 20,
1118 * "minX": 481,
1119 * "minY": 471,
1120 * "width": 37,
1121 * "height": 58,
1122 * "lane": 0,
1123 * "padding": 0,
1124 * "segment": "sign",
1125 * "text": "AS14c20S27106M518x529S14c20481x471S27106503x489",
1126 * "zoom": 1
1127 * },
1128 * {
1129 * "x": 57,
1130 * "y": 118,
1131 * "minX": 482,
1132 * "minY": 468,
1133 * "width": 36,
1134 * "height": 65,
1135 * "lane": 0,
1136 * "padding": 0,
1137 * "segment": "sign",
1138 * "text": "AS18701S1870aS2e734S20500M518x533S1870a489x515S18701482x490S20500508x496S2e734500x468",
1139 * "zoom": 1
1140 * },
1141 * {
1142 * "x": 39,
1143 * "y": 203,
1144 * "minX": 464,
1145 * "minY": 496,
1146 * "width": 72,
1147 * "height": 8,
1148 * "lane": 0,
1149 * "padding": 0,
1150 * "segment": "symbol",
1151 * "text": "S38800464x496",
1152 * "zoom": 1
1153 * }
1154 * ]
1155 * ]
1156 * }
1157 */
1158 const columns$1 = (fswText, options) => {
1159 if (typeof fswText !== 'string') return {};
1160 const values = columnDefaultsMerge$1(options);
1161 let input = parse$3.text(fswText);
1162 let cursor = 0;
1163 let cols = [];
1164 let col = [];
1165 let plus = 0;
1166 let center = parseInt(values.width / 2);
1167 let maxHeight = values.height - values.margin;
1168 let pullable = true;
1169 let finalize = false;
1170 for (let val of input) {
1171 let informed = info$1(val);
1172 cursor += plus;
1173 if (values.punctuation.spacing) {
1174 cursor += informed.segment == 'sign' ? values.pad : 0;
1175 } else {
1176 cursor += values.pad;
1177 }
1178 finalize = cursor + informed.height > maxHeight;
1179 if (finalize && informed.segment == 'symbol' && values.punctuation.pull && pullable) {
1180 finalize = false;
1181 pullable = false;
1182 }
1183 if (col.length == 0) {
1184 finalize = false;
1185 }
1186 if (finalize) {
1187 cursor = values.pad;
1188 cols.push(col);
1189 col = [];
1190 pullable = true;
1191 }
1192 col.push(Object.assign(informed, {
1193 x: center + values.offset * informed.lane - (500 - informed.minX) * informed.zoom * values.style.zoom,
1194 y: cursor,
1195 text: val
1196 }));
1197 cursor += informed.height * informed.zoom * values.style.zoom;
1198 if (values.punctuation.spacing) {
1199 plus = informed.segment == 'sign' ? values.pad : values.punctuation.pad;
1200 } else {
1201 plus = values.pad;
1202 }
1203 }
1204 if (col.length) {
1205 cols.push(col);
1206 }
1207
1208 // over height issue when pulling punctuation
1209 if (values.punctuation.pull) {
1210 for (let col of cols) {
1211 let last = col[col.length - 1];
1212 let diff = last.y + last.height - (values.height - values.margin);
1213 if (diff > 0) {
1214 let adj = parseInt(diff / col.length) + 1;
1215 for (let i in col) {
1216 col[i].y -= adj * i + adj;
1217 }
1218 }
1219 }
1220 }
1221
1222 // contract, expand, adjust
1223 let widths = [];
1224 for (let col of cols) {
1225 let min = [center - values.offset - values.pad];
1226 let max = [center + values.offset + values.pad];
1227 for (let item of col) {
1228 min.push(item.x - values.pad);
1229 max.push(item.x + item.width + values.pad);
1230 }
1231 min = Math.min(...min);
1232 max = Math.max(...max);
1233 let width = values.width;
1234 let adj = 0;
1235 if (!values.dynamic) {
1236 adj = center - parseInt((min + max) / 2);
1237 } else {
1238 width = max - min;
1239 adj = -min;
1240 }
1241 for (let item of col) {
1242 item.x += adj;
1243 }
1244 widths.push(width);
1245 }
1246 return {
1247 'options': values,
1248 'widths': widths,
1249 'columns': cols
1250 };
1251 };
1252
1253 /**
1254 * Array of numbers for kinds of symbols: writing, location, and punctuation.
1255 * @alias fsw.kind
1256 * @type {number[]}
1257 */
1258 const kind$1 = [0x100, 0x37f, 0x387];
1259
1260 /**
1261 * Array of numbers for categories of symbols: hand, movement, dynamics, head, trunk & limb, location, and punctuation.
1262 * @alias fsw.category
1263 * @type {number[]}
1264 */
1265 const category$1 = [0x100, 0x205, 0x2f7, 0x2ff, 0x36d, 0x37f, 0x387];
1266
1267 /**
1268 * Array of numbers for the 30 symbol groups.
1269 * @alias fsw.group
1270 * @type {number[]}
1271 */
1272 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];
1273
1274 /**
1275 * Object of symbol ranges with starting and ending numbers.
1276 *
1277 * { all, writing, hand, movement, dynamic, head, hcenter, vcenter, trunk, limb, location, punctuation }
1278 * @alias fsw.ranges
1279 * @type {object}
1280 */
1281 const ranges$1 = {
1282 'all': [0x100, 0x38b],
1283 'writing': [0x100, 0x37e],
1284 'hand': [0x100, 0x204],
1285 'movement': [0x205, 0x2f6],
1286 'dynamic': [0x2f7, 0x2fe],
1287 'head': [0x2ff, 0x36c],
1288 'hcenter': [0x2ff, 0x36c],
1289 'vcenter': [0x2ff, 0x375],
1290 'trunk': [0x36d, 0x375],
1291 'limb': [0x376, 0x37e],
1292 'location': [0x37f, 0x386],
1293 'punctuation': [0x387, 0x38b]
1294 };
1295
1296 /**
1297 * Function to test if symbol is of a certain type.
1298 * @function fsw.isType
1299 * @param {string} key - an FSW symbol key
1300 * @param {string} type - the name of a symbol range
1301 * @returns {boolean} is symbol of specified type
1302 * @example
1303 * fsw.isType('S10000', 'hand')
1304 *
1305 * return true
1306 */
1307 const isType$1 = (key, type) => {
1308 const parsed = parse$3.symbol(key);
1309 if (parsed.symbol) {
1310 const dec = parseInt(parsed.symbol.slice(1, 4), 16);
1311 const range = ranges$1[type];
1312 if (range) {
1313 return range[0] <= dec && range[1] >= dec;
1314 }
1315 }
1316 return false;
1317 };
1318
1319 /**
1320 * Array of colors associated with the seven symbol categories.
1321 * @alias fsw.colors
1322 * @type {string[]}
1323 */
1324 const colors$1 = ['#0000CC', '#CC0000', '#FF0099', '#006600', '#000000', '#884411', '#FF9900'];
1325
1326 /**
1327 * Function that returns the standardized color for a symbol.
1328 * @function fsw.colorize
1329 * @param {string} key - an FSW symbol key
1330 * @returns {string} name of standardized color for symbol
1331 * @example
1332 * fsw.colorize('S10000')
1333 *
1334 * return '#0000CC'
1335 */
1336 const colorize$1 = key => {
1337 const parsed = parse$3.symbol(key);
1338 let color = '#000000';
1339 if (parsed.symbol) {
1340 const dec = parseInt(parsed.symbol.slice(1, 4), 16);
1341 const index = category$1.findIndex(val => val > dec);
1342 color = colors$1[index < 0 ? 6 : index - 1];
1343 }
1344 return color;
1345 };
1346
1347 /** The fsw module contains functions for handling Formal SignWriting in ASCII (FSW) characters.
1348 * [FSW characters definition](https://tools.ietf.org/id/draft-slevinski-formal-signwriting-09.html#name-formal-signwriting-in-ascii)
1349 * @module fsw
1350 */
1351
1352 var index$3 = /*#__PURE__*/Object.freeze({
1353 __proto__: null,
1354 re: re$4,
1355 parse: parse$3,
1356 compose: compose$3,
1357 info: info$1,
1358 columnDefaults: columnDefaults$1,
1359 columnDefaultsMerge: columnDefaultsMerge$1,
1360 columns: columns$1,
1361 kind: kind$1,
1362 category: category$1,
1363 group: group$1,
1364 ranges: ranges$1,
1365 isType: isType$1,
1366 colors: colors$1,
1367 colorize: colorize$1
1368 });
1369
1370 /**
1371 * Object of regular expressions for FSW query strings
1372 *
1373 * @alias fswquery.re
1374 * @type {object}
1375 * @property {string} base - FSW symbol base with neither fill or rotation
1376 * @property {string} coord - FSW coordinate of X and Y values separated by 'x'
1377 * @property {string} var - variance string for searching sign box
1378 * @property {string} symbol - FSW symbol key starting with 'S'
1379 * @property {string} range - FSW range starting with 'R'
1380 * @property {string} item - FSW symbol or range query string
1381 * @property {string} list - several FSW symbols and FSW ranges as a logical OR for searching
1382 * @property {string} prefix - a sequential list of FSW symbol keys starting with 'A'
1383 * @property {string} signbox - several groups of FSW lists, each group having a coordinate
1384 * @property {string} full - a query string to search prefix in order and the signbox with variance
1385 */
1386 let re$1 = {
1387 'base': '[123][0-9a-f]{2}',
1388 'coord': '(?:[0-9]{3}x[0-9]{3})?',
1389 'var': 'V[0-9]+'
1390 };
1391 re$1.symbol = `S${re$1.base}[0-5u][0-9a-fu]`;
1392 re$1.range = `R${re$1.base}t${re$1.base}`;
1393 re$1.item = `(?:${re$1.symbol}|${re$1.range})`;
1394 re$1.list = `${re$1.item}(?:o${re$1.item})*`;
1395 re$1.prefix = `(?:A(?:${re$1.list})+)?T`;
1396 re$1.signbox = `(?:${re$1.list}${re$1.coord})*`;
1397 re$1.full = `Q(${re$1.prefix})?(${re$1.signbox})?(${re$1.var})?(-?)`;
1398
1399 const parsePrefix$1 = text => {
1400 return {
1401 required: true,
1402 parts: text == 'T' ? undefined : text.match(new RegExp(`${re$1.list}`, 'g')).map(part => {
1403 if (part.includes('o')) {
1404 return ['or'].concat(part.match(new RegExp(`(${re$1.item})`, 'g')).map(part => part[0] == 'S' ? part : part.slice(1).split('t')));
1405 } else {
1406 return part[0] == 'S' ? part : part.slice(1).split('t');
1407 }
1408 })
1409 };
1410 };
1411 const parseSignbox$1 = text => {
1412 return text.match(new RegExp(`(${re$1.list}${re$1.coord})`, 'g')).map(part => {
1413 let coord, front;
1414 if (part.includes('x')) {
1415 coord = fsw2coord(part.slice(-7));
1416 front = part.slice(0, -7);
1417 } else {
1418 front = part;
1419 }
1420 if (front.includes('o')) {
1421 return {
1422 or: front.split('o').map(part => {
1423 if (part.includes('S')) {
1424 return part;
1425 } else {
1426 return part.slice(1).split('t');
1427 }
1428 }),
1429 coord,
1430 coord
1431 };
1432 } else if (front.includes('S')) {
1433 return {
1434 symbol: front,
1435 coord: coord
1436 };
1437 } else {
1438 return {
1439 range: front.slice(1).split('t'),
1440 coord: coord
1441 };
1442 }
1443 });
1444 };
1445
1446 /**
1447 * Function to parse FSW query string to object
1448 * @function fswquery.parse
1449 * @param {string} fswQueryString - an FSW query string
1450 * @returns {QueryObject} elements of a of query string identified by regular expression
1451 * @example
1452 * fswquery.parse('QAS10000S10500oS20500oR2fft304TS100uuR205t206oS207uu510x510V5-')
1453 *
1454 * return {
1455 * "query": true,
1456 * "prefix": {
1457 * "required": true,
1458 * "parts": [
1459 * "S10000",
1460 * [
1461 * "or",
1462 * "S10500",
1463 * "S20500",
1464 * [
1465 * "2ff",
1466 * "304"
1467 * ]
1468 * ]
1469 * ]
1470 * },
1471 * "signbox": [
1472 * {
1473 * "symbol": "S100uu"
1474 * },
1475 * {
1476 * "or": [
1477 * [
1478 * "205",
1479 * "206"
1480 * ],
1481 * "S207uu"
1482 * ],
1483 * "coord": [
1484 * 510,
1485 * 510
1486 * ]
1487 * }
1488 * ],
1489 * "variance": 5,
1490 * "style": true
1491 * }
1492 */
1493 const parse$2 = fswQueryString => {
1494 const query = typeof fswQueryString === 'string' ? fswQueryString.match(new RegExp(`^${re$1.full}`)) : undefined;
1495 return {
1496 'query': query ? true : undefined,
1497 'prefix': query && query[1] ? parsePrefix$1(query[1]) : undefined,
1498 'signbox': query && query[2] ? parseSignbox$1(query[2]) : undefined,
1499 'variance': query && query[3] ? parseInt(query[3].slice(1)) : undefined,
1500 'style': query && query[4] ? true : undefined
1501 };
1502 };
1503
1504 /**
1505 * Function to compose FSW query string from object
1506 * @function fswquery.compose
1507 * @param {QueryObject} fswQueryObject - an object of query options
1508 * @returns {string} FSW query string
1509 * @example
1510 * fswquery.compose({
1511 * query: true,
1512 * prefix: {
1513 * required: true,
1514 * parts: [
1515 * 'S10000',
1516 * ['100', '204'],
1517 * 'S20500'
1518 * ]
1519 * },
1520 * signbox: [
1521 * { symbol: 'S20000' },
1522 * {
1523 * range: ['100', '105'],
1524 * coord: [500, 500]
1525 * }
1526 * ],
1527 * variance: 5,
1528 * style: true
1529 * })
1530 *
1531 * return 'QAS10000R100t204S20500TS20000R100t105500x500V5-'
1532 */
1533 const compose$2 = fswQueryObject => {
1534 if (!fswQueryObject || !fswQueryObject.query) {
1535 return undefined;
1536 }
1537 let query = 'Q';
1538 if (fswQueryObject.prefix && fswQueryObject.prefix.required) {
1539 if (Array.isArray(fswQueryObject.prefix.parts)) {
1540 query += 'A';
1541 query += fswQueryObject.prefix.parts.map(part => {
1542 if (typeof part === 'string') {
1543 return part;
1544 } else {
1545 if (Array.isArray(part) && part.length == 2) {
1546 return `R${part[0]}t${part[1]}`;
1547 } else if (Array.isArray(part) && part.length > 2 && part[0] == 'or') {
1548 part.shift();
1549 return part.map(part => {
1550 if (typeof part === 'string') {
1551 return part;
1552 } else {
1553 if (Array.isArray(part) && part.length == 2) {
1554 return `R${part[0]}t${part[1]}`;
1555 }
1556 }
1557 }).join('o');
1558 }
1559 }
1560 }).join('');
1561 }
1562 query += 'T';
1563 }
1564 if (Array.isArray(fswQueryObject.signbox)) {
1565 query += fswQueryObject.signbox.map(part => {
1566 let out;
1567 if (part.or) {
1568 out = part.or.map(item => {
1569 if (typeof item === 'string') {
1570 return item;
1571 } else {
1572 if (Array.isArray(item) && item.length == 2) {
1573 return `R${item[0]}t${item[1]}`;
1574 }
1575 }
1576 }).join('o');
1577 } else if (part.symbol) {
1578 out = part.symbol;
1579 } else {
1580 if (part.range && Array.isArray(part.range) && part.range.length == 2) {
1581 out = `R${part.range[0]}t${part.range[1]}`;
1582 }
1583 }
1584 return out + (Array.isArray(part.coord) && part.coord.length == 2 ? part.coord.join('x') : '');
1585 }).join('');
1586 }
1587 query += fswQueryObject.style ? '-' : '';
1588 query = query.match(new RegExp(`^${re$1.full}`))[0];
1589 return query;
1590 };
1591
1592 /**
1593 * Function to convert an FSW sign to a query string
1594 *
1595 * For the flags parameter, use one or more of the following.
1596 * - A: exact symbol in temporal prefix
1597 * - a: general symbol in temporal prefix
1598 * - S: exact symbol in spatial signbox
1599 * - s: general symbol in spatial signbox
1600 * - L: spatial signbox symbol at location
1601 * @function fswquery.fsw2query
1602 * @param {string} fswSign - FSW sign
1603 * @param {string} flags - flags for query string creation
1604 * @returns {string} FSW query string
1605 * @example
1606 * fswquery.fsw2query('AS10011S10019S2e704S2e748M525x535S2e748483x510S10011501x466S2e704510x500S10019476x475', 'ASL')
1607 *
1608 * return 'QAS10011S10019S2e704S2e748TS2e748483x510S10011501x466S2e704510x500S10019476x475'
1609 */
1610 const fsw2query = (fswSign, flags) => {
1611 let query = '';
1612 const parsed = parse$3.sign(fswSign);
1613 if (parsed.box) {
1614 const A_flag = flags.indexOf('A') > -1;
1615 const a_flag = flags.indexOf('a') > -1;
1616 const S_flag = flags.indexOf('S') > -1;
1617 const s_flag = flags.indexOf('s') > -1;
1618 const L_flag = flags.indexOf('L') > -1;
1619 if (A_flag || a_flag || S_flag || s_flag) {
1620 if ((A_flag || a_flag) && parsed.sequence) {
1621 query += 'A';
1622 query += parsed.sequence.map(sym => sym.slice(0, 4) + (a_flag ? 'uu' : sym.slice(4, 6))).join('');
1623 query += 'T';
1624 }
1625 if ((S_flag || s_flag) && parsed.spatials) {
1626 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('');
1627 }
1628 }
1629 return query ? "Q" + query : undefined;
1630 } else {
1631 return undefined;
1632 }
1633 };
1634
1635 //needs rewritten, but it works
1636 /**
1637 * Function to transform a range to a regular expression
1638 * @function fswquery.range
1639 * @param {(number|string)} min - either a decimal number or hexidecimal string
1640 * @param {(number|string)} max - either a decimal number or hexidecimal string
1641 * @param {boolean?} hex - if true, the regular expression will match a hexidecimal range
1642 * @returns {string} a regular expression that matches a range
1643 * @example
1644 * fswquery.range(500,750)
1645 *
1646 * return '(([56][0-9][0-9])|(7[0-4][0-9])|(750))'
1647 * @example
1648 * fswquery.range('100','10e',true)
1649 *
1650 * return '10[0-9a-e]'
1651 */
1652 const range$1 = (min, max, hex) => {
1653 let pattern;
1654 let re;
1655 let diff;
1656 let tmax;
1657 let cnt;
1658 let minV;
1659 let maxV;
1660 if (!hex) {
1661 hex = '';
1662 }
1663 min = ("000" + min).slice(-3);
1664 max = '' + max;
1665 pattern = '';
1666 if (min === max) {
1667 return min;
1668 }
1669
1670 //ending pattern will be series of connected OR ranges
1671 re = [];
1672
1673 //first pattern+ 10's don't match and the min 1's are not zero
1674 //odd number to 9
1675 if (!(min[0] == max[0] && min[1] == max[1])) {
1676 if (min[2] != '0') {
1677 pattern = min[0] + min[1];
1678 if (hex) {
1679 //switch for dex
1680 switch (min[2]) {
1681 case "f":
1682 pattern += 'f';
1683 break;
1684 case "e":
1685 pattern += '[ef]';
1686 break;
1687 case "d":
1688 case "c":
1689 case "b":
1690 case "a":
1691 pattern += '[' + min[2] + '-f]';
1692 break;
1693 default:
1694 switch (min[2]) {
1695 case "9":
1696 pattern += '[9a-f]';
1697 break;
1698 case "8":
1699 pattern += '[89a-f]';
1700 break;
1701 default:
1702 pattern += '[' + min[2] + '-9a-f]';
1703 break;
1704 }
1705 break;
1706 }
1707 diff = 15 - parseInt(min[2], 16) + 1;
1708 min = '' + (parseInt(min, 16) + diff).toString(16);
1709 re.push(pattern);
1710 } else {
1711 //switch for dex
1712 switch (min[2]) {
1713 case "9":
1714 pattern += '9';
1715 break;
1716 case "8":
1717 pattern += '[89]';
1718 break;
1719 default:
1720 pattern += '[' + min[2] + '-9]';
1721 break;
1722 }
1723 diff = 9 - min[2] + 1;
1724 min = '' + (min * 1 + diff);
1725 re.push(pattern);
1726 }
1727 }
1728 }
1729 pattern = '';
1730
1731 //if hundreds are different, get odd to 99 or ff
1732 if (min[0] != max[0]) {
1733 if (min[1] != '0') {
1734 if (hex) {
1735 //scrape to ff
1736 pattern = min[0];
1737 switch (min[1]) {
1738 case "f":
1739 pattern += 'f';
1740 break;
1741 case "e":
1742 pattern += '[ef]';
1743 break;
1744 case "d":
1745 case "c":
1746 case "b":
1747 case "a":
1748 pattern += '[' + min[1] + '-f]';
1749 break;
1750 case "9":
1751 pattern += '[9a-f]';
1752 break;
1753 case "8":
1754 pattern += '[89a-f]';
1755 break;
1756 default:
1757 pattern += '[' + min[1] + '-9a-f]';
1758 break;
1759 }
1760 pattern += '[0-9a-f]';
1761 diff = 15 - parseInt(min[1], 16) + 1;
1762 min = '' + (parseInt(min, 16) + diff * 16).toString(16);
1763 re.push(pattern);
1764 } else {
1765 //scrape to 99
1766 pattern = min[0];
1767 diff = 9 - min[1] + 1;
1768 switch (min[1]) {
1769 case "9":
1770 pattern += '9';
1771 break;
1772 case "8":
1773 pattern += '[89]';
1774 break;
1775 default:
1776 pattern += '[' + min[1] + '-9]';
1777 break;
1778 }
1779 pattern += '[0-9]';
1780 diff = 9 - min[1] + 1;
1781 min = '' + (min * 1 + diff * 10);
1782 re.push(pattern);
1783 }
1784 }
1785 }
1786 pattern = '';
1787
1788 //if hundreds are different, get to same
1789 if (min[0] != max[0]) {
1790 if (hex) {
1791 diff = parseInt(max[0], 16) - parseInt(min[0], 16);
1792 tmax = (parseInt(min[0], 16) + diff - 1).toString(16);
1793 switch (diff) {
1794 case 1:
1795 pattern = min[0];
1796 break;
1797 case 2:
1798 pattern = '[' + min[0] + tmax + ']';
1799 break;
1800 default:
1801 if (parseInt(min[0], 16) > 9) {
1802 minV = 'h';
1803 } else {
1804 minV = 'd';
1805 }
1806 if (parseInt(tmax, 16) > 9) {
1807 maxV = 'h';
1808 } else {
1809 maxV = 'd';
1810 }
1811 switch (minV + maxV) {
1812 case "dd":
1813 pattern += '[' + min[0] + '-' + tmax + ']';
1814 break;
1815 case "dh":
1816 diff = 9 - min[0];
1817 //firs get up to 9
1818 switch (diff) {
1819 case 0:
1820 pattern += '[9';
1821 break;
1822 case 1:
1823 pattern += '[89';
1824 break;
1825 default:
1826 pattern += '[' + min[0] + '-9';
1827 break;
1828 }
1829 switch (tmax[0]) {
1830 case 'a':
1831 pattern += 'a]';
1832 break;
1833 case 'b':
1834 pattern += 'ab]';
1835 break;
1836 default:
1837 pattern += 'a-' + tmax + ']';
1838 break;
1839 }
1840 break;
1841 case "hh":
1842 pattern += '[' + min[0] + '-' + tmax + ']';
1843 break;
1844 }
1845 }
1846 pattern += '[0-9a-f][0-9a-f]';
1847 diff = parseInt(max[0], 16) - parseInt(min[0], 16);
1848 min = '' + (parseInt(min, 16) + diff * 256).toString(16);
1849 re.push(pattern);
1850 } else {
1851 diff = max[0] - min[0];
1852 tmax = min[0] * 1 + diff - 1;
1853 switch (diff) {
1854 case 1:
1855 pattern = min[0];
1856 break;
1857 case 2:
1858 pattern = '[' + min[0] + tmax + ']';
1859 break;
1860 default:
1861 pattern = '[' + min[0] + '-' + tmax + ']';
1862 break;
1863 }
1864 pattern += '[0-9][0-9]';
1865 min = '' + (min * 1 + diff * 100);
1866 re.push(pattern);
1867 }
1868 }
1869 pattern = '';
1870
1871 //if tens are different, get to same
1872 if (min[1] != max[1]) {
1873 if (hex) {
1874 diff = parseInt(max[1], 16) - parseInt(min[1], 16);
1875 tmax = (parseInt(min[1], 16) + diff - 1).toString(16);
1876 pattern = min[0];
1877 switch (diff) {
1878 case 1:
1879 pattern += min[1];
1880 break;
1881 case 2:
1882 pattern += '[' + min[1] + tmax + ']';
1883 break;
1884 default:
1885 if (parseInt(min[1], 16) > 9) {
1886 minV = 'h';
1887 } else {
1888 minV = 'd';
1889 }
1890 if (parseInt(tmax, 16) > 9) {
1891 maxV = 'h';
1892 } else {
1893 maxV = 'd';
1894 }
1895 switch (minV + maxV) {
1896 case "dd":
1897 pattern += '[' + min[1];
1898 if (diff > 1) {
1899 pattern += '-';
1900 }
1901 pattern += tmax + ']';
1902 break;
1903 case "dh":
1904 diff = 9 - min[1];
1905 //firs get up to 9
1906 switch (diff) {
1907 case 0:
1908 pattern += '[9';
1909 break;
1910 case 1:
1911 pattern += '[89';
1912 break;
1913 default:
1914 pattern += '[' + min[1] + '-9';
1915 break;
1916 }
1917 switch (max[1]) {
1918 case 'a':
1919 pattern += ']';
1920 break;
1921 case 'b':
1922 pattern += 'a]';
1923 break;
1924 default:
1925 pattern += 'a-' + (parseInt(max[1], 16) - 1).toString(16) + ']';
1926 break;
1927 }
1928 break;
1929 case "hh":
1930 pattern += '[' + min[1];
1931 if (diff > 1) {
1932 pattern += '-';
1933 }
1934 pattern += (parseInt(max[1], 16) - 1).toString(16) + ']';
1935 break;
1936 }
1937 break;
1938 }
1939 pattern += '[0-9a-f]';
1940 diff = parseInt(max[1], 16) - parseInt(min[1], 16);
1941 min = '' + (parseInt(min, 16) + diff * 16).toString(16);
1942 re.push(pattern);
1943 } else {
1944 diff = max[1] - min[1];
1945 tmax = min[1] * 1 + diff - 1;
1946 pattern = min[0];
1947 switch (diff) {
1948 case 1:
1949 pattern += min[1];
1950 break;
1951 case 2:
1952 pattern += '[' + min[1] + tmax + ']';
1953 break;
1954 default:
1955 pattern += '[' + min[1] + '-' + tmax + ']';
1956 break;
1957 }
1958 pattern += '[0-9]';
1959 min = '' + (min * 1 + diff * 10);
1960 re.push(pattern);
1961 }
1962 }
1963 pattern = '';
1964
1965 //if digits are different, get to same
1966 if (min[2] != max[2]) {
1967 if (hex) {
1968 pattern = min[0] + min[1];
1969 diff = parseInt(max[2], 16) - parseInt(min[2], 16);
1970 if (parseInt(min[2], 16) > 9) {
1971 minV = 'h';
1972 } else {
1973 minV = 'd';
1974 }
1975 if (parseInt(max[2], 16) > 9) {
1976 maxV = 'h';
1977 } else {
1978 maxV = 'd';
1979 }
1980 switch (minV + maxV) {
1981 case "dd":
1982 pattern += '[' + min[2];
1983 if (diff > 1) {
1984 pattern += '-';
1985 }
1986 pattern += max[2] + ']';
1987 break;
1988 case "dh":
1989 diff = 9 - min[2];
1990 //firs get up to 9
1991 switch (diff) {
1992 case 0:
1993 pattern += '[9';
1994 break;
1995 case 1:
1996 pattern += '[89';
1997 break;
1998 default:
1999 pattern += '[' + min[2] + '-9';
2000 break;
2001 }
2002 switch (max[2]) {
2003 case 'a':
2004 pattern += 'a]';
2005 break;
2006 case 'b':
2007 pattern += 'ab]';
2008 break;
2009 default:
2010 pattern += 'a-' + max[2] + ']';
2011 break;
2012 }
2013 break;
2014 case "hh":
2015 pattern += '[' + min[2];
2016 if (diff > 1) {
2017 pattern += '-';
2018 }
2019 pattern += max[2] + ']';
2020 break;
2021 }
2022 diff = parseInt(max[2], 16) - parseInt(min[2], 16);
2023 min = '' + (parseInt(min, 16) + diff).toString(16);
2024 re.push(pattern);
2025 } else {
2026 diff = max[2] - min[2];
2027 pattern = min[0] + min[1];
2028 switch (diff) {
2029 case 0:
2030 pattern += min[2];
2031 break;
2032 case 1:
2033 pattern += '[' + min[2] + max[2] + ']';
2034 break;
2035 default:
2036 pattern += '[' + min[2] + '-' + max[2] + ']';
2037 break;
2038 }
2039 min = '' + (min * 1 + diff);
2040 re.push(pattern);
2041 }
2042 }
2043 pattern = '';
2044
2045 //last place is whole hundred
2046 if (min[2] == '0' && max[2] == '0') {
2047 pattern = max;
2048 re.push(pattern);
2049 }
2050 pattern = '';
2051 cnt = re.length;
2052 if (cnt == 1) {
2053 pattern = re[0];
2054 } else {
2055 pattern = re.join(')|(');
2056 pattern = '((' + pattern + '))';
2057 }
2058 return pattern;
2059 };
2060
2061 const regexSymbol = sym => {
2062 let segment = sym.slice(0, 4);
2063 let fill = sym.slice(4, 5);
2064 if (fill == 'u') {
2065 segment += '[0-5]';
2066 } else {
2067 segment += fill;
2068 }
2069 let rotate = sym.slice(5, 6);
2070 if (rotate == 'u') {
2071 segment += '[0-9a-f]';
2072 } else {
2073 segment += rotate;
2074 }
2075 return segment;
2076 };
2077 const regexRange$1 = symRange => {
2078 let from = symRange.slice(1, 4);
2079 let to = symRange.slice(5, 8);
2080 return 'S' + range$1(from, to, 'hex') + '[0-5][0-9a-f]';
2081 };
2082
2083 //needs rewritten, but it works
2084 /**
2085 * Function to transform an FSW query string to one or more regular expressions
2086 * @function fswquery.regex
2087 * @param {string} query - an FSW query string
2088 * @returns {string[]} an array of one or more regular expressions
2089 * @example
2090 * fswquery.regex('QS100uuS20500480x520')
2091 *
2092 * return [
2093 * '(?: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})*',
2094 * '(?: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})*'
2095 * ]
2096 */
2097 const regex$1 = query => {
2098 query = query.match(new RegExp(`^${re$1.full}`))[0];
2099 if (!query) {
2100 return '';
2101 }
2102 var matches;
2103 var matchesOr;
2104 var matched;
2105 var orList;
2106 var i;
2107 var j;
2108 var segment;
2109 var coord;
2110 var x;
2111 var y;
2112 var fuzz = 20;
2113 var q_style = '(' + re$3.full + ')?';
2114 var q_sortable;
2115 if (query == 'Q') {
2116 return [re$4.prefix + "?" + re$4.signbox];
2117 }
2118 if (query == 'Q-') {
2119 return [re$4.prefix + "?" + re$4.signbox + q_style];
2120 }
2121 if (query == 'QT') {
2122 return [re$4.prefix + re$4.signbox];
2123 }
2124 if (query == 'QT-') {
2125 return [re$4.prefix + re$4.signbox + q_style];
2126 }
2127 var segments = [];
2128 var sortable = query.indexOf('T') + 1;
2129 if (sortable) {
2130 q_sortable = '(A';
2131 var qat = query.slice(0, sortable);
2132 query = query.replace(qat, '');
2133 if (qat == 'QT') {
2134 q_sortable += '(' + re$4.symbol + ')+)';
2135 } else {
2136 matches = qat.match(new RegExp('(' + re$1.list + ')', 'g'));
2137 if (matches) {
2138 for (i = 0; i < matches.length; i += 1) {
2139 orList = [];
2140 matchesOr = matches[i].match(new RegExp('(' + re$1.symbol + '|' + re$1.range + ')', 'g'));
2141 if (matchesOr) {
2142 for (j = 0; j < matchesOr.length; j += 1) {
2143 matched = matchesOr[j].match(new RegExp(re$1.symbol));
2144 if (matched) {
2145 orList.push(regexSymbol(matched[0]));
2146 } else {
2147 orList.push(regexRange$1(matchesOr[j]));
2148 }
2149 }
2150 if (orList.length == 1) {
2151 q_sortable += orList[0];
2152 } else {
2153 q_sortable += '(' + orList.join('|') + ')';
2154 }
2155 }
2156 }
2157 q_sortable += '(' + re$4.symbol + ')*)';
2158 }
2159 }
2160 }
2161 //get the variance
2162 matches = query.match(new RegExp(re$1.var, 'g'));
2163 if (matches) {
2164 fuzz = matches.toString().slice(1) * 1;
2165 }
2166 //this gets all symbols and ranges with or without location
2167 matches = query.match(new RegExp(re$1.list + re$1.coord, 'g'));
2168 if (matches) {
2169 for (i = 0; i < matches.length; i += 1) {
2170 orList = [];
2171 matchesOr = matches[i].match(new RegExp('(' + re$1.symbol + '|' + re$1.range + ')', 'g'));
2172 if (matchesOr) {
2173 for (j = 0; j < matchesOr.length; j += 1) {
2174 matched = matchesOr[j].match(new RegExp(re$1.symbol));
2175 if (matched) {
2176 orList.push(regexSymbol(matched[0]));
2177 } else {
2178 orList.push(regexRange$1(matchesOr[j]));
2179 }
2180 }
2181 if (orList.length == 1) {
2182 segment = orList[0];
2183 } else {
2184 segment = '(' + orList.join('|') + ')';
2185 }
2186 }
2187 if (matches[i].includes('x')) {
2188 coord = fsw2coord(matches[i].slice(-7));
2189 x = coord[0];
2190 y = coord[1];
2191 segment += range$1(x - fuzz, x + fuzz);
2192 segment += 'x';
2193 segment += range$1(y - fuzz, y + fuzz);
2194 } else {
2195 segment += re$4.coord;
2196 }
2197
2198 // add to general fsw word
2199 segment = re$4.signbox + segment + '(' + re$4.symbol + re$4.coord + ')*';
2200 if (sortable) {
2201 segment = q_sortable + segment;
2202 } else {
2203 segment = re$4.prefix + "?" + segment;
2204 }
2205 if (query.indexOf('-') > 0) {
2206 segment += q_style;
2207 }
2208 segments.push(segment);
2209 }
2210 }
2211 if (!segments.length) {
2212 if (query.indexOf('-') > 0) {
2213 segment += q_style;
2214 }
2215 segments.push(q_sortable + re$4.signbox);
2216 }
2217 return segments;
2218 };
2219
2220 //needs rewritten, but it works
2221 /**
2222 * Function that uses a query string to match signs from a string of text.
2223 * @function fswquery.results
2224 * @param {string} query - an FSW query string
2225 * @param {string} text - a string of text containing multiple signs
2226 * @returns {string[]} an array of FSW signs
2227 * @example
2228 * fswquery.results('QAS10011T','AS10011S10019S2e704S2e748M525x535S2e748483x510S10011501x466S2e704510x500S10019476x475 AS15a21S15a07S21100S2df04S2df14M521x538S15a07494x488S15a21498x489S2df04498x517S2df14497x461S21100479x486 AS1f010S10018S20600M519x524S10018485x494S1f010490x494S20600481x476')
2229 *
2230 * return [
2231 * 'AS10011S10019S2e704S2e748M525x535S2e748483x510S10011501x466S2e704510x500S10019476x475'
2232 * ]
2233 */
2234 const results$1 = (query, text) => {
2235 if (!text) {
2236 return [];
2237 }
2238 let pattern;
2239 let matches;
2240 let parts;
2241 let words;
2242 let re = regex$1(query);
2243 if (!re) {
2244 return [];
2245 }
2246 let i;
2247 for (i = 0; i < re.length; i += 1) {
2248 pattern = re[i];
2249 matches = text.match(new RegExp(pattern, 'g'));
2250 if (matches) {
2251 text = matches.join(' ');
2252 } else {
2253 text = '';
2254 }
2255 }
2256 if (text) {
2257 parts = text.split(' ');
2258 words = parts.filter(function (element) {
2259 return element in parts ? false : parts[element] = true;
2260 }, {});
2261 } else {
2262 words = [];
2263 }
2264 return words;
2265 };
2266
2267 //needs rewritten, but it works
2268 /**
2269 * Function that uses an FSW query string to match signs from multiple lines of text.
2270 * @function fswquery.lines
2271 * @param {string} query - an FSW query string
2272 * @param {string} text - multiple lines of text, each starting with an FSW sign
2273 * @returns {string[]} an array of lines of text, each starting with an FSW sign
2274 * @example
2275 * fswquery.lines('QAS10011T',`AS10011S10019S2e704S2e748M525x535S2e748483x510S10011501x466S2e704510x500S10019476x475 line one
2276 * AS15a21S15a07S21100S2df04S2df14M521x538S15a07494x488S15a21498x489S2df04498x517S2df14497x461S21100479x486 line two
2277 * AS1f010S10018S20600M519x524S10018485x494S1f010490x494S20600481x476 line three`)
2278 *
2279 * return [
2280 * 'AS10011S10019S2e704S2e748M525x535S2e748483x510S10011501x466S2e704510x500S10019476x475 line one'
2281 * ]
2282 */
2283 const lines$1 = (query, text) => {
2284 if (!text) {
2285 return [];
2286 }
2287 let pattern;
2288 let matches;
2289 let parts;
2290 let words;
2291 let re = regex$1(query);
2292 if (!re) {
2293 return [];
2294 }
2295 let i;
2296 for (i = 0; i < re.length; i += 1) {
2297 pattern = re[i];
2298 pattern = '^' + pattern + '.*';
2299 matches = text.match(new RegExp(pattern, 'mg'));
2300 if (matches) {
2301 text = matches.join("\n");
2302 } else {
2303 text = '';
2304 }
2305 }
2306 if (text) {
2307 parts = text.split("\n");
2308 words = parts.filter(function (element) {
2309 return element in parts ? false : parts[element] = true;
2310 }, {});
2311 } else {
2312 words = [];
2313 }
2314 return words;
2315 };
2316
2317 /** The fswquery module contains functions for handling the FSW query language.
2318 * [Query Language definition](https://tools.ietf.org/id/draft-slevinski-formal-signwriting-09.html#name-query-language)
2319 * @module fswquery
2320 */
2321
2322 var index$2 = /*#__PURE__*/Object.freeze({
2323 __proto__: null,
2324 re: re$1,
2325 parse: parse$2,
2326 compose: compose$2,
2327 fsw2query: fsw2query,
2328 range: range$1,
2329 regex: regex$1,
2330 results: results$1,
2331 lines: lines$1
2332 });
2333
2334 const parse$1 = {
2335 /**
2336 * Function to parse an swu symbol with optional coordinate and style string
2337 * @function swu.parse.symbol
2338 * @param {string} swuSym - an swu symbol
2339 * @returns {SymbolObject} elements of swu symbol
2340 * @example
2341 * swu.parse.symbol('񀀁𝤆𝤆-C')
2342 *
2343 * return {
2344 * 'symbol': '񀀁',
2345 * 'coord': [500, 500],
2346 * 'style': '-C'
2347 * }
2348 */
2349 symbol: swuSym => {
2350 const regex = `^(${re$2.symbol})(${re$2.coord})?(${re$3.full})?`;
2351 const symbol = typeof swuSym === 'string' ? swuSym.match(new RegExp(regex)) : undefined;
2352 return {
2353 'symbol': symbol ? symbol[1] : undefined,
2354 'coord': symbol && symbol[2] ? swu2coord(symbol[2]) : undefined,
2355 'style': symbol ? symbol[3] : undefined
2356 };
2357 },
2358 /**
2359 * Function to parse an swu sign with style string
2360 * @function swu.parse.sign
2361 * @param {string} swuSign - an swu sign
2362 * @returns {SignObject} elements of swu sign
2363 * @example
2364 * swu.parse.sign('𝠀񀀒񀀚񋚥񋛩𝠃𝤟𝤩񋛩𝣵𝤐񀀒𝤇𝣤񋚥𝤐𝤆񀀚𝣮𝣭-C')
2365 *
2366 * return {
2367 * sequence: ['񀀒','񀀚','񋚥','񋛩'],
2368 * box: '𝠃',
2369 * max: [525, 535],
2370 * spatials: [
2371 * {
2372 * symbol: '񋛩',
2373 * coord: [483, 510]
2374 * },
2375 * {
2376 * symbol: '񀀒',
2377 * coord: [501, 466]
2378 * },
2379 * {
2380 * symbol: '񋚥',
2381 * coord: [510, 500]
2382 * },
2383 * {
2384 * symbol: '񀀚',
2385 * coord: [476, 475]
2386 * }
2387 * ],
2388 * style: '-C'
2389 * }
2390 */
2391 sign: swuSign => {
2392 const regex = `^(${re$2.prefix})?(${re$2.signbox})(${re$3.full})?`;
2393 const sign = typeof swuSign === 'string' ? swuSign.match(new RegExp(regex)) : undefined;
2394 if (sign) {
2395 return {
2396 'sequence': sign[1] ? sign[1].slice(2).match(/.{2}/g) : undefined,
2397 'box': sign[2].slice(0, 2),
2398 'max': swu2coord(sign[2].slice(2, 6)),
2399 'spatials': sign[2].length < 7 ? undefined : sign[2].slice(6).match(/(.{6})/g).map(m => {
2400 return {
2401 symbol: m.slice(0, 2),
2402 coord: swu2coord(m.slice(2))
2403 };
2404 }),
2405 'style': sign[3]
2406 };
2407 } else {
2408 return {};
2409 }
2410 },
2411 /**
2412 * Function to parse an swu text
2413 * @function swu.parse.text
2414 * @param {string} swuText - an swu text
2415 * @returns {string[]} swu signs and punctuations
2416 * @example
2417 * swu.parse.text('𝠀񁲡񈩧𝠃𝤘𝤣񁲡𝣳𝣩񈩧𝤉𝣻 𝠀񃊢񃊫񋛕񆇡𝠃𝤘𝤧񃊫𝣻𝤕񃊢𝣴𝣼񆇡𝤎𝤂񋛕𝤆𝣦 񏌁𝣢𝤂')
2418 *
2419 * return [
2420 * '𝠀񁲡񈩧𝠃𝤘𝤣񁲡𝣳𝣩񈩧𝤉𝣻',
2421 * '𝠀񃊢񃊫񋛕񆇡𝠃𝤘𝤧񃊫𝣻𝤕񃊢𝣴𝣼񆇡𝤎𝤂񋛕𝤆𝣦',
2422 * '񏌁𝣢𝤂'
2423 * ]
2424 */
2425 text: swuText => {
2426 if (typeof swuText !== 'string') return [];
2427 const regex = `(${re$2.sign}(${re$3.full})?|${re$2.spatial}(${re$3.full})?)`;
2428 const matches = swuText.match(new RegExp(regex, 'g'));
2429 return matches ? [...matches] : [];
2430 }
2431 };
2432
2433 /**
2434 * Function to encode SWU characters using the UTF-16 escape format.
2435 * @function swu.encode
2436 * @param {string} swu - SWU characters
2437 * @returns {string} UTF-16 escape format
2438 * @example
2439 * swu.encode('񀀁𝤆𝤆')
2440 *
2441 * return '\\uD8C0\\uDC01\\uD836\\uDD06\\uD836\\uDD06'
2442 */
2443 const encode = swu => swu.replace(/[\u007F-\uFFFF]/g, function (chr) {
2444 return "\\u" + ("0000" + chr.charCodeAt(0).toString(16)).substr(-4).toUpperCase();
2445 });
2446
2447 /**
2448 * Function to decode UTF-16 escape format to SWU characters.
2449 * @function swu.decode
2450 * @param {string} encoded - UTF-16 escape format
2451 * @returns {string} SWU characters
2452 * @example
2453 * swu.decode('\\uD8C0\\uDC01\\uD836\\uDD06\\uD836\\uDD06')
2454 *
2455 * return '񀀁𝤆𝤆'
2456 */
2457 const decode = encoded => encoded.replace(/\\u([0-9A-F]{4})/g, function (match, chr) {
2458 return String.fromCharCode(parseInt(chr, 16));
2459 });
2460
2461 /**
2462 * Function to decompose an SWU character into UTF-16 surrogate pairs.
2463 * @function swu.pair
2464 * @param {string} swuChar - an SWU character
2465 * @returns {string[]} an array of UTF-16 surrogate pairs
2466 * @example
2467 * swu.pair('񀀁')
2468 *
2469 * return ['D8C0', 'DC01']
2470 */
2471 const pair = swuChar => [swuChar.charCodeAt(0).toString(16).toUpperCase(), swuChar.charCodeAt(1).toString(16).toUpperCase()];
2472
2473 const compose$1 = {
2474 /**
2475 * Function to compose an swu symbol with optional coordinate and style string
2476 * @function swu.compose.symbol
2477 * @param {SymbolObject} swuSymObject - an swu symbol object
2478 * @returns {string} an swu symbol string
2479 * @example
2480 * swu.compose.symbol({
2481 * 'symbol': '񀀁',
2482 * 'coord': [500, 500],
2483 * 'style': '-C'
2484 * })
2485 *
2486 * return '񀀁𝤆𝤆-C'
2487 */
2488 symbol: swuSymObject => {
2489 if (typeof swuSymObject !== 'object' || swuSymObject === null) return undefined;
2490 if (typeof swuSymObject.symbol === 'string') {
2491 const symbol = (swuSymObject.symbol.match(re$2.symbol) || [''])[0];
2492 if (symbol) {
2493 const x = swuSymObject.coord && swuSymObject.coord[0] || '';
2494 const y = swuSymObject.coord && swuSymObject.coord[1] || '';
2495 const coord = x && y ? coord2swu([x, y]) : '';
2496 const styleStr = typeof swuSymObject.style === 'string' && (swuSymObject.style.match(re$3.full) || [''])[0] || '';
2497 return symbol + coord + styleStr;
2498 }
2499 }
2500 return undefined;
2501 },
2502 /**
2503 * Function to compose an swu sign with style string
2504 * @function swu.compose.sign
2505 * @param {SignObject} swuSignObject - an swu sign object
2506 * @returns {string} an swu sign string
2507 * @example
2508 * swu.compose.sign({
2509 * sequence: ['񀀒','񀀚','񋚥','񋛩'],
2510 * box: '𝠃',
2511 * max: [525, 535],
2512 * spatials: [
2513 * {
2514 * symbol: '񋛩',
2515 * coord: [483, 510]
2516 * },
2517 * {
2518 * symbol: '񀀒',
2519 * coord: [501, 466]
2520 * },
2521 * {
2522 * symbol: '񋚥',
2523 * coord: [510, 500]
2524 * },
2525 * {
2526 * symbol: '񀀚',
2527 * coord: [476, 475]
2528 * }
2529 * ],
2530 * style: '-C'
2531 * })
2532 *
2533 * return '𝠀񀀒񀀚񋚥񋛩𝠃𝤟𝤩񋛩𝣵𝤐񀀒𝤇𝣤񋚥𝤐𝤆񀀚𝣮𝣭-C'
2534 */
2535 sign: swuSignObject => {
2536 if (typeof swuSignObject !== 'object' || swuSignObject === null) return undefined;
2537 let box = typeof swuSignObject.box !== 'string' ? '𝠃' : (swuSignObject.box + '𝠃').match(re$2.box);
2538 const x = swuSignObject.max && swuSignObject.max[0] || '';
2539 const y = swuSignObject.max && swuSignObject.max[1] || '';
2540 const max = x && y ? coord2swu([x, y]) : undefined;
2541 if (!max) return undefined;
2542 let prefix = '';
2543 if (swuSignObject.sequence && Array.isArray(swuSignObject.sequence)) {
2544 prefix = swuSignObject.sequence.map(key => (key.match(re$2.symbol) || [''])[0]).join('');
2545 prefix = prefix ? '𝠀' + prefix : '';
2546 }
2547 let signbox = '';
2548 if (swuSignObject.spatials && Array.isArray(swuSignObject.spatials)) {
2549 signbox = swuSignObject.spatials.map(spatial => {
2550 if (typeof spatial.symbol === 'string') {
2551 const symbol = (spatial.symbol.match(re$2.symbol) || [''])[0];
2552 if (symbol) {
2553 const x = spatial.coord && spatial.coord[0] || '';
2554 const y = spatial.coord && spatial.coord[1] || '';
2555 const coord = x && y ? coord2swu([x, y]) : '';
2556 if (coord) {
2557 return symbol + coord;
2558 }
2559 }
2560 }
2561 return '';
2562 }).join('');
2563 }
2564 const styleStr = typeof swuSignObject.style === 'string' && (swuSignObject.style.match(re$3.full) || [''])[0] || '';
2565 return prefix + box + max + signbox + styleStr;
2566 }
2567 };
2568
2569 /**
2570 * Function to gather sizing information about an swu sign or symbol
2571 * @function swu.info
2572 * @param {string} swu - an swu sign or symbol
2573 * @returns {SegmentInfo} information about the swu string
2574 * @example
2575 * swu.info('𝠀񁲡񈩧𝠂𝤘𝤣񁲡𝣳𝣩񈩧𝤉𝣻-P10Z2')
2576 *
2577 * return {
2578 * minX: 481,
2579 * minY: 471,
2580 * width: 37,
2581 * height: 58,
2582 * lane: -1,
2583 * padding: 10,
2584 * segment: 'sign',
2585 * zoom: 2
2586 * }
2587 */
2588 const info = swu => {
2589 let lanes = {
2590 '𝠁': 0,
2591 '𝠂': -1,
2592 '𝠃': 0,
2593 '𝠄': 1
2594 };
2595 let parsed = parse$1.sign(swu);
2596 let width, height, segment, x1, x2, y1, y2, lane;
2597 if (parsed.spatials) {
2598 x1 = Math.min(...parsed.spatials.map(spatial => spatial.coord[0]));
2599 x2 = parsed.max[0];
2600 width = x2 - x1;
2601 y1 = Math.min(...parsed.spatials.map(spatial => spatial.coord[1]));
2602 y2 = parsed.max[1];
2603 height = y2 - y1;
2604 segment = 'sign';
2605 lane = parsed.box;
2606 } else {
2607 parsed = parse$1.symbol(swu);
2608 lane = "𝠃";
2609 if (parsed.coord) {
2610 x1 = parsed.coord[0];
2611 width = (500 - x1) * 2;
2612 y1 = parsed.coord[1];
2613 height = (500 - y1) * 2;
2614 segment = 'symbol';
2615 } else {
2616 x1 = 490;
2617 width = 20;
2618 y1 = 490;
2619 height = 20;
2620 segment = 'none';
2621 }
2622 }
2623 let style = parse$4(parsed.style);
2624 let zoom = style.zoom || 1;
2625 let padding = style.padding || 0;
2626 return {
2627 minX: x1,
2628 minY: y1,
2629 width: width,
2630 height: height,
2631 segment: segment,
2632 lane: lanes[lane],
2633 padding: padding,
2634 zoom: zoom
2635 };
2636 };
2637
2638 const columnDefaults = {
2639 'height': 500,
2640 'width': 150,
2641 'offset': 50,
2642 'pad': 20,
2643 'margin': 5,
2644 'dynamic': false,
2645 'background': undefined,
2646 'punctuation': {
2647 'spacing': true,
2648 'pad': 30,
2649 'pull': true
2650 },
2651 'style': {
2652 'detail': ['black', 'white'],
2653 'zoom': 1
2654 }
2655 };
2656
2657 /**
2658 * Function to an object of column options with default values
2659 *
2660 * @function swu.columnDefaultsMerge
2661 * @param {ColumnOptions} options - object of column options
2662 * @returns {ColumnOptions} object of column options merged with column defaults
2663 * @example
2664 * swu.columnDefaultsMerge({height: 500,width:150})
2665 *
2666 * return {
2667 * "height": 500,
2668 * "width": 150,
2669 * "offset": 50,
2670 * "pad": 20,
2671 * "margin": 5,
2672 * "dynamic": false,
2673 * "punctuation": {
2674 * "spacing": true,
2675 * "pad": 30,
2676 * "pull": true
2677 * },
2678 * "style": {
2679 * "detail": [
2680 * "black",
2681 * "white"
2682 * ],
2683 * "zoom": 1
2684 * }
2685 * }
2686 */
2687 const columnDefaultsMerge = options => {
2688 if (typeof options !== 'object') options = {};
2689 return {
2690 ...columnDefaults,
2691 ...options,
2692 punctuation: {
2693 ...columnDefaults.punctuation,
2694 ...options.punctuation
2695 },
2696 style: {
2697 ...columnDefaults.style,
2698 ...options.style
2699 }
2700 };
2701 };
2702
2703 /**
2704 * Function to transform an SWU text to an array of columns
2705 *
2706 * @function swu.columns
2707 * @param {string} swuText - SWU text of signs and punctuation
2708 * @param {ColumnOptions} options - object of column options
2709 * @returns {{options:ColumnOptions,widths:number[],columns:ColumnData}} object of column options, widths array, and column data
2710 * @example
2711 * swu.columns('𝠀񁲡񈩧𝠃𝤘𝤣񁲡𝣳𝣩񈩧𝤉𝣻 𝠀񃊢񃊫񋛕񆇡𝠃𝤘𝤧񃊫𝣻𝤕񃊢𝣴𝣼񆇡𝤎𝤂񋛕𝤆𝣦 񏌁𝣢𝤂', {height: 500,width:150})
2712 *
2713 * return {
2714 * "options": {
2715 * "height": 500,
2716 * "width": 150,
2717 * "offset": 50,
2718 * "pad": 20,
2719 * "margin": 5,
2720 * "dynamic": false,
2721 * "punctuation": {
2722 * "spacing": true,
2723 * "pad": 30,
2724 * "pull": true
2725 * },
2726 * "style": {
2727 * "detail": [
2728 * "black",
2729 * "white"
2730 * ],
2731 * "zoom": 1
2732 * }
2733 * },
2734 * "widths": [
2735 * 150
2736 * ],
2737 * "columns": [
2738 * [
2739 * {
2740 * "x": 56,
2741 * "y": 20,
2742 * "minX": 481,
2743 * "minY": 471,
2744 * "width": 37,
2745 * "height": 58,
2746 * "lane": 0,
2747 * "padding": 0,
2748 * "segment": "sign",
2749 * "text": "𝠀񁲡񈩧𝠃𝤘𝤣񁲡𝣳𝣩񈩧𝤉𝣻",
2750 * "zoom": 1
2751 * },
2752 * {
2753 * "x": 57,
2754 * "y": 118,
2755 * "minX": 482,
2756 * "minY": 468,
2757 * "width": 36,
2758 * "height": 65,
2759 * "lane": 0,
2760 * "padding": 0,
2761 * "segment": "sign",
2762 * "text": "𝠀񃊢񃊫񋛕񆇡𝠃𝤘𝤧񃊫𝣻𝤕񃊢𝣴𝣼񆇡𝤎𝤂񋛕𝤆𝣦",
2763 * "zoom": 1
2764 * },
2765 * {
2766 * "x": 39,
2767 * "y": 203,
2768 * "minX": 464,
2769 * "minY": 496,
2770 * "width": 72,
2771 * "height": 8,
2772 * "lane": 0,
2773 * "padding": 0,
2774 * "segment": "symbol",
2775 * "text": "񏌁𝣢𝤂",
2776 * "zoom": 1
2777 * }
2778 * ]
2779 * ]
2780 * }
2781 */
2782 const columns = (swuText, options) => {
2783 if (typeof swuText !== 'string') return {};
2784 const values = columnDefaultsMerge(options);
2785 let input = parse$1.text(swuText);
2786 let cursor = 0;
2787 let cols = [];
2788 let col = [];
2789 let plus = 0;
2790 let center = parseInt(values.width / 2);
2791 let maxHeight = values.height - values.margin;
2792 let pullable = true;
2793 let finalize = false;
2794 for (let val of input) {
2795 let informed = info(val);
2796 cursor += plus;
2797 if (values.punctuation.spacing) {
2798 cursor += informed.segment == 'sign' ? values.pad : 0;
2799 } else {
2800 cursor += values.pad;
2801 }
2802 finalize = cursor + informed.height > maxHeight;
2803 if (finalize && informed.segment == 'symbol' && values.punctuation.pull && pullable) {
2804 finalize = false;
2805 pullable = false;
2806 }
2807 if (col.length == 0) {
2808 finalize = false;
2809 }
2810 if (finalize) {
2811 cursor = values.pad;
2812 cols.push(col);
2813 col = [];
2814 pullable = true;
2815 }
2816 col.push(Object.assign(informed, {
2817 x: center + values.offset * informed.lane - (500 - informed.minX) * informed.zoom * values.style.zoom,
2818 y: cursor,
2819 text: val
2820 }));
2821 cursor += informed.height * informed.zoom * values.style.zoom;
2822 if (values.punctuation.spacing) {
2823 plus = informed.segment == 'sign' ? values.pad : values.punctuation.pad;
2824 } else {
2825 plus = values.pad;
2826 }
2827 }
2828 if (col.length) {
2829 cols.push(col);
2830 }
2831
2832 // over height issue when pulling punctuation
2833 if (values.punctuation.pull) {
2834 for (let col of cols) {
2835 let last = col[col.length - 1];
2836 let diff = last.y + last.height - (values.height - values.margin);
2837 if (diff > 0) {
2838 let adj = parseInt(diff / col.length) + 1;
2839 for (let i in col) {
2840 col[i].y -= adj * i + adj;
2841 }
2842 }
2843 }
2844 }
2845
2846 // contract, expand, adjust
2847 let widths = [];
2848 for (let col of cols) {
2849 let min = [center - values.offset - values.pad];
2850 let max = [center + values.offset + values.pad];
2851 for (let item of col) {
2852 min.push(item.x - values.pad);
2853 max.push(item.x + item.width + values.pad);
2854 }
2855 min = Math.min(...min);
2856 max = Math.max(...max);
2857 let width = values.width;
2858 let adj = 0;
2859 if (!values.dynamic) {
2860 adj = center - parseInt((min + max) / 2);
2861 } else {
2862 width = max - min;
2863 adj = -min;
2864 }
2865 for (let item of col) {
2866 item.x += adj;
2867 }
2868 widths.push(width);
2869 }
2870 return {
2871 'options': values,
2872 'widths': widths,
2873 'columns': cols
2874 };
2875 };
2876
2877 /**
2878 * Array of plane 4 code points for kinds of symbols: writing, location, and punctuation.
2879 * @alias swu.kind
2880 * @type {array}
2881 */
2882 const kind = [0x40001, 0x4efa1, 0x4f2a1];
2883
2884 /**
2885 * Array of plane 4 code points for categories of symbols: hand, movement, dynamics, head, trunk & limb, location, and punctuation.
2886 * @alias swu.category
2887 * @type {array}
2888 */
2889 const category = [0x40001, 0x461e1, 0x4bca1, 0x4bfa1, 0x4e8e1, 0x4efa1, 0x4f2a1];
2890
2891 /**
2892 * Array of plane 4 code points for the 30 symbol groups.
2893 * @alias swu.group
2894 * @type {array}
2895 */
2896 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];
2897
2898 /**
2899 * Object of symbol ranges with starting and ending code points on plane 4.
2900 *
2901 * { all, writing, hand, movement, dynamic, head, hcenter, vcenter, trunk, limb, location, punctuation }
2902 * @alias swu.ranges
2903 * @type {object}
2904 */
2905 const ranges = {
2906 'all': [0x40001, 0x4f480],
2907 'writing': [0x40001, 0x4efa0],
2908 'hand': [0x40001, 0x461e0],
2909 'movement': [0x461e1, 0x4bca0],
2910 'dynamic': [0x4bca1, 0x4bfa0],
2911 'head': [0x4bfa1, 0x4e8e0],
2912 'hcenter': [0x4bfa1, 0x4e8e0],
2913 'vcenter': [0x4bfa1, 0x4ec40],
2914 'trunk': [0x4e8e1, 0x4ec40],
2915 'limb': [0x4ec41, 0x4efa0],
2916 'location': [0x4efa1, 0x4f2a0],
2917 'punctuation': [0x4f2a1, 0x4f480]
2918 };
2919
2920 /**
2921 * Function to test if symbol is of a certain type.
2922 * @function swu.isType
2923 * @param {string} swuSym - an SWU symbol character
2924 * @param {string} type - the name of a symbol range
2925 * @returns {boolean} is symbol of specified type
2926 * @example
2927 * swu.isType('񀀁', 'hand')
2928 *
2929 * return true
2930 */
2931 const isType = (swuSym, type) => {
2932 const parsed = parse$1.symbol(swuSym);
2933 if (parsed.symbol) {
2934 const code = swu2code(parsed.symbol);
2935 const range = ranges[type];
2936 if (range) {
2937 return range[0] <= code && range[1] >= code;
2938 }
2939 }
2940 return false;
2941 };
2942
2943 /**
2944 * Array of colors associated with the seven symbol categories.
2945 * @alias swu.colors
2946 * @type {array}
2947 */
2948 const colors = ['#0000CC', '#CC0000', '#FF0099', '#006600', '#000000', '#884411', '#FF9900'];
2949
2950 /**
2951 * Function that returns the standardized color for a symbol.
2952 * @function swu.colorize
2953 * @param {string} swuSym - an SWU symbol character
2954 * @returns {string} name of standardized color for symbol
2955 * @example
2956 * swu.colorize('񀀁')
2957 *
2958 * return '#0000CC'
2959 */
2960 const colorize = swuSym => {
2961 const parsed = parse$1.symbol(swuSym);
2962 let color = '#000000';
2963 if (parsed.symbol) {
2964 const code = swu2code(parsed.symbol);
2965 const index = category.findIndex(val => val > code);
2966 color = colors[index < 0 ? 6 : index - 1];
2967 }
2968 return color;
2969 };
2970
2971 /** The swu module contains functions for handling SignWriting in Unicode (SWU) characters.
2972 * [SWU characters definition](https://tools.ietf.org/id/draft-slevinski-formal-signwriting-09.html#name-signwriting-in-unicode-swu)
2973 * @module swu
2974 */
2975
2976 var index$1 = /*#__PURE__*/Object.freeze({
2977 __proto__: null,
2978 re: re$2,
2979 parse: parse$1,
2980 encode: encode,
2981 decode: decode,
2982 pair: pair,
2983 compose: compose$1,
2984 info: info,
2985 columnDefaults: columnDefaults,
2986 columnDefaultsMerge: columnDefaultsMerge,
2987 columns: columns,
2988 kind: kind,
2989 category: category,
2990 group: group,
2991 ranges: ranges,
2992 isType: isType,
2993 colors: colors,
2994 colorize: colorize
2995 });
2996
2997 /**
2998 * Object of regular expressions for SWU query strings
2999 *
3000 * @alias swuquery.re
3001 * @type {object}
3002 * @property {string} base - SWU symbol
3003 * @property {string} coord - SWU coordinate of X and Y number characters
3004 * @property {string} var - variance string for searching sign box
3005 * @property {string} symbol - SWU symbol character with ignore fill and rotation flags
3006 * @property {string} range - SWU range starting with 'R'
3007 * @property {string} item - SWU symbol or range query string
3008 * @property {string} list - several SWU symbols and SWU ranges as a logical OR for searching
3009 * @property {string} prefix - a sequential list of SWU symbol characters starting with SWU 'A' character
3010 * @property {string} signbox - several groups of SWU lists, each group having a coordinate
3011 * @property {string} full - a query string to search prefix in order and the signbox with variance
3012 */
3013 let re = {
3014 'base': '(?:(?:\uD8C0[\uDC01-\uDFFF])|(?:[\uD8C1-\uD8FC][\uDC00-\uDFFF])|(?:\uD8FD[\uDC00-\uDC80]))',
3015 'coord': '(?:(?:\uD836[\uDC0C-\uDDFF]){2})?',
3016 'var': 'V[0-9]+'
3017 };
3018 re.symbol = `${re.base}f?r?`;
3019 re.range = `R${re.base}${re.base}`;
3020 re.item = `(?:${re.symbol}|${re.range})`;
3021 re.list = `${re.item}(?:o${re.item})*`;
3022 re.prefix = `(?:A(?:${re.list})+)?T`;
3023 re.signbox = `(?:${re.list}${re.coord})*`;
3024 re.full = `Q(${re.prefix})?(${re.signbox})?(${re.var})?(-?)`;
3025
3026 const parsePrefix = text => {
3027 return {
3028 required: true,
3029 parts: text == 'T' ? undefined : text.match(new RegExp(`(${re.list})`, 'g')).map(part => {
3030 if (part.includes('o')) {
3031 return ['or'].concat(part.match(new RegExp(`(${re.item})`, 'g')).map(part => part[0] != 'R' ? part : [part.slice(1, 3), part.slice(3, 5)]));
3032 } else {
3033 return part[0] != 'R' ? part : [part.slice(1, 3), part.slice(3, 5)];
3034 }
3035 })
3036 };
3037 };
3038 const parseSignbox = text => {
3039 return text.match(new RegExp(`(${re.list}${re.coord})`, 'g')).map(part => {
3040 let coord, front;
3041 coord = part.match(new RegExp(`${re$2.coord}`));
3042 if (coord) {
3043 coord = swu2coord(coord[0]);
3044 front = part.slice(0, -4);
3045 } else {
3046 coord = undefined;
3047 front = part;
3048 }
3049 if (front.includes('o')) {
3050 return {
3051 or: front.split('o').map(part => {
3052 if (!part.includes('R')) {
3053 return part;
3054 } else {
3055 return [part.slice(1, 3), part.slice(3, 5)];
3056 }
3057 }),
3058 coord,
3059 coord
3060 };
3061 } else if (!front.includes('R')) {
3062 return {
3063 symbol: front,
3064 coord: coord
3065 };
3066 } else {
3067 return {
3068 range: [front.slice(1, 3), front.slice(3, 5)],
3069 coord: coord
3070 };
3071 }
3072 });
3073 };
3074
3075 /**
3076 * Function to parse SWU query string to object
3077 * @function swuquery.parse
3078 * @param {string} swuQueryString - an SWU query string
3079 * @returns {QueryObject} elements of an SWU query string
3080 * @example
3081 * swuquery.parse('QA񀀁R񀀁񆆑񆇡T񆀁R񀀁񀇱𝤆𝤆V5-')
3082 *
3083 * return {
3084 * query: true,
3085 * prefix: {
3086 * required: true,
3087 * parts: [
3088 * '񀀁',
3089 * ['񀀁', '񆆑'],
3090 * '񆇡'
3091 * ]
3092 * },
3093 * signbox: [
3094 * { symbol: '񆀁' },
3095 * {
3096 * range: ['񀀁', '񀇱'],
3097 * coord: [500, 500]
3098 * }
3099 * ],
3100 * variance: 5,
3101 * style: true
3102 * }
3103 */
3104 const parse = swuQueryString => {
3105 const query = typeof swuQueryString === 'string' ? swuQueryString.match(new RegExp(`^${re.full}`)) : undefined;
3106 return {
3107 'query': query ? true : undefined,
3108 'prefix': query && query[1] ? parsePrefix(query[1]) : undefined,
3109 'signbox': query && query[2] ? parseSignbox(query[2]) : undefined,
3110 'variance': query && query[3] ? parseInt(query[3].slice(1)) : undefined,
3111 'style': query && query[4] ? true : undefined
3112 };
3113 };
3114
3115 /**
3116 * Function to compose SWU query string from object
3117 * @function swuquery.compose
3118 * @param {QueryObject} swuQueryObject - an object of query options
3119 * @returns {string} SWU query string
3120 * @example
3121 * swuquery.compose({
3122 * query: true,
3123 * prefix: {
3124 * required: true,
3125 * parts: [
3126 * '񀀁',
3127 * ['񀀁', '񆆑'],
3128 * '񆇡'
3129 * ]
3130 * },
3131 * signbox: [
3132 * { symbol: '񆀁' },
3133 * {
3134 * range: ['񀀁', '񀇱'],
3135 * coord: [500, 500]
3136 * }
3137 * ],
3138 * variance: 5,
3139 * style: true
3140 * })
3141 *
3142 * return 'QA񀀁R񀀁񆆑񆇡T񆀁R񀀁񀇱𝤆𝤆V5-'
3143 */
3144 const compose = swuQueryObject => {
3145 if (!swuQueryObject || !swuQueryObject.query) {
3146 return undefined;
3147 }
3148 let query = 'Q';
3149 if (swuQueryObject.prefix && swuQueryObject.prefix.required) {
3150 if (Array.isArray(swuQueryObject.prefix.parts)) {
3151 query += 'A';
3152 query += swuQueryObject.prefix.parts.map(part => {
3153 if (typeof part === 'string') {
3154 return part;
3155 } else {
3156 if (Array.isArray(part) && part.length == 2) {
3157 return `R${part[0]}${part[1]}`;
3158 } else if (Array.isArray(part) && part.length > 2 && part[0] == 'or') {
3159 part.shift();
3160 return part.map(part => {
3161 if (typeof part === 'string') {
3162 return part;
3163 } else {
3164 if (Array.isArray(part) && part.length == 2) {
3165 return `R${part[0]}${part[1]}`;
3166 }
3167 }
3168 }).join('o');
3169 }
3170 }
3171 }).join('');
3172 }
3173 query += 'T';
3174 }
3175 if (Array.isArray(swuQueryObject.signbox)) {
3176 query += swuQueryObject.signbox.map(part => {
3177 let out;
3178 if (part.or) {
3179 out = part.or.map(item => {
3180 if (typeof item === 'string') {
3181 return item;
3182 } else {
3183 if (Array.isArray(item) && item.length == 2) {
3184 return `R${item[0]}${item[1]}`;
3185 }
3186 }
3187 }).join('o');
3188 } else if (part.symbol) {
3189 out = part.symbol;
3190 } else {
3191 if (part.range && Array.isArray(part.range) && part.range.length == 2) {
3192 out = `R${part.range[0]}${part.range[1]}`;
3193 }
3194 }
3195 return out + (Array.isArray(part.coord) && part.coord.length == 2 ? coord2swu(part.coord) : '');
3196 }).join('');
3197 }
3198 query += swuQueryObject.style ? '-' : '';
3199 query = query.match(new RegExp(`^${re.full}`))[0];
3200 return query;
3201 };
3202
3203 /**
3204 * Function to convert an SWU sign to a query string
3205 *
3206 * For the flags parameter, use one or more of the following.
3207 * - A: exact symbol in temporal prefix
3208 * - a: general symbol in temporal prefix
3209 * - S: exact symbol in spatial signbox
3210 * - s: general symbol in spatial signbox
3211 * - L: spatial signbox symbol at location
3212 * @function swuquery.swu2query
3213 * @param {string} swuSign - SWU sign
3214 * @param {string} flags - flags for query string creation
3215 * @returns {string} SWU query string
3216 * @example
3217 * swuquery.swu2query('𝠀񀀒񀀚񋚥񋛩𝠃𝤟𝤩񋛩𝣵𝤐񀀒𝤇𝣤񋚥𝤐𝤆񀀚𝣮𝣭', 'ASL')
3218 *
3219 * return 'QA񀀒񀀚񋚥񋛩T񋛩𝣵𝤐񀀒𝤇𝣤񋚥𝤐𝤆񀀚𝣮𝣭'
3220 */
3221 const swu2query = (swuSign, flags) => {
3222 let query = '';
3223 const parsed = parse$1.sign(swuSign);
3224 if (parsed.box) {
3225 const A_flag = flags.indexOf('A') > -1;
3226 const a_flag = flags.indexOf('a') > -1;
3227 const S_flag = flags.indexOf('S') > -1;
3228 const s_flag = flags.indexOf('s') > -1;
3229 const L_flag = flags.indexOf('L') > -1;
3230 if (A_flag || a_flag || S_flag || s_flag) {
3231 if ((A_flag || a_flag) && parsed.sequence) {
3232 query += 'A';
3233 query += parsed.sequence.map(sym => sym + (a_flag ? 'fr' : '')).join('');
3234 query += 'T';
3235 }
3236 if ((S_flag || s_flag) && parsed.spatials) {
3237 query += parsed.spatials.map(spatial => spatial.symbol + (s_flag ? 'fr' : '') + (L_flag ? coord2swu(spatial.coord) : '')).join('');
3238 }
3239 }
3240 return query ? "Q" + query : undefined;
3241 } else {
3242 return undefined;
3243 }
3244 };
3245
3246 //needs rewritten, but it works
3247 /**
3248 * Function to transform a range of SWU characters to a regular expression
3249 * @function swuquery.range
3250 * @param {string} min - an SWU character
3251 * @param {string} max - an SWU character
3252 * @returns {string} a regular expression that matches a range of SWU characters
3253 * @example
3254 * swuquery.range('񀀁', '񀇡')
3255 *
3256 * return '\uD8C0[\uDC01-\uDDE1]'
3257 * @example
3258 * swuquery.range('𝣔', '𝤸')
3259 *
3260 * return '\uD836[\uDCD4-\uDD38]'
3261 */
3262 const range = (min, max) => {
3263 if (min > max) return '';
3264 let pattern = '';
3265 let cnt;
3266 let re = [];
3267 min = pair(min);
3268 max = pair(max);
3269 if (min.length != 2 && max.length != 2) return '';
3270 // HEAD // min[0] with range of min[1] to (DFFF or max[1])
3271 if (min[0] == max[0]) {
3272 if (min[1] == max[1]) {
3273 pattern = '\\u' + min[0] + '\\u' + min[1];
3274 re.push(pattern);
3275 } else {
3276 pattern = '\\u' + min[0] + '[\\u' + min[1] + '-\\u' + max[1] + ']';
3277 re.push(pattern);
3278 }
3279 } else {
3280 if (min[1] == "DFFF") {
3281 pattern = '\\u' + min[0] + '\\uDFFF';
3282 } else {
3283 pattern = '\\u' + min[0] + '[\\u' + min[1] + '-\\uDFFF]';
3284 }
3285 re.push(pattern);
3286
3287 // BODY // range of (min[0] +1) to (max[0] -1) with all DC00-DFFF
3288 let diff = parseInt(max[0], 16) - parseInt(min[0], 16);
3289 if (diff == 2) {
3290 pattern = '\\u' + (parseInt(min[0], 16) + 1).toString(16).toUpperCase();
3291 pattern += '[\\uDC00-\\uDFFF]';
3292 re.push(pattern);
3293 }
3294 if (diff > 2) {
3295 pattern = '[';
3296 pattern += '\\u' + (parseInt(min[0], 16) + 1).toString(16).toUpperCase();
3297 pattern += '-\\u' + (parseInt(max[0], 16) - 1).toString(16).toUpperCase();
3298 pattern += '][\\uDC00-\\uDFFF]';
3299 re.push(pattern);
3300 }
3301
3302 // TAIL // max[0] with range of DC00 to max[1]
3303 if (max[1] == "DC00") {
3304 pattern = '\\u' + max[0] + '\\uDC00';
3305 } else {
3306 pattern = '\\u' + max[0] + '[\\uDC00-\\u' + max[1] + ']';
3307 }
3308 re.push(pattern);
3309 }
3310 cnt = re.length;
3311 if (cnt == 1) {
3312 pattern = re[0];
3313 } else {
3314 pattern = re.join(')|(');
3315 pattern = '((' + pattern + '))';
3316 }
3317 return decode(pattern);
3318 };
3319
3320 //needs rewritten, but it works
3321 /**
3322 * Function to transform an SWU symbol with fill and rotation flags to a regular expression
3323 * @function swuquery.symbolRanges
3324 * @param {string} symbolFR - an SWU character with optional flags of 'f' for any fill and 'r' for any rotation
3325 * @returns {string} a regular expression that matches one or more ranges of SWU symbols
3326 * @example <caption>Match an exact symbol</caption>
3327 * swuquery.symbolRanges('񀀁')
3328 *
3329 * return '\uD8C0\uDC01');
3330 * @example <caption>Match a symbol with any fill</caption>
3331 * swuquery.symbolRanges('񀀁f')
3332 *
3333 * return '(\uD8C0\uDC01|\uD8C0\uDC11|\uD8C0\uDC21|\uD8C0\uDC31|\uD8C0\uDC41|\uD8C0\uDC51)'
3334 * @example <caption>Match a symbol with any rotation</caption>
3335 * swuquery.symbolRanges('񀀁r')
3336 *
3337 * return '\uD8C0[\uDC01-\uDC10]'
3338 * @example <caption>Match a symbol with any fill or rotation</caption>
3339 * swuquery.symbolRanges('񀀁fr')
3340 *
3341 * return '\uD8C0[\uDC01-\uDC60]'
3342 */
3343 const symbolRanges = symbolFR => {
3344 let match = symbolFR.match(new RegExp(re.symbol));
3345 if (match) {
3346 let sym = match[0].slice(0, 2);
3347 let key = swu2key(sym);
3348 let base = key.slice(0, 4);
3349 let start, end;
3350 if (match[0].slice(-2) == 'fr') {
3351 start = key2swu(base + "00");
3352 end = key2swu(base + "5f");
3353 return range(start, end);
3354 } else if (match[0].slice(-1) == 'r') {
3355 start = key2swu(key.slice(0, 5) + '0');
3356 end = key2swu(key.slice(0, 5) + 'f');
3357 return range(start, end);
3358 } else if (match[0].slice(-1) == 'f') {
3359 let list = [0, 1, 2, 3, 4, 5].map(function (f) {
3360 return key2swu(base + f + key.slice(-1));
3361 });
3362 return "(" + list.join("|") + ")";
3363 } else {
3364 return sym;
3365 }
3366 } else {
3367 return '';
3368 }
3369 };
3370
3371 const regexRange = symRange => {
3372 let from = swu2key(symRange.slice(1, 3));
3373 let to = swu2key(symRange.slice(-2));
3374 from = key2swu(from.slice(0, 4) + '00');
3375 to = key2swu(to.slice(0, 4) + '5f');
3376 return range(from, to);
3377 };
3378
3379 //needs rewritten, but it works
3380 /**
3381 * Function to transform an SWU query string to one or more regular expressions
3382 * @function swuquery.regex
3383 * @param {string} query - an SWU query string
3384 * @returns {string[]} an array of one or more regular expressions
3385 * @example
3386 * swuquery.regex('QA񀀒T')
3387 *
3388 * return [
3389 * '(\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})*'
3390 * ]
3391 */
3392 const regex = query => {
3393 query = query.match(new RegExp(`^${re.full}`))[0];
3394 if (!query) {
3395 return '';
3396 }
3397 let matches;
3398 let matchesOr;
3399 let matched;
3400 let orList;
3401 let i;
3402 let j;
3403 let coord;
3404 let segment;
3405 let x;
3406 let y;
3407 let fuzz = 20;
3408 let re_sym = re$2.symbol;
3409 let re_coord = re$2.coord;
3410 let re_signbox = re$2.box;
3411 let re_seq = re$2.sort;
3412 let re_word = re_signbox + re_coord + '(' + re_sym + re_coord + ')*';
3413 let re_sortable = '(' + re_seq + '(' + re_sym + ')+)';
3414 let q_var = '(V[0-9]+)';
3415 let q_style = '(' + re$3.full + ')?';
3416 let q_sortable;
3417 if (query == 'Q') {
3418 return [re$2.sign];
3419 }
3420 if (query == 'Q-') {
3421 return [re$2.sign + "(" + re$3.full + ")?"];
3422 }
3423 if (query == 'QT') {
3424 return [re$2.sortable];
3425 }
3426 if (query == 'QT-') {
3427 return [re$2.sortable + "(" + re$3.full + ")?"];
3428 }
3429 let segments = [];
3430 let sortable = query.indexOf('T') + 1;
3431 if (sortable) {
3432 q_sortable = '(' + re$2.sort;
3433 let qat = query.slice(0, sortable);
3434 query = query.replace(qat, '');
3435 if (qat == 'QT') {
3436 q_sortable += '(' + re_sym + ')+)';
3437 } else {
3438 matches = qat.match(new RegExp('(' + re.list + ')', 'g'));
3439 if (matches) {
3440 for (i = 0; i < matches.length; i += 1) {
3441 orList = [];
3442 matchesOr = matches[i].match(new RegExp('(' + re.range + '|' + re.symbol + ')', 'g'));
3443 if (matchesOr) {
3444 for (j = 0; j < matchesOr.length; j += 1) {
3445 matched = matchesOr[j].match(new RegExp(re.range));
3446 if (matched) {
3447 orList.push(regexRange(matchesOr[j]));
3448 } else {
3449 orList.push(symbolRanges(matchesOr[j]));
3450 }
3451 }
3452 if (orList.length == 1) {
3453 q_sortable += orList[0];
3454 } else {
3455 q_sortable += '(' + orList.join('|') + ')';
3456 }
3457 }
3458 }
3459 q_sortable += '(' + re$2.symbol + ')*)';
3460 }
3461 }
3462 }
3463
3464 //get the variance
3465 matches = query.match(new RegExp(q_var, 'g'));
3466 if (matches) {
3467 fuzz = matches.toString().slice(1) * 1;
3468 }
3469
3470 //this gets all symbols and ranges with or without location
3471 matches = query.match(new RegExp(re.list + re.coord, 'g'));
3472 if (matches) {
3473 for (i = 0; i < matches.length; i += 1) {
3474 orList = [];
3475 matchesOr = matches[i].match(new RegExp('(' + re.range + '|' + re.symbol + ')', 'g'));
3476 if (matchesOr) {
3477 for (j = 0; j < matchesOr.length; j += 1) {
3478 matched = matchesOr[j].match(new RegExp(re.range));
3479 if (matched) {
3480 orList.push(regexRange(matchesOr[j]));
3481 } else {
3482 orList.push(symbolRanges(matchesOr[j]));
3483 }
3484 }
3485 if (orList.length == 1) {
3486 segment = orList[0];
3487 } else {
3488 segment = '(' + orList.join('|') + ')';
3489 }
3490 }
3491 coord = matches[i].match(new RegExp(`${re$2.coord}`));
3492 if (coord) {
3493 coord = swu2coord(coord[0]);
3494 x = coord[0];
3495 y = coord[1];
3496 segment += range(num2swu(x - fuzz), num2swu(x + fuzz));
3497 segment += range(num2swu(y - fuzz), num2swu(y + fuzz));
3498 } else {
3499 segment += re$2.coord;
3500 }
3501
3502 // add to general swu word
3503 segment = re_word + segment + '(' + re_sym + re_coord + ')*';
3504 if (sortable) {
3505 segment = q_sortable + segment;
3506 } else {
3507 segment = re_sortable + "?" + segment;
3508 }
3509 if (query.indexOf('-') > 0) {
3510 segment += q_style;
3511 }
3512 segments.push(segment);
3513 }
3514 }
3515 if (!segments.length) {
3516 if (query.indexOf('-') > 0) {
3517 segment += q_style;
3518 }
3519 segments.push(q_sortable + re_word);
3520 }
3521 return segments;
3522 };
3523
3524 //needs rewritten, but it works
3525 /**
3526 * Function that uses a query string to match signs from a string of text.
3527 * @function swuquery.results
3528 * @param {string} query - an SWU query string
3529 * @param {string} text - a string of text containing multiple signs
3530 * @returns {string[]} an array of SWU signs
3531 * @example
3532 * swuquery.results('QA񀀒T','𝠀񀀒񀀚񋚥񋛩𝠃𝤟𝤩񋛩𝣵𝤐񀀒𝤇𝣤񋚥𝤐𝤆񀀚𝣮𝣭 𝠀񂇢񂇈񆙡񋎥񋎵𝠃𝤛𝤬񂇈𝤀𝣺񂇢𝤄𝣻񋎥𝤄𝤗񋎵𝤃𝣟񆙡𝣱𝣸 𝠀񅨑񀀙񆉁𝠃𝤙𝤞񀀙𝣷𝤀񅨑𝣼𝤀񆉁𝣳𝣮')
3533 *
3534 * return [
3535 * '𝠀񀀒񀀚񋚥񋛩𝠃𝤟𝤩񋛩𝣵𝤐񀀒𝤇𝣤񋚥𝤐𝤆񀀚𝣮𝣭'
3536 * ]
3537 */
3538 const results = (query, text) => {
3539 if (!text) {
3540 return [];
3541 }
3542 let pattern;
3543 let matches;
3544 let parts;
3545 let words;
3546 let res = regex(query);
3547 if (!res) {
3548 return [];
3549 }
3550 let i;
3551 for (i = 0; i < res.length; i += 1) {
3552 pattern = res[i];
3553 matches = text.match(new RegExp(pattern, 'g'));
3554 if (matches) {
3555 text = matches.join(' ');
3556 } else {
3557 text = '';
3558 }
3559 }
3560 if (text) {
3561 parts = text.split(' ');
3562 words = parts.filter(function (element) {
3563 return element in parts ? false : parts[element] = true;
3564 }, {});
3565 } else {
3566 words = [];
3567 }
3568 return words;
3569 };
3570
3571 //needs rewritten, but it works
3572 /**
3573 * Function that uses an SWU query string to match signs from multiple lines of text.
3574 * @function swuquery.lines
3575 * @param {string} query - an SWU query string
3576 * @param {string} text - multiple lines of text, each starting with an SWU sign
3577 * @returns {string[]} an array of lines of text, each starting with an SWU sign
3578 * @example
3579 * swuquery.lines('QA񀀒T',`𝠀񀀒񀀚񋚥񋛩𝠃𝤟𝤩񋛩𝣵𝤐񀀒𝤇𝣤񋚥𝤐𝤆񀀚𝣮𝣭 line one
3580 * 𝠀񂇢񂇈񆙡񋎥񋎵𝠃𝤛𝤬񂇈𝤀𝣺񂇢𝤄𝣻񋎥𝤄𝤗񋎵𝤃𝣟񆙡𝣱𝣸 line two
3581 * 𝠀񅨑񀀙񆉁𝠃𝤙𝤞񀀙𝣷𝤀񅨑𝣼𝤀񆉁𝣳𝣮 line three`)
3582 *
3583 * return [
3584 * '𝠀񀀒񀀚񋚥񋛩𝠃𝤟𝤩񋛩𝣵𝤐񀀒𝤇𝣤񋚥𝤐𝤆񀀚𝣮𝣭 line one'
3585 * ]
3586 */
3587 const lines = (query, text) => {
3588 if (!text) {
3589 return [];
3590 }
3591 let pattern;
3592 let matches;
3593 let parts;
3594 let words;
3595 let res = regex(query);
3596 if (!res) {
3597 return [];
3598 }
3599 let i;
3600 for (i = 0; i < res.length; i += 1) {
3601 pattern = res[i];
3602 pattern = '^' + pattern + '.*';
3603 matches = text.match(new RegExp(pattern, 'mg'));
3604 if (matches) {
3605 text = matches.join("\n");
3606 } else {
3607 text = '';
3608 }
3609 }
3610 if (text) {
3611 parts = text.split("\n");
3612 words = parts.filter(function (element) {
3613 return element in parts ? false : parts[element] = true;
3614 }, {});
3615 } else {
3616 words = [];
3617 }
3618 return words;
3619 };
3620
3621 /** The swuquery module contains functions for handling the SWU query language.
3622 * [Query Language definition](https://tools.ietf.org/id/draft-slevinski-formal-signwriting-09.html#name-query-language)
3623 * @module swuquery
3624 */
3625
3626 var index = /*#__PURE__*/Object.freeze({
3627 __proto__: null,
3628 re: re,
3629 parse: parse,
3630 compose: compose,
3631 swu2query: swu2query,
3632 range: range,
3633 symbolRanges: symbolRanges,
3634 regex: regex,
3635 results: results,
3636 lines: lines
3637 });
3638
3639 exports.convert = index$4;
3640 exports.fsw = index$3;
3641 exports.fswquery = index$2;
3642 exports.style = index$5;
3643 exports.swu = index$1;
3644 exports.swuquery = index;
3645
3646 Object.defineProperty(exports, '__esModule', { value: true });
3647
3648}));
3649
3650/* support ongoing development */
3651/* https://patreon.com/signwriting */
3652/* https://donate.sutton-signwriting.io */