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