UNPKG

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