UNPKG

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