UNPKG

77.3 kBJavaScriptView Raw
1/**
2* Sutton SignWriting TrueType Font Module v1.5.2 (https://github.com/sutton-signwriting/font-ttf)
3* Author: Steve Slevinski (https://SteveSlevinski.me)
4* swu.mjs is released under the MIT License.
5*/
6
7/**
8* Sutton SignWriting Core Module v1.5.11 (https://github.com/sutton-signwriting/core)
9* Author: Steve Slevinski (https://SteveSlevinski.me)
10* convert.mjs is released under the MIT License.
11*/
12
13/**
14 * Function to convert a number to an SWU number character
15 * @function convert.num2swu
16 * @param {number} num - Integer value for number
17 * @returns {string} SWU number character
18 * @example
19 * convert.num2swu(500)
20 *
21 * return '𝤆'
22 */
23const num2swu$1 = num => String.fromCodePoint(0x1D80C + parseInt(num) - 250);
24
25/**
26 * Function to convert an array of x,y integers to two SWU number characters
27 * @function convert.coord2swu
28 * @param {number[]} coord - Array of x,y integers
29 * @returns {string} Two SWU number character
30 * @example
31 * convert.coord2swu([500, 500])
32 *
33 * return '𝤆𝤆'
34 */
35const coord2swu$1 = coord => coord.map(num => num2swu$1(num)).join('');
36
37/**
38 * Function to convert an SWU symbol character to a code point on plane 4
39 * @function convert.swu2code
40 * @param {string} swuSym - SWU symbol character
41 * @returns {number} Code point on plane 4
42 * @example
43 * convert.swu2code('񀀁')
44 *
45 * return 0x40001
46 */
47const swu2code$1 = swuSym => parseInt(swuSym.codePointAt(0));
48
49/**
50 * Function to convert a code point on plane 4 to an SWU symbol character
51 * @function convert.code2swu
52 * @param {number} code - Code point on plane 4
53 * @returns {string} SWU symbol character
54 * @example
55 * convert.code2swu(0x40001)
56 *
57 * return '񀀁'
58 */
59const code2swu = code => String.fromCodePoint(code);
60
61/**
62 * Function to convert an SWU symbol character to a 16-bit ID
63 * @function convert.swu2id
64 * @param {string} swuSym - SWU symbol character
65 * @returns {number} 16-bit ID
66 * @example
67 * convert.swu2id('񀀁')
68 *
69 * return 1
70 */
71const swu2id = swuSym => swu2code$1(swuSym) - 0x40000;
72
73/**
74 * Function to convert an SWU symbol character to an FSW symbol key
75 * @function convert.swu2key
76 * @param {string} swuSym - SWU symbol character
77 * @returns {string} FSW symbol key
78 * @example
79 * convert.swu2key('񀀁')
80 *
81 * return 'S10000'
82 */
83const swu2key = swuSym => {
84 const symcode = swu2code$1(swuSym) - 0x40001;
85 const base = parseInt(symcode / 96);
86 const fill = parseInt((symcode - base * 96) / 16);
87 const rotation = parseInt(symcode - base * 96 - fill * 16);
88 return 'S' + (base + 0x100).toString(16) + fill.toString(16) + rotation.toString(16);
89};
90
91/**
92 * Function to convert an FSW symbol key to an SWU symbol character
93 * @function convert.key2swu
94 * @param {string} key - FSW symbol key
95 * @returns {string} SWU symbol character
96 * @example
97 * convert.key2swu('S10000')
98 *
99 * return '񀀁'
100 */
101const 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));
102
103/* support ongoing development */
104/* https://patreon.com/signwriting */
105/* https://donate.sutton-signwriting.io */
106
107/**
108* Sutton SignWriting Core Module v1.5.11 (https://github.com/sutton-signwriting/core)
109* Author: Steve Slevinski (https://SteveSlevinski.me)
110* swu.mjs is released under the MIT License.
111*/
112
113/**
114 * Object of regular expressions for SWU strings in UTF-16
115 *
116 * @alias swu.re
117 * @property {string} symbol - regular expressions for a symbol
118 * @property {string} coord - regular expressions for a coordinate
119 * @property {string} sort - regular expressions for the sorting marker
120 * @property {string} box - regular expression for a signbox marker
121 * @property {string} prefix - regular expression for a sorting marker followed by one or more symbols
122 * @property {string} spatial - regular expression for a symbol followed by a coordinate
123 * @property {string} signbox - regular expression for a signbox marker, max coordinate and zero or more spatial symbols
124 * @property {string} sign - regular expression for an optional prefix followed by a signbox
125 * @property {string} sortable - regular expression for a mandatory prefix followed by a signbox
126 */
127let re$1 = {
128 'symbol': '(?:(?:\uD8C0[\uDC01-\uDFFF])|(?:[\uD8C1-\uD8FC][\uDC00-\uDFFF])|(?:\uD8FD[\uDC00-\uDC80]))',
129 'coord': '(?:\uD836[\uDC0C-\uDDFF]){2}',
130 'sort': '\uD836\uDC00',
131 'box': '\uD836[\uDC01-\uDC04]'
132};
133re$1.prefix = `(?:${re$1.sort}(?:${re$1.symbol})+)`;
134re$1.spatial = `${re$1.symbol}${re$1.coord}`;
135re$1.signbox = `${re$1.box}${re$1.coord}(?:${re$1.spatial})*`;
136re$1.sign = `${re$1.prefix}?${re$1.signbox}`;
137re$1.sortable = `${re$1.prefix}${re$1.signbox}`;
138
139/**
140 * Object of regular expressions for style strings
141 *
142 * @alias style.re
143 * @type {object}
144 * @property {string} colorize - regular expression for colorize section
145 * @property {string} colorhex - regular expression for color hex values with 3 or 6 characters
146 * @property {string} colorname - regular expression for css color name
147 * @property {string} padding - regular expression for padding section
148 * @property {string} zoom - regular expression for zoom section
149 * @property {string} classbase - regular expression for class name definition
150 * @property {string} id - regular expression for id definition
151 * @property {string} colorbase - regular expression for color hex or color name
152 * @property {string} color - regular expression for single color entry
153 * @property {string} colors - regular expression for double color entry
154 * @property {string} background - regular expression for background section
155 * @property {string} detail - regular expression for color details for line and optional fill
156 * @property {string} detailsym - regular expression for color details for individual symbols
157 * @property {string} classes - regular expression for one or more class names
158 * @property {string} full - full regular expression for style string
159 */
160let re$2 = {
161 'colorize': 'C',
162 'colorhex': '(?:[0-9a-fA-F]{3}){1,2}',
163 'colorname': '[a-zA-Z]+',
164 'padding': 'P[0-9]{2}',
165 'zoom': 'Z(?:[0-9]+(?:\\.[0-9]+)?|x)',
166 'classbase': '-?[_a-zA-Z][_a-zA-Z0-9-]{0,100}',
167 'id': '[a-zA-Z][_a-zA-Z0-9-]{0,100}'
168};
169re$2.colorbase = `(?:${re$2.colorhex}|${re$2.colorname})`;
170re$2.color = `_${re$2.colorbase}_`;
171re$2.colors = `_${re$2.colorbase}(?:,${re$2.colorbase})?_`;
172re$2.background = `G${re$2.color}`;
173re$2.detail = `D${re$2.colors}`;
174re$2.detailsym = `D[0-9]{2}${re$2.colors}`;
175re$2.classes = `${re$2.classbase}(?: ${re$2.classbase})*`;
176re$2.full = `-(${re$2.colorize})?(${re$2.padding})?(${re$2.background})?(${re$2.detail})?(${re$2.zoom})?(?:-((?:${re$2.detailsym})*))?(?:-(${re$2.classes})?!(?:(${re$2.id})!)?)?`;
177
178const prefixColor$1 = color => {
179 const regex = new RegExp(`^${re$2.colorhex}$`);
180 return (regex.test(color) ? '#' : '') + color;
181};
182const definedProps$1 = obj => Object.fromEntries(Object.entries(obj).filter(([k, v]) => v !== undefined));
183
184/**
185 * Function to parse style string to object
186 * @function style.parse
187 * @param {string} styleString - a style string
188 * @returns {StyleObject} elements of style string
189 * @example
190 * style.parse('-CP10G_blue_D_red,Cyan_')
191 *
192 * return {
193 * 'colorize': true,
194 * 'padding': 10,
195 * 'background': 'blue',
196 * 'detail': ['red', 'Cyan']
197 * }
198 */
199const parse$1 = styleString => {
200 const regex = `^${re$2.full}`;
201 const m = (typeof styleString === 'string' ? styleString.match(new RegExp(regex)) : []) || [];
202 return definedProps$1({
203 'colorize': !m[1] ? undefined : !!m[1],
204 'padding': !m[2] ? undefined : parseInt(m[2].slice(1)),
205 'background': !m[3] ? undefined : prefixColor$1(m[3].slice(2, -1)),
206 'detail': !m[4] ? undefined : m[4].slice(2, -1).split(',').map(prefixColor$1),
207 'zoom': !m[5] ? undefined : m[5] === 'Zx' ? 'x' : parseFloat(m[5].slice(1)),
208 'detailsym': !m[6] ? undefined : m[6].match(new RegExp(re$2.detailsym, 'g')).map(val => {
209 const parts = val.split('_');
210 const detail = parts[1].split(',').map(prefixColor$1);
211 return {
212 'index': parseInt(parts[0].slice(1)),
213 'detail': detail
214 };
215 }),
216 'classes': !m[7] ? undefined : m[7],
217 'id': !m[8] ? undefined : m[8]
218 });
219};
220
221/** 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.
222 * [Characters set definitions](https://tools.ietf.org/id/draft-slevinski-formal-signwriting-09.html#name-characters)
223 * @module convert
224 */
225
226/**
227 * Function to convert an SWU number character to an integer
228 * @function convert.swu2num
229 * @param {string} swuNum - SWU number character
230 * @returns {number} Integer value for number
231 * @example
232 * convert.swu2num('𝤆')
233 *
234 * return 500
235 */
236const swu2num = swuNum => parseInt(swuNum.codePointAt(0)) - 0x1D80C + 250;
237
238/**
239 * Function to convert a number to an SWU number character
240 * @function convert.num2swu
241 * @param {number} num - Integer value for number
242 * @returns {string} SWU number character
243 * @example
244 * convert.num2swu(500)
245 *
246 * return '𝤆'
247 */
248const num2swu = num => String.fromCodePoint(0x1D80C + parseInt(num) - 250);
249
250/**
251 * Function to convert two SWU number characters to an array of x,y integers
252 * @function convert.swu2coord
253 * @param {string} swuCoord - Two SWU number character
254 * @returns {number[]} Array of x,y integers
255 * @example
256 * convert.swu2coord('𝤆𝤆')
257 *
258 * return [500, 500]
259 */
260const swu2coord = swuCoord => [swu2num(swuCoord.slice(0, 2)), swu2num(swuCoord.slice(2, 4))];
261
262/**
263 * Function to convert an array of x,y integers to two SWU number characters
264 * @function convert.coord2swu
265 * @param {number[]} coord - Array of x,y integers
266 * @returns {string} Two SWU number character
267 * @example
268 * convert.coord2swu([500, 500])
269 *
270 * return '𝤆𝤆'
271 */
272const coord2swu = coord => coord.map(num => num2swu(num)).join('');
273
274/**
275 * Function to convert an SWU symbol character to a code point on plane 4
276 * @function convert.swu2code
277 * @param {string} swuSym - SWU symbol character
278 * @returns {number} Code point on plane 4
279 * @example
280 * convert.swu2code('񀀁')
281 *
282 * return 0x40001
283 */
284const swu2code = swuSym => parseInt(swuSym.codePointAt(0));
285
286const parse$2 = {
287 /**
288 * Function to parse an swu symbol with optional coordinate and style string
289 * @function swu.parse.symbol
290 * @param {string} swuSym - an swu symbol
291 * @returns {SymbolObject} elements of swu symbol
292 * @example
293 * swu.parse.symbol('񀀁𝤆𝤆-C')
294 *
295 * return {
296 * 'symbol': '񀀁',
297 * 'coord': [500, 500],
298 * 'style': '-C'
299 * }
300 */
301 symbol: swuSym => {
302 const regex = `^(${re$1.symbol})(${re$1.coord})?(${re$2.full})?`;
303 const symbol = typeof swuSym === 'string' ? swuSym.match(new RegExp(regex)) : undefined;
304 return {
305 'symbol': symbol ? symbol[1] : undefined,
306 'coord': symbol && symbol[2] ? swu2coord(symbol[2]) : undefined,
307 'style': symbol ? symbol[3] : undefined
308 };
309 },
310 /**
311 * Function to parse an swu sign with style string
312 * @function swu.parse.sign
313 * @param {string} swuSign - an swu sign
314 * @returns {SignObject} elements of swu sign
315 * @example
316 * swu.parse.sign('𝠀񀀒񀀚񋚥񋛩𝠃𝤟𝤩񋛩𝣵𝤐񀀒𝤇𝣤񋚥𝤐𝤆񀀚𝣮𝣭-C')
317 *
318 * return {
319 * sequence: ['񀀒','񀀚','񋚥','񋛩'],
320 * box: '𝠃',
321 * max: [525, 535],
322 * spatials: [
323 * {
324 * symbol: '񋛩',
325 * coord: [483, 510]
326 * },
327 * {
328 * symbol: '񀀒',
329 * coord: [501, 466]
330 * },
331 * {
332 * symbol: '񋚥',
333 * coord: [510, 500]
334 * },
335 * {
336 * symbol: '񀀚',
337 * coord: [476, 475]
338 * }
339 * ],
340 * style: '-C'
341 * }
342 */
343 sign: swuSign => {
344 const regex = `^(${re$1.prefix})?(${re$1.signbox})(${re$2.full})?`;
345 const sign = typeof swuSign === 'string' ? swuSign.match(new RegExp(regex)) : undefined;
346 if (sign) {
347 return {
348 'sequence': sign[1] ? sign[1].slice(2).match(/.{2}/g) : undefined,
349 'box': sign[2].slice(0, 2),
350 'max': swu2coord(sign[2].slice(2, 6)),
351 'spatials': sign[2].length < 7 ? undefined : sign[2].slice(6).match(/(.{6})/g).map(m => {
352 return {
353 symbol: m.slice(0, 2),
354 coord: swu2coord(m.slice(2))
355 };
356 }),
357 'style': sign[3]
358 };
359 } else {
360 return {};
361 }
362 },
363 /**
364 * Function to parse an swu text
365 * @function swu.parse.text
366 * @param {string} swuText - an swu text
367 * @returns {string[]} swu signs and punctuations
368 * @example
369 * swu.parse.text('𝠀񁲡񈩧𝠃𝤘𝤣񁲡𝣳𝣩񈩧𝤉𝣻 𝠀񃊢񃊫񋛕񆇡𝠃𝤘𝤧񃊫𝣻𝤕񃊢𝣴𝣼񆇡𝤎𝤂񋛕𝤆𝣦 񏌁𝣢𝤂')
370 *
371 * return [
372 * '𝠀񁲡񈩧𝠃𝤘𝤣񁲡𝣳𝣩񈩧𝤉𝣻',
373 * '𝠀񃊢񃊫񋛕񆇡𝠃𝤘𝤧񃊫𝣻𝤕񃊢𝣴𝣼񆇡𝤎𝤂񋛕𝤆𝣦',
374 * '񏌁𝣢𝤂'
375 * ]
376 */
377 text: swuText => {
378 if (typeof swuText !== 'string') return [];
379 const regex = `(${re$1.sign}(${re$2.full})?|${re$1.spatial}(${re$2.full})?)`;
380 const matches = swuText.match(new RegExp(regex, 'g'));
381 return matches ? [...matches] : [];
382 }
383};
384
385const compose$1 = {
386 /**
387 * Function to compose an swu symbol with optional coordinate and style string
388 * @function swu.compose.symbol
389 * @param {SymbolObject} swuSymObject - an swu symbol object
390 * @returns {string} an swu symbol string
391 * @example
392 * swu.compose.symbol({
393 * 'symbol': '񀀁',
394 * 'coord': [500, 500],
395 * 'style': '-C'
396 * })
397 *
398 * return '񀀁𝤆𝤆-C'
399 */
400 symbol: swuSymObject => {
401 if (typeof swuSymObject !== 'object' || swuSymObject === null) return undefined;
402 if (typeof swuSymObject.symbol === 'string') {
403 const symbol = (swuSymObject.symbol.match(re$1.symbol) || [''])[0];
404 if (symbol) {
405 const x = swuSymObject.coord && swuSymObject.coord[0] || '';
406 const y = swuSymObject.coord && swuSymObject.coord[1] || '';
407 const coord = x && y ? coord2swu([x, y]) : '';
408 const styleStr = typeof swuSymObject.style === 'string' && (swuSymObject.style.match(re$2.full) || [''])[0] || '';
409 return symbol + coord + styleStr;
410 }
411 }
412 return undefined;
413 },
414 /**
415 * Function to compose an swu sign with style string
416 * @function swu.compose.sign
417 * @param {SignObject} swuSignObject - an swu sign object
418 * @returns {string} an swu sign string
419 * @example
420 * swu.compose.sign({
421 * sequence: ['񀀒','񀀚','񋚥','񋛩'],
422 * box: '𝠃',
423 * max: [525, 535],
424 * spatials: [
425 * {
426 * symbol: '񋛩',
427 * coord: [483, 510]
428 * },
429 * {
430 * symbol: '񀀒',
431 * coord: [501, 466]
432 * },
433 * {
434 * symbol: '񋚥',
435 * coord: [510, 500]
436 * },
437 * {
438 * symbol: '񀀚',
439 * coord: [476, 475]
440 * }
441 * ],
442 * style: '-C'
443 * })
444 *
445 * return '𝠀񀀒񀀚񋚥񋛩𝠃𝤟𝤩񋛩𝣵𝤐񀀒𝤇𝣤񋚥𝤐𝤆񀀚𝣮𝣭-C'
446 */
447 sign: swuSignObject => {
448 if (typeof swuSignObject !== 'object' || swuSignObject === null) return undefined;
449 let box = typeof swuSignObject.box !== 'string' ? '𝠃' : (swuSignObject.box + '𝠃').match(re$1.box);
450 const x = swuSignObject.max && swuSignObject.max[0] || '';
451 const y = swuSignObject.max && swuSignObject.max[1] || '';
452 const max = x && y ? coord2swu([x, y]) : undefined;
453 if (!max) return undefined;
454 let prefix = '';
455 if (swuSignObject.sequence && Array.isArray(swuSignObject.sequence)) {
456 prefix = swuSignObject.sequence.map(key => (key.match(re$1.symbol) || [''])[0]).join('');
457 prefix = prefix ? '𝠀' + prefix : '';
458 }
459 let signbox = '';
460 if (swuSignObject.spatials && Array.isArray(swuSignObject.spatials)) {
461 signbox = swuSignObject.spatials.map(spatial => {
462 if (typeof spatial.symbol === 'string') {
463 const symbol = (spatial.symbol.match(re$1.symbol) || [''])[0];
464 if (symbol) {
465 const x = spatial.coord && spatial.coord[0] || '';
466 const y = spatial.coord && spatial.coord[1] || '';
467 const coord = x && y ? coord2swu([x, y]) : '';
468 if (coord) {
469 return symbol + coord;
470 }
471 }
472 }
473 return '';
474 }).join('');
475 }
476 const styleStr = typeof swuSignObject.style === 'string' && (swuSignObject.style.match(re$2.full) || [''])[0] || '';
477 return prefix + box + max + signbox + styleStr;
478 }
479};
480
481/**
482 * Function to gather sizing information about an swu sign or symbol
483 * @function swu.info
484 * @param {string} swu - an swu sign or symbol
485 * @returns {SegmentInfo} information about the swu string
486 * @example
487 * swu.info('𝠀񁲡񈩧𝠂𝤘𝤣񁲡𝣳𝣩񈩧𝤉𝣻-P10Z2')
488 *
489 * return {
490 * minX: 481,
491 * minY: 471,
492 * width: 37,
493 * height: 58,
494 * lane: -1,
495 * padding: 10,
496 * segment: 'sign',
497 * zoom: 2
498 * }
499 */
500const info = swu => {
501 let lanes = {
502 '𝠁': 0,
503 '𝠂': -1,
504 '𝠃': 0,
505 '𝠄': 1
506 };
507 let parsed = parse$2.sign(swu);
508 let width, height, segment, x1, x2, y1, y2, lane;
509 if (parsed.spatials) {
510 x1 = Math.min(...parsed.spatials.map(spatial => spatial.coord[0]));
511 x2 = parsed.max[0];
512 width = x2 - x1;
513 y1 = Math.min(...parsed.spatials.map(spatial => spatial.coord[1]));
514 y2 = parsed.max[1];
515 height = y2 - y1;
516 segment = 'sign';
517 lane = parsed.box;
518 } else {
519 parsed = parse$2.symbol(swu);
520 lane = "𝠃";
521 if (parsed.coord) {
522 x1 = parsed.coord[0];
523 width = (500 - x1) * 2;
524 y1 = parsed.coord[1];
525 height = (500 - y1) * 2;
526 segment = 'symbol';
527 } else {
528 x1 = 490;
529 width = 20;
530 y1 = 490;
531 height = 20;
532 segment = 'none';
533 }
534 }
535 let style = parse$1(parsed.style);
536 let zoom = style.zoom || 1;
537 let padding = style.padding || 0;
538 return {
539 minX: x1,
540 minY: y1,
541 width: width,
542 height: height,
543 segment: segment,
544 lane: lanes[lane],
545 padding: padding,
546 zoom: zoom
547 };
548};
549
550const columnDefaults = {
551 'height': 500,
552 'width': 150,
553 'offset': 50,
554 'pad': 20,
555 'margin': 5,
556 'dynamic': false,
557 'background': undefined,
558 'punctuation': {
559 'spacing': true,
560 'pad': 30,
561 'pull': true
562 },
563 'style': {
564 'detail': ['black', 'white'],
565 'zoom': 1
566 }
567};
568
569/**
570 * Function to an object of column options with default values
571 *
572 * @function swu.columnDefaultsMerge
573 * @param {ColumnOptions} options - object of column options
574 * @returns {ColumnOptions} object of column options merged with column defaults
575 * @example
576 * swu.columnDefaultsMerge({height: 500,width:150})
577 *
578 * return {
579 * "height": 500,
580 * "width": 150,
581 * "offset": 50,
582 * "pad": 20,
583 * "margin": 5,
584 * "dynamic": false,
585 * "punctuation": {
586 * "spacing": true,
587 * "pad": 30,
588 * "pull": true
589 * },
590 * "style": {
591 * "detail": [
592 * "black",
593 * "white"
594 * ],
595 * "zoom": 1
596 * }
597 * }
598 */
599const columnDefaultsMerge = options => {
600 if (typeof options !== 'object') options = {};
601 return {
602 ...columnDefaults,
603 ...options,
604 punctuation: {
605 ...columnDefaults.punctuation,
606 ...options.punctuation
607 },
608 style: {
609 ...columnDefaults.style,
610 ...options.style
611 }
612 };
613};
614
615/**
616 * Function to transform an SWU text to an array of columns
617 *
618 * @function swu.columns
619 * @param {string} swuText - SWU text of signs and punctuation
620 * @param {ColumnOptions} options - object of column options
621 * @returns {{options:ColumnOptions,widths:number[],columns:ColumnData}} object of column options, widths array, and column data
622 * @example
623 * swu.columns('𝠀񁲡񈩧𝠃𝤘𝤣񁲡𝣳𝣩񈩧𝤉𝣻 𝠀񃊢񃊫񋛕񆇡𝠃𝤘𝤧񃊫𝣻𝤕񃊢𝣴𝣼񆇡𝤎𝤂񋛕𝤆𝣦 񏌁𝣢𝤂', {height: 500,width:150})
624 *
625 * return {
626 * "options": {
627 * "height": 500,
628 * "width": 150,
629 * "offset": 50,
630 * "pad": 20,
631 * "margin": 5,
632 * "dynamic": false,
633 * "punctuation": {
634 * "spacing": true,
635 * "pad": 30,
636 * "pull": true
637 * },
638 * "style": {
639 * "detail": [
640 * "black",
641 * "white"
642 * ],
643 * "zoom": 1
644 * }
645 * },
646 * "widths": [
647 * 150
648 * ],
649 * "columns": [
650 * [
651 * {
652 * "x": 56,
653 * "y": 20,
654 * "minX": 481,
655 * "minY": 471,
656 * "width": 37,
657 * "height": 58,
658 * "lane": 0,
659 * "padding": 0,
660 * "segment": "sign",
661 * "text": "𝠀񁲡񈩧𝠃𝤘𝤣񁲡𝣳𝣩񈩧𝤉𝣻",
662 * "zoom": 1
663 * },
664 * {
665 * "x": 57,
666 * "y": 118,
667 * "minX": 482,
668 * "minY": 468,
669 * "width": 36,
670 * "height": 65,
671 * "lane": 0,
672 * "padding": 0,
673 * "segment": "sign",
674 * "text": "𝠀񃊢񃊫񋛕񆇡𝠃𝤘𝤧񃊫𝣻𝤕񃊢𝣴𝣼񆇡𝤎𝤂񋛕𝤆𝣦",
675 * "zoom": 1
676 * },
677 * {
678 * "x": 39,
679 * "y": 203,
680 * "minX": 464,
681 * "minY": 496,
682 * "width": 72,
683 * "height": 8,
684 * "lane": 0,
685 * "padding": 0,
686 * "segment": "symbol",
687 * "text": "񏌁𝣢𝤂",
688 * "zoom": 1
689 * }
690 * ]
691 * ]
692 * }
693 */
694const columns = (swuText, options) => {
695 if (typeof swuText !== 'string') return {};
696 const values = columnDefaultsMerge(options);
697 let input = parse$2.text(swuText);
698 let cursor = 0;
699 let cols = [];
700 let col = [];
701 let plus = 0;
702 let center = parseInt(values.width / 2);
703 let maxHeight = values.height - values.margin;
704 let pullable = true;
705 let finalize = false;
706 for (let val of input) {
707 let informed = info(val);
708 cursor += plus;
709 if (values.punctuation.spacing) {
710 cursor += informed.segment == 'sign' ? values.pad : 0;
711 } else {
712 cursor += values.pad;
713 }
714 finalize = cursor + informed.height > maxHeight;
715 if (finalize && informed.segment == 'symbol' && values.punctuation.pull && pullable) {
716 finalize = false;
717 pullable = false;
718 }
719 if (col.length == 0) {
720 finalize = false;
721 }
722 if (finalize) {
723 cursor = values.pad;
724 cols.push(col);
725 col = [];
726 pullable = true;
727 }
728 col.push(Object.assign(informed, {
729 x: center + values.offset * informed.lane - (500 - informed.minX) * informed.zoom * values.style.zoom,
730 y: cursor,
731 text: val
732 }));
733 cursor += informed.height * informed.zoom * values.style.zoom;
734 if (values.punctuation.spacing) {
735 plus = informed.segment == 'sign' ? values.pad : values.punctuation.pad;
736 } else {
737 plus = values.pad;
738 }
739 }
740 if (col.length) {
741 cols.push(col);
742 }
743
744 // over height issue when pulling punctuation
745 if (values.punctuation.pull) {
746 for (let col of cols) {
747 let last = col[col.length - 1];
748 let diff = last.y + last.height - (values.height - values.margin);
749 if (diff > 0) {
750 let adj = parseInt(diff / col.length) + 1;
751 for (let i in col) {
752 col[i].y -= adj * i + adj;
753 }
754 }
755 }
756 }
757
758 // contract, expand, adjust
759 let widths = [];
760 for (let col of cols) {
761 let min = [center - values.offset - values.pad];
762 let max = [center + values.offset + values.pad];
763 for (let item of col) {
764 min.push(item.x - values.pad);
765 max.push(item.x + item.width + values.pad);
766 }
767 min = Math.min(...min);
768 max = Math.max(...max);
769 let width = values.width;
770 let adj = 0;
771 if (!values.dynamic) {
772 adj = center - parseInt((min + max) / 2);
773 } else {
774 width = max - min;
775 adj = -min;
776 }
777 for (let item of col) {
778 item.x += adj;
779 }
780 widths.push(width);
781 }
782 return {
783 'options': values,
784 'widths': widths,
785 'columns': cols
786 };
787};
788
789/**
790 * Array of plane 4 code points for categories of symbols: hand, movement, dynamics, head, trunk & limb, location, and punctuation.
791 * @alias swu.category
792 * @type {array}
793 */
794const category = [0x40001, 0x461e1, 0x4bca1, 0x4bfa1, 0x4e8e1, 0x4efa1, 0x4f2a1];
795
796/**
797 * Object of symbol ranges with starting and ending code points on plane 4.
798 *
799 * { all, writing, hand, movement, dynamic, head, hcenter, vcenter, trunk, limb, location, punctuation }
800 * @alias swu.ranges
801 * @type {object}
802 */
803const ranges = {
804 'all': [0x40001, 0x4f480],
805 'writing': [0x40001, 0x4efa0],
806 'hand': [0x40001, 0x461e0],
807 'movement': [0x461e1, 0x4bca0],
808 'dynamic': [0x4bca1, 0x4bfa0],
809 'head': [0x4bfa1, 0x4e8e0],
810 'hcenter': [0x4bfa1, 0x4e8e0],
811 'vcenter': [0x4bfa1, 0x4ec40],
812 'trunk': [0x4e8e1, 0x4ec40],
813 'limb': [0x4ec41, 0x4efa0],
814 'location': [0x4efa1, 0x4f2a0],
815 'punctuation': [0x4f2a1, 0x4f480]
816};
817
818/**
819 * Array of colors associated with the seven symbol categories.
820 * @alias swu.colors
821 * @type {array}
822 */
823const colors = ['#0000CC', '#CC0000', '#FF0099', '#006600', '#000000', '#884411', '#FF9900'];
824
825/**
826 * Function that returns the standardized color for a symbol.
827 * @function swu.colorize
828 * @param {string} swuSym - an SWU symbol character
829 * @returns {string} name of standardized color for symbol
830 * @example
831 * swu.colorize('񀀁')
832 *
833 * return '#0000CC'
834 */
835const colorize = swuSym => {
836 const parsed = parse$2.symbol(swuSym);
837 let color = '#000000';
838 if (parsed.symbol) {
839 const code = swu2code(parsed.symbol);
840 const index = category.findIndex(val => val > code);
841 color = colors[index < 0 ? 6 : index - 1];
842 }
843 return color;
844};
845
846/* support ongoing development */
847/* https://patreon.com/signwriting */
848/* https://donate.sutton-signwriting.io */
849
850let sizes = {};
851const zoom = 2;
852const bound = 76 * zoom;
853let context;
854
855/**
856 * Function that returns the size of a symbol using an id
857 * @function font.symbolSize
858 * @param {number} id - a 16-bit number of a symbol
859 * @returns {number[]} width and height of symbol
860 * @example
861 * font.symbolSize(1)
862 *
863 * return [15,30]
864 */
865const symbolSize$1 = function (id) {
866 if (id in sizes) {
867 return [...sizes[id]];
868 }
869 if (!context) {
870 const canvaser = document.createElement("canvas");
871 canvaser.width = bound;
872 canvaser.height = bound;
873 context = canvaser.getContext("2d", {
874 willReadFrequently: true
875 });
876 }
877 context.clearRect(0, 0, bound, bound);
878 context.font = 30 * zoom + "px 'SuttonSignWritingLine'";
879 context.fillText(String.fromCodePoint(id + 0xF0000), 0, 0);
880 const imgData = context.getImageData(0, 0, bound, bound).data;
881 let w, h, i, s;
882 wloop: for (w = bound - 1; w >= 0; w--) {
883 for (h = 0; h < bound; h += 1) {
884 for (s = 0; s < 4; s += 1) {
885 i = w * 4 + h * 4 * bound + s;
886 if (imgData[i]) {
887 break wloop;
888 }
889 }
890 }
891 }
892 var width = w;
893 hloop: for (h = bound - 1; h >= 0; h--) {
894 for (w = 0; w < width; w += 1) {
895 for (s = 0; s < 4; s += 1) {
896 i = w * 4 + h * 4 * bound + s;
897 if (imgData[i]) {
898 break hloop;
899 }
900 }
901 }
902 }
903 var height = h + 1;
904 width = Math.ceil(width / zoom);
905 height = Math.ceil(height / zoom);
906 // Rounding error in chrome. Manual fixes.
907 if (14394 == id) {
908 width = 19;
909 }
910 if ([10468, 10480, 10496, 10512, 10500, 10532, 10548, 10862, 10878, 10894, 11058, 11074, 11476, 11488, 11492, 11504, 11508, 11520, 10516, 10910, 10926, 11042, 11082, 10942].includes(id)) {
911 width = 20;
912 }
913 if (31921 == id) {
914 width = 22;
915 }
916 if (38460 == id) {
917 width = 23;
918 }
919 if ([20164, 20212].includes(id)) {
920 width = 25;
921 }
922 if (31894 == id) {
923 width = 28;
924 }
925 if (46698 == id) {
926 width = 29;
927 }
928 if (29606 == id) {
929 width = 30;
930 }
931 if (44855 == id) {
932 width = 40;
933 }
934 if (32667 == id) {
935 width = 50;
936 }
937 if ([11088, 11474, 11490, 11506].includes(id)) {
938 height = 20;
939 }
940 if (6285 == id) {
941 height = 21;
942 }
943 if (40804 == id) {
944 height = 31;
945 }
946 if (41475 == id) {
947 height = 36;
948 }
949 // Error in chrome. Manual fix.
950 // if (width==0 && height==0) {
951 if (width == 0 && height == 0) {
952 const sizefix = {
953 9: [15, 30],
954 10: [21, 30],
955 11: [30, 15],
956 12: [30, 21],
957 13: [15, 30],
958 14: [21, 30]
959 };
960 if (id in sizefix) {
961 width = sizefix[id][0];
962 height = sizefix[id][1];
963 }
964 }
965 if (width == 0 && height == 0) {
966 return undefined;
967 }
968 sizes[id] = [width, height];
969 return [width, height];
970};
971
972/**
973 * Function that returns the size of a symbol using an SWU symbol character
974 * @function swu.symbolSize
975 * @param {string} swu - an SWU symbol character
976 * @returns {number[]} width and height of symbol
977 * @example
978 * swu.symbolSize("񀀁")
979 *
980 * return [15,30]
981 */
982const symbolSize = function (swu) {
983 const parsed = parse$2.symbol(swu);
984 if (!parsed.symbol) {
985 return undefined;
986 }
987 return symbolSize$1(swu2id(swu));
988};
989
990/**
991 * Function that returns a plane 15 character for a symbol line using an id
992 * @function font.symbolLine
993 * @param {number} id - a 16-bit number of a symbol
994 * @returns {string} character for symbol line
995 * @example
996 * font.symbolLine(1)
997 *
998 * return '󰀁'
999 */
1000const symbolLine$1 = function (id) {
1001 return String.fromCodePoint(id + 0xF0000);
1002};
1003
1004/**
1005 * Function that returns a plane 16 character for a symbol fill using an id
1006 * @function font.symbolFill
1007 * @param {number} id - a 16-bit number of a symbol
1008 * @returns {string} character for symbol fill
1009 * @example
1010 * font.symbolFill(1)
1011 *
1012 * return '􀀁'
1013 */
1014const symbolFill$1 = function (id) {
1015 return String.fromCodePoint(id + 0x100000);
1016};
1017
1018/**
1019 * Function that creates two text elements for a symbol using an id
1020 * @function font.symbolText
1021 * @param {number} id - a 16-bit number of a symbol
1022 * @returns {string} SVG segment for line and fill
1023 * @example
1024 * font.symbolText(1)
1025 *
1026 * return ` <text class="sym-fill" fill="white" style="pointer-events:none;font-family:'SuttonSignWritingFill';font-size:30px;">􀀁</text>
1027 * <text class="sym-line" fill="black" style="pointer-events:none;font-family:'SuttonSignWritingLine';font-size:30px;">󰀁</text>`
1028 */
1029const symbolText$1 = function (id) {
1030 return ` <text class="sym-fill" fill="white" style="pointer-events:none;font-family:'SuttonSignWritingFill';font-size:30px;">${symbolFill$1(id)}</text>
1031 <text class="sym-line" fill="black" style="pointer-events:none;font-family:'SuttonSignWritingLine';font-size:30px;">${symbolLine$1(id)}</text>`;
1032};
1033
1034/**
1035 * Function that returns a plane 15 character for a symbol line using an SWU symbol character
1036 * @function swu.symbolLine
1037 * @param {string} swu - an SWU symbol character
1038 * @returns {string} character for symbol line
1039 * @example
1040 * swu.symbolLine('񀀁')
1041 *
1042 * return '󰀁'
1043 */
1044const symbolLine = function (swu) {
1045 return symbolLine$1(swu2id(swu));
1046};
1047
1048/**
1049 * Function that returns a plane 165 character for a symbol fill using an SWU symbol character
1050 * @function swu.symbolFill
1051 * @param {string} swu - an SWU symbol character
1052 * @returns {string} character for symbol fill
1053 * @example
1054 * swu.symbolFill('񀀁')
1055 *
1056 * return '􀀁'
1057 */
1058const symbolFill = function (swu) {
1059 return symbolFill$1(swu2id(swu));
1060};
1061
1062/**
1063 * Function that creates two text elements for a symbol using an SWU symbol character
1064 * @function swu.symbolText
1065 * @param {string} swu - an SWU symbol character
1066 * @returns {string} svg segment for line and fill
1067 * @example
1068 * swu.symbolText('񀀁')
1069 *
1070 * return ` <text class="sym-fill" fill="white" style="pointer-events:none;font-family:'SuttonSignWritingFill';font-size:30px;">􀀁</text>
1071 * <text class="sym-line" fill="black" style="pointer-events:none;font-family:'SuttonSignWritingLine';font-size:30px;">󰀁</text>`
1072 */
1073const symbolText = function (swu) {
1074 return symbolText$1(swu2id(swu));
1075};
1076
1077/**
1078* Sutton SignWriting Core Module v1.5.11 (https://github.com/sutton-signwriting/core)
1079* Author: Steve Slevinski (https://SteveSlevinski.me)
1080* style.mjs is released under the MIT License.
1081*/
1082
1083/**
1084 * Object of regular expressions for style strings
1085 *
1086 * @alias style.re
1087 * @type {object}
1088 * @property {string} colorize - regular expression for colorize section
1089 * @property {string} colorhex - regular expression for color hex values with 3 or 6 characters
1090 * @property {string} colorname - regular expression for css color name
1091 * @property {string} padding - regular expression for padding section
1092 * @property {string} zoom - regular expression for zoom section
1093 * @property {string} classbase - regular expression for class name definition
1094 * @property {string} id - regular expression for id definition
1095 * @property {string} colorbase - regular expression for color hex or color name
1096 * @property {string} color - regular expression for single color entry
1097 * @property {string} colors - regular expression for double color entry
1098 * @property {string} background - regular expression for background section
1099 * @property {string} detail - regular expression for color details for line and optional fill
1100 * @property {string} detailsym - regular expression for color details for individual symbols
1101 * @property {string} classes - regular expression for one or more class names
1102 * @property {string} full - full regular expression for style string
1103 */
1104let re = {
1105 'colorize': 'C',
1106 'colorhex': '(?:[0-9a-fA-F]{3}){1,2}',
1107 'colorname': '[a-zA-Z]+',
1108 'padding': 'P[0-9]{2}',
1109 'zoom': 'Z(?:[0-9]+(?:\\.[0-9]+)?|x)',
1110 'classbase': '-?[_a-zA-Z][_a-zA-Z0-9-]{0,100}',
1111 'id': '[a-zA-Z][_a-zA-Z0-9-]{0,100}'
1112};
1113re.colorbase = `(?:${re.colorhex}|${re.colorname})`;
1114re.color = `_${re.colorbase}_`;
1115re.colors = `_${re.colorbase}(?:,${re.colorbase})?_`;
1116re.background = `G${re.color}`;
1117re.detail = `D${re.colors}`;
1118re.detailsym = `D[0-9]{2}${re.colors}`;
1119re.classes = `${re.classbase}(?: ${re.classbase})*`;
1120re.full = `-(${re.colorize})?(${re.padding})?(${re.background})?(${re.detail})?(${re.zoom})?(?:-((?:${re.detailsym})*))?(?:-(${re.classes})?!(?:(${re.id})!)?)?`;
1121
1122const prefixColor = color => {
1123 const regex = new RegExp(`^${re.colorhex}$`);
1124 return (regex.test(color) ? '#' : '') + color;
1125};
1126const definedProps = obj => Object.fromEntries(Object.entries(obj).filter(([k, v]) => v !== undefined));
1127
1128/**
1129 * Function to parse style string to object
1130 * @function style.parse
1131 * @param {string} styleString - a style string
1132 * @returns {StyleObject} elements of style string
1133 * @example
1134 * style.parse('-CP10G_blue_D_red,Cyan_')
1135 *
1136 * return {
1137 * 'colorize': true,
1138 * 'padding': 10,
1139 * 'background': 'blue',
1140 * 'detail': ['red', 'Cyan']
1141 * }
1142 */
1143const parse = styleString => {
1144 const regex = `^${re.full}`;
1145 const m = (typeof styleString === 'string' ? styleString.match(new RegExp(regex)) : []) || [];
1146 return definedProps({
1147 'colorize': !m[1] ? undefined : !!m[1],
1148 'padding': !m[2] ? undefined : parseInt(m[2].slice(1)),
1149 'background': !m[3] ? undefined : prefixColor(m[3].slice(2, -1)),
1150 'detail': !m[4] ? undefined : m[4].slice(2, -1).split(',').map(prefixColor),
1151 'zoom': !m[5] ? undefined : m[5] === 'Zx' ? 'x' : parseFloat(m[5].slice(1)),
1152 'detailsym': !m[6] ? undefined : m[6].match(new RegExp(re.detailsym, 'g')).map(val => {
1153 const parts = val.split('_');
1154 const detail = parts[1].split(',').map(prefixColor);
1155 return {
1156 'index': parseInt(parts[0].slice(1)),
1157 'detail': detail
1158 };
1159 }),
1160 'classes': !m[7] ? undefined : m[7],
1161 'id': !m[8] ? undefined : m[8]
1162 });
1163};
1164
1165/**
1166 * Function to compose style string from object
1167 * @function style.compose
1168 * @param {StyleObject} styleObject - an object of style options
1169 * @returns {string} style string
1170 * @example
1171 * style.compose({
1172 * 'colorize': true,
1173 * 'padding': 10,
1174 * 'background': 'blue',
1175 * 'detail': ['red', 'Cyan'],
1176 * 'zoom': 1.1,
1177 * 'detailsym': [
1178 * {
1179 * 'index': 1,
1180 * 'detail': ['#ff00ff']
1181 * },
1182 * {
1183 * 'index': 2,
1184 * 'detail': ['yellow', 'green']
1185 * }
1186 * ],
1187 * 'classes': 'primary blinking',
1188 * 'id': 'cursor'
1189 * })
1190 *
1191 * return '-CP10G_blue_D_red,Cyan_Z1.1-D01_ff00ff_D02_yellow,green_-primary blinking!cursor!'
1192 */
1193const compose = styleObject => {
1194 if (typeof styleObject !== 'object' || styleObject === null) return undefined;
1195
1196 // three sections
1197 let style1 = '-';
1198 style1 += !styleObject.colorize ? '' : 'C';
1199 const padding = parseInt(styleObject.padding);
1200 style1 += !padding || padding <= 0 || padding > 99 ? '' : 'P' + (padding > 9 ? padding : '0' + padding);
1201 const background = !styleObject.background || !(typeof styleObject.background === 'string') ? undefined : styleObject.background.match(re.colorbase)[0];
1202 style1 += !background ? '' : 'G_' + background + '_';
1203 const detail1 = !styleObject.detail || !styleObject.detail[0] || !(typeof styleObject.detail[0] === 'string') ? undefined : styleObject.detail[0].match(re.colorbase)[0];
1204 const detail2 = !styleObject.detail || !styleObject.detail[1] || !(typeof styleObject.detail[1] === 'string') ? undefined : styleObject.detail[1].match(re.colorbase)[0];
1205 if (detail1) {
1206 style1 += 'D_' + detail1;
1207 if (detail2) {
1208 style1 += ',' + detail2;
1209 }
1210 style1 += '_';
1211 }
1212 const zoom = styleObject.zoom === 'x' ? 'x' : parseFloat(styleObject.zoom);
1213 style1 += !zoom || zoom <= 0 ? '' : 'Z' + zoom;
1214 let style2 = '';
1215 const detailsym = !styleObject.detailsym || !Array.isArray(styleObject.detailsym) ? [] : styleObject.detailsym.map(styleObject => {
1216 const index = parseInt(styleObject.index);
1217 if (!index || index <= 0 || index > 99) return '';
1218 let style = 'D' + (index > 9 ? index : '0' + index);
1219 const detail1 = !styleObject.detail || !styleObject.detail[0] ? undefined : styleObject.detail[0].match(re.colorbase)[0];
1220 const detail2 = !styleObject.detail || !styleObject.detail[1] ? undefined : styleObject.detail[1].match(re.colorbase)[0];
1221 if (detail1) {
1222 style += '_' + detail1;
1223 if (detail2) {
1224 style += ',' + detail2;
1225 }
1226 style += '_';
1227 }
1228 return style;
1229 });
1230 style2 += detailsym.join('');
1231 let style3 = '';
1232 const classes = !styleObject.classes || !(typeof styleObject.classes === 'string') ? undefined : styleObject.classes.match(re.classes)[0];
1233 style3 += !classes ? '' : classes;
1234 const id = !styleObject.id || !(typeof styleObject.id === 'string') ? undefined : styleObject.id.match(re.id)[0];
1235 style3 += classes || id ? '!' : '';
1236 style3 += !id ? '' : id + '!';
1237 return style1 + (style2 || style3 ? '-' + style2 : '') + (style3 ? '-' + style3 : '');
1238};
1239
1240/* support ongoing development */
1241/* https://patreon.com/signwriting */
1242/* https://donate.sutton-signwriting.io */
1243
1244/**
1245 * Function that creates an SVG image from an SWU symbol key with an optional style string
1246 * @function swu.symbolSvgBody
1247 * @param {string} swuSym - an SWU symbol key with optional style string
1248 * @returns {string} body of SVG for symbol
1249 * @example
1250 * swu.symbolSvgBody('S10000')
1251 *
1252 * return `<text font-size="0">S10000</text>
1253 * <g transform="translate(500,500)">
1254 * <text class="sym-fill" fill="white" style="pointer-events:none;font-family:'SuttonSignWritingFill';font-size:30px;">􀀁</text>
1255 * <text class="sym-line" fill="black" style="pointer-events:none;font-family:'SuttonSignWritingLine';font-size:30px;">󰀁</text>
1256 * </g>`
1257 */
1258const symbolSvgBody = swuSym => {
1259 const parsed = parse$2.symbol(swuSym);
1260 const blank = '';
1261 if (!parsed.symbol) return blank;
1262 let styling = parse(parsed.style);
1263 let x1, y1, x2, y2;
1264 if (parsed.coord) {
1265 x1 = parsed.coord[0];
1266 y1 = parsed.coord[1];
1267 x2 = 500 + (500 - x1);
1268 y2 = 500 + (500 - y1);
1269 } else {
1270 let size = symbolSize(parsed.symbol);
1271 if (!size) return blank;
1272 x1 = 500 - parseInt((size[0] + 1) / 2);
1273 y1 = 500 - parseInt((size[1] + 1) / 2);
1274 x2 = 500 + (500 - x1);
1275 y2 = 500 + (500 - y1);
1276 }
1277 let symSvg = symbolText(parsed.symbol);
1278 symSvg = ` <g transform="translate(${x1},${y1})">
1279${symSvg}
1280 </g>`;
1281 let line;
1282 if (styling.colorize) {
1283 line = colorize(parsed.symbol);
1284 } else if (styling.detail) {
1285 line = styling.detail[0];
1286 }
1287 if (line) {
1288 symSvg = symSvg.replace(/class="sym-line" fill="black"/, `class="sym-line" fill="${line}"`);
1289 }
1290 let fill = styling.detail && styling.detail[1];
1291 if (fill) {
1292 symSvg = symSvg.replace(/class="sym-fill" fill="white"/, `class="sym-fill" fill="${fill}"`);
1293 }
1294 let background = '';
1295 if (styling.padding) {
1296 x1 -= styling.padding;
1297 y1 -= styling.padding;
1298 x2 += styling.padding;
1299 y2 += styling.padding;
1300 }
1301 if (styling.background) {
1302 background = `\n <rect x="${x1}" y="${y1}" width="${x2 - x1}" height="${y2 - y1}" style="fill:${styling.background};" />`;
1303 }
1304 return ` <text font-size="0">${swuSym}</text>${background}
1305${symSvg}`;
1306};
1307
1308/**
1309 * Function that creates an SVG image from an SWU symbol key with an optional style string
1310 * @function swu.symbolSvg
1311 * @param {string} swuSym - an SWU symbol key with optional style string
1312 * @returns {string} SVG for symbol
1313 * @example
1314 * swu.symbolSvg('S10000')
1315 *
1316 * return `<svg version="1.1" xmlns="http://www.w3.org/2000/svg" width="15" height="30" viewBox="500 500 15 30">
1317 * <text font-size="0">S10000</text>
1318 * <g transform="translate(500,500)">
1319 * <text class="sym-fill" fill="white" style="pointer-events:none;font-family:'SuttonSignWritingFill';font-size:30px;">􀀁</text>
1320 * <text class="sym-line" fill="black" style="pointer-events:none;font-family:'SuttonSignWritingLine';font-size:30px;">󰀁</text>
1321 * </g>
1322 * </svg>`
1323 */
1324const symbolSvg = swuSym => {
1325 const parsed = parse$2.symbol(swuSym);
1326 const blank = '<svg version="1.1" xmlns="http://www.w3.org/2000/svg" width="1" height="1"></svg>';
1327 if (!parsed.symbol) return blank;
1328 let styling = parse(parsed.style);
1329 let x1, y1, x2, y2;
1330 if (parsed.coord) {
1331 x1 = parsed.coord[0];
1332 y1 = parsed.coord[1];
1333 x2 = 500 + (500 - x1);
1334 y2 = 500 + (500 - y1);
1335 } else {
1336 let size = symbolSize(parsed.symbol);
1337 if (!size) return blank;
1338 x1 = parseInt(500 - size[0] / 2);
1339 y1 = parseInt(500 - size[1] / 2);
1340 x2 = x1 + size[0];
1341 y2 = y1 + size[1];
1342 }
1343 let classes = '';
1344 if (styling.classes) {
1345 classes = ` class="${styling.classes}"`;
1346 }
1347 let id = '';
1348 if (styling.id) {
1349 id = ` id="${styling.id}"`;
1350 }
1351 if (styling.padding) {
1352 x1 -= styling.padding;
1353 y1 -= styling.padding;
1354 x2 += styling.padding;
1355 y2 += styling.padding;
1356 }
1357 let sizing = '';
1358 if (styling.zoom != 'x') {
1359 sizing = ` width="${(x2 - x1) * (styling.zoom ? styling.zoom : 1)}" height="${(y2 - y1) * (styling.zoom ? styling.zoom : 1)}"`;
1360 }
1361 return `<svg${classes}${id} version="1.1" xmlns="http://www.w3.org/2000/svg"${sizing} viewBox="${x1} ${y1} ${x2 - x1} ${y2 - y1}">
1362${symbolSvgBody(swuSym)}
1363</svg>`;
1364};
1365
1366const symbolCanvas = function (swuSym) {
1367 const parsed = parse$2.symbol(swuSym);
1368 if (parsed.symbol) {
1369 let size = symbolSize(parsed.symbol);
1370 if (size) {
1371 const canvas = document.createElement('canvas');
1372 const context = canvas.getContext('2d');
1373 let styling = parse(parsed.style);
1374 let line = 'black';
1375 if (styling.colorize) {
1376 line = colorize(parsed.symbol);
1377 } else if (styling.detail) {
1378 line = styling.detail[0];
1379 }
1380 let fill = styling.detail && styling.detail[1] || 'white';
1381 let x1 = 500;
1382 let x2 = x1 + size[0];
1383 let y1 = 500;
1384 let y2 = y1 + size[1];
1385 if (styling.padding) {
1386 x1 -= styling.padding;
1387 y1 -= styling.padding;
1388 x2 += styling.padding;
1389 y2 += styling.padding;
1390 }
1391 let sizing = 1;
1392 if (styling.zoom != 'x') {
1393 sizing = styling.zoom;
1394 }
1395 let w = (x2 - x1) * sizing;
1396 let h = (y2 - y1) * sizing;
1397 canvas.width = w ? w : 1;
1398 canvas.height = h ? h : 1;
1399 if (styling.background) {
1400 context.rect(0, 0, w, h);
1401 context.fillStyle = styling.background;
1402 context.fill();
1403 }
1404 context.font = 30 * sizing + "px 'SuttonSignWritingFill'";
1405 context.fillStyle = fill;
1406 context.fillText(symbolFill(parsed.symbol), (500 - x1) * sizing, (500 - y1) * sizing);
1407 context.font = 30 * sizing + "px 'SuttonSignWritingLine'";
1408 context.fillStyle = line;
1409 context.fillText(symbolLine(parsed.symbol), (500 - x1) * sizing, (500 - y1) * sizing);
1410 return canvas;
1411 }
1412 }
1413};
1414
1415/**
1416 * Function that creates a PNG data url from an SWU symbol character with an optional style string
1417 * @function swu.symbolPng
1418 * @param {string} swuSym - an SWU symbol character with optional style string
1419 * @returns {string} png image for symbol as data url
1420 * @example
1421 * swu.symbolPng('񀀁-CP10G_green_Z2')
1422 *
1423 * return 'data:image/png;base64,iVBORw...'
1424 */
1425const symbolPng = swuSym => {
1426 const canvas = symbolCanvas(swuSym);
1427 const png = canvas.toDataURL("image/png");
1428 canvas.remove();
1429 return png;
1430};
1431
1432const blank = null;
1433
1434/**
1435 * Function that normalizes a symbol with a minimum coordinate for a center of 500,500
1436 * @function swu.symbolNormalize
1437 * @param {string} swuSym - an SWU symbol character with optional coordinate and style string
1438 * @returns {string} normalized SWU symbol
1439 * @example
1440 * swu.symbolNormalize('񀀁')
1441 *
1442 * return '񀀁𝣿𝣷'
1443 */
1444const symbolNormalize = swuSym => {
1445 const parsed = parse$2.symbol(swuSym);
1446 if (parsed.symbol) {
1447 let size = symbolSize(parsed.symbol);
1448 if (size) {
1449 return `${parsed.symbol}${coord2swu$1([500 - parseInt((size[0] + 1) / 2), 500 - parseInt((size[1] + 1) / 2)])}${parsed.style || ''}`;
1450 }
1451 } else {
1452 return blank;
1453 }
1454};
1455
1456/**
1457 * Function that mirrors a symbol
1458 * @function swu.symbolMirror
1459 * @param {string} swuSym - an SWU symbol with optional coordinate and style string
1460 * @returns {string} mirrored SWU symbol
1461 * @example
1462 * swu.symbolMirror('񀀁')
1463 *
1464 * return '񀀉'
1465 */
1466const symbolMirror = swuSym => {
1467 let parsed = parse$2.symbol(swuSym);
1468 if (!parsed.symbol) {
1469 return swuSym;
1470 }
1471 const size = symbolSize(parsed.symbol);
1472 if (!size) {
1473 return swuSym;
1474 }
1475 parsed.symbol = swu2key(parsed.symbol);
1476 const base = parsed.symbol.slice(0, 4);
1477 let fill = parsed.symbol.slice(4, 5);
1478 let rot = parseInt(parsed.symbol.slice(5, 6), 16);
1479 const key1 = base + "08";
1480 const key2 = base + "18";
1481 var rAdd;
1482 if (symbolSize(key2swu(key1)) || symbolSize(key2swu(key2))) {
1483 rAdd = 8;
1484 } else {
1485 if (rot === 0 || rot == 4) {
1486 rAdd = 0;
1487 }
1488 if (rot == 1 || rot == 5) {
1489 rAdd = 6;
1490 }
1491 if (rot == 2 || rot == 6) {
1492 rAdd = 4;
1493 }
1494 if (rot == 3 || rot == 7) {
1495 rAdd = 2;
1496 }
1497 }
1498 let key = '';
1499 while (!key || !symbolSize(key2swu(key))) {
1500 rot += rAdd;
1501 if (rot > 7 && rAdd < 8) {
1502 rot = rot - 8;
1503 }
1504 if (rot > 15) {
1505 rot = rot - 16;
1506 }
1507 key = base + fill + rot.toString(16);
1508 }
1509 parsed.symbol = key2swu(key);
1510 return compose$1.symbol(parsed);
1511};
1512
1513/**
1514 * Function that rotates a symbol
1515 * @function swu.symbolRotate
1516 * @param {string} swuSym - an SWU symbol with optional coordinate and style string
1517 * @param {boolean} [clockwise=true] - rotate the symbol clockwise
1518 * @returns {string} rotated SWU symbol
1519 * @example
1520 * swu.symbolRotate('񀀁')
1521 *
1522 * return '񀀈'
1523 */
1524const symbolRotate = (swuSym, clockwise = true) => {
1525 let parsed = parse$2.symbol(swuSym);
1526 if (!parsed.symbol) {
1527 return swuSym;
1528 }
1529 const size = symbolSize(parsed.symbol);
1530 if (!size) {
1531 return swuSym;
1532 }
1533 parsed.symbol = swu2key(parsed.symbol);
1534 const step = clockwise ? 1 : -1;
1535 const base = parsed.symbol.slice(0, 4);
1536 let fill = parsed.symbol.slice(4, 5);
1537 let rot = parseInt(parsed.symbol.slice(5, 6), 16);
1538 let key = '';
1539 while (!key || !symbolSize(key2swu(key))) {
1540 if (rot > 7) {
1541 rot += step;
1542 if (rot > 15) {
1543 rot = 8;
1544 }
1545 if (rot < 8) {
1546 rot = 15;
1547 }
1548 key = base + fill + rot.toString(16);
1549 } else {
1550 rot -= step;
1551 if (rot > 7) {
1552 rot = 0;
1553 }
1554 if (rot < 0) {
1555 rot = 7;
1556 }
1557 key = base + fill + rot;
1558 }
1559 }
1560 parsed.symbol = key2swu(key);
1561 return compose$1.symbol(parsed);
1562};
1563
1564/**
1565 * Function that changes the fill of a symbol
1566 * @function swu.symbolFlop
1567 * @param {string} swuSym - an SWU symbol with optional coordinate and style string
1568 * @param {boolean} [positive=true] - increase the symbol fill
1569 * @returns {string} SWU symbol with changed fill
1570 * @example
1571 * swu.symbolFlop('񀀁')
1572 *
1573 * return '񀀑'
1574 */
1575const symbolFlop = (swuSym, positive = true) => {
1576 let parsed = parse$2.symbol(swuSym);
1577 if (!parsed.symbol) {
1578 return swuSym;
1579 }
1580 const size = symbolSize(parsed.symbol);
1581 if (!size) {
1582 return swuSym;
1583 }
1584 parsed.symbol = swu2key(parsed.symbol);
1585 const step = positive ? 1 : -1;
1586 const base = parsed.symbol.slice(0, 4);
1587 let fill = parseInt(parsed.symbol.slice(4, 5));
1588 let rot = parsed.symbol.slice(5, 6);
1589 let key = '';
1590 while (!key || !symbolSize(key2swu(key))) {
1591 fill += step;
1592 if (fill > 5) {
1593 fill = 0;
1594 }
1595 if (fill < 0) {
1596 fill = 5;
1597 }
1598 key = base + fill + rot;
1599 }
1600 parsed.symbol = key2swu(key);
1601 return compose$1.symbol(parsed);
1602};
1603
1604/**
1605 * Function that changes the base of a symbol
1606 * @function swu.symbolScroll
1607 * @param {string} swuSym - an SWU symbol with optional coordinate and style string
1608 * @param {boolean} [positive=true] - increase the symbol base
1609 * @returns {string} SWU symbol with changed base
1610 * @example
1611 * swu.symbolScroll('񀀁')
1612 *
1613 * return '񀁡'
1614 */
1615const symbolScroll = (swuSym, positive = true) => {
1616 let parsed = parse$2.symbol(swuSym);
1617 if (!parsed.symbol) {
1618 return swuSym;
1619 }
1620 const size = symbolSize(parsed.symbol);
1621 if (!size) {
1622 return swuSym;
1623 }
1624 parsed.symbol = swu2key(parsed.symbol);
1625 const step = positive ? 1 : -1;
1626 const base = parseInt(parsed.symbol.slice(1, 4), 16) + step;
1627 const fill = parsed.symbol.slice(4, 5);
1628 const rot = parsed.symbol.slice(5, 6);
1629 const key = 'S' + base.toString(16) + fill + rot;
1630 if (key.length == 6 && symbolSize(key2swu(key))) {
1631 parsed.symbol = key;
1632 }
1633 parsed.symbol = key2swu(parsed.symbol);
1634 return compose$1.symbol(parsed);
1635};
1636
1637/**
1638 * Function that creates an SVG image from an SWU sign with an optional style string
1639 * @function swu.signSvgBody
1640 * @param {string} swuSign - an SWU sign with optional style string
1641 * @returns {string} body of SVG for sign
1642 * @example
1643 * swu.signSvgBody('M525x535S2e748483x510S10011501x466S2e704510x500S10019476x475')
1644 *
1645 * return `<text font-size="0">M525x535S2e748483x510S10011501x466S2e704510x500S10019476x475</text>
1646 * <g transform="translate(483,510)">
1647 * <text class="sym-fill" fill="white" style="pointer-events:none;font-family:'SuttonSignWritingFill';font-size:30px;">􋛩</text>
1648 * <text class="sym-line" fill="black" style="pointer-events:none;font-family:'SuttonSignWritingLine';font-size:30px;">󻛩</text>
1649 * </g>
1650 * <g transform="translate(501,466)">
1651 * <text class="sym-fill" fill="white" style="pointer-events:none;font-family:'SuttonSignWritingFill';font-size:30px;">􀀒</text>
1652 * <text class="sym-line" fill="black" style="pointer-events:none;font-family:'SuttonSignWritingLine';font-size:30px;">󰀒</text>
1653 * </g>
1654 * <g transform="translate(510,500)">
1655 * <text class="sym-fill" fill="white" style="pointer-events:none;font-family:'SuttonSignWritingFill';font-size:30px;">􋚥</text>
1656 * <text class="sym-line" fill="black" style="pointer-events:none;font-family:'SuttonSignWritingLine';font-size:30px;">󻚥</text>
1657 * </g>
1658 * <g transform="translate(476,475)">
1659 * <text class="sym-fill" fill="white" style="pointer-events:none;font-family:'SuttonSignWritingFill';font-size:30px;">􀀚</text>
1660 * <text class="sym-line" fill="black" style="pointer-events:none;font-family:'SuttonSignWritingLine';font-size:30px;">󰀚</text>
1661 * </g>`
1662 */
1663const signSvgBody = swuSign => {
1664 let parsed = parse$2.sign(swuSign);
1665 const blank = '';
1666 if (parsed.spatials) {
1667 let styling = parse(parsed.style);
1668 if (styling.detailsym) {
1669 styling.detailsym.forEach(sym => {
1670 if (parsed.spatials[sym.index - 1]) {
1671 parsed.spatials[sym.index - 1].detail = sym.detail;
1672 }
1673 });
1674 }
1675 let x1 = Math.min(...parsed.spatials.map(spatial => spatial.coord[0]));
1676 let y1 = Math.min(...parsed.spatials.map(spatial => spatial.coord[1]));
1677 let x2 = parsed.max[0];
1678 let y2 = parsed.max[1];
1679 let background = '';
1680 if (styling.padding) {
1681 x1 -= styling.padding;
1682 y1 -= styling.padding;
1683 x2 += styling.padding;
1684 y2 += styling.padding;
1685 }
1686 if (styling.background) {
1687 background = `\n <rect x="${x1}" y="${y1}" width="${x2 - x1}" height="${y2 - y1}" style="fill:${styling.background};" />`;
1688 }
1689 let svg = ` <text font-size="0">${swuSign}</text>${background}`;
1690 const line = styling.detail && styling.detail[0];
1691 const fill = styling.detail && styling.detail[1];
1692 svg += '\n' + parsed.spatials.map(spatial => {
1693 let svg = symbolText(spatial.symbol);
1694 let symLine = line;
1695 if (spatial.detail) {
1696 symLine = spatial.detail[0];
1697 } else if (styling.colorize) {
1698 symLine = colorize(spatial.symbol);
1699 }
1700 if (symLine) {
1701 svg = svg.replace(/class="sym-line" fill="black"/, `class="sym-line" fill="${symLine}"`);
1702 }
1703 let symFill = fill;
1704 if (spatial.detail && spatial.detail[1]) {
1705 symFill = spatial.detail[1];
1706 }
1707 if (symFill) {
1708 svg = svg.replace(/class="sym-fill" fill="white"/, `class="sym-fill" fill="${symFill}"`);
1709 }
1710 return ` <g transform="translate(${spatial.coord[0]},${spatial.coord[1]})">
1711${svg}
1712 </g>`;
1713 }).join('\n');
1714 return svg;
1715 }
1716 return blank;
1717};
1718
1719/**
1720 * Function that creates an SVG image from an SWU sign with an optional style string
1721 * @function swu.signSvg
1722 * @param {string} swuSign - an SWU sign with optional style string
1723 * @returns {string} SVG for sign
1724 * @example
1725 * swu.signSvg('M525x535S2e748483x510S10011501x466S2e704510x500S10019476x475')
1726 *
1727 * return `<svg version="1.1" xmlns="http://www.w3.org/2000/svg" width="49" height="69" viewBox="476 466 49 69">
1728 * <text font-size="0">M525x535S2e748483x510S10011501x466S2e704510x500S10019476x475</text>
1729 * <g transform="translate(483,510)">
1730 * <text class="sym-fill" fill="white" style="pointer-events:none;font-family:'SuttonSignWritingFill';font-size:30px;">􋛩</text>
1731 * <text class="sym-line" fill="black" style="pointer-events:none;font-family:'SuttonSignWritingLine';font-size:30px;">󻛩</text>
1732 * </g>
1733 * <g transform="translate(501,466)">
1734 * <text class="sym-fill" fill="white" style="pointer-events:none;font-family:'SuttonSignWritingFill';font-size:30px;">􀀒</text>
1735 * <text class="sym-line" fill="black" style="pointer-events:none;font-family:'SuttonSignWritingLine';font-size:30px;">󰀒</text>
1736 * </g>
1737 * <g transform="translate(510,500)">
1738 * <text class="sym-fill" fill="white" style="pointer-events:none;font-family:'SuttonSignWritingFill';font-size:30px;">􋚥</text>
1739 * <text class="sym-line" fill="black" style="pointer-events:none;font-family:'SuttonSignWritingLine';font-size:30px;">󻚥</text>
1740 * </g>
1741 * <g transform="translate(476,475)">
1742 * <text class="sym-fill" fill="white" style="pointer-events:none;font-family:'SuttonSignWritingFill';font-size:30px;">􀀚</text>
1743 * <text class="sym-line" fill="black" style="pointer-events:none;font-family:'SuttonSignWritingLine';font-size:30px;">󰀚</text>
1744 * </g>
1745 * </svg>`
1746 */
1747const signSvg = swuSign => {
1748 let parsed = parse$2.sign(swuSign);
1749 const blank = '<svg version="1.1" xmlns="http://www.w3.org/2000/svg" width="1" height="1"></svg>';
1750 if (parsed.spatials) {
1751 let styling = parse(parsed.style);
1752 let x1 = Math.min(...parsed.spatials.map(spatial => spatial.coord[0]));
1753 let y1 = Math.min(...parsed.spatials.map(spatial => spatial.coord[1]));
1754 let x2 = parsed.max[0];
1755 let y2 = parsed.max[1];
1756 let classes = '';
1757 if (styling.classes) {
1758 classes = ` class="${styling.classes}"`;
1759 }
1760 let id = '';
1761 if (styling.id) {
1762 id = ` id="${styling.id}"`;
1763 }
1764 if (styling.padding) {
1765 x1 -= styling.padding;
1766 y1 -= styling.padding;
1767 x2 += styling.padding;
1768 y2 += styling.padding;
1769 }
1770 let sizing = '';
1771 if (styling.zoom != 'x') {
1772 sizing = ` width="${(x2 - x1) * (styling.zoom ? styling.zoom : 1)}" height="${(y2 - y1) * (styling.zoom ? styling.zoom : 1)}"`;
1773 }
1774 let svg = `<svg${classes}${id} version="1.1" xmlns="http://www.w3.org/2000/svg"${sizing} viewBox="${x1} ${y1} ${x2 - x1} ${y2 - y1}">
1775`;
1776 svg += signSvgBody(swuSign);
1777 svg += '\n</svg>';
1778 return svg;
1779 }
1780 return blank;
1781};
1782
1783const signCanvas = function (swuSign) {
1784 const parsed = parse$2.sign(swuSign);
1785 if (parsed.spatials) {
1786 const canvas = document.createElement('canvas');
1787 const context = canvas.getContext('2d');
1788 let styling = parse(parsed.style);
1789 if (styling.detailsym) {
1790 styling.detailsym.forEach(sym => {
1791 if (parsed.spatials[sym.index - 1]) {
1792 parsed.spatials[sym.index - 1].detail = sym.detail;
1793 }
1794 });
1795 }
1796 let x1 = Math.min(...parsed.spatials.map(spatial => spatial.coord[0]));
1797 let y1 = Math.min(...parsed.spatials.map(spatial => spatial.coord[1]));
1798 let x2 = parsed.max[0];
1799 let y2 = parsed.max[1];
1800 if (styling.padding) {
1801 x1 -= styling.padding;
1802 y1 -= styling.padding;
1803 x2 += styling.padding;
1804 y2 += styling.padding;
1805 }
1806 let sizing = 1;
1807 if (styling.zoom != 'x') {
1808 sizing = styling.zoom;
1809 }
1810 let w = (x2 - x1) * sizing;
1811 let h = (y2 - y1) * sizing;
1812 canvas.width = w ? w : 1;
1813 canvas.height = h ? h : 1;
1814 if (styling.background) {
1815 context.rect(0, 0, w, h);
1816 context.fillStyle = styling.background;
1817 context.fill();
1818 }
1819 const line = styling.detail && styling.detail[0] || "black";
1820 const fill = styling.detail && styling.detail[1] || "white";
1821 parsed.spatials.forEach(spatial => {
1822 let symLine = line;
1823 if (spatial.detail) {
1824 symLine = spatial.detail[0];
1825 } else if (styling.colorize) {
1826 symLine = colorize(spatial.symbol);
1827 }
1828 let symFill = fill;
1829 if (spatial.detail && spatial.detail[1]) {
1830 symFill = spatial.detail[1];
1831 }
1832 context.font = 30 * sizing + "px 'SuttonSignWritingFill'";
1833 context.fillStyle = symFill;
1834 context.fillText(symbolFill(spatial.symbol), (spatial.coord[0] - x1) * sizing, (spatial.coord[1] - y1) * sizing);
1835 context.font = 30 * sizing + "px 'SuttonSignWritingLine'";
1836 context.fillStyle = symLine;
1837 context.fillText(symbolLine(spatial.symbol), (spatial.coord[0] - x1) * sizing, (spatial.coord[1] - y1) * sizing);
1838 });
1839 return canvas;
1840 }
1841};
1842
1843/**
1844 * Function that creates a PNG data url from an SWU sign with an optional style string
1845 * @function swu.signPng
1846 * @param {string} swuSign - an SWU sign with optional style string
1847 * @returns {string} png image for sign as data url
1848 * @example
1849 * swu.signPng('𝠀񀀒񀀚񋚥񋛩𝠃𝤟𝤩񋛩𝣵𝤐񀀒𝤇𝣤񋚥𝤐𝤆񀀚𝣮𝣭')
1850 *
1851 * return 'data:image/png;base64,iVBORw...'
1852 */
1853const signPng = swuSign => {
1854 const canvas = signCanvas(swuSign);
1855 const png = canvas.toDataURL("image/png");
1856 canvas.remove();
1857 return png;
1858};
1859
1860/**
1861 * Function that normalizes an SWU sign for a center of 500,500
1862 * @function swu.signNormalize
1863 * @param {string} swuSign - an SWU sign with optional style string
1864 * @returns {string} normalized SWU sign
1865 * @example
1866 * swu.signNormalize('𝠀񀀒񀀚񋚥񋛩𝠃𝤟𝤩񋛩𝣵𝤐񀀒𝤇𝣤񋚥𝤐𝤆񀀚𝣮𝣭')
1867 *
1868 * return '𝠀񀀒񀀚񋚥񋛩𝠃𝤟𝤩񋛩𝣵𝤐񀀒𝤇𝣤񋚥𝤐𝤆񀀚𝣮𝣭'
1869 */
1870const signNormalize = swuSign => {
1871 const parsed = parse$2.sign(swuSign);
1872 if (parsed.spatials) {
1873 const symbolsizes = parsed.spatials.reduce((output, spatial) => {
1874 const size = symbolSize(spatial.symbol);
1875 output[spatial.symbol] = {
1876 width: size[0],
1877 height: size[1]
1878 };
1879 return output;
1880 }, {});
1881 const bbox = symbols => {
1882 const x1 = Math.min(...symbols.map(spatial => spatial.coord[0]));
1883 const y1 = Math.min(...symbols.map(spatial => spatial.coord[1]));
1884 const x2 = Math.max(...symbols.map(spatial => spatial.coord[0] + parseInt(symbolsizes[spatial.symbol].width)));
1885 const y2 = Math.max(...symbols.map(spatial => spatial.coord[1] + parseInt(symbolsizes[spatial.symbol].height)));
1886 return {
1887 x1: x1,
1888 y1: y1,
1889 x2: x2,
1890 y2: y2
1891 };
1892 };
1893 const hrange = ranges['hcenter'];
1894 const hsyms = parsed.spatials.filter(spatial => {
1895 const dec = parseInt(spatial.symbol.slice(1, 4), 16);
1896 return hrange[0] <= dec && hrange[1] >= dec;
1897 });
1898 const vrange = ranges['vcenter'];
1899 const vsyms = parsed.spatials.filter(spatial => {
1900 const dec = parseInt(spatial.symbol.slice(1, 4), 16);
1901 return vrange[0] <= dec && vrange[1] >= dec;
1902 });
1903 let abox = bbox(parsed.spatials);
1904 let max = [abox.x2, abox.y2];
1905 if (hsyms.length) {
1906 const hbox = bbox(hsyms);
1907 abox.x1 = hbox.x1;
1908 abox.x2 = hbox.x2;
1909 }
1910 if (vsyms.length) {
1911 const vbox = bbox(vsyms);
1912 abox.y1 = vbox.y1;
1913 abox.y2 = vbox.y2;
1914 }
1915 const offset = [parseInt((abox.x2 + abox.x1) / 2) - 500, parseInt((abox.y2 + abox.y1) / 2) - 500];
1916 const swuout = (parsed.sequence ? '𝠀' + parsed.sequence.join('') : '') + parsed.box + coord2swu$1([max[0] - offset[0], max[1] - offset[1]]) + parsed.spatials.map(spatial => spatial.symbol + coord2swu$1([spatial.coord[0] - offset[0], spatial.coord[1] - offset[1]])).join('') + (parsed.style || '');
1917 return swuout;
1918 }
1919};
1920
1921/**
1922 * Function that creates an SVG image for a column of SWU
1923 * @function swu.columnSvg
1924 * @param {ColumnData} swuColumn - an array of objects with information about FSW signs and punctuation
1925 * @param {ColumnOptions} options - an object of column options
1926 * @returns {string} column svg
1927 * @example
1928 * swu.columnSvg([
1929 * {
1930 * "x": 56,
1931 * "y": 20,
1932 * "minX": 481,
1933 * "minY": 471,
1934 * "width": 37,
1935 * "height": 58,
1936 * "lane": 0,
1937 * "padding": 0,
1938 * "segment": "sign",
1939 * "text": "𝠀񁲡񈩧𝠃𝤘𝤣񁲡𝣳𝣩񈩧𝤉𝣻",
1940 * "zoom": 1
1941 * },
1942 * {
1943 * "x": 57,
1944 * "y": 118,
1945 * "minX": 482,
1946 * "minY": 468,
1947 * "width": 36,
1948 * "height": 65,
1949 * "lane": 0,
1950 * "padding": 0,
1951 * "segment": "sign",
1952 * "text": "𝠀񃊢񃊫񋛕񆇡𝠃𝤘𝤧񃊫𝣻𝤕񃊢𝣴𝣼񆇡𝤎𝤂񋛕𝤆𝣦",
1953 * "zoom": 1
1954 * },
1955 * {
1956 * "x": 39,
1957 * "y": 203,
1958 * "minX": 464,
1959 * "minY": 496,
1960 * "width": 72,
1961 * "height": 8,
1962 * "lane": 0,
1963 * "padding": 0,
1964 * "segment": "symbol",
1965 * "text": "񏌁𝣢𝤂",
1966 * "zoom": 1
1967 * }
1968 * ],
1969 * {
1970 * "height": 250,
1971 * "width": 150,
1972 * })
1973 *
1974 * return `<svg version="1.1" xmlns="http://www.w3.org/2000/svg" width="150" height="250" viewBox="0 0 150 250">
1975 * <g transform="translate(56,20) scale(1) translate(-481,-471) ">
1976 * <text font-size="0">𝠀񁲡񈩧𝠃𝤘𝤣񁲡𝣳𝣩񈩧𝤉𝣻-D_black,white_Z1</text>
1977 * <g transform="translate(481,471)">
1978 * <text class="sym-fill" fill="white" style="pointer-events:none;font-family:'SuttonSignWritingFill';font-size:30px;">􁲥</text>
1979 * <text class="sym-line" fill="black" style="pointer-events:none;font-family:'SuttonSignWritingLine';font-size:30px;">󹲥</text>
1980 * </g>
1981 * <g transform="translate(503,489)">
1982 * <text class="sym-fill" fill="white" style="pointer-events:none;font-family:'SuttonSignWritingFill';font-size:30px;">􈊧</text>
1983 * <text class="sym-line" fill="black" style="pointer-events:none;font-family:'SuttonSignWritingLine';font-size:30px;">󸊧</text>
1984 * </g>
1985 * </g>
1986 * <g transform="translate(57,118) scale(1) translate(-482,-468) ">
1987 * <text font-size="0">𝠀񃊢񃊫񋛕񆇡𝠃𝤘𝤧񃊫𝣻𝤕񃊢𝣴𝣼񆇡𝤎𝤂񋛕𝤆𝣦-D_black,white_Z1</text>
1988 * <g transform="translate(489,515)">
1989 * <text class="sym-fill" fill="white" style="pointer-events:none;font-family:'SuttonSignWritingFill';font-size:30px;">􃊫</text>
1990 * <text class="sym-line" fill="black" style="pointer-events:none;font-family:'SuttonSignWritingLine';font-size:30px;">󳊫</text>
1991 * </g>
1992 * <g transform="translate(482,490)">
1993 * <text class="sym-fill" fill="white" style="pointer-events:none;font-family:'SuttonSignWritingFill';font-size:30px;">􃊢</text>
1994 * <text class="sym-line" fill="black" style="pointer-events:none;font-family:'SuttonSignWritingLine';font-size:30px;">󳊢</text>
1995 * </g>
1996 * <g transform="translate(508,496)">
1997 * <text class="sym-fill" fill="white" style="pointer-events:none;font-family:'SuttonSignWritingFill';font-size:30px;">􆇡</text>
1998 * <text class="sym-line" fill="black" style="pointer-events:none;font-family:'SuttonSignWritingLine';font-size:30px;">󶇡</text>
1999 * </g>
2000 * <g transform="translate(500,468)">
2001 * <text class="sym-fill" fill="white" style="pointer-events:none;font-family:'SuttonSignWritingFill';font-size:30px;">􋛕</text>
2002 * <text class="sym-line" fill="black" style="pointer-events:none;font-family:'SuttonSignWritingLine';font-size:30px;">󻛕</text>
2003 * </g>
2004 * </g>
2005 * <g transform="translate(39,203) scale(1) translate(-464,-496) ">
2006 * <text font-size="0">񏌁𝣢𝤂-D_black,white_Z1</text>
2007 * <g transform="translate(464,496)">
2008 * <text class="sym-fill" fill="white" style="pointer-events:none;font-family:'SuttonSignWritingFill';font-size:30px;">􏌁</text>
2009 * <text class="sym-line" fill="black" style="pointer-events:none;font-family:'SuttonSignWritingLine';font-size:30px;">󿌁</text>
2010 * </g>
2011 * </g>
2012 * </svg>`
2013 */
2014const columnSvg = (swuColumn, options) => {
2015 //if (typeof swuColumn !== 'array') return blank;
2016 if (typeof options !== 'object') options = {};
2017 const values = Object.assign(columnDefaults, options);
2018 let x1 = 0;
2019 let y1 = 0;
2020 let x2 = values.width;
2021 let y2 = values.height;
2022 let background = '';
2023 if (values.background) {
2024 background = `\n <rect x="${x1}" y="${y1}" width="${x2 - x1}" height="${y2 - y1}" style="fill:${values.background};" />`;
2025 }
2026 let sizing = ` width="${values.width}" height="${values.height}"`;
2027 let svg = `<svg version="1.1" xmlns="http://www.w3.org/2000/svg"${sizing} viewBox="${x1} ${y1} ${x2 - x1} ${y2 - y1}">
2028 <text font-size="0">${x1}</text>${background}`;
2029 svg += swuColumn.map(item => {
2030 const dash = item.text.indexOf('-');
2031 if (dash > 0) {
2032 const itemStyle = item.text.substring(dash);
2033 const newStyle = {
2034 ...values.style,
2035 ...parse(itemStyle)
2036 };
2037 item.text = item.text.replace(itemStyle, compose(newStyle));
2038 } else {
2039 item.text += compose(values.style);
2040 }
2041 item.zoom = item.zoom * values.style.zoom;
2042 return '<g transform="translate(' + item.x + ',' + item.y + ') scale(' + item.zoom + ') translate(' + -item.minX + ',' + -item.minY + ') ">' + (item.segment == "sign" ? signSvgBody(item.text) : symbolSvgBody(item.text)) + '</g>';
2043 }).join('\n');
2044 svg += '\n</svg>';
2045 return svg;
2046};
2047
2048/**
2049 * Function that creates an array of SVG column images for an SWU text
2050 * @function swu.columnsSvg
2051 * @param {string} swuText - a text of SWU signs and punctuation
2052 * @param {ColumnOptions} options - an object of column options
2053 * @returns {string[]} array of SVG columns
2054 * @example
2055 * swu.columnsSvg('𝠀񁲡񈩧𝠃𝤘𝤣񁲡𝣳𝣩񈩧𝤉𝣻 𝠀񃊢񃊫񋛕񆇡𝠃𝤘𝤧񃊫𝣻𝤕񃊢𝣴𝣼񆇡𝤎𝤂񋛕𝤆𝣦 񏌁𝣢𝤂',{
2056 * "height": 250,
2057 * "width": 150,
2058 * })
2059 *
2060 * return [`<svg version="1.1" xmlns="http://www.w3.org/2000/svg" width="150" height="250" viewBox="0 0 150 250">
2061 * <g transform="translate(56,20) scale(1) translate(-481,-471) ">
2062 * <text font-size="0">𝠀񁲡񈩧𝠃𝤘𝤣񁲡𝣳𝣩񈩧𝤉𝣻-D_black,white_Z1</text>
2063 * <g transform="translate(481,471)">
2064 * <text class="sym-fill" fill="white" style="pointer-events:none;font-family:'SuttonSignWritingFill';font-size:30px;">􁲥</text>
2065 * <text class="sym-line" fill="black" style="pointer-events:none;font-family:'SuttonSignWritingLine';font-size:30px;">󹲥</text>
2066 * </g>
2067 * <g transform="translate(503,489)">
2068 * <text class="sym-fill" fill="white" style="pointer-events:none;font-family:'SuttonSignWritingFill';font-size:30px;">􈊧</text>
2069 * <text class="sym-line" fill="black" style="pointer-events:none;font-family:'SuttonSignWritingLine';font-size:30px;">󸊧</text>
2070 * </g>
2071 * </g>
2072 * <g transform="translate(57,118) scale(1) translate(-482,-468) ">
2073 * <text font-size="0">𝠀񃊢񃊫񋛕񆇡𝠃𝤘𝤧񃊫𝣻𝤕񃊢𝣴𝣼񆇡𝤎𝤂񋛕𝤆𝣦-D_black,white_Z1</text>
2074 * <g transform="translate(489,515)">
2075 * <text class="sym-fill" fill="white" style="pointer-events:none;font-family:'SuttonSignWritingFill';font-size:30px;">􃊫</text>
2076 * <text class="sym-line" fill="black" style="pointer-events:none;font-family:'SuttonSignWritingLine';font-size:30px;">󳊫</text>
2077 * </g>
2078 * <g transform="translate(482,490)">
2079 * <text class="sym-fill" fill="white" style="pointer-events:none;font-family:'SuttonSignWritingFill';font-size:30px;">􃊢</text>
2080 * <text class="sym-line" fill="black" style="pointer-events:none;font-family:'SuttonSignWritingLine';font-size:30px;">󳊢</text>
2081 * </g>
2082 * <g transform="translate(508,496)">
2083 * <text class="sym-fill" fill="white" style="pointer-events:none;font-family:'SuttonSignWritingFill';font-size:30px;">􆇡</text>
2084 * <text class="sym-line" fill="black" style="pointer-events:none;font-family:'SuttonSignWritingLine';font-size:30px;">󶇡</text>
2085 * </g>
2086 * <g transform="translate(500,468)">
2087 * <text class="sym-fill" fill="white" style="pointer-events:none;font-family:'SuttonSignWritingFill';font-size:30px;">􋛕</text>
2088 * <text class="sym-line" fill="black" style="pointer-events:none;font-family:'SuttonSignWritingLine';font-size:30px;">󻛕</text>
2089 * </g>
2090 * </g>
2091 * <g transform="translate(39,203) scale(1) translate(-464,-496) ">
2092 * <text font-size="0">񏌁𝣢𝤂-D_black,white_Z1</text>
2093 * <g transform="translate(464,496)">
2094 * <text class="sym-fill" fill="white" style="pointer-events:none;font-family:'SuttonSignWritingFill';font-size:30px;">􏌁</text>
2095 * <text class="sym-line" fill="black" style="pointer-events:none;font-family:'SuttonSignWritingLine';font-size:30px;">󿌁</text>
2096 * </g>
2097 * </g>
2098 * </svg>`]
2099 */
2100const columnsSvg = function (swuText, options) {
2101 if (typeof options !== 'object') options = {};
2102 let values = columns(swuText, options);
2103 let cols = values.columns.map((col, i) => {
2104 return columnSvg(col, {
2105 ...values.options,
2106 ...{
2107 width: values.widths[i]
2108 }
2109 });
2110 });
2111 return cols;
2112};
2113
2114const columnCanvas = function (swuColumn, options) {
2115 if (typeof options !== 'object') options = {};
2116 const values = Object.assign(columnDefaults, options);
2117 const canvas = document.createElement('canvas');
2118 canvas.width = values.width;
2119 canvas.height = values.height;
2120 const context = canvas.getContext('2d');
2121 if (values.background) {
2122 context.rect(0, 0, values.width, values.height);
2123 context.fillStyle = values.background;
2124 context.fill();
2125 }
2126 swuColumn.map(item => {
2127 const dash = item.text.indexOf('-');
2128 if (dash > 0) {
2129 const itemStyle = item.text.substring(dash);
2130 const newStyle = {
2131 ...values.style,
2132 ...parse(itemStyle)
2133 };
2134 item.text = item.text.replace(itemStyle, compose(newStyle));
2135 } else {
2136 item.text += compose(values.style);
2137 }
2138 item.zoom = item.zoom * values.style.zoom;
2139 let parsed = {};
2140 if (item.segment == "sign") {
2141 parsed = parse$2.sign(item.text);
2142 } else {
2143 let sym = parse$2.symbol(item.text);
2144 parsed.style = sym.style;
2145 parsed.spatials = [sym];
2146 }
2147 let styling = parse(parsed.style);
2148 if (styling.background) {
2149 context.fillStyle = styling.background;
2150 context.fillRect(item.x - styling.padding * item.zoom, item.y - styling.padding * item.zoom, (item.width + styling.padding * 2) * item.zoom, (item.height + styling.padding * 2) * item.zoom);
2151 }
2152 if (styling.detailsym) {
2153 styling.detailsym.forEach(sym => {
2154 if (parsed.spatials[sym.index - 1]) {
2155 parsed.spatials[sym.index - 1].detail = sym.detail;
2156 }
2157 });
2158 }
2159 const line = styling.detail && styling.detail[0] || "black";
2160 const fill = styling.detail && styling.detail[1] || "white";
2161 parsed.spatials.forEach(spatial => {
2162 let symLine = line;
2163 if (spatial.detail) {
2164 symLine = spatial.detail[0];
2165 } else if (styling.colorize) {
2166 symLine = colorize(spatial.symbol);
2167 }
2168 let symFill = fill;
2169 if (spatial.detail && spatial.detail[1]) {
2170 symFill = spatial.detail[1];
2171 }
2172 context.font = 30 * item.zoom + "px 'SuttonSignWritingFill'";
2173 context.fillStyle = symFill;
2174 context.fillText(symbolFill(spatial.symbol), item.x + (spatial.coord[0] - item.minX) * item.zoom, item.y + (spatial.coord[1] - item.minY) * item.zoom);
2175 context.font = 30 * item.zoom + "px 'SuttonSignWritingLine'";
2176 context.fillStyle = symLine;
2177 context.fillText(symbolLine(spatial.symbol), item.x + (spatial.coord[0] - item.minX) * item.zoom, item.y + (spatial.coord[1] - item.minY) * item.zoom);
2178 });
2179 });
2180 return canvas;
2181};
2182
2183/**
2184 * Function that creates a PNG data url for a column of SWU
2185 * @function swu.columnPng
2186 * @param {ColumnData} swuColumn - an array of SWU signs and punctuation with coordinates
2187 * @param {ColumnOptions} options - an object of column options
2188 * @returns {string} column png data url
2189 * @example
2190 * swu.columnPng([
2191 * {
2192 * "x": 56,
2193 * "y": 20,
2194 * "minX": 481,
2195 * "minY": 471,
2196 * "width": 37,
2197 * "height": 58,
2198 * "lane": 0,
2199 * "padding": 0,
2200 * "segment": "sign",
2201 * "text": "𝠀񁲡񈩧𝠃𝤘𝤣񁲡𝣳𝣩񈩧𝤉𝣻",
2202 * "zoom": 1
2203 * },
2204 * {
2205 * "x": 57,
2206 * "y": 118,
2207 * "minX": 482,
2208 * "minY": 468,
2209 * "width": 36,
2210 * "height": 65,
2211 * "lane": 0,
2212 * "padding": 0,
2213 * "segment": "sign",
2214 * "text": "𝠀񃊢񃊫񋛕񆇡𝠃𝤘𝤧񃊫𝣻𝤕񃊢𝣴𝣼񆇡𝤎𝤂񋛕𝤆𝣦",
2215 * "zoom": 1
2216 * },
2217 * {
2218 * "x": 39,
2219 * "y": 203,
2220 * "minX": 464,
2221 * "minY": 496,
2222 * "width": 72,
2223 * "height": 8,
2224 * "lane": 0,
2225 * "padding": 0,
2226 * "segment": "symbol",
2227 * "text": "񏌁𝣢𝤂",
2228 * "zoom": 1
2229 * }
2230 * ],
2231 * {
2232 * "height": 250,
2233 * "width": 150,
2234 * })
2235 *
2236 * return 'data:image/png;base64,iVBORw...'
2237 */
2238const columnPng = (swuColumn, options) => {
2239 const canvas = columnCanvas(swuColumn, options);
2240 const png = canvas.toDataURL("image/png");
2241 canvas.remove();
2242 return png;
2243};
2244
2245/**
2246 * Function that creates an SVG image for a column of SWU
2247 * @function swu.columnsPng
2248 * @param {string} swuText - an array of SWU signs and punctuation with coordinates
2249 * @param {ColumnOptions} options - an object of column options
2250 * @returns {string[]} array of PNG data urls
2251 * @example
2252 * swu.columnsPng('𝠀񁲡񈩧𝠃𝤘𝤣񁲡𝣳𝣩񈩧𝤉𝣻 𝠀񃊢񃊫񋛕񆇡𝠃𝤘𝤧񃊫𝣻𝤕񃊢𝣴𝣼񆇡𝤎𝤂񋛕𝤆𝣦 񏌁𝣢𝤂',{
2253 * "height": 250,
2254 * "width": 150,
2255 * })
2256 *
2257 * return ['data:image/png;base64,iVBORw...']
2258 */
2259const columnsPng = function (swuText, options) {
2260 if (typeof options !== 'object') options = {};
2261 let values = columns(swuText, options);
2262 let cols = values.columns.map((col, i) => {
2263 return columnPng(col, {
2264 ...values.options,
2265 ...{
2266 width: values.widths[i]
2267 }
2268 });
2269 });
2270 return cols;
2271};
2272
2273export { columnPng, columnSvg, columnsPng, columnsSvg, signNormalize, signPng, signSvg, signSvgBody, symbolFill, symbolFlop, symbolLine, symbolMirror, symbolNormalize, symbolPng, symbolRotate, symbolScroll, symbolSize, symbolSvg, symbolSvgBody, symbolText };
2274
2275/* support ongoing development on https://patreon.com/signwriting */