UNPKG

26.1 kBJavaScriptView Raw
1/**
2* Sutton SignWriting Core Module v1.4.2 (https://github.com/sutton-signwriting/core)
3* Author: Steve Slevinski (https://SteveSlevinski.me)
4* swu.mjs is released under the MIT License.
5*/
6
7/**
8 * Object of regular expressions for SWU strings in UTF-16
9 *
10 * @alias swu.re
11 * @property {string} symbol - regular expressions for a symbol
12 * @property {string} coord - regular expressions for a coordinate
13 * @property {string} sort - regular expressions for the sorting marker
14 * @property {string} box - regular expression for a signbox marker
15 * @property {string} prefix - regular expression for a sorting marker followed by one or more symbols
16 * @property {string} spatial - regular expression for a symbol followed by a coordinate
17 * @property {string} signbox - regular expression for a signbox marker, max coordinate and zero or more spatial symbols
18 * @property {string} sign - regular expression for an optional prefix followed by a signbox
19 * @property {string} sortable - regular expression for a mandatory prefix followed by a signbox
20 */
21let re$1 = {
22 'symbol': '(?:(?:\uD8C0[\uDC01-\uDFFF])|(?:[\uD8C1-\uD8FC][\uDC00-\uDFFF])|(?:\uD8FD[\uDC00-\uDC80]))',
23 'coord': '(?:\uD836[\uDC0C-\uDDFF]){2}',
24 'sort': '\uD836\uDC00',
25 'box': '\uD836[\uDC01-\uDC04]'
26};
27re$1.prefix = `(?:${re$1.sort}(?:${re$1.symbol})+)`;
28re$1.spatial = `${re$1.symbol}${re$1.coord}`;
29re$1.signbox = `${re$1.box}${re$1.coord}(?:${re$1.spatial})*`;
30re$1.sign = `${re$1.prefix}?${re$1.signbox}`;
31re$1.sortable = `${re$1.prefix}${re$1.signbox}`;
32
33/**
34 * Object of regular expressions for style strings
35 *
36 * @alias style.re
37 * @type {object}
38 * @property {string} colorize - regular expression for colorize section
39 * @property {string} colorhex - regular expression for color hex values with 3 or 6 characters
40 * @property {string} colorname - regular expression for css color name
41 * @property {string} padding - regular expression for padding section
42 * @property {string} zoom - regular expression for zoom section
43 * @property {string} classbase - regular expression for class name definition
44 * @property {string} id - regular expression for id definition
45 * @property {string} colorbase - regular expression for color hex or color name
46 * @property {string} color - regular expression for single color entry
47 * @property {string} colors - regular expression for double color entry
48 * @property {string} background - regular expression for background section
49 * @property {string} detail - regular expression for color details for line and optional fill
50 * @property {string} detailsym - regular expression for color details for individual symbols
51 * @property {string} classes - regular expression for one or more class names
52 * @property {string} full - full regular expression for style string
53 */
54let re = {
55 'colorize': 'C',
56 'colorhex': '(?:[0-9a-fA-F]{3}){1,2}',
57 'colorname': '[a-zA-Z]+',
58 'padding': 'P[0-9]{2}',
59 'zoom': 'Z(?:[0-9]+(?:\\.[0-9]+)?|x)',
60 'classbase': '-?[_a-zA-Z][_a-zA-Z0-9-]{0,100}',
61 'id': '[a-zA-Z][_a-zA-Z0-9-]{0,100}'
62};
63re.colorbase = `(?:${re.colorhex}|${re.colorname})`;
64re.color = `_${re.colorbase}_`;
65re.colors = `_${re.colorbase}(?:,${re.colorbase})?_`;
66re.background = `G${re.color}`;
67re.detail = `D${re.colors}`;
68re.detailsym = `D[0-9]{2}${re.colors}`;
69re.classes = `${re.classbase}(?: ${re.classbase})*`;
70re.full = `-(${re.colorize})?(${re.padding})?(${re.background})?(${re.detail})?(${re.zoom})?(?:-((?:${re.detailsym})*))?(?:-(${re.classes})?!(?:(${re.id})!)?)?`;
71
72const prefixColor = color => {
73 const regex = new RegExp(`^${re.colorhex}$`);
74 return (regex.test(color) ? '#' : '') + color;
75};
76
77const definedProps = obj => Object.fromEntries(Object.entries(obj).filter(([k, v]) => v !== undefined));
78/**
79 * Function to parse style string to object
80 * @function style.parse
81 * @param {string} styleString - a style string
82 * @returns {StyleObject} elements of style string
83 * @example
84 * style.parse('-CP10G_blue_D_red,Cyan_')
85 *
86 * return {
87 * 'colorize': true,
88 * 'padding': 10,
89 * 'background': 'blue',
90 * 'detail': ['red', 'Cyan']
91 * }
92 */
93
94
95const parse$1 = styleString => {
96 const regex = `^${re.full}`;
97 const m = (typeof styleString === 'string' ? styleString.match(new RegExp(regex)) : []) || [];
98 return definedProps({
99 'colorize': !m[1] ? undefined : !!m[1],
100 'padding': !m[2] ? undefined : parseInt(m[2].slice(1)),
101 'background': !m[3] ? undefined : prefixColor(m[3].slice(2, -1)),
102 'detail': !m[4] ? undefined : m[4].slice(2, -1).split(',').map(prefixColor),
103 'zoom': !m[5] ? undefined : m[5] === 'Zx' ? 'x' : parseFloat(m[5].slice(1)),
104 'detailsym': !m[6] ? undefined : m[6].match(new RegExp(re.detailsym, 'g')).map(val => {
105 const parts = val.split('_');
106 const detail = parts[1].split(',').map(prefixColor);
107 return {
108 'index': parseInt(parts[0].slice(1)),
109 'detail': detail
110 };
111 }),
112 'classes': !m[7] ? undefined : m[7],
113 'id': !m[8] ? undefined : m[8]
114 });
115};
116
117/** 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.
118 * [Characters set definitions](https://tools.ietf.org/id/draft-slevinski-formal-signwriting-09.html#name-characters)
119 * @module convert
120 */
121/**
122 * Function to convert an SWU number character to an integer
123 * @function convert.swu2num
124 * @param {string} swuNum - SWU number character
125 * @returns {number} Integer value for number
126 * @example
127 * convert.swu2num('𝤆')
128 *
129 * return 500
130 */
131
132
133const swu2num = swuNum => parseInt(swuNum.codePointAt(0)) - 0x1D80C + 250;
134/**
135 * Function to convert a number to an SWU number character
136 * @function convert.num2swu
137 * @param {number} num - Integer value for number
138 * @returns {string} SWU number character
139 * @example
140 * convert.num2swu(500)
141 *
142 * return '𝤆'
143 */
144
145
146const num2swu = num => String.fromCodePoint(0x1D80C + parseInt(num) - 250);
147/**
148 * Function to convert two SWU number characters to an array of x,y integers
149 * @function convert.swu2coord
150 * @param {string} swuCoord - Two SWU number character
151 * @returns {number[]} Array of x,y integers
152 * @example
153 * convert.swu2coord('𝤆𝤆')
154 *
155 * return [500, 500]
156 */
157
158
159const swu2coord = swuCoord => [swu2num(swuCoord.slice(0, 2)), swu2num(swuCoord.slice(2, 4))];
160/**
161 * Function to convert an array of x,y integers to two SWU number characters
162 * @function convert.coord2swu
163 * @param {number[]} coord - Array of x,y integers
164 * @returns {string} Two SWU number character
165 * @example
166 * convert.coord2swu([500, 500])
167 *
168 * return '𝤆𝤆'
169 */
170
171
172const coord2swu = coord => coord.map(num => num2swu(num)).join('');
173/**
174 * Function to convert an SWU symbol character to a code point on plane 4
175 * @function convert.swu2code
176 * @param {string} swuSym - SWU symbol character
177 * @returns {number} Code point on plane 4
178 * @example
179 * convert.swu2code('񀀁')
180 *
181 * return 0x40001
182 */
183
184
185const swu2code = swuSym => parseInt(swuSym.codePointAt(0));
186
187const parse = {
188 /**
189 * Function to parse an swu symbol with optional coordinate and style string
190 * @function swu.parse.symbol
191 * @param {string} swuSym - an swu symbol
192 * @returns {object} elements of swu symbol
193 * @example
194 * swu.parse.symbol('񀀁𝤆𝤆-C')
195 *
196 * return {
197 * 'symbol': '񀀁',
198 * 'coord': [500, 500],
199 * 'style': '-C'
200 * }
201 */
202 symbol: swuSym => {
203 const regex = `^(${re$1.symbol})(${re$1.coord})?(${re.full})?`;
204 const symbol = typeof swuSym === 'string' ? swuSym.match(new RegExp(regex)) : undefined;
205 return {
206 'symbol': symbol ? symbol[1] : undefined,
207 'coord': symbol && symbol[2] ? swu2coord(symbol[2]) : undefined,
208 'style': symbol ? symbol[3] : undefined
209 };
210 },
211
212 /**
213 * Function to parse an swu sign with style string
214 * @function swu.parse.sign
215 * @param {string} swuSign - an swu sign
216 * @returns {object} elements of swu sign
217 * @example
218 * swu.parse.sign('𝠀񀀒񀀚񋚥񋛩𝠃𝤟𝤩񋛩𝣵𝤐񀀒𝤇𝣤񋚥𝤐𝤆񀀚𝣮𝣭-C')
219 *
220 * return {
221 * sequence: ['񀀒','񀀚','񋚥','񋛩'],
222 * box: '𝠃',
223 * max: [525, 535],
224 * spatials: [
225 * {
226 * symbol: '񋛩',
227 * coord: [483, 510]
228 * },
229 * {
230 * symbol: '񀀒',
231 * coord: [501, 466]
232 * },
233 * {
234 * symbol: '񋚥',
235 * coord: [510, 500]
236 * },
237 * {
238 * symbol: '񀀚',
239 * coord: [476, 475]
240 * }
241 * ],
242 * style: '-C'
243 * }
244 */
245 sign: swuSign => {
246 const regex = `^(${re$1.prefix})?(${re$1.signbox})(${re.full})?`;
247 const sign = typeof swuSign === 'string' ? swuSign.match(new RegExp(regex)) : undefined;
248
249 if (sign) {
250 return {
251 'sequence': sign[1] ? sign[1].slice(2).match(/.{2}/g) : undefined,
252 'box': sign[2].slice(0, 2),
253 'max': swu2coord(sign[2].slice(2, 6)),
254 'spatials': sign[2].length < 7 ? undefined : sign[2].slice(6).match(/(.{6})/g).map(m => {
255 return {
256 symbol: m.slice(0, 2),
257 coord: swu2coord(m.slice(2))
258 };
259 }),
260 'style': sign[3]
261 };
262 } else {
263 return {};
264 }
265 },
266
267 /**
268 * Function to parse an swu text
269 * @function swu.parse.text
270 * @param {string} swuText - an swu text
271 * @returns {array} swu signs and punctuations
272 * @example
273 * swu.parse.text('𝠀񁲡񈩧𝠃𝤘𝤣񁲡𝣳𝣩񈩧𝤉𝣻 𝠀񃊢񃊫񋛕񆇡𝠃𝤘𝤧񃊫𝣻𝤕񃊢𝣴𝣼񆇡𝤎𝤂񋛕𝤆𝣦 񏌁𝣢𝤂')
274 *
275 * return [
276 * '𝠀񁲡񈩧𝠃𝤘𝤣񁲡𝣳𝣩񈩧𝤉𝣻',
277 * '𝠀񃊢񃊫񋛕񆇡𝠃𝤘𝤧񃊫𝣻𝤕񃊢𝣴𝣼񆇡𝤎𝤂񋛕𝤆𝣦',
278 * '񏌁𝣢𝤂'
279 * ]
280 */
281 text: swuText => {
282 if (typeof swuText !== 'string') return [];
283 const regex = `(${re$1.sign}(${re.full})?|${re$1.spatial}(${re.full})?)`;
284 const matches = swuText.match(new RegExp(regex, 'g'));
285 return matches ? [...matches] : [];
286 }
287};
288/**
289 * Function to encode SWU characters using the UTF-16 escape format.
290 * @function swu.encode
291 * @param {string} swu - SWU characters
292 * @returns {string} UTF-16 escape format
293 * @example
294 * swu.encode('񀀁𝤆𝤆')
295 *
296 * return '\\uD8C0\\uDC01\\uD836\\uDD06\\uD836\\uDD06'
297 */
298
299const encode = text => text.replace(/[\u007F-\uFFFF]/g, function (chr) {
300 return "\\u" + ("0000" + chr.charCodeAt(0).toString(16)).substr(-4).toUpperCase();
301});
302/**
303 * Function to decode UTF-16 escape format to SWU characters.
304 * @function swu.decode
305 * @param {string} encoded - UTF-16 escape format
306 * @returns {string} SWU characters
307 * @example
308 * swu.decode('\\uD8C0\\uDC01\\uD836\\uDD06\\uD836\\uDD06')
309 *
310 * return '񀀁𝤆𝤆'
311 */
312
313
314const decode = encoded => encoded.replace(/\\u([0-9A-F]{4})/g, function (match, chr) {
315 return String.fromCharCode(parseInt(chr, 16));
316});
317/**
318 * Function to decompose an SWU character into UTF-16 surrogate pairs.
319 * @function swu.pair
320 * @param {string} swuChar - an SWU character
321 * @returns {string[]} an array of UTF-16 surrogate pairs
322 * @example
323 * swu.pair('񀀁')
324 *
325 * return ['D8C0', 'DC01']
326 */
327
328
329const pair = swuChar => [swuChar.charCodeAt(0).toString(16).toUpperCase(), swuChar.charCodeAt(1).toString(16).toUpperCase()];
330
331const compose = {
332 /**
333 * Function to compose an swu symbol with optional coordinate and style string
334 * @function swu.compose.symbol
335 * @param {object} swuSymObject - an swu symbol object
336 * @param {string} swuSymObject.symbol - an swu symbol key
337 * @param {number[]} swuSymObject.coord - top-left coordinate of symbol with 500,500 center
338 * @param {string} swuSymObject.style - a style string for custom appearance
339 * @returns {string} an swu symbol string
340 * @example
341 * swu.compose.symbol({
342 * 'symbol': '񀀁',
343 * 'coord': [500, 500],
344 * 'style': '-C'
345 * })
346 *
347 * return '񀀁𝤆𝤆-C'
348 */
349 symbol: swuSymObject => {
350 if (typeof swuSymObject !== 'object' || swuSymObject === null) return undefined;
351
352 if (typeof swuSymObject.symbol === 'string') {
353 const symbol = (swuSymObject.symbol.match(re$1.symbol) || [''])[0];
354
355 if (symbol) {
356 const x = swuSymObject.coord && swuSymObject.coord[0] || '';
357 const y = swuSymObject.coord && swuSymObject.coord[1] || '';
358 const coord = x && y ? coord2swu([x, y]) : '';
359 const styleStr = typeof swuSymObject.style === 'string' && (swuSymObject.style.match(re.full) || [''])[0] || '';
360 return symbol + coord + styleStr;
361 }
362 }
363
364 return undefined;
365 },
366
367 /**
368 * Function to compose an swu sign with style string
369 * @function swu.compose.sign
370 * @param {object} swuSignObject - an swu sign object
371 * @param {string[]} swuSignObject.sequence - an ordered array of symbols
372 * @param {string} swuSignObject.box - a choice of signbox marker: horizontal Box, Left, Middle, and Right lane
373 * @param {number[]} swuSignObject.max - max bottom-right coordinate of the signbox space
374 * @param {{symbol:string,coord:number[]}[]} swuSignObject.spatials - array of symbols with top-left coordinate placement
375 * @param {string} swuSignObject.style - a style string for custom appearance
376 * @returns {string} an swu sign string
377 * @example
378 * swu.compose.sign({
379 * sequence: ['񀀒','񀀚','񋚥','񋛩'],
380 * box: '𝠃',
381 * max: [525, 535],
382 * spatials: [
383 * {
384 * symbol: '񋛩',
385 * coord: [483, 510]
386 * },
387 * {
388 * symbol: '񀀒',
389 * coord: [501, 466]
390 * },
391 * {
392 * symbol: '񋚥',
393 * coord: [510, 500]
394 * },
395 * {
396 * symbol: '񀀚',
397 * coord: [476, 475]
398 * }
399 * ],
400 * style: '-C'
401 * })
402 *
403 * return '𝠀񀀒񀀚񋚥񋛩𝠃𝤟𝤩񋛩𝣵𝤐񀀒𝤇𝣤񋚥𝤐𝤆񀀚𝣮𝣭-C'
404 */
405 sign: swuSignObject => {
406 if (typeof swuSignObject !== 'object' || swuSignObject === null) return undefined;
407 let box = typeof swuSignObject.box !== 'string' ? '𝠃' : (swuSignObject.box + '𝠃').match(re$1.box);
408 const x = swuSignObject.max && swuSignObject.max[0] || '';
409 const y = swuSignObject.max && swuSignObject.max[1] || '';
410 const max = x && y ? coord2swu([x, y]) : undefined;
411 if (!max) return undefined;
412 let prefix = '';
413
414 if (swuSignObject.sequence && Array.isArray(swuSignObject.sequence)) {
415 prefix = swuSignObject.sequence.map(key => (key.match(re$1.symbol) || [''])[0]).join('');
416 prefix = prefix ? '𝠀' + prefix : '';
417 }
418
419 let signbox = '';
420
421 if (swuSignObject.spatials && Array.isArray(swuSignObject.spatials)) {
422 signbox = swuSignObject.spatials.map(spatial => {
423 if (typeof spatial.symbol === 'string') {
424 const symbol = (spatial.symbol.match(re$1.symbol) || [''])[0];
425
426 if (symbol) {
427 const x = spatial.coord && spatial.coord[0] || '';
428 const y = spatial.coord && spatial.coord[1] || '';
429 const coord = x && y ? coord2swu([x, y]) : '';
430
431 if (coord) {
432 return symbol + coord;
433 }
434 }
435 }
436
437 return '';
438 }).join('');
439 }
440
441 const styleStr = typeof swuSignObject.style === 'string' && (swuSignObject.style.match(re.full) || [''])[0] || '';
442 return prefix + box + max + signbox + styleStr;
443 }
444};
445
446/**
447 * Function to gather sizing information about an swu sign or symbol
448 * @function swu.info
449 * @param {string} swu - an swu sign or symbol
450 * @returns {object} information about the swu string
451 * @example
452 * swu.info('𝠀񁲡񈩧𝠂𝤘𝤣񁲡𝣳𝣩񈩧𝤉𝣻-P10Z2')
453 *
454 * return {
455 * minX: 481,
456 * minY: 471,
457 * width: 37,
458 * height: 58,
459 * segment: 'sign',
460 * lane: -1
461 * padding: 10,
462 * zoom: 2
463 * }
464 */
465
466const info = swu => {
467 let lanes = {
468 '𝠁': 0,
469 '𝠂': -1,
470 '𝠃': 0,
471 '𝠄': 1
472 };
473 let parsed = parse.sign(swu);
474 let width, height, segment, x1, x2, y1, y2, lane;
475
476 if (parsed.spatials) {
477 x1 = Math.min(...parsed.spatials.map(spatial => spatial.coord[0]));
478 x2 = parsed.max[0];
479 width = x2 - x1;
480 y1 = Math.min(...parsed.spatials.map(spatial => spatial.coord[1]));
481 y2 = parsed.max[1];
482 height = y2 - y1;
483 segment = 'sign';
484 lane = parsed.box;
485 } else {
486 parsed = parse.symbol(swu);
487 lane = "𝠃";
488
489 if (parsed.coord) {
490 x1 = parsed.coord[0];
491 width = (500 - x1) * 2;
492 y1 = parsed.coord[1];
493 height = (500 - y1) * 2;
494 segment = 'symbol';
495 } else {
496 x1 = 490;
497 width = 20;
498 y1 = 490;
499 height = 20;
500 segment = 'none';
501 }
502 }
503
504 let style = parse$1(parsed.style);
505 let zoom = style.zoom || 1;
506 let padding = style.padding || 0;
507 return {
508 minX: x1,
509 minY: y1,
510 width: width,
511 height: height,
512 segment: segment,
513 lane: lanes[lane],
514 padding: padding,
515 zoom: zoom
516 };
517};
518
519const columnDefaults = {
520 'height': 500,
521 'width': 150,
522 'offset': 50,
523 'pad': 20,
524 'margin': 5,
525 'dynamic': false,
526 'background': undefined,
527 'punctuation': {
528 'spacing': true,
529 'pad': 30,
530 'pull': true
531 },
532 'style': {
533 'detail': ['black', 'white'],
534 'zoom': 1
535 }
536};
537/**
538 * Function to an object of column options with default values
539 *
540 * @function swu.columnDefaultsMerge
541 * @param {ColumnOptions} options - object of column options
542 * @returns {ColumnOptions} object of column options merged with column defaults
543 * @example
544 * swu.columnDefaultsMerge({height: 500,width:150})
545 *
546 * return {
547 * "height": 500,
548 * "width": 150,
549 * "offset": 50,
550 * "pad": 20,
551 * "margin": 5,
552 * "dynamic": false,
553 * "punctuation": {
554 * "spacing": true,
555 * "pad": 30,
556 * "pull": true
557 * },
558 * "style": {
559 * "detail": [
560 * "black",
561 * "white"
562 * ],
563 * "zoom": 1
564 * }
565 * }
566 */
567
568const columnDefaultsMerge = options => {
569 if (typeof options !== 'object') options = {};
570 return { ...columnDefaults,
571 ...options,
572 punctuation: { ...columnDefaults.punctuation,
573 ...options.punctuation
574 },
575 style: { ...columnDefaults.style,
576 ...options.style
577 }
578 };
579};
580/**
581 * Function to transform an SWU text to an array of columns
582 *
583 * @function swu.columns
584 * @param {string} swuText - SWU text of signs and punctuation
585 * @param {ColumnOptions} options - object of column options
586 * @returns {{options:ColumnOptions,widths:number[],columns:ColumnData}} object of column options, widths array, and column data
587 * @example
588 * swu.columns('𝠀񁲡񈩧𝠃𝤘𝤣񁲡𝣳𝣩񈩧𝤉𝣻 𝠀񃊢񃊫񋛕񆇡𝠃𝤘𝤧񃊫𝣻𝤕񃊢𝣴𝣼񆇡𝤎𝤂񋛕𝤆𝣦 񏌁𝣢𝤂', {height: 500,width:150})
589 *
590 * return {
591 * "options": {
592 * "height": 500,
593 * "width": 150,
594 * "offset": 50,
595 * "pad": 20,
596 * "margin": 5,
597 * "dynamic": false,
598 * "punctuation": {
599 * "spacing": true,
600 * "pad": 30,
601 * "pull": true
602 * },
603 * "style": {
604 * "detail": [
605 * "black",
606 * "white"
607 * ],
608 * "zoom": 1
609 * }
610 * },
611 * "widths": [
612 * 150
613 * ],
614 * "columns": [
615 * [
616 * {
617 * "x": 56,
618 * "y": 20,
619 * "minX": 481,
620 * "minY": 471,
621 * "width": 37,
622 * "height": 58,
623 * "lane": 0,
624 * "padding": 0,
625 * "segment": "sign",
626 * "text": "𝠀񁲡񈩧𝠃𝤘𝤣񁲡𝣳𝣩񈩧𝤉𝣻",
627 * "zoom": 1
628 * },
629 * {
630 * "x": 57,
631 * "y": 118,
632 * "minX": 482,
633 * "minY": 468,
634 * "width": 36,
635 * "height": 65,
636 * "lane": 0,
637 * "padding": 0,
638 * "segment": "sign",
639 * "text": "𝠀񃊢񃊫񋛕񆇡𝠃𝤘𝤧񃊫𝣻𝤕񃊢𝣴𝣼񆇡𝤎𝤂񋛕𝤆𝣦",
640 * "zoom": 1
641 * },
642 * {
643 * "x": 39,
644 * "y": 203,
645 * "minX": 464,
646 * "minY": 496,
647 * "width": 72,
648 * "height": 8,
649 * "lane": 0,
650 * "padding": 0,
651 * "segment": "symbol",
652 * "text": "񏌁𝣢𝤂",
653 * "zoom": 1
654 * }
655 * ]
656 * ]
657 * }
658 */
659
660
661const columns = (swuText, options) => {
662 if (typeof swuText !== 'string') return {};
663 const values = columnDefaultsMerge(options);
664 let input = parse.text(swuText);
665 let cursor = 0;
666 let cols = [];
667 let col = [];
668 let plus = 0;
669 let center = parseInt(values.width / 2);
670 let maxHeight = values.height - values.margin;
671 let pullable = true;
672 let finalize = false;
673
674 for (let val of input) {
675 let informed = info(val);
676 cursor += plus;
677
678 if (values.punctuation.spacing) {
679 cursor += informed.segment == 'sign' ? values.pad : 0;
680 } else {
681 cursor += values.pad;
682 }
683
684 finalize = cursor + informed.height > maxHeight;
685
686 if (finalize && informed.segment == 'symbol' && values.punctuation.pull && pullable) {
687 finalize = false;
688 pullable = false;
689 }
690
691 if (col.length == 0) {
692 finalize = false;
693 }
694
695 if (finalize) {
696 cursor = values.pad;
697 cols.push(col);
698 col = [];
699 pullable = true;
700 }
701
702 col.push(Object.assign(informed, {
703 x: center + values.offset * informed.lane - (500 - informed.minX) * informed.zoom * values.style.zoom,
704 y: cursor,
705 text: val
706 }));
707 cursor += informed.height * informed.zoom * values.style.zoom;
708
709 if (values.punctuation.spacing) {
710 plus = informed.segment == 'sign' ? values.pad : values.punctuation.pad;
711 } else {
712 plus = values.pad;
713 }
714 }
715
716 if (col.length) {
717 cols.push(col);
718 } // over height issue when pulling punctuation
719
720
721 if (values.punctuation.pull) {
722 for (let col of cols) {
723 let last = col[col.length - 1];
724 let diff = last.y + last.height - (values.height - values.margin);
725
726 if (diff > 0) {
727 let adj = parseInt(diff / col.length) + 1;
728
729 for (let i in col) {
730 col[i].y -= adj * i + adj;
731 }
732 }
733 }
734 } // contract, expand, adjust
735
736
737 let widths = [];
738
739 for (let col of cols) {
740 let min = [center - values.offset - values.pad];
741 let max = [center + values.offset + values.pad];
742
743 for (let item of col) {
744 min.push(item.x - values.pad);
745 max.push(item.x + item.width + values.pad);
746 }
747
748 min = Math.min(...min);
749 max = Math.max(...max);
750 let width = values.width;
751 let adj = 0;
752
753 if (!values.dynamic) {
754 adj = center - parseInt((min + max) / 2);
755 } else {
756 width = max - min;
757 adj = -min;
758 }
759
760 for (let item of col) {
761 item.x += adj;
762 }
763
764 widths.push(width);
765 }
766
767 return {
768 'options': values,
769 'widths': widths,
770 'columns': cols
771 };
772};
773
774/**
775 * Array of plane 4 code points for kinds of symbols: writing, location, and punctuation.
776 * @alias swu.kind
777 * @type {array}
778 */
779
780const kind = [0x40001, 0x4efa1, 0x4f2a1];
781/**
782 * Array of plane 4 code points for categories of symbols: hand, movement, dynamics, head, trunk & limb, location, and punctuation.
783 * @alias swu.category
784 * @type {array}
785 */
786
787const category = [0x40001, 0x461e1, 0x4bca1, 0x4bfa1, 0x4e8e1, 0x4efa1, 0x4f2a1];
788/**
789 * Array of plane 4 code points for the 30 symbol groups.
790 * @alias swu.group
791 * @type {array}
792 */
793
794const group = [0x40001, 0x40541, 0x40b41, 0x41981, 0x41c81, 0x43241, 0x43d81, 0x445c1, 0x44ce1, 0x45be1, 0x461e1, 0x46841, 0x46fc1, 0x47fe1, 0x485e1, 0x49301, 0x49e41, 0x4a4a1, 0x4afe1, 0x4b521, 0x4bca1, 0x4bfa1, 0x4c3c1, 0x4cfc1, 0x4d621, 0x4e161, 0x4e8e1, 0x4ec41, 0x4efa1, 0x4f2a1];
795/**
796 * Object of symbol ranges with starting and ending code points on plane 4.
797 *
798 * { all, writing, hand, movement, dynamic, head, hcenter, vcenter, trunk, limb, location, punctuation }
799 * @alias swu.ranges
800 * @type {object}
801 */
802
803const ranges = {
804 'all': [0x40001, 0x4f480],
805 'writing': [0x40001, 0x4efa0],
806 'hand': [0x40001, 0x461e0],
807 'movement': [0x461e1, 0x4bca0],
808 'dynamic': [0x4bca1, 0x4bfa0],
809 'head': [0x4bfa1, 0x4e8e0],
810 'hcenter': [0x4bfa1, 0x4e8e0],
811 'vcenter': [0x4bfa1, 0x4ec40],
812 'trunk': [0x4e8e1, 0x4ec40],
813 'limb': [0x4ec41, 0x4efa0],
814 'location': [0x4efa1, 0x4f2a0],
815 'punctuation': [0x4f2a1, 0x4f480]
816};
817/**
818 * Function to test if symbol is of a certain type.
819 * @function swu.isType
820 * @param {string} swuSym - an SWU symbol character
821 * @param {string} type - the name of a symbol range
822 * @returns {boolean} is symbol of specified type
823 * @example
824 * swu.isType('񀀁', 'hand')
825 *
826 * return true
827 */
828
829const isType = (swuSym, type) => {
830 const parsed = parse.symbol(swuSym);
831
832 if (parsed.symbol) {
833 const code = swu2code(parsed.symbol);
834 const range = ranges[type];
835
836 if (range) {
837 return range[0] <= code && range[1] >= code;
838 }
839 }
840
841 return false;
842};
843
844/**
845 * Array of colors associated with the seven symbol categories.
846 * @alias swu.colors
847 * @type {array}
848 */
849
850const colors = ['#0000CC', '#CC0000', '#FF0099', '#006600', '#000000', '#884411', '#FF9900'];
851/**
852 * Function that returns the standardized color for a symbol.
853 * @function swu.colorize
854 * @param {string} swuSym - an SWU symbol character
855 * @returns {string} name of standardized color for symbol
856 * @example
857 * swu.colorize('񀀁')
858 *
859 * return '#0000CC'
860 */
861
862const colorize = swuSym => {
863 const parsed = parse.symbol(swuSym);
864 let color = '#000000';
865
866 if (parsed.symbol) {
867 const code = swu2code(parsed.symbol);
868 const index = category.findIndex(val => val > code);
869 color = colors[index < 0 ? 6 : index - 1];
870 }
871
872 return color;
873};
874
875export { category, colorize, colors, columnDefaults, columnDefaultsMerge, columns, compose, decode, encode, group, info, isType, kind, pair, parse, ranges, re$1 as re };
876
877/* support ongoing development on https://patreon.com/signwriting */