UNPKG

11.6 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* fsw.mjs is released under the MIT License.
5*/
6
7/**
8 * Object of regular expressions for FSW strings
9 *
10 * { symbol, coord, sort, box, prefix, spatial, signbox, sign, sortable }
11 * @alias fsw.re
12 * @type {object}
13 */
14let re = {
15 'symbol': 'S[123][0-9a-f]{2}[0-5][0-9a-f]',
16 'coord': '[0-9]{3}x[0-9]{3}',
17 'sort': 'A',
18 'box': '[BLMR]'
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 FSW coordinate string to an array of x,y integers
58 * @function convert.fsw2coord
59 * @param {string} fswCoord - An FSW coordinate string
60 * @returns {number[]} Array of x,y integers
61 * @example
62 * convert.fsw2coord('500x500')
63 *
64 * return [500, 500]
65 */
66
67
68const fsw2coord = fswCoord => fswCoord.split('x').map(num => parseInt(num));
69
70const parse = {
71 /**
72 * Function to parse an fsw symbol with optional coordinate and style string
73 * @function fsw.parse.symbol
74 * @param {string} fswSym - an fsw symbol
75 * @returns {object} elements of fsw symbol
76 * @example
77 * fsw.parse.symbol('S10000500x500-C')
78 *
79 * return {
80 * 'symbol': 'S10000',
81 * 'coord': [500, 500],
82 * 'style': '-C'
83 * }
84 */
85 symbol: fswSym => {
86 const regex = `^(${re.symbol})(${re.coord})?(${re$1.full})?`;
87 const symbol = typeof fswSym === 'string' ? fswSym.match(new RegExp(regex)) : undefined;
88 return {
89 'symbol': symbol ? symbol[1] : undefined,
90 'coord': symbol && symbol[2] ? fsw2coord(symbol[2]) : undefined,
91 'style': symbol ? symbol[3] : undefined
92 };
93 },
94
95 /**
96 * Function to parse an fsw sign with style string
97 * @function fsw.parse.sign
98 * @param {string} fswSign - an fsw sign
99 * @returns {object} elements of fsw sign
100 * @example
101 * fsw.parse.sign('AS10011S10019S2e704S2e748M525x535S2e748483x510S10011501x466S2e704510x500S10019476x475-C')
102 *
103 * return {
104 * sequence: ['S10011', 'S10019', 'S2e704', 'S2e748'],
105 * box: 'M',
106 * max: [525, 535],
107 * spatials: [
108 * {
109 * symbol: 'S2e748',
110 * coord: [483, 510]
111 * },
112 * {
113 * symbol: 'S10011',
114 * coord: [501, 466]
115 * },
116 * {
117 * symbol: 'S2e704',
118 * coord: [510, 500]
119 * },
120 * {
121 * symbol: 'S10019',
122 * coord: [476, 475]
123 * }
124 * ],
125 * style: '-C'
126 * }
127 */
128 sign: fswSign => {
129 const regex = `^(${re.prefix})?(${re.signbox})(${re$1.full})?`;
130 const sign = typeof fswSign === 'string' ? fswSign.match(new RegExp(regex)) : undefined;
131
132 if (sign) {
133 return {
134 'sequence': sign[1] ? sign[1].slice(1).match(/.{6}/g) : undefined,
135 'box': sign[2][0],
136 'max': fsw2coord(sign[2].slice(1, 8)),
137 'spatials': sign[2].length < 9 ? undefined : sign[2].slice(8).match(/(.{13})/g).map(m => {
138 return {
139 symbol: m.slice(0, 6),
140 coord: [parseInt(m.slice(6, 9)), parseInt(m.slice(10, 13))]
141 };
142 }),
143 'style': sign[3]
144 };
145 } else {
146 return {};
147 }
148 }
149};
150
151const compose = {
152 /**
153 * Function to compose an fsw symbol with optional coordinate and style string
154 * @function fsw.compose.symbol
155 * @param {object} fswSymObject - an fsw symbol object
156 * @param {string} fswSymObject.symbol - an fsw symbol key
157 * @param {number[]} fswSymObject.coord - top-left coordinate of symbol with 500,500 center
158 * @param {string} fswSymObject.style - a style string for custom appearance
159 * @returns {string} an fsw symbol string
160 * @example
161 * fsw.compose.symbol({
162 * 'symbol': 'S10000',
163 * 'coord': [480, 480],
164 * 'style': '-C'
165 * })
166 *
167 * return 'S10000480x480-C'
168 */
169 symbol: fswSymObject => {
170 if (typeof fswSymObject.symbol === 'string') {
171 const symbol = (fswSymObject.symbol.match(re.symbol) || [''])[0];
172
173 if (symbol) {
174 const x = (fswSymObject.coord && fswSymObject.coord[0] || '').toString();
175 const y = (fswSymObject.coord && fswSymObject.coord[1] || '').toString();
176 const coord = ((x + 'x' + y).match(re.coord) || [''])[0] || '';
177 const styleStr = typeof fswSymObject.style === 'string' && (fswSymObject.style.match(re$1.full) || [''])[0] || '';
178 return symbol + coord + styleStr;
179 }
180 }
181
182 return undefined;
183 },
184
185 /**
186 * Function to compose an fsw sign with style string
187 * @function fsw.compose.sign
188 * @param {string[]} fswSignObject.sequence - an ordered array of symbols
189 * @param {string} fswSignObject.box - a choice BLMR: horizontal Box, Left, Middle, and Right lane
190 * @param {number[]} fswSymObject.max - max bottom left coordinate of the signbox space
191 * @param {{symbol:string,coord:number[]}[]} fswSymObject.spatials - array of symbols with top-left coordinate placement
192 * @param {string} fswSymObject.style - a style string for custom appearance
193 * @returns {string} an fsw sign string
194 * @example
195 * fsw.compose.sign({
196 * sequence: ['S10011', 'S10019', 'S2e704', 'S2e748'],
197 * box: 'M',
198 * max: [525, 535],
199 * spatials: [
200 * {
201 * symbol: 'S2e748',
202 * coord: [483, 510]
203 * },
204 * {
205 * symbol: 'S10011',
206 * coord: [501, 466]
207 * },
208 * {
209 * symbol: 'S2e704',
210 * coord: [510, 500]
211 * },
212 * {
213 * symbol: 'S10019',
214 * coord: [476, 475]
215 * }
216 * ],
217 * style: '-C'
218 * })
219 *
220 * return 'AS10011S10019S2e704S2e748M525x535S2e748483x510S10011501x466S2e704510x500S10019476x475-C'
221 */
222 sign: fswSignObject => {
223 let box = typeof fswSignObject.box !== 'string' ? 'M' : (fswSignObject.box + 'M').match(re.box);
224 const x = (fswSignObject.max && fswSignObject.max[0] || '').toString();
225 const y = (fswSignObject.max && fswSignObject.max[1] || '').toString();
226 const max = ((x + 'x' + y).match(re.coord) || [''])[0] || '';
227 if (!max) return undefined;
228 let prefix = '';
229
230 if (fswSignObject.sequence && Array.isArray(fswSignObject.sequence)) {
231 prefix = fswSignObject.sequence.map(key => (key.match(re.symbol) || [''])[0]).join('');
232 prefix = prefix ? 'A' + prefix : '';
233 }
234
235 let signbox = '';
236
237 if (fswSignObject.spatials && Array.isArray(fswSignObject.spatials)) {
238 signbox = fswSignObject.spatials.map(spatial => {
239 if (typeof spatial.symbol === 'string') {
240 const symbol = (spatial.symbol.match(re.symbol) || [''])[0];
241
242 if (symbol) {
243 const x = (spatial.coord && spatial.coord[0] || '').toString();
244 const y = (spatial.coord && spatial.coord[1] || '').toString();
245 const coord = ((x + 'x' + y).match(re.coord) || [''])[0] || '';
246
247 if (coord) {
248 return symbol + coord;
249 }
250 }
251 }
252
253 return '';
254 }).join('');
255 }
256
257 const styleStr = typeof fswSignObject.style === 'string' && (fswSignObject.style.match(re$1.full) || [''])[0] || '';
258 return prefix + box + max + signbox + styleStr;
259 }
260};
261
262/**
263 * Array of numbers for kinds of symbols: writing, location, and punctuation.
264 * @alias fsw.kind
265 * @type {array}
266 */
267
268const kind = [0x100, 0x37f, 0x387];
269/**
270 * Array of numbers for categories of symbols: hand, movement, dynamics, head, trunk & limb, location, and punctuation.
271 * @alias fsw.category
272 * @type {array}
273 */
274
275const category = [0x100, 0x205, 0x2f7, 0x2ff, 0x36d, 0x37f, 0x387];
276/**
277 * Array of numbers for the 30 symbol groups.
278 * @alias fsw.group
279 * @type {array}
280 */
281
282const group = [0x100, 0x10e, 0x11e, 0x144, 0x14c, 0x186, 0x1a4, 0x1ba, 0x1cd, 0x1f5, 0x205, 0x216, 0x22a, 0x255, 0x265, 0x288, 0x2a6, 0x2b7, 0x2d5, 0x2e3, 0x2f7, 0x2ff, 0x30a, 0x32a, 0x33b, 0x359, 0x36d, 0x376, 0x37f, 0x387];
283/**
284 * Object of symbol ranges with starting and ending numbers.
285 *
286 * { all, writing, hand, movement, dynamic, head, hcenter, vcenter, trunk, limb, location, punctuation }
287 * @alias fsw.ranges
288 * @type {object}
289 */
290
291const ranges = {
292 'all': [0x100, 0x38b],
293 'writing': [0x100, 0x37e],
294 'hand': [0x100, 0x204],
295 'movement': [0x205, 0x2f6],
296 'dynamic': [0x2f7, 0x2fe],
297 'head': [0x2ff, 0x36c],
298 'hcenter': [0x2ff, 0x36c],
299 'vcenter': [0x2ff, 0x375],
300 'trunk': [0x36d, 0x375],
301 'limb': [0x376, 0x37e],
302 'location': [0x37f, 0x386],
303 'punctuation': [0x387, 0x38b]
304};
305/**
306 * Function to test if symbol is of a certain type.
307 * @function fsw.isType
308 * @param {string} key - an FSW symbol key
309 * @param {string} type - the name of a symbol range
310 * @returns {boolean} is symbol of specified type
311 * @example
312 * fsw.isType('S10000', 'hand')
313 *
314 * return true
315 */
316
317const isType = (key, type) => {
318 const parsed = parse.symbol(key);
319
320 if (parsed.symbol) {
321 const dec = parseInt(parsed.symbol.slice(1, 4), 16);
322 const range = ranges[type];
323
324 if (range) {
325 return range[0] <= dec && range[1] >= dec;
326 }
327 }
328
329 return false;
330};
331
332/**
333 * Array of colors associated with the seven symbol categories.
334 * @alias fsw.colors
335 * @type {array}
336 */
337
338const colors = ['#0000CC', '#CC0000', '#FF0099', '#006600', '#000000', '#884411', '#FF9900'];
339/**
340 * Function that returns the standardized color for a symbol.
341 * @function fsw.colorize
342 * @param {string} key - an FSW symbol key
343 * @returns {string} name of standardized color for symbol
344 * @example
345 * fsw.colorize('S10000')
346 *
347 * return '#0000CC'
348 */
349
350const colorize = key => {
351 const parsed = parse.symbol(key);
352 let color = '#000000';
353
354 if (parsed.symbol) {
355 const dec = parseInt(parsed.symbol.slice(1, 4), 16);
356 const index = category.findIndex(val => val > dec);
357 color = colors[index < 0 ? 6 : index - 1];
358 }
359
360 return color;
361};
362
363export { category, colorize, colors, compose, group, isType, kind, parse, ranges, re };
364
365/* support ongoing development on https://patreon.com/signwriting */