UNPKG

14.7 kBJavaScriptView Raw
1/**
2* Sutton SignWriting Core Module v1.2.0 (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 * { symbol, coord, sort, box, prefix, spatial, signbox, sign, sortable }
11 * @alias swu.re
12 * @type {object}
13 */
14let re = {
15 'symbol': '(?:(?:\uD8C0[\uDC01-\uDFFF])|(?:[\uD8C1-\uD8FC][\uDC00-\uDFFF])|(?:\uD8FD[\uDC00-\uDC80]))',
16 'coord': '(?:\uD836[\uDC0C-\uDDFF]){2}',
17 'sort': '\uD836\uDC00',
18 'box': '\uD836[\uDC01-\uDC04]'
19};
20re.prefix = `(?:${re.sort}(?:${re.symbol})+)`;
21re.spatial = `${re.symbol}${re.coord}`;
22re.signbox = `${re.box}${re.coord}(?:${re.spatial})*`;
23re.sign = `${re.prefix}?${re.signbox}`;
24re.sortable = `${re.prefix}${re.signbox}`;
25
26/**
27 * Object of regular expressions for style strings
28 *
29 * { colorize, colorhex, colorname, padding, zoom, zoomsym, classbase, id, colorbase, color, colors, background, detail, detailsym, classes, full }
30 * @alias style.re
31 * @type {object}
32 */
33let re$1 = {
34 'colorize': 'C',
35 'colorhex': '(?:[0-9a-fA-F]{3}){1,2}',
36 'colorname': '[a-zA-Z]+',
37 'padding': 'P[0-9]{2}',
38 'zoom': 'Z(?:[0-9]+(?:\\.[0-9]+)?|x)',
39 'zoomsym': 'Z[0-9]{2},[0-9]+(?:\\.[0-9]+)?(?:,[0-9]{3}x[0-9]{3})?',
40 'classbase': '-?[_a-zA-Z][_a-zA-Z0-9-]{0,100}',
41 'id': '[a-zA-Z][_a-zA-Z0-9-]{0,100}'
42};
43re$1.colorbase = `(?:${re$1.colorhex}|${re$1.colorname})`;
44re$1.color = `_${re$1.colorbase}_`;
45re$1.colors = `_${re$1.colorbase}(?:,${re$1.colorbase})?_`;
46re$1.background = `G${re$1.color}`;
47re$1.detail = `D${re$1.colors}`;
48re$1.detailsym = `D[0-9]{2}${re$1.colors}`;
49re$1.classes = `${re$1.classbase}(?: ${re$1.classbase})*`;
50re$1.full = `-(${re$1.colorize})?(${re$1.padding})?(${re$1.background})?(${re$1.detail})?(${re$1.zoom})?(?:-((?:${re$1.detailsym})*)((?:${re$1.zoomsym})*))?(?:-(${re$1.classes})?!(?:(${re$1.id})!)?)?`;
51
52/** 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.
53 * [Characters set definitions](https://tools.ietf.org/id/draft-slevinski-formal-signwriting-07.html#rfc.section.2.2)
54 * @module convert
55 */
56/**
57 * Function to convert an SWU number character to an integer
58 * @function convert.swu2num
59 * @param {string} swuNum - SWU number character
60 * @returns {number} Integer value for number
61 * @example
62 * convert.swu2num('𝤆')
63 *
64 * return 500
65 */
66
67
68const swu2num = swuNum => parseInt(swuNum.codePointAt(0)) - 0x1D80C + 250;
69/**
70 * Function to convert a number to an SWU number character
71 * @function convert.num2swu
72 * @param {number} num - Integer value for number
73 * @returns {string} SWU number character
74 * @example
75 * convert.num2swu(500)
76 *
77 * return '𝤆'
78 */
79
80
81const num2swu = num => String.fromCodePoint(0x1D80C + parseInt(num) - 250);
82/**
83 * Function to convert two SWU number characters to an array of x,y integers
84 * @function convert.swu2coord
85 * @param {string} swuCoord - Two SWU number character
86 * @returns {number[]} Array of x,y integers
87 * @example
88 * convert.swu2coord('𝤆𝤆')
89 *
90 * return [500, 500]
91 */
92
93
94const swu2coord = swuCoord => [swu2num(swuCoord.slice(0, 2)), swu2num(swuCoord.slice(2, 4))];
95/**
96 * Function to convert an array of x,y integers to two SWU number characters
97 * @function convert.coord2swu
98 * @param {number[]} coord - Array of x,y integers
99 * @returns {string} Two SWU number character
100 * @example
101 * convert.coord2swu([500, 500])
102 *
103 * return '𝤆𝤆'
104 */
105
106
107const coord2swu = coord => coord.map(num => num2swu(num)).join('');
108/**
109 * Function to convert an SWU symbol character to a code point on plane 4
110 * @function convert.swu2code
111 * @param {string} swuSym - SWU symbol character
112 * @returns {number} Code point on plane 4
113 * @example
114 * convert.swu2code('񀀁')
115 *
116 * return 0x40001
117 */
118
119
120const swu2code = swuSym => parseInt(swuSym.codePointAt(0));
121
122const parse = {
123 /**
124 * Function to parse an swu symbol with optional coordinate and style string
125 * @function swu.parse.symbol
126 * @param {string} swuSym - an swu symbol
127 * @returns {object} elements of swu symbol
128 * @example
129 * swu.parse.symbol('񀀁𝤆𝤆-C')
130 *
131 * return {
132 * 'symbol': '񀀁',
133 * 'coord': [500, 500],
134 * 'style': '-C'
135 * }
136 */
137 symbol: swuSym => {
138 const regex = `^(${re.symbol})(${re.coord})?(${re$1.full})?`;
139 const symbol = typeof swuSym === 'string' ? swuSym.match(new RegExp(regex)) : undefined;
140 return {
141 'symbol': symbol ? symbol[1] : undefined,
142 'coord': symbol && symbol[2] ? swu2coord(symbol[2]) : undefined,
143 'style': symbol ? symbol[3] : undefined
144 };
145 },
146
147 /**
148 * Function to parse an swu sign with style string
149 * @function swu.parse.sign
150 * @param {string} swuSign - an swu sign
151 * @returns {object} elements of swu sign
152 * @example
153 * swu.parse.sign('𝠀񀀒񀀚񋚥񋛩𝠃𝤟𝤩񋛩𝣵𝤐񀀒𝤇𝣤񋚥𝤐𝤆񀀚𝣮𝣭-C')
154 *
155 * return {
156 * sequence: ['񀀒','񀀚','񋚥','񋛩''],
157 * box: '𝠃',
158 * max: [525, 535],
159 * spatials: [
160 * {
161 * symbol: '񋛩',
162 * coord: [483, 510]
163 * },
164 * {
165 * symbol: '񀀒',
166 * coord: [501, 466]
167 * },
168 * {
169 * symbol: '񋚥',
170 * coord: [510, 500]
171 * },
172 * {
173 * symbol: '񀀚',
174 * coord: [476, 475]
175 * }
176 * ],
177 * style: '-C'
178 * }
179 */
180 sign: swuSign => {
181 const regex = `^(${re.prefix})?(${re.signbox})(${re$1.full})?`;
182 const sign = typeof swuSign === 'string' ? swuSign.match(new RegExp(regex)) : undefined;
183
184 if (sign) {
185 return {
186 'sequence': sign[1] ? sign[1].slice(2).match(/.{2}/g) : undefined,
187 'box': sign[2].slice(0, 2),
188 'max': swu2coord(sign[2].slice(2, 6)),
189 'spatials': sign[2].length < 7 ? undefined : sign[2].slice(6).match(/(.{6})/g).map(m => {
190 return {
191 symbol: m.slice(0, 2),
192 coord: swu2coord(m.slice(2))
193 };
194 }),
195 'style': sign[3]
196 };
197 } else {
198 return {};
199 }
200 }
201};
202/**
203 * Function to encode SWU characters using the UTF-16 escape format.
204 * @function swu.encode
205 * @param {string} swu - SWU characters
206 * @returns {string} UTF-16 escape format
207 * @example
208 * swu.encode('񀀁𝤆𝤆')
209 *
210 * return '\\uD8C0\\uDC01\\uD836\\uDD06\\uD836\\uDD06'
211 */
212
213const encode = text => text.replace(/[\u007F-\uFFFF]/g, function (chr) {
214 return "\\u" + ("0000" + chr.charCodeAt(0).toString(16)).substr(-4).toUpperCase();
215});
216/**
217 * Function to decode UTF-16 escape format to SWU characters.
218 * @function swu.decode
219 * @param {string} encoded - UTF-16 escape format
220 * @returns {string} SWU characters
221 * @example
222 * swu.decode('\\uD8C0\\uDC01\\uD836\\uDD06\\uD836\\uDD06')
223 *
224 * return '񀀁𝤆𝤆'
225 */
226
227
228const decode = encoded => encoded.replace(/\\u([0-9A-F]{4})/g, function (match, chr) {
229 return String.fromCharCode(parseInt(chr, 16));
230});
231/**
232 * Function to decompose an SWU character into UTF-16 surrogate pairs.
233 * @function swu.pair
234 * @param {string} swuChar - an SWU character
235 * @returns {string[]} an array of UTF-16 surrogate pairs
236 * @example
237 * swu.pair('񀀁')
238 *
239 * return ['D8C0', 'DC01']
240 */
241
242
243const pair = swuChar => [swuChar.charCodeAt(0).toString(16).toUpperCase(), swuChar.charCodeAt(1).toString(16).toUpperCase()];
244
245const compose = {
246 /**
247 * Function to compose an swu symbol with optional coordinate and style string
248 * @function swu.compose.symbol
249 * @param {object} swuSymObject - an swu symbol object
250 * @param {string} swuSymObject.symbol - an swu symbol key
251 * @param {number[]} swuSymObject.coord - top-left coordinate of symbol with 500,500 center
252 * @param {string} swuSymObject.style - a style string for custom appearance
253 * @returns {string} an swu symbol string
254 * @example
255 * swu.compose.symbol({
256 * 'symbol': '񀀁',
257 * 'coord': [500, 500],
258 * 'style': '-C'
259 * })
260 *
261 * return '񀀁𝤆𝤆-C'
262 */
263 symbol: swuSymObject => {
264 if (typeof swuSymObject !== 'object' || swuSymObject === null) return undefined;
265
266 if (typeof swuSymObject.symbol === 'string') {
267 const symbol = (swuSymObject.symbol.match(re.symbol) || [''])[0];
268
269 if (symbol) {
270 const x = swuSymObject.coord && swuSymObject.coord[0] || '';
271 const y = swuSymObject.coord && swuSymObject.coord[1] || '';
272 const coord = x && y ? coord2swu([x, y]) : '';
273 const styleStr = typeof swuSymObject.style === 'string' && (swuSymObject.style.match(re$1.full) || [''])[0] || '';
274 return symbol + coord + styleStr;
275 }
276 }
277
278 return undefined;
279 },
280
281 /**
282 * Function to compose an swu sign with style string
283 * @function swu.compose.sign
284 * @param {string[]} swuSignObject.sequence - an ordered array of symbols
285 * @param {string} swuSignObject.box - a choice BLMR: horizontal Box, Left, Middle, and Right lane
286 * @param {number[]} swuSymObject.max - max bottom left coordinate of the signbox space
287 * @param {{symbol:string,coord:number[]}[]} swuSymObject.spatials - array of symbols with top-left coordinate placement
288 * @param {string} swuSymObject.style - a style string for custom appearance
289 * @returns {string} an swu sign string
290 * @example
291 * swu.compose.sign({
292 * sequence: ['񀀒','񀀚','񋚥','񋛩''],
293 * box: '𝠃',
294 * max: [525, 535],
295 * spatials: [
296 * {
297 * symbol: '񋛩',
298 * coord: [483, 510]
299 * },
300 * {
301 * symbol: '񀀒',
302 * coord: [501, 466]
303 * },
304 * {
305 * symbol: '񋚥',
306 * coord: [510, 500]
307 * },
308 * {
309 * symbol: '񀀚',
310 * coord: [476, 475]
311 * }
312 * ],
313 * style: '-C'
314 * })
315 *
316 * return '𝠀񀀒񀀚񋚥񋛩𝠃𝤟𝤩񋛩𝣵𝤐񀀒𝤇𝣤񋚥𝤐𝤆񀀚𝣮𝣭-C'
317 */
318 sign: swuSignObject => {
319 if (typeof swuSignObject !== 'object' || swuSignObject === null) return undefined;
320 let box = typeof swuSignObject.box !== 'string' ? '𝠃' : (swuSignObject.box + '𝠃').match(re.box);
321 const x = swuSignObject.max && swuSignObject.max[0] || '';
322 const y = swuSignObject.max && swuSignObject.max[1] || '';
323 const max = x && y ? coord2swu([x, y]) : undefined;
324 if (!max) return undefined;
325 let prefix = '';
326
327 if (swuSignObject.sequence && Array.isArray(swuSignObject.sequence)) {
328 prefix = swuSignObject.sequence.map(key => (key.match(re.symbol) || [''])[0]).join('');
329 prefix = prefix ? '𝠀' + prefix : '';
330 }
331
332 let signbox = '';
333
334 if (swuSignObject.spatials && Array.isArray(swuSignObject.spatials)) {
335 signbox = swuSignObject.spatials.map(spatial => {
336 if (typeof spatial.symbol === 'string') {
337 const symbol = (spatial.symbol.match(re.symbol) || [''])[0];
338
339 if (symbol) {
340 const x = spatial.coord && spatial.coord[0] || '';
341 const y = spatial.coord && spatial.coord[1] || '';
342 const coord = x && y ? coord2swu([x, y]) : '';
343
344 if (coord) {
345 return symbol + coord;
346 }
347 }
348 }
349
350 return '';
351 }).join('');
352 }
353
354 const styleStr = typeof swuSignObject.style === 'string' && (swuSignObject.style.match(re$1.full) || [''])[0] || '';
355 return prefix + box + max + signbox + styleStr;
356 }
357};
358
359/**
360 * Array of plane 4 code points for kinds of symbols: writing, location, and punctuation.
361 * @alias swu.kind
362 * @type {array}
363 */
364
365const kind = [0x40001, 0x4efa1, 0x4f2a1];
366/**
367 * Array of plane 4 code points for categories of symbols: hand, movement, dynamics, head, trunk & limb, location, and punctuation.
368 * @alias swu.category
369 * @type {array}
370 */
371
372const category = [0x40001, 0x461e1, 0x4bca1, 0x4bfa1, 0x4e8e1, 0x4efa1, 0x4f2a1];
373/**
374 * Array of plane 4 code points for the 30 symbol groups.
375 * @alias swu.group
376 * @type {array}
377 */
378
379const 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];
380/**
381 * Object of symbol ranges with starting and ending code points on plane 4.
382 *
383 * { all, writing, hand, movement, dynamic, head, hcenter, vcenter, trunk, limb, location, punctuation }
384 * @alias swu.ranges
385 * @type {object}
386 */
387
388const ranges = {
389 'all': [0x40001, 0x4f480],
390 'writing': [0x40001, 0x4efa0],
391 'hand': [0x40001, 0x461e0],
392 'movement': [0x461e1, 0x4bca0],
393 'dynamic': [0x4bca1, 0x4bfa0],
394 'head': [0x4bfa1, 0x4e8e0],
395 'hcenter': [0x4bfa1, 0x4e8e0],
396 'vcenter': [0x4bfa1, 0x4ec40],
397 'trunk': [0x4e8e1, 0x4ec40],
398 'limb': [0x4ec41, 0x4efa0],
399 'location': [0x4efa1, 0x4f2a0],
400 'punctuation': [0x4f2a1, 0x4f480]
401};
402/**
403 * Function to test if symbol is of a certain type.
404 * @function swu.isType
405 * @param {string} swuSym - an SWU symbol character
406 * @param {string} type - the name of a symbol range
407 * @returns {boolean} is symbol of specified type
408 * @example
409 * swu.isType('񀀁', 'hand')
410 *
411 * return true
412 */
413
414const isType = (swuSym, type) => {
415 const parsed = parse.symbol(swuSym);
416
417 if (parsed.symbol) {
418 const code = swu2code(parsed.symbol);
419 const range = ranges[type];
420
421 if (range) {
422 return range[0] <= code && range[1] >= code;
423 }
424 }
425
426 return false;
427};
428
429/**
430 * Array of colors associated with the seven symbol categories.
431 * @alias swu.colors
432 * @type {array}
433 */
434
435const colors = ['#0000CC', '#CC0000', '#FF0099', '#006600', '#000000', '#884411', '#FF9900'];
436/**
437 * Function that returns the standardized color for a symbol.
438 * @function swu.colorize
439 * @param {string} swuSym - an SWU symbol character
440 * @returns {string} name of standardized color for symbol
441 * @example
442 * swu.colorize('񀀁')
443 *
444 * return '#0000CC'
445 */
446
447const colorize = swuSym => {
448 const parsed = parse.symbol(swuSym);
449 let color = '#000000';
450
451 if (parsed.symbol) {
452 const code = swu2code(parsed.symbol);
453 const index = category.findIndex(val => val > code);
454 color = colors[index < 0 ? 6 : index - 1];
455 }
456
457 return color;
458};
459
460export { category, colorize, colors, compose, decode, encode, group, isType, kind, pair, parse, ranges, re };
461
462/* support ongoing development on https://patreon.com/signwriting */