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