UNPKG

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