UNPKG

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