/** * Sutton SignWriting Core Module v1.4.2 (https://github.com/sutton-signwriting/core) * Author: Steve Slevinski (https://SteveSlevinski.me) * core.cjs is released under the MIT License. */ 'use strict'; Object.defineProperty(exports, '__esModule', { value: true }); /** * Object of regular expressions for FSW strings * * @alias fsw.re * @property {string} symbol - regular expressions for a symbol * @property {string} coord - regular expressions for a coordinate * @property {string} sort - regular expressions for the sorting marker * @property {string} box - regular expression for a signbox marker * @property {string} prefix - regular expression for a sorting marker followed by one or more symbols * @property {string} spatial - regular expression for a symbol followed by a coordinate * @property {string} signbox - regular expression for a signbox marker, max coordinate and zero or more spatial symbols * @property {string} sign - regular expression for an optional prefix followed by a signbox * @property {string} sortable - regular expression for a mandatory prefix followed by a signbox */ let re$4 = { 'symbol': 'S[123][0-9a-f]{2}[0-5][0-9a-f]', 'coord': '[0-9]{3}x[0-9]{3}', 'sort': 'A', 'box': '[BLMR]' }; re$4.prefix = `(?:${re$4.sort}(?:${re$4.symbol})+)`; re$4.spatial = `${re$4.symbol}${re$4.coord}`; re$4.signbox = `${re$4.box}${re$4.coord}(?:${re$4.spatial})*`; re$4.sign = `${re$4.prefix}?${re$4.signbox}`; re$4.sortable = `${re$4.prefix}${re$4.signbox}`; /** * Object of regular expressions for style strings * * @alias style.re * @type {object} * @property {string} colorize - regular expression for colorize section * @property {string} colorhex - regular expression for color hex values with 3 or 6 characters * @property {string} colorname - regular expression for css color name * @property {string} padding - regular expression for padding section * @property {string} zoom - regular expression for zoom section * @property {string} classbase - regular expression for class name definition * @property {string} id - regular expression for id definition * @property {string} colorbase - regular expression for color hex or color name * @property {string} color - regular expression for single color entry * @property {string} colors - regular expression for double color entry * @property {string} background - regular expression for background section * @property {string} detail - regular expression for color details for line and optional fill * @property {string} detailsym - regular expression for color details for individual symbols * @property {string} classes - regular expression for one or more class names * @property {string} full - full regular expression for style string */ let re$3 = { 'colorize': 'C', 'colorhex': '(?:[0-9a-fA-F]{3}){1,2}', 'colorname': '[a-zA-Z]+', 'padding': 'P[0-9]{2}', 'zoom': 'Z(?:[0-9]+(?:\\.[0-9]+)?|x)', 'classbase': '-?[_a-zA-Z][_a-zA-Z0-9-]{0,100}', 'id': '[a-zA-Z][_a-zA-Z0-9-]{0,100}' }; re$3.colorbase = `(?:${re$3.colorhex}|${re$3.colorname})`; re$3.color = `_${re$3.colorbase}_`; re$3.colors = `_${re$3.colorbase}(?:,${re$3.colorbase})?_`; re$3.background = `G${re$3.color}`; re$3.detail = `D${re$3.colors}`; re$3.detailsym = `D[0-9]{2}${re$3.colors}`; re$3.classes = `${re$3.classbase}(?: ${re$3.classbase})*`; re$3.full = `-(${re$3.colorize})?(${re$3.padding})?(${re$3.background})?(${re$3.detail})?(${re$3.zoom})?(?:-((?:${re$3.detailsym})*))?(?:-(${re$3.classes})?!(?:(${re$3.id})!)?)?`; const prefixColor = color => { const regex = new RegExp(`^${re$3.colorhex}$`); return (regex.test(color) ? '#' : '') + color; }; const definedProps = obj => Object.fromEntries(Object.entries(obj).filter(([k, v]) => v !== undefined)); /** * Function to parse style string to object * @function style.parse * @param {string} styleString - a style string * @returns {StyleObject} elements of style string * @example * style.parse('-CP10G_blue_D_red,Cyan_') * * return { * 'colorize': true, * 'padding': 10, * 'background': 'blue', * 'detail': ['red', 'Cyan'] * } */ const parse$4 = styleString => { const regex = `^${re$3.full}`; const m = (typeof styleString === 'string' ? styleString.match(new RegExp(regex)) : []) || []; return definedProps({ 'colorize': !m[1] ? undefined : !!m[1], 'padding': !m[2] ? undefined : parseInt(m[2].slice(1)), 'background': !m[3] ? undefined : prefixColor(m[3].slice(2, -1)), 'detail': !m[4] ? undefined : m[4].slice(2, -1).split(',').map(prefixColor), 'zoom': !m[5] ? undefined : m[5] === 'Zx' ? 'x' : parseFloat(m[5].slice(1)), 'detailsym': !m[6] ? undefined : m[6].match(new RegExp(re$3.detailsym, 'g')).map(val => { const parts = val.split('_'); const detail = parts[1].split(',').map(prefixColor); return { 'index': parseInt(parts[0].slice(1)), 'detail': detail }; }), 'classes': !m[7] ? undefined : m[7], 'id': !m[8] ? undefined : m[8] }); }; /** * Function to compose style string from object * @function style.compose * @param {StyleObject} styleObject - an object of style options * @returns {string} style string * @example * style.compose({ * 'colorize': true, * 'padding': 10, * 'background': 'blue', * 'detail': ['red', 'Cyan'], * 'zoom': 1.1, * 'detailsym': [ * { * 'index': 1, * 'detail': ['#ff00ff'] * }, * { * 'index': 2, * 'detail': ['yellow', 'green'] * } * ], * 'classes': 'primary blinking', * 'id': 'cursor' * }) * * return '-CP10G_blue_D_red,Cyan_Z1.1-D01_ff00ff_D02_yellow,green_-primary blinking!cursor!' */ const compose$4 = styleObject => { if (typeof styleObject !== 'object' || styleObject === null) return undefined; // three sections let style1 = '-'; style1 += !styleObject.colorize ? '' : 'C'; const padding = parseInt(styleObject.padding); style1 += !padding || padding <= 0 || padding > 99 ? '' : 'P' + (padding > 9 ? padding : '0' + padding); const background = !styleObject.background || !(typeof styleObject.background === 'string') ? undefined : styleObject.background.match(re$3.colorbase)[0]; style1 += !background ? '' : 'G_' + background + '_'; const detail1 = !styleObject.detail || !styleObject.detail[0] || !(typeof styleObject.detail[0] === 'string') ? undefined : styleObject.detail[0].match(re$3.colorbase)[0]; const detail2 = !styleObject.detail || !styleObject.detail[1] || !(typeof styleObject.detail[1] === 'string') ? undefined : styleObject.detail[1].match(re$3.colorbase)[0]; if (detail1) { style1 += 'D_' + detail1; if (detail2) { style1 += ',' + detail2; } style1 += '_'; } const zoom = styleObject.zoom === 'x' ? 'x' : parseFloat(styleObject.zoom); style1 += !zoom || zoom <= 0 ? '' : 'Z' + zoom; let style2 = ''; const detailsym = !styleObject.detailsym || !Array.isArray(styleObject.detailsym) ? [] : styleObject.detailsym.map(styleObject => { const index = parseInt(styleObject.index); if (!index || index <= 0 || index > 99) return ''; let style = 'D' + (index > 9 ? index : '0' + index); const detail1 = !styleObject.detail || !styleObject.detail[0] ? undefined : styleObject.detail[0].match(re$3.colorbase)[0]; const detail2 = !styleObject.detail || !styleObject.detail[1] ? undefined : styleObject.detail[1].match(re$3.colorbase)[0]; if (detail1) { style += '_' + detail1; if (detail2) { style += ',' + detail2; } style += '_'; } return style; }); style2 += detailsym.join(''); let style3 = ''; const classes = !styleObject.classes || !(typeof styleObject.classes === 'string') ? undefined : styleObject.classes.match(re$3.classes)[0]; style3 += !classes ? '' : classes; const id = !styleObject.id || !(typeof styleObject.id === 'string') ? undefined : styleObject.id.match(re$3.id)[0]; style3 += classes || id ? '!' : ''; style3 += !id ? '' : id + '!'; return style1 + (style2 || style3 ? '-' + style2 : '') + (style3 ? '-' + style3 : ''); }; /** The style module contains regular expressions and functions for parsing and composing style strings. * [Style string definition](https://tools.ietf.org/id/draft-slevinski-formal-signwriting-09.html#name-styling-string) * @module style */ var index$5 = /*#__PURE__*/Object.freeze({ __proto__: null, re: re$3, parse: parse$4, compose: compose$4 }); /** * Object of regular expressions for SWU strings in UTF-16 * * @alias swu.re * @property {string} symbol - regular expressions for a symbol * @property {string} coord - regular expressions for a coordinate * @property {string} sort - regular expressions for the sorting marker * @property {string} box - regular expression for a signbox marker * @property {string} prefix - regular expression for a sorting marker followed by one or more symbols * @property {string} spatial - regular expression for a symbol followed by a coordinate * @property {string} signbox - regular expression for a signbox marker, max coordinate and zero or more spatial symbols * @property {string} sign - regular expression for an optional prefix followed by a signbox * @property {string} sortable - regular expression for a mandatory prefix followed by a signbox */ let re$2 = { 'symbol': '(?:(?:\uD8C0[\uDC01-\uDFFF])|(?:[\uD8C1-\uD8FC][\uDC00-\uDFFF])|(?:\uD8FD[\uDC00-\uDC80]))', 'coord': '(?:\uD836[\uDC0C-\uDDFF]){2}', 'sort': '\uD836\uDC00', 'box': '\uD836[\uDC01-\uDC04]' }; re$2.prefix = `(?:${re$2.sort}(?:${re$2.symbol})+)`; re$2.spatial = `${re$2.symbol}${re$2.coord}`; re$2.signbox = `${re$2.box}${re$2.coord}(?:${re$2.spatial})*`; re$2.sign = `${re$2.prefix}?${re$2.signbox}`; re$2.sortable = `${re$2.prefix}${re$2.signbox}`; /** 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. * [Characters set definitions](https://tools.ietf.org/id/draft-slevinski-formal-signwriting-09.html#name-characters) * @module convert */ /** * Function to convert an SWU structural marker to FSW equivalent * @function convert.swu2mark * @param {string} swuMark - character for SWU structural marker * @returns {string} FSW structural marker * @example * convert.swu2mark('𝠀') * * return 'A' */ const swu2mark = swuMark => { return { '𝠀': 'A', '𝠁': 'B', '𝠂': 'L', '𝠃': 'M', '𝠄': 'R' }[swuMark]; }; /** * Function to convert an FSW structural marker to SWU equivalent * @function convert.mark2swu * @param {string} fswMark - character for FSW structural marker * @returns {string} SWU structural marker * @example * convert.mark2swu('A') * * return '𝠀' */ const mark2swu = fswMark => { return { 'A': '𝠀', 'B': '𝠁', 'L': '𝠂', 'M': '𝠃', 'R': '𝠄' }[fswMark]; }; /** * Function to convert an SWU number character to an integer * @function convert.swu2num * @param {string} swuNum - SWU number character * @returns {number} Integer value for number * @example * convert.swu2num('𝤆') * * return 500 */ const swu2num = swuNum => parseInt(swuNum.codePointAt(0)) - 0x1D80C + 250; /** * Function to convert a number to an SWU number character * @function convert.num2swu * @param {number} num - Integer value for number * @returns {string} SWU number character * @example * convert.num2swu(500) * * return '𝤆' */ const num2swu = num => String.fromCodePoint(0x1D80C + parseInt(num) - 250); /** * Function to convert two SWU number characters to an array of x,y integers * @function convert.swu2coord * @param {string} swuCoord - Two SWU number character * @returns {number[]} Array of x,y integers * @example * convert.swu2coord('𝤆𝤆') * * return [500, 500] */ const swu2coord = swuCoord => [swu2num(swuCoord.slice(0, 2)), swu2num(swuCoord.slice(2, 4))]; /** * Function to convert an array of x,y integers to two SWU number characters * @function convert.coord2swu * @param {number[]} coord - Array of x,y integers * @returns {string} Two SWU number character * @example * convert.coord2swu([500, 500]) * * return '𝤆𝤆' */ const coord2swu = coord => coord.map(num => num2swu(num)).join(''); /** * Function to convert an FSW coordinate string to an array of x,y integers * @function convert.fsw2coord * @param {string} fswCoord - An FSW coordinate string * @returns {number[]} Array of x,y integers * @example * convert.fsw2coord('500x500') * * return [500, 500] */ const fsw2coord = fswCoord => fswCoord.split('x').map(num => parseInt(num)); /** * Function to convert an array of x,y integers to an FSW coordinate string * @function convert.coord2fsw * @param {number[]} coord - Array of x,y integers * @returns {string} An FSW coordinate string * @example * convert.coord2fsw([500, 500]) * * return '500x500' */ const coord2fsw = coord => coord.join('x'); /** * Function to convert an SWU symbol character to a code point on plane 4 * @function convert.swu2code * @param {string} swuSym - SWU symbol character * @returns {number} Code point on plane 4 * @example * convert.swu2code('񀀁') * * return 0x40001 */ const swu2code = swuSym => parseInt(swuSym.codePointAt(0)); /** * Function to convert a code point on plane 4 to an SWU symbol character * @function convert.code2swu * @param {number} code - Code point on plane 4 * @returns {string} SWU symbol character * @example * convert.code2swu(0x40001) * * return '񀀁' */ const code2swu = code => String.fromCodePoint(code); /** * Function to convert an SWU symbol character to a 16-bit ID * @function convert.swu2id * @param {string} swuSym - SWU symbol character * @returns {number} 16-bit ID * @example * convert.swu2id('񀀁') * * return 1 */ const swu2id = swuSym => swu2code(swuSym) - 0x40000; /** * Function to convert a 16-bit ID to an SWU symbol character * @function convert.id2swu * @param {number} id - 16-bit ID * @returns {string} SWU symbol character * @example * convert.id2swu(1) * * return '񀀁' */ const id2swu = id => code2swu(id + 0x40000); /** * Function to convert an FSW symbol key to a 16-bit ID * @function convert.key2id * @param {string} key - FSW symbol key * @returns {number} 16-bit ID * @example * convert.key2id('S10000') * * return 1 */ const key2id = key => 1 + (parseInt(key.slice(1, 4), 16) - 256) * 96 + parseInt(key.slice(4, 5), 16) * 16 + parseInt(key.slice(5, 6), 16); /** * Function to convert a 16-bit ID to an FSW symbol key * @function convert.id2key * @param {number} id - 16-bit ID * @returns {string} FSW symbol key * @example * convert.id2key(1) * * return 'S10000' */ const id2key = id => { const symcode = id - 1; const base = parseInt(symcode / 96); const fill = parseInt((symcode - base * 96) / 16); const rotation = parseInt(symcode - base * 96 - fill * 16); return 'S' + (base + 0x100).toString(16) + fill.toString(16) + rotation.toString(16); }; /** * Function to convert an SWU symbol character to an FSW symbol key * @function convert.swu2key * @param {string} swuSym - SWU symbol character * @returns {string} FSW symbol key * @example * convert.swu2key('񀀁') * * return 'S10000' */ const swu2key = swuSym => { const symcode = swu2code(swuSym) - 0x40001; const base = parseInt(symcode / 96); const fill = parseInt((symcode - base * 96) / 16); const rotation = parseInt(symcode - base * 96 - fill * 16); return 'S' + (base + 0x100).toString(16) + fill.toString(16) + rotation.toString(16); }; /** * Function to convert an FSW symbol key to an SWU symbol character * @function convert.key2swu * @param {string} key - FSW symbol key * @returns {string} SWU symbol character * @example * convert.key2swu('S10000') * * return '񀀁' */ const key2swu = key => code2swu(0x40001 + (parseInt(key.slice(1, 4), 16) - 256) * 96 + parseInt(key.slice(4, 5), 16) * 16 + parseInt(key.slice(5, 6), 16)); /** * Function to convert SWU text to FSW text * @function convert.swu2fsw * @param {string} swuText - SWU text * @returns {string} FSW text * @example * convert.swu2fsw('𝠀񀀒񀀚񋚥񋛩𝠃𝤟𝤩񋛩𝣵𝤐񀀒𝤇𝣤񋚥𝤐𝤆񀀚𝣮𝣭') * * return 'AS10011S10019S2e704S2e748M525x535S2e748483x510S10011501x466S2e704510x500S10019476x475' */ const swu2fsw = swuText => { if (!swuText) return ''; let fsw = swuText.replace(/𝠀/g, "A").replace(/𝠁/g, "B").replace(/𝠂/g, "L").replace(/𝠃/g, "M").replace(/𝠄/g, "R"); const syms = fsw.match(new RegExp(re$2.symbol, 'g')); if (syms) { syms.forEach(function (sym) { fsw = fsw.replace(sym, swu2key(sym)); }); } const coords = fsw.match(new RegExp(re$2.coord, 'g')); if (coords) { coords.forEach(function (coord) { fsw = fsw.replace(coord, swu2coord(coord).join('x')); }); } return fsw; }; /** * Function to convert FSW text to SWU text * @function convert.fsw2swu * @param {string} fswText - FSW text * @returns {string} SWU text * @example * convert.fsw2swu('AS10011S10019S2e704S2e748M525x535S2e748483x510S10011501x466S2e704510x500S10019476x475') * * return '𝠀񀀒񀀚񋚥񋛩𝠃𝤟𝤩񋛩𝣵𝤐񀀒𝤇𝣤񋚥𝤐𝤆񀀚𝣮𝣭' */ const fsw2swu = fswText => { if (!fswText) return ''; const prefixes = fswText.match(new RegExp(re$4.prefix, 'g')); if (prefixes) { prefixes.forEach(function (prefix) { fswText = fswText.replace(prefix, '𝠀' + prefix.slice(1).match(/.{6}/g).map(key => key2swu(key)).join('')); }); } const boxes = fswText.match(new RegExp(re$4.box + re$4.coord, 'g')); if (boxes) { boxes.forEach(function (boxes) { fswText = fswText.replace(boxes, mark2swu(boxes.slice(0, 1)) + coord2swu(fsw2coord(boxes.slice(1, 8)))); }); } const spatials = fswText.match(new RegExp(re$4.spatial, 'g')); if (spatials) { spatials.forEach(function (spatial) { fswText = fswText.replace(spatial, key2swu(spatial.slice(0, 6)) + coord2swu(fsw2coord(spatial.slice(6, 13)))); }); } return fswText; }; var index$4 = /*#__PURE__*/Object.freeze({ __proto__: null, swu2mark: swu2mark, mark2swu: mark2swu, swu2num: swu2num, num2swu: num2swu, swu2coord: swu2coord, coord2swu: coord2swu, fsw2coord: fsw2coord, coord2fsw: coord2fsw, swu2code: swu2code, code2swu: code2swu, swu2id: swu2id, id2swu: id2swu, key2id: key2id, id2key: id2key, swu2key: swu2key, key2swu: key2swu, swu2fsw: swu2fsw, fsw2swu: fsw2swu }); const parse$3 = { /** * Function to parse an fsw symbol with optional coordinate and style string * @function fsw.parse.symbol * @param {string} fswSym - an fsw symbol * @returns {object} elements of fsw symbol * @example * fsw.parse.symbol('S10000500x500-C') * * return { * 'symbol': 'S10000', * 'coord': [500, 500], * 'style': '-C' * } */ symbol: fswSym => { const regex = `^(${re$4.symbol})(${re$4.coord})?(${re$3.full})?`; const symbol = typeof fswSym === 'string' ? fswSym.match(new RegExp(regex)) : undefined; return { 'symbol': symbol ? symbol[1] : undefined, 'coord': symbol && symbol[2] ? fsw2coord(symbol[2]) : undefined, 'style': symbol ? symbol[3] : undefined }; }, /** * Function to parse an fsw sign with style string * @function fsw.parse.sign * @param {string} fswSign - an fsw sign * @returns {object} elements of fsw sign * @example * fsw.parse.sign('AS10011S10019S2e704S2e748M525x535S2e748483x510S10011501x466S2e704510x500S10019476x475-C') * * return { * sequence: ['S10011', 'S10019', 'S2e704', 'S2e748'], * box: 'M', * max: [525, 535], * spatials: [ * { * symbol: 'S2e748', * coord: [483, 510] * }, * { * symbol: 'S10011', * coord: [501, 466] * }, * { * symbol: 'S2e704', * coord: [510, 500] * }, * { * symbol: 'S10019', * coord: [476, 475] * } * ], * style: '-C' * } */ sign: fswSign => { const regex = `^(${re$4.prefix})?(${re$4.signbox})(${re$3.full})?`; const sign = typeof fswSign === 'string' ? fswSign.match(new RegExp(regex)) : undefined; if (sign) { return { 'sequence': sign[1] ? sign[1].slice(1).match(/.{6}/g) : undefined, 'box': sign[2][0], 'max': fsw2coord(sign[2].slice(1, 8)), 'spatials': sign[2].length < 9 ? undefined : sign[2].slice(8).match(/(.{13})/g).map(m => { return { symbol: m.slice(0, 6), coord: [parseInt(m.slice(6, 9)), parseInt(m.slice(10, 13))] }; }), 'style': sign[3] }; } else { return {}; } }, /** * Function to parse an fsw text * @function fsw.parse.text * @param {string} fswText - an fsw text * @returns {array} fsw signs and punctuations * @example * fsw.parse.text('AS14c20S27106M518x529S14c20481x471S27106503x489 AS18701S1870aS2e734S20500M518x533S1870a489x515S18701482x490S20500508x496S2e734500x468 S38800464x496') * * return [ * 'AS14c20S27106M518x529S14c20481x471S27106503x489', * 'AS18701S1870aS2e734S20500M518x533S1870a489x515S18701482x490S20500508x496S2e734500x468', * 'S38800464x496' * ] */ text: fswText => { if (typeof fswText !== 'string') return []; const regex = `(${re$4.sign}(${re$3.full})?|${re$4.spatial}(${re$3.full})?)`; const matches = fswText.match(new RegExp(regex, 'g')); return matches ? [...matches] : []; } }; const compose$3 = { /** * Function to compose an fsw symbol with optional coordinate and style string * @function fsw.compose.symbol * @param {object} fswSymObject - an fsw symbol object * @param {string} fswSymObject.symbol - an fsw symbol key * @param {number[]} fswSymObject.coord - top-left coordinate of symbol * @param {string} fswSymObject.style - a style string for custom appearance * @returns {string} an fsw symbol string * @example * fsw.compose.symbol({ * 'symbol': 'S10000', * 'coord': [480, 480], * 'style': '-C' * }) * * return 'S10000480x480-C' */ symbol: fswSymObject => { if (typeof fswSymObject.symbol === 'string') { const symbol = (fswSymObject.symbol.match(re$4.symbol) || [''])[0]; if (symbol) { const x = (fswSymObject.coord && fswSymObject.coord[0] || '').toString(); const y = (fswSymObject.coord && fswSymObject.coord[1] || '').toString(); const coord = ((x + 'x' + y).match(re$4.coord) || [''])[0] || ''; const styleStr = typeof fswSymObject.style === 'string' && (fswSymObject.style.match(re$3.full) || [''])[0] || ''; return symbol + coord + styleStr; } } return undefined; }, /** * Function to compose an fsw sign with style string * @function fsw.compose.sign * @param {object} fswSymObject - an fsw sign object * @param {string[]} fswSignObject.sequence - an ordered array of symbols * @param {string} fswSignObject.box - a choice BLMR: horizontal Box, Left, Middle, and Right lane * @param {number[]} fswSignObject.max - max bottom-right coordinate of the signbox space * @param {{symbol:string,coord:number[]}[]} fswSignObject.spatials - array of symbols with top-left coordinate placement * @param {string} fswSignObject.style - a style string for custom appearance * @returns {string} an fsw sign string * @example * fsw.compose.sign({ * sequence: ['S10011', 'S10019', 'S2e704', 'S2e748'], * box: 'M', * max: [525, 535], * spatials: [ * { * symbol: 'S2e748', * coord: [483, 510] * }, * { * symbol: 'S10011', * coord: [501, 466] * }, * { * symbol: 'S2e704', * coord: [510, 500] * }, * { * symbol: 'S10019', * coord: [476, 475] * } * ], * style: '-C' * }) * * return 'AS10011S10019S2e704S2e748M525x535S2e748483x510S10011501x466S2e704510x500S10019476x475-C' */ sign: fswSignObject => { let box = typeof fswSignObject.box !== 'string' ? 'M' : (fswSignObject.box + 'M').match(re$4.box); const x = (fswSignObject.max && fswSignObject.max[0] || '').toString(); const y = (fswSignObject.max && fswSignObject.max[1] || '').toString(); const max = ((x + 'x' + y).match(re$4.coord) || [''])[0] || ''; if (!max) return undefined; let prefix = ''; if (fswSignObject.sequence && Array.isArray(fswSignObject.sequence)) { prefix = fswSignObject.sequence.map(key => (key.match(re$4.symbol) || [''])[0]).join(''); prefix = prefix ? 'A' + prefix : ''; } let signbox = ''; if (fswSignObject.spatials && Array.isArray(fswSignObject.spatials)) { signbox = fswSignObject.spatials.map(spatial => { if (typeof spatial.symbol === 'string') { const symbol = (spatial.symbol.match(re$4.symbol) || [''])[0]; if (symbol) { const x = (spatial.coord && spatial.coord[0] || '').toString(); const y = (spatial.coord && spatial.coord[1] || '').toString(); const coord = ((x + 'x' + y).match(re$4.coord) || [''])[0] || ''; if (coord) { return symbol + coord; } } } return ''; }).join(''); } const styleStr = typeof fswSignObject.style === 'string' && (fswSignObject.style.match(re$3.full) || [''])[0] || ''; return prefix + box + max + signbox + styleStr; } }; /** * Function to gather sizing information about an fsw sign or symbol * @function fsw.info * @param {string} fsw - an fsw sign or symbol * @returns {object} information about the fsw string * @example * fsw.info('AS14c20S27106L518x529S14c20481x471S27106503x489-P10Z2') * * return { * minX: 481, * minY: 471, * width: 37, * height: 58, * zoom: 2, * padding: 10, * segment: 'sign', * lane: -1 * } */ const info$1 = fsw => { let lanes = { "B": 0, "L": -1, "M": 0, "R": 1 }; let parsed = parse$3.sign(fsw); let width, height, segment, x1, x2, y1, y2, lane; if (parsed.spatials) { x1 = Math.min(...parsed.spatials.map(spatial => spatial.coord[0])); x2 = parsed.max[0]; width = x2 - x1; y1 = Math.min(...parsed.spatials.map(spatial => spatial.coord[1])); y2 = parsed.max[1]; height = y2 - y1; segment = 'sign'; lane = parsed.box; } else { parsed = parse$3.symbol(fsw); lane = "M"; if (parsed.coord) { x1 = parsed.coord[0]; width = (500 - x1) * 2; y1 = parsed.coord[1]; height = (500 - y1) * 2; segment = 'symbol'; } else { x1 = 490; width = 20; y1 = 490; height = 20; segment = 'none'; } } let style = parse$4(parsed.style); let zoom = style.zoom || 1; let padding = style.padding || 0; return { minX: x1, minY: y1, width: width, height: height, segment: segment, lane: lanes[lane], padding: padding, zoom: zoom }; }; const columnDefaults$1 = { 'height': 500, 'width': 150, 'offset': 50, 'pad': 20, 'margin': 5, 'dynamic': false, 'background': undefined, 'punctuation': { 'spacing': true, 'pad': 30, 'pull': true }, 'style': { 'detail': ['black', 'white'], 'zoom': 1 } }; /** * Function to an object of column options with default values * * @function fsw.columnDefaultsMerge * @param {ColumnOptions} options - object of column options * @returns {ColumnOptions} object of column options merged with column defaults * @example * fsw.columnDefaultsMerge({height: 500,width:150}) * * return { * "height": 500, * "width": 150, * "offset": 50, * "pad": 20, * "margin": 5, * "dynamic": false, * "punctuation": { * "spacing": true, * "pad": 30, * "pull": true * }, * "style": { * "detail": [ * "black", * "white" * ], * "zoom": 1 * } * } */ const columnDefaultsMerge$1 = options => { if (typeof options !== 'object') options = {}; return { ...columnDefaults$1, ...options, punctuation: { ...columnDefaults$1.punctuation, ...options.punctuation }, style: { ...columnDefaults$1.style, ...options.style } }; }; /** * Function to transform an FSW text to an array of columns * * @function fsw.columns * @param {string} fswText - FSW text of signs and punctuation * @param {ColumnOptions} options - object of column options * @returns {{options:ColumnOptions,widths:number[],columns:ColumnData}} object of column options, widths array, and column data * @example * fsw.columns('AS14c20S27106M518x529S14c20481x471S27106503x489 AS18701S1870aS2e734S20500M518x533S1870a489x515S18701482x490S20500508x496S2e734500x468 S38800464x496', {height: 500,width:150}) * * return { * "options": { * "height": 500, * "width": 150, * "offset": 50, * "pad": 20, * "margin": 5, * "dynamic": false, * "punctuation": { * "spacing": true, * "pad": 30, * "pull": true * }, * "style": { * "detail": [ * "black", * "white" * ], * "zoom": 1 * } * }, * "widths": [ * 150 * ], * "columns": [ * [ * { * "x": 56, * "y": 20, * "minX": 481, * "minY": 471, * "width": 37, * "height": 58, * "lane": 0, * "padding": 0, * "segment": "sign", * "text": "AS14c20S27106M518x529S14c20481x471S27106503x489", * "zoom": 1 * }, * { * "x": 57, * "y": 118, * "minX": 482, * "minY": 468, * "width": 36, * "height": 65, * "lane": 0, * "padding": 0, * "segment": "sign", * "text": "AS18701S1870aS2e734S20500M518x533S1870a489x515S18701482x490S20500508x496S2e734500x468", * "zoom": 1 * }, * { * "x": 39, * "y": 203, * "minX": 464, * "minY": 496, * "width": 72, * "height": 8, * "lane": 0, * "padding": 0, * "segment": "symbol", * "text": "S38800464x496", * "zoom": 1 * } * ] * ] * } */ const columns$1 = (fswText, options) => { if (typeof fswText !== 'string') return {}; const values = columnDefaultsMerge$1(options); let input = parse$3.text(fswText); let cursor = 0; let cols = []; let col = []; let plus = 0; let center = parseInt(values.width / 2); let maxHeight = values.height - values.margin; let pullable = true; let finalize = false; for (let val of input) { let informed = info$1(val); cursor += plus; if (values.punctuation.spacing) { cursor += informed.segment == 'sign' ? values.pad : 0; } else { cursor += values.pad; } finalize = cursor + informed.height > maxHeight; if (finalize && informed.segment == 'symbol' && values.punctuation.pull && pullable) { finalize = false; pullable = false; } if (col.length == 0) { finalize = false; } if (finalize) { cursor = values.pad; cols.push(col); col = []; pullable = true; } col.push(Object.assign(informed, { x: center + values.offset * informed.lane - (500 - informed.minX) * informed.zoom * values.style.zoom, y: cursor, text: val })); cursor += informed.height * informed.zoom * values.style.zoom; if (values.punctuation.spacing) { plus = informed.segment == 'sign' ? values.pad : values.punctuation.pad; } else { plus = values.pad; } } if (col.length) { cols.push(col); } // over height issue when pulling punctuation if (values.punctuation.pull) { for (let col of cols) { let last = col[col.length - 1]; let diff = last.y + last.height - (values.height - values.margin); if (diff > 0) { let adj = parseInt(diff / col.length) + 1; for (let i in col) { col[i].y -= adj * i + adj; } } } } // contract, expand, adjust let widths = []; for (let col of cols) { let min = [center - values.offset - values.pad]; let max = [center + values.offset + values.pad]; for (let item of col) { min.push(item.x - values.pad); max.push(item.x + item.width + values.pad); } min = Math.min(...min); max = Math.max(...max); let width = values.width; let adj = 0; if (!values.dynamic) { adj = center - parseInt((min + max) / 2); } else { width = max - min; adj = -min; } for (let item of col) { item.x += adj; } widths.push(width); } return { 'options': values, 'widths': widths, 'columns': cols }; }; /** * Array of numbers for kinds of symbols: writing, location, and punctuation. * @alias fsw.kind * @type {array} */ const kind$1 = [0x100, 0x37f, 0x387]; /** * Array of numbers for categories of symbols: hand, movement, dynamics, head, trunk & limb, location, and punctuation. * @alias fsw.category * @type {array} */ const category$1 = [0x100, 0x205, 0x2f7, 0x2ff, 0x36d, 0x37f, 0x387]; /** * Array of numbers for the 30 symbol groups. * @alias fsw.group * @type {array} */ const group$1 = [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]; /** * Object of symbol ranges with starting and ending numbers. * * { all, writing, hand, movement, dynamic, head, hcenter, vcenter, trunk, limb, location, punctuation } * @alias fsw.ranges * @type {object} */ const ranges$1 = { 'all': [0x100, 0x38b], 'writing': [0x100, 0x37e], 'hand': [0x100, 0x204], 'movement': [0x205, 0x2f6], 'dynamic': [0x2f7, 0x2fe], 'head': [0x2ff, 0x36c], 'hcenter': [0x2ff, 0x36c], 'vcenter': [0x2ff, 0x375], 'trunk': [0x36d, 0x375], 'limb': [0x376, 0x37e], 'location': [0x37f, 0x386], 'punctuation': [0x387, 0x38b] }; /** * Function to test if symbol is of a certain type. * @function fsw.isType * @param {string} key - an FSW symbol key * @param {string} type - the name of a symbol range * @returns {boolean} is symbol of specified type * @example * fsw.isType('S10000', 'hand') * * return true */ const isType$1 = (key, type) => { const parsed = parse$3.symbol(key); if (parsed.symbol) { const dec = parseInt(parsed.symbol.slice(1, 4), 16); const range = ranges$1[type]; if (range) { return range[0] <= dec && range[1] >= dec; } } return false; }; /** * Array of colors associated with the seven symbol categories. * @alias fsw.colors * @type {array} */ const colors$1 = ['#0000CC', '#CC0000', '#FF0099', '#006600', '#000000', '#884411', '#FF9900']; /** * Function that returns the standardized color for a symbol. * @function fsw.colorize * @param {string} key - an FSW symbol key * @returns {string} name of standardized color for symbol * @example * fsw.colorize('S10000') * * return '#0000CC' */ const colorize$1 = key => { const parsed = parse$3.symbol(key); let color = '#000000'; if (parsed.symbol) { const dec = parseInt(parsed.symbol.slice(1, 4), 16); const index = category$1.findIndex(val => val > dec); color = colors$1[index < 0 ? 6 : index - 1]; } return color; }; /** The fsw module contains functions for handling Formal SignWriitng in ASCII (FSW) characters. * [FSW characters definition](https://tools.ietf.org/id/draft-slevinski-formal-signwriting-09.html#name-formal-signwriting-in-ascii) * @module fsw */ var index$3 = /*#__PURE__*/Object.freeze({ __proto__: null, re: re$4, parse: parse$3, compose: compose$3, info: info$1, columnDefaults: columnDefaults$1, columnDefaultsMerge: columnDefaultsMerge$1, columns: columns$1, kind: kind$1, category: category$1, group: group$1, ranges: ranges$1, isType: isType$1, colors: colors$1, colorize: colorize$1 }); /** * Object of regular expressions for FSW query strings * * { base, coord, var, symbol, range, item, list, prefix, signbox, full } * @alias fswquery.re * @type {object} */ let re$1 = { 'base': '[123][0-9a-f]{2}', 'coord': '(?:[0-9]{3}x[0-9]{3})?', 'var': 'V[0-9]+' }; re$1.symbol = `S${re$1.base}[0-5u][0-9a-fu]`; re$1.range = `R${re$1.base}t${re$1.base}`; re$1.item = `(?:${re$1.symbol}|${re$1.range})`; re$1.list = `${re$1.item}(?:o${re$1.item})*`; re$1.prefix = `(?:A(?:${re$1.list})+)?T`; re$1.signbox = `(?:${re$1.list}${re$1.coord})*`; re$1.full = `Q(${re$1.prefix})?(${re$1.signbox})?(${re$1.var})?(-?)`; const parsePrefix$1 = text => { return { required: true, parts: text == 'T' ? undefined : text.match(new RegExp(`${re$1.list}`, 'g')).map(part => { if (part.includes('o')) { return ['or'].concat(part.match(new RegExp(`(${re$1.item})`, 'g')).map(part => part[0] == 'S' ? part : part.slice(1).split('t'))); } else { return part[0] == 'S' ? part : part.slice(1).split('t'); } }) }; }; const parseSignbox$1 = text => { return text.match(new RegExp(`(${re$1.list}${re$1.coord})`, 'g')).map(part => { let coord, front; if (part.includes('x')) { coord = fsw2coord(part.slice(-7)); front = part.slice(0, -7); } else { front = part; } if (front.includes('o')) { return { or: front.split('o').map(part => { if (part.includes('S')) { return part; } else { return part.slice(1).split('t'); } }), coord, coord }; } else if (front.includes('S')) { return { symbol: front, coord: coord }; } else { return { range: front.slice(1).split('t'), coord: coord }; } }); }; /** * Function to parse FSW query string to object * @function fswquery.parse * @param {string} fswQueryString - an FSW query string * @returns {object} elements of an FSW query string * @example * fswquery.parse('QAS10000S10500oS20500oR2fft304TS100uuR205t206oS207uu510x510V5-') * * return { * "query": true, * "prefix": { * "required": true, * "parts": [ * "S10000", * [ * "or", * "S10500", * "S20500", * [ * "2ff", * "304" * ] * ] * ] * }, * "signbox": [ * { * "symbol": "S100uu" * }, * { * "or": [ * [ * "205", * "206" * ], * "S207uu" * ], * "coord": [ * 510, * 510 * ] * } * ], * "variance": 5, * "style": true * } */ const parse$2 = fswQueryString => { const query = typeof fswQueryString === 'string' ? fswQueryString.match(new RegExp(`^${re$1.full}`)) : undefined; return { 'query': query ? true : undefined, 'prefix': query && query[1] ? parsePrefix$1(query[1]) : undefined, 'signbox': query && query[2] ? parseSignbox$1(query[2]) : undefined, 'variance': query && query[3] ? parseInt(query[3].slice(1)) : undefined, 'style': query && query[4] ? true : undefined }; }; /** * Function to compose FSW query string from object * @function fswquery.compose * @param {object} fswQueryObject - an object of style options * @param {boolean} fswQueryObject.query - required true for FSW query object * @param {object} fswQueryObject.prefix - an object for prefix elements * @param {boolean} fswQueryObject.prefix.required - true if sorting prefix is required * @param {(string|string[]|(string|string[])[])[]} fswQueryObject.prefix.parts - array of symbol strings, range arrays, and OR arrays of strings and range arrays * @param {({symbol:string,coord:number[]}|{range:string[],coord:number[]}|{or:(string|string[])[],coord:number[]})[]} fswQueryObject.signbox - array of objects for symbols, ranges, and list of symbols or ranges, with optional coordinates * @param {number} fswQueryObject.variance - amount that x or y coordinates can vary and find a match, defaults to 20 * @param {boolean} fswQueryObject.style - boolean value for including style string in matches * @returns {string} FSW query string * @example * fswquery.compose({ * query: true, * prefix: { * required: true, * parts: [ * 'S10000', * ['100', '204'], * 'S20500' * ] * }, * signbox: [ * { symbol: 'S20000' }, * { * range: ['100', '105'], * coord: [500, 500] * } * ], * variance: 5, * style: true * }) * * return 'QAS10000R100t204S20500TS20000R100t105500x500V5-' */ const compose$2 = fswQueryObject => { if (!fswQueryObject || !fswQueryObject.query) { return undefined; } let query = 'Q'; if (fswQueryObject.prefix && fswQueryObject.prefix.required) { if (Array.isArray(fswQueryObject.prefix.parts)) { query += 'A'; query += fswQueryObject.prefix.parts.map(part => { if (typeof part === 'string') { return part; } else { if (Array.isArray(part) && part.length == 2) { return `R${part[0]}t${part[1]}`; } else if (Array.isArray(part) && part.length > 2 && part[0] == 'or') { part.shift(); return part.map(part => { if (typeof part === 'string') { return part; } else { if (Array.isArray(part) && part.length == 2) { return `R${part[0]}t${part[1]}`; } } }).join('o'); } } }).join(''); } query += 'T'; } if (Array.isArray(fswQueryObject.signbox)) { query += fswQueryObject.signbox.map(part => { let out; if (part.or) { out = part.or.map(item => { if (typeof item === 'string') { return item; } else { if (Array.isArray(item) && item.length == 2) { return `R${item[0]}t${item[1]}`; } } }).join('o'); } else if (part.symbol) { out = part.symbol; } else { if (part.range && Array.isArray(part.range) && part.range.length == 2) { out = `R${part.range[0]}t${part.range[1]}`; } } return out + (Array.isArray(part.coord) && part.coord.length == 2 ? part.coord.join('x') : ''); }).join(''); } query += fswQueryObject.style ? '-' : ''; query = query.match(new RegExp(`^${re$1.full}`))[0]; return query; }; /** * Function to convert an FSW sign to a query string * * For the flags parameter, use one or more of the following. * - A: exact symbol in temporal prefix * - a: general symbol in temporal prefix * - S: exact symbol in spatial signbox * - s: general symbol in spatial signbox * - L: spatial signbox symbol at location * @function fswquery.fsw2query * @param {string} fswSign - FSW sign * @param {string} flags - flags for query string creation * @returns {string} FSW query string * @example * fswquery.fsw2query('AS10011S10019S2e704S2e748M525x535S2e748483x510S10011501x466S2e704510x500S10019476x475', 'ASL') * * return 'QAS10011S10019S2e704S2e748TS2e748483x510S10011501x466S2e704510x500S10019476x475' */ const fsw2query = (fswSign, flags) => { let query = ''; const parsed = parse$3.sign(fswSign); if (parsed.box) { const A_flag = flags.indexOf('A') > -1; const a_flag = flags.indexOf('a') > -1; const S_flag = flags.indexOf('S') > -1; const s_flag = flags.indexOf('s') > -1; const L_flag = flags.indexOf('L') > -1; if (A_flag || a_flag || S_flag || s_flag) { if ((A_flag || a_flag) && parsed.sequence) { query += 'A'; query += parsed.sequence.map(sym => sym.slice(0, 4) + (a_flag ? 'uu' : sym.slice(4, 6))).join(''); query += 'T'; } if ((S_flag || s_flag) && parsed.spatials) { query += parsed.spatials.map(spatial => spatial.symbol.slice(0, 4) + (s_flag ? 'uu' : spatial.symbol.slice(4, 6)) + (L_flag ? spatial.coord.join('x') : '')).join(''); } } return query ? "Q" + query : undefined; } else { return undefined; } }; //needs rewritten, but it works /** * Function to transform a range to a regular expression * @function fswquery.range * @param {(number|string)} min - either a decimal number or hexidecimal string * @param {(number|string)} max - either a decimal number or hexidecimal string * @param {boolean?} hex - if true, the regular expression will match a hexidecimal range * @returns {string} a regular expression that matches a range * @example * fswquery.range(500,750) * * return '(([56][0-9][0-9])|(7[0-4][0-9])|(750))' * @example * fswquery.range('100','10e',true) * * return '10[0-9a-e]' */ const range$1 = (min, max, hex) => { let pattern; let re; let diff; let tmax; let cnt; let minV; let maxV; if (!hex) { hex = ''; } min = ("000" + min).slice(-3); max = '' + max; pattern = ''; if (min === max) { return min; } //ending pattern will be series of connected OR ranges re = []; //first pattern+ 10's don't match and the min 1's are not zero //odd number to 9 if (!(min[0] == max[0] && min[1] == max[1])) { if (min[2] != '0') { pattern = min[0] + min[1]; if (hex) { //switch for dex switch (min[2]) { case "f": pattern += 'f'; break; case "e": pattern += '[ef]'; break; case "d": case "c": case "b": case "a": pattern += '[' + min[2] + '-f]'; break; default: switch (min[2]) { case "9": pattern += '[9a-f]'; break; case "8": pattern += '[89a-f]'; break; default: pattern += '[' + min[2] + '-9a-f]'; break; } break; } diff = 15 - parseInt(min[2], 16) + 1; min = '' + (parseInt(min, 16) + diff).toString(16); re.push(pattern); } else { //switch for dex switch (min[2]) { case "9": pattern += '9'; break; case "8": pattern += '[89]'; break; default: pattern += '[' + min[2] + '-9]'; break; } diff = 9 - min[2] + 1; min = '' + (min * 1 + diff); re.push(pattern); } } } pattern = ''; //if hundreds are different, get odd to 99 or ff if (min[0] != max[0]) { if (min[1] != '0') { if (hex) { //scrape to ff pattern = min[0]; switch (min[1]) { case "f": pattern += 'f'; break; case "e": pattern += '[ef]'; break; case "d": case "c": case "b": case "a": pattern += '[' + min[1] + '-f]'; break; case "9": pattern += '[9a-f]'; break; case "8": pattern += '[89a-f]'; break; default: pattern += '[' + min[1] + '-9a-f]'; break; } pattern += '[0-9a-f]'; diff = 15 - parseInt(min[1], 16) + 1; min = '' + (parseInt(min, 16) + diff * 16).toString(16); re.push(pattern); } else { //scrape to 99 pattern = min[0]; diff = 9 - min[1] + 1; switch (min[1]) { case "9": pattern += '9'; break; case "8": pattern += '[89]'; break; default: pattern += '[' + min[1] + '-9]'; break; } pattern += '[0-9]'; diff = 9 - min[1] + 1; min = '' + (min * 1 + diff * 10); re.push(pattern); } } } pattern = ''; //if hundreds are different, get to same if (min[0] != max[0]) { if (hex) { diff = parseInt(max[0], 16) - parseInt(min[0], 16); tmax = (parseInt(min[0], 16) + diff - 1).toString(16); switch (diff) { case 1: pattern = min[0]; break; case 2: pattern = '[' + min[0] + tmax + ']'; break; default: if (parseInt(min[0], 16) > 9) { minV = 'h'; } else { minV = 'd'; } if (parseInt(tmax, 16) > 9) { maxV = 'h'; } else { maxV = 'd'; } switch (minV + maxV) { case "dd": pattern += '[' + min[0] + '-' + tmax + ']'; break; case "dh": diff = 9 - min[0]; //firs get up to 9 switch (diff) { case 0: pattern += '[9'; break; case 1: pattern += '[89'; break; default: pattern += '[' + min[0] + '-9'; break; } switch (tmax[0]) { case 'a': pattern += 'a]'; break; case 'b': pattern += 'ab]'; break; default: pattern += 'a-' + tmax + ']'; break; } break; case "hh": pattern += '[' + min[0] + '-' + tmax + ']'; break; } } pattern += '[0-9a-f][0-9a-f]'; diff = parseInt(max[0], 16) - parseInt(min[0], 16); min = '' + (parseInt(min, 16) + diff * 256).toString(16); re.push(pattern); } else { diff = max[0] - min[0]; tmax = min[0] * 1 + diff - 1; switch (diff) { case 1: pattern = min[0]; break; case 2: pattern = '[' + min[0] + tmax + ']'; break; default: pattern = '[' + min[0] + '-' + tmax + ']'; break; } pattern += '[0-9][0-9]'; min = '' + (min * 1 + diff * 100); re.push(pattern); } } pattern = ''; //if tens are different, get to same if (min[1] != max[1]) { if (hex) { diff = parseInt(max[1], 16) - parseInt(min[1], 16); tmax = (parseInt(min[1], 16) + diff - 1).toString(16); pattern = min[0]; switch (diff) { case 1: pattern += min[1]; break; case 2: pattern += '[' + min[1] + tmax + ']'; break; default: if (parseInt(min[1], 16) > 9) { minV = 'h'; } else { minV = 'd'; } if (parseInt(tmax, 16) > 9) { maxV = 'h'; } else { maxV = 'd'; } switch (minV + maxV) { case "dd": pattern += '[' + min[1]; if (diff > 1) { pattern += '-'; } pattern += tmax + ']'; break; case "dh": diff = 9 - min[1]; //firs get up to 9 switch (diff) { case 0: pattern += '[9'; break; case 1: pattern += '[89'; break; default: pattern += '[' + min[1] + '-9'; break; } switch (max[1]) { case 'a': pattern += ']'; break; case 'b': pattern += 'a]'; break; default: pattern += 'a-' + (parseInt(max[1], 16) - 1).toString(16) + ']'; break; } break; case "hh": pattern += '[' + min[1]; if (diff > 1) { pattern += '-'; } pattern += (parseInt(max[1], 16) - 1).toString(16) + ']'; break; } break; } pattern += '[0-9a-f]'; diff = parseInt(max[1], 16) - parseInt(min[1], 16); min = '' + (parseInt(min, 16) + diff * 16).toString(16); re.push(pattern); } else { diff = max[1] - min[1]; tmax = min[1] * 1 + diff - 1; pattern = min[0]; switch (diff) { case 1: pattern += min[1]; break; case 2: pattern += '[' + min[1] + tmax + ']'; break; default: pattern += '[' + min[1] + '-' + tmax + ']'; break; } pattern += '[0-9]'; min = '' + (min * 1 + diff * 10); re.push(pattern); } } pattern = ''; //if digits are different, get to same if (min[2] != max[2]) { if (hex) { pattern = min[0] + min[1]; diff = parseInt(max[2], 16) - parseInt(min[2], 16); if (parseInt(min[2], 16) > 9) { minV = 'h'; } else { minV = 'd'; } if (parseInt(max[2], 16) > 9) { maxV = 'h'; } else { maxV = 'd'; } switch (minV + maxV) { case "dd": pattern += '[' + min[2]; if (diff > 1) { pattern += '-'; } pattern += max[2] + ']'; break; case "dh": diff = 9 - min[2]; //firs get up to 9 switch (diff) { case 0: pattern += '[9'; break; case 1: pattern += '[89'; break; default: pattern += '[' + min[2] + '-9'; break; } switch (max[2]) { case 'a': pattern += 'a]'; break; case 'b': pattern += 'ab]'; break; default: pattern += 'a-' + max[2] + ']'; break; } break; case "hh": pattern += '[' + min[2]; if (diff > 1) { pattern += '-'; } pattern += max[2] + ']'; break; } diff = parseInt(max[2], 16) - parseInt(min[2], 16); min = '' + (parseInt(min, 16) + diff).toString(16); re.push(pattern); } else { diff = max[2] - min[2]; pattern = min[0] + min[1]; switch (diff) { case 0: pattern += min[2]; break; case 1: pattern += '[' + min[2] + max[2] + ']'; break; default: pattern += '[' + min[2] + '-' + max[2] + ']'; break; } min = '' + (min * 1 + diff); re.push(pattern); } } pattern = ''; //last place is whole hundred if (min[2] == '0' && max[2] == '0') { pattern = max; re.push(pattern); } pattern = ''; cnt = re.length; if (cnt == 1) { pattern = re[0]; } else { pattern = re.join(')|('); pattern = '((' + pattern + '))'; } return pattern; }; const regexSymbol = sym => { let segment = sym.slice(0, 4); let fill = sym.slice(4, 5); if (fill == 'u') { segment += '[0-5]'; } else { segment += fill; } let rotate = sym.slice(5, 6); if (rotate == 'u') { segment += '[0-9a-f]'; } else { segment += rotate; } return segment; }; const regexRange$1 = symRange => { let from = symRange.slice(1, 4); let to = symRange.slice(5, 8); return 'S' + range$1(from, to, 'hex') + '[0-5][0-9a-f]'; }; //needs rewritten, but it works /** * Function to transform an FSW query string to one or more regular expressions * @function fswquery.regex * @param {string} query - an FSW query string * @returns {string[]} an array of one or more regular expressions * @example * fswquery.regex('QS100uuS20500480x520') * * return [ * '(?:A(?:S[123][0-9a-f]{2}[0-5][0-9a-f])+)?[BLMR]([0-9]{3}x[0-9]{3})(S[123][0-9a-f]{2}[0-5][0-9a-f][0-9]{3}x[0-9]{3})*S100[0-5][0-9a-f][0-9]{3}x[0-9]{3}(S[123][0-9a-f]{2}[0-5][0-9a-f][0-9]{3}x[0-9]{3})*', * '(?:A(?:S[123][0-9a-f]{2}[0-5][0-9a-f])+)?[BLMR]([0-9]{3}x[0-9]{3})(S[123][0-9a-f]{2}[0-5][0-9a-f][0-9]{3}x[0-9]{3})*S20500((4[6-9][0-9])|(500))x((5[0-3][0-9])|(540))(S[123][0-9a-f]{2}[0-5][0-9a-f][0-9]{3}x[0-9]{3})*' * ] */ const regex$1 = query => { query = query.match(new RegExp(`^${re$1.full}`))[0]; if (!query) { return ''; } var matches; var matchesOr; var matched; var orList; var i; var j; var segment; var coord; var x; var y; var fuzz = 20; var q_style = '(' + re$3.full + ')?'; var q_sortable; if (query == 'Q') { return [re$4.prefix + "?" + re$4.signbox]; } if (query == 'Q-') { return [re$4.prefix + "?" + re$4.signbox + q_style]; } if (query == 'QT') { return [re$4.prefix + re$4.signbox]; } if (query == 'QT-') { return [re$4.prefix + re$4.signbox + q_style]; } var segments = []; var sortable = query.indexOf('T') + 1; if (sortable) { q_sortable = '(A'; var qat = query.slice(0, sortable); query = query.replace(qat, ''); if (qat == 'QT') { q_sortable += '(' + re$4.symbol + ')+)'; } else { matches = qat.match(new RegExp('(' + re$1.list + ')', 'g')); if (matches) { for (i = 0; i < matches.length; i += 1) { orList = []; matchesOr = matches[i].match(new RegExp('(' + re$1.symbol + '|' + re$1.range + ')', 'g')); if (matchesOr) { for (j = 0; j < matchesOr.length; j += 1) { matched = matchesOr[j].match(new RegExp(re$1.symbol)); if (matched) { orList.push(regexSymbol(matched[0])); } else { orList.push(regexRange$1(matchesOr[j])); } } if (orList.length == 1) { q_sortable += orList[0]; } else { q_sortable += '(' + orList.join('|') + ')'; } } } q_sortable += '(' + re$4.symbol + ')*)'; } } } //get the variance matches = query.match(new RegExp(re$1.var, 'g')); if (matches) { fuzz = matches.toString().slice(1) * 1; } //this gets all symbols and ranges with or without location matches = query.match(new RegExp(re$1.list + re$1.coord, 'g')); if (matches) { for (i = 0; i < matches.length; i += 1) { orList = []; matchesOr = matches[i].match(new RegExp('(' + re$1.symbol + '|' + re$1.range + ')', 'g')); if (matchesOr) { for (j = 0; j < matchesOr.length; j += 1) { matched = matchesOr[j].match(new RegExp(re$1.symbol)); if (matched) { orList.push(regexSymbol(matched[0])); } else { orList.push(regexRange$1(matchesOr[j])); } } if (orList.length == 1) { segment = orList[0]; } else { segment = '(' + orList.join('|') + ')'; } } if (matches[i].includes('x')) { coord = fsw2coord(matches[i].slice(-7)); x = coord[0]; y = coord[1]; segment += range$1(x - fuzz, x + fuzz); segment += 'x'; segment += range$1(y - fuzz, y + fuzz); } else { segment += re$4.coord; } // add to general fsw word segment = re$4.signbox + segment + '(' + re$4.symbol + re$4.coord + ')*'; if (sortable) { segment = q_sortable + segment; } else { segment = re$4.prefix + "?" + segment; } if (query.indexOf('-') > 0) { segment += q_style; } segments.push(segment); } } if (!segments.length) { if (query.indexOf('-') > 0) { segment += q_style; } segments.push(q_sortable + re$4.signbox); } return segments; }; /** * Function that uses a query string to match signs from a string of text. * @function fswquery.results * @param {string} query - an FSW query string * @param {string} text - a string of text containing multiple signs * @returns {string[]} an array of FSW signs * @example * fswquery.results('QAS10011T','AS10011S10019S2e704S2e748M525x535S2e748483x510S10011501x466S2e704510x500S10019476x475 AS15a21S15a07S21100S2df04S2df14M521x538S15a07494x488S15a21498x489S2df04498x517S2df14497x461S21100479x486 AS1f010S10018S20600M519x524S10018485x494S1f010490x494S20600481x476') * * return [ * 'AS10011S10019S2e704S2e748M525x535S2e748483x510S10011501x466S2e704510x500S10019476x475' * ] */ const results$1 = (query, text) => { if (!text) { return []; } let pattern; let matches; let parts; let words; let re = regex$1(query); if (!re) { return []; } let i; for (i = 0; i < re.length; i += 1) { pattern = re[i]; matches = text.match(new RegExp(pattern, 'g')); if (matches) { text = matches.join(' '); } else { text = ''; } } if (text) { parts = text.split(' '); words = parts.filter(function (element) { return element in parts ? false : parts[element] = true; }, {}); } else { words = []; } return words; }; //needs rewritten, but it works /** * Function that uses an FSW query string to match signs from multiple lines of text. * @function fswquery.lines * @param {string} query - an FSW query string * @param {string} text - multiple lines of text, each starting with an FSW sign * @returns {string[]} an array of lines of text, each starting with an FSW sign * @example * fswquery.lines('QAS10011T',`AS10011S10019S2e704S2e748M525x535S2e748483x510S10011501x466S2e704510x500S10019476x475 line one * AS15a21S15a07S21100S2df04S2df14M521x538S15a07494x488S15a21498x489S2df04498x517S2df14497x461S21100479x486 line two * AS1f010S10018S20600M519x524S10018485x494S1f010490x494S20600481x476 line three`) * * return [ * 'AS10011S10019S2e704S2e748M525x535S2e748483x510S10011501x466S2e704510x500S10019476x475 line one' * ] */ const lines$1 = (query, text) => { if (!text) { return []; } let pattern; let matches; let parts; let words; let re = regex$1(query); if (!re) { return []; } let i; for (i = 0; i < re.length; i += 1) { pattern = re[i]; pattern = '^' + pattern + '.*'; matches = text.match(new RegExp(pattern, 'mg')); if (matches) { text = matches.join("\n"); } else { text = ''; } } if (text) { parts = text.split("\n"); words = parts.filter(function (element) { return element in parts ? false : parts[element] = true; }, {}); } else { words = []; } return words; }; /** The fswquery module contains functions for handling the FSW query language. * [Query Language definition](https://tools.ietf.org/id/draft-slevinski-formal-signwriting-09.html#name-query-language) * @module fswquery */ var index$2 = /*#__PURE__*/Object.freeze({ __proto__: null, re: re$1, parse: parse$2, compose: compose$2, fsw2query: fsw2query, range: range$1, regex: regex$1, results: results$1, lines: lines$1 }); const parse$1 = { /** * Function to parse an swu symbol with optional coordinate and style string * @function swu.parse.symbol * @param {string} swuSym - an swu symbol * @returns {object} elements of swu symbol * @example * swu.parse.symbol('񀀁𝤆𝤆-C') * * return { * 'symbol': '񀀁', * 'coord': [500, 500], * 'style': '-C' * } */ symbol: swuSym => { const regex = `^(${re$2.symbol})(${re$2.coord})?(${re$3.full})?`; const symbol = typeof swuSym === 'string' ? swuSym.match(new RegExp(regex)) : undefined; return { 'symbol': symbol ? symbol[1] : undefined, 'coord': symbol && symbol[2] ? swu2coord(symbol[2]) : undefined, 'style': symbol ? symbol[3] : undefined }; }, /** * Function to parse an swu sign with style string * @function swu.parse.sign * @param {string} swuSign - an swu sign * @returns {object} elements of swu sign * @example * swu.parse.sign('𝠀񀀒񀀚񋚥񋛩𝠃𝤟𝤩񋛩𝣵𝤐񀀒𝤇𝣤񋚥𝤐𝤆񀀚𝣮𝣭-C') * * return { * sequence: ['񀀒','񀀚','񋚥','񋛩'], * box: '𝠃', * max: [525, 535], * spatials: [ * { * symbol: '񋛩', * coord: [483, 510] * }, * { * symbol: '񀀒', * coord: [501, 466] * }, * { * symbol: '񋚥', * coord: [510, 500] * }, * { * symbol: '񀀚', * coord: [476, 475] * } * ], * style: '-C' * } */ sign: swuSign => { const regex = `^(${re$2.prefix})?(${re$2.signbox})(${re$3.full})?`; const sign = typeof swuSign === 'string' ? swuSign.match(new RegExp(regex)) : undefined; if (sign) { return { 'sequence': sign[1] ? sign[1].slice(2).match(/.{2}/g) : undefined, 'box': sign[2].slice(0, 2), 'max': swu2coord(sign[2].slice(2, 6)), 'spatials': sign[2].length < 7 ? undefined : sign[2].slice(6).match(/(.{6})/g).map(m => { return { symbol: m.slice(0, 2), coord: swu2coord(m.slice(2)) }; }), 'style': sign[3] }; } else { return {}; } }, /** * Function to parse an swu text * @function swu.parse.text * @param {string} swuText - an swu text * @returns {array} swu signs and punctuations * @example * swu.parse.text('𝠀񁲡񈩧𝠃𝤘𝤣񁲡𝣳𝣩񈩧𝤉𝣻 𝠀񃊢񃊫񋛕񆇡𝠃𝤘𝤧񃊫𝣻𝤕񃊢𝣴𝣼񆇡𝤎𝤂񋛕𝤆𝣦 񏌁𝣢𝤂') * * return [ * '𝠀񁲡񈩧𝠃𝤘𝤣񁲡𝣳𝣩񈩧𝤉𝣻', * '𝠀񃊢񃊫񋛕񆇡𝠃𝤘𝤧񃊫𝣻𝤕񃊢𝣴𝣼񆇡𝤎𝤂񋛕𝤆𝣦', * '񏌁𝣢𝤂' * ] */ text: swuText => { if (typeof swuText !== 'string') return []; const regex = `(${re$2.sign}(${re$3.full})?|${re$2.spatial}(${re$3.full})?)`; const matches = swuText.match(new RegExp(regex, 'g')); return matches ? [...matches] : []; } }; /** * Function to encode SWU characters using the UTF-16 escape format. * @function swu.encode * @param {string} swu - SWU characters * @returns {string} UTF-16 escape format * @example * swu.encode('񀀁𝤆𝤆') * * return '\\uD8C0\\uDC01\\uD836\\uDD06\\uD836\\uDD06' */ const encode = text => text.replace(/[\u007F-\uFFFF]/g, function (chr) { return "\\u" + ("0000" + chr.charCodeAt(0).toString(16)).substr(-4).toUpperCase(); }); /** * Function to decode UTF-16 escape format to SWU characters. * @function swu.decode * @param {string} encoded - UTF-16 escape format * @returns {string} SWU characters * @example * swu.decode('\\uD8C0\\uDC01\\uD836\\uDD06\\uD836\\uDD06') * * return '񀀁𝤆𝤆' */ const decode = encoded => encoded.replace(/\\u([0-9A-F]{4})/g, function (match, chr) { return String.fromCharCode(parseInt(chr, 16)); }); /** * Function to decompose an SWU character into UTF-16 surrogate pairs. * @function swu.pair * @param {string} swuChar - an SWU character * @returns {string[]} an array of UTF-16 surrogate pairs * @example * swu.pair('񀀁') * * return ['D8C0', 'DC01'] */ const pair = swuChar => [swuChar.charCodeAt(0).toString(16).toUpperCase(), swuChar.charCodeAt(1).toString(16).toUpperCase()]; const compose$1 = { /** * Function to compose an swu symbol with optional coordinate and style string * @function swu.compose.symbol * @param {object} swuSymObject - an swu symbol object * @param {string} swuSymObject.symbol - an swu symbol key * @param {number[]} swuSymObject.coord - top-left coordinate of symbol with 500,500 center * @param {string} swuSymObject.style - a style string for custom appearance * @returns {string} an swu symbol string * @example * swu.compose.symbol({ * 'symbol': '񀀁', * 'coord': [500, 500], * 'style': '-C' * }) * * return '񀀁𝤆𝤆-C' */ symbol: swuSymObject => { if (typeof swuSymObject !== 'object' || swuSymObject === null) return undefined; if (typeof swuSymObject.symbol === 'string') { const symbol = (swuSymObject.symbol.match(re$2.symbol) || [''])[0]; if (symbol) { const x = swuSymObject.coord && swuSymObject.coord[0] || ''; const y = swuSymObject.coord && swuSymObject.coord[1] || ''; const coord = x && y ? coord2swu([x, y]) : ''; const styleStr = typeof swuSymObject.style === 'string' && (swuSymObject.style.match(re$3.full) || [''])[0] || ''; return symbol + coord + styleStr; } } return undefined; }, /** * Function to compose an swu sign with style string * @function swu.compose.sign * @param {object} swuSignObject - an swu sign object * @param {string[]} swuSignObject.sequence - an ordered array of symbols * @param {string} swuSignObject.box - a choice of signbox marker: horizontal Box, Left, Middle, and Right lane * @param {number[]} swuSignObject.max - max bottom-right coordinate of the signbox space * @param {{symbol:string,coord:number[]}[]} swuSignObject.spatials - array of symbols with top-left coordinate placement * @param {string} swuSignObject.style - a style string for custom appearance * @returns {string} an swu sign string * @example * swu.compose.sign({ * sequence: ['񀀒','񀀚','񋚥','񋛩'], * box: '𝠃', * max: [525, 535], * spatials: [ * { * symbol: '񋛩', * coord: [483, 510] * }, * { * symbol: '񀀒', * coord: [501, 466] * }, * { * symbol: '񋚥', * coord: [510, 500] * }, * { * symbol: '񀀚', * coord: [476, 475] * } * ], * style: '-C' * }) * * return '𝠀񀀒񀀚񋚥񋛩𝠃𝤟𝤩񋛩𝣵𝤐񀀒𝤇𝣤񋚥𝤐𝤆񀀚𝣮𝣭-C' */ sign: swuSignObject => { if (typeof swuSignObject !== 'object' || swuSignObject === null) return undefined; let box = typeof swuSignObject.box !== 'string' ? '𝠃' : (swuSignObject.box + '𝠃').match(re$2.box); const x = swuSignObject.max && swuSignObject.max[0] || ''; const y = swuSignObject.max && swuSignObject.max[1] || ''; const max = x && y ? coord2swu([x, y]) : undefined; if (!max) return undefined; let prefix = ''; if (swuSignObject.sequence && Array.isArray(swuSignObject.sequence)) { prefix = swuSignObject.sequence.map(key => (key.match(re$2.symbol) || [''])[0]).join(''); prefix = prefix ? '𝠀' + prefix : ''; } let signbox = ''; if (swuSignObject.spatials && Array.isArray(swuSignObject.spatials)) { signbox = swuSignObject.spatials.map(spatial => { if (typeof spatial.symbol === 'string') { const symbol = (spatial.symbol.match(re$2.symbol) || [''])[0]; if (symbol) { const x = spatial.coord && spatial.coord[0] || ''; const y = spatial.coord && spatial.coord[1] || ''; const coord = x && y ? coord2swu([x, y]) : ''; if (coord) { return symbol + coord; } } } return ''; }).join(''); } const styleStr = typeof swuSignObject.style === 'string' && (swuSignObject.style.match(re$3.full) || [''])[0] || ''; return prefix + box + max + signbox + styleStr; } }; /** * Function to gather sizing information about an swu sign or symbol * @function swu.info * @param {string} swu - an swu sign or symbol * @returns {object} information about the swu string * @example * swu.info('𝠀񁲡񈩧𝠂𝤘𝤣񁲡𝣳𝣩񈩧𝤉𝣻-P10Z2') * * return { * minX: 481, * minY: 471, * width: 37, * height: 58, * segment: 'sign', * lane: -1 * padding: 10, * zoom: 2 * } */ const info = swu => { let lanes = { '𝠁': 0, '𝠂': -1, '𝠃': 0, '𝠄': 1 }; let parsed = parse$1.sign(swu); let width, height, segment, x1, x2, y1, y2, lane; if (parsed.spatials) { x1 = Math.min(...parsed.spatials.map(spatial => spatial.coord[0])); x2 = parsed.max[0]; width = x2 - x1; y1 = Math.min(...parsed.spatials.map(spatial => spatial.coord[1])); y2 = parsed.max[1]; height = y2 - y1; segment = 'sign'; lane = parsed.box; } else { parsed = parse$1.symbol(swu); lane = "𝠃"; if (parsed.coord) { x1 = parsed.coord[0]; width = (500 - x1) * 2; y1 = parsed.coord[1]; height = (500 - y1) * 2; segment = 'symbol'; } else { x1 = 490; width = 20; y1 = 490; height = 20; segment = 'none'; } } let style = parse$4(parsed.style); let zoom = style.zoom || 1; let padding = style.padding || 0; return { minX: x1, minY: y1, width: width, height: height, segment: segment, lane: lanes[lane], padding: padding, zoom: zoom }; }; const columnDefaults = { 'height': 500, 'width': 150, 'offset': 50, 'pad': 20, 'margin': 5, 'dynamic': false, 'background': undefined, 'punctuation': { 'spacing': true, 'pad': 30, 'pull': true }, 'style': { 'detail': ['black', 'white'], 'zoom': 1 } }; /** * Function to an object of column options with default values * * @function swu.columnDefaultsMerge * @param {ColumnOptions} options - object of column options * @returns {ColumnOptions} object of column options merged with column defaults * @example * swu.columnDefaultsMerge({height: 500,width:150}) * * return { * "height": 500, * "width": 150, * "offset": 50, * "pad": 20, * "margin": 5, * "dynamic": false, * "punctuation": { * "spacing": true, * "pad": 30, * "pull": true * }, * "style": { * "detail": [ * "black", * "white" * ], * "zoom": 1 * } * } */ const columnDefaultsMerge = options => { if (typeof options !== 'object') options = {}; return { ...columnDefaults, ...options, punctuation: { ...columnDefaults.punctuation, ...options.punctuation }, style: { ...columnDefaults.style, ...options.style } }; }; /** * Function to transform an SWU text to an array of columns * * @function swu.columns * @param {string} swuText - SWU text of signs and punctuation * @param {ColumnOptions} options - object of column options * @returns {{options:ColumnOptions,widths:number[],columns:ColumnData}} object of column options, widths array, and column data * @example * swu.columns('𝠀񁲡񈩧𝠃𝤘𝤣񁲡𝣳𝣩񈩧𝤉𝣻 𝠀񃊢񃊫񋛕񆇡𝠃𝤘𝤧񃊫𝣻𝤕񃊢𝣴𝣼񆇡𝤎𝤂񋛕𝤆𝣦 񏌁𝣢𝤂', {height: 500,width:150}) * * return { * "options": { * "height": 500, * "width": 150, * "offset": 50, * "pad": 20, * "margin": 5, * "dynamic": false, * "punctuation": { * "spacing": true, * "pad": 30, * "pull": true * }, * "style": { * "detail": [ * "black", * "white" * ], * "zoom": 1 * } * }, * "widths": [ * 150 * ], * "columns": [ * [ * { * "x": 56, * "y": 20, * "minX": 481, * "minY": 471, * "width": 37, * "height": 58, * "lane": 0, * "padding": 0, * "segment": "sign", * "text": "𝠀񁲡񈩧𝠃𝤘𝤣񁲡𝣳𝣩񈩧𝤉𝣻", * "zoom": 1 * }, * { * "x": 57, * "y": 118, * "minX": 482, * "minY": 468, * "width": 36, * "height": 65, * "lane": 0, * "padding": 0, * "segment": "sign", * "text": "𝠀񃊢񃊫񋛕񆇡𝠃𝤘𝤧񃊫𝣻𝤕񃊢𝣴𝣼񆇡𝤎𝤂񋛕𝤆𝣦", * "zoom": 1 * }, * { * "x": 39, * "y": 203, * "minX": 464, * "minY": 496, * "width": 72, * "height": 8, * "lane": 0, * "padding": 0, * "segment": "symbol", * "text": "񏌁𝣢𝤂", * "zoom": 1 * } * ] * ] * } */ const columns = (swuText, options) => { if (typeof swuText !== 'string') return {}; const values = columnDefaultsMerge(options); let input = parse$1.text(swuText); let cursor = 0; let cols = []; let col = []; let plus = 0; let center = parseInt(values.width / 2); let maxHeight = values.height - values.margin; let pullable = true; let finalize = false; for (let val of input) { let informed = info(val); cursor += plus; if (values.punctuation.spacing) { cursor += informed.segment == 'sign' ? values.pad : 0; } else { cursor += values.pad; } finalize = cursor + informed.height > maxHeight; if (finalize && informed.segment == 'symbol' && values.punctuation.pull && pullable) { finalize = false; pullable = false; } if (col.length == 0) { finalize = false; } if (finalize) { cursor = values.pad; cols.push(col); col = []; pullable = true; } col.push(Object.assign(informed, { x: center + values.offset * informed.lane - (500 - informed.minX) * informed.zoom * values.style.zoom, y: cursor, text: val })); cursor += informed.height * informed.zoom * values.style.zoom; if (values.punctuation.spacing) { plus = informed.segment == 'sign' ? values.pad : values.punctuation.pad; } else { plus = values.pad; } } if (col.length) { cols.push(col); } // over height issue when pulling punctuation if (values.punctuation.pull) { for (let col of cols) { let last = col[col.length - 1]; let diff = last.y + last.height - (values.height - values.margin); if (diff > 0) { let adj = parseInt(diff / col.length) + 1; for (let i in col) { col[i].y -= adj * i + adj; } } } } // contract, expand, adjust let widths = []; for (let col of cols) { let min = [center - values.offset - values.pad]; let max = [center + values.offset + values.pad]; for (let item of col) { min.push(item.x - values.pad); max.push(item.x + item.width + values.pad); } min = Math.min(...min); max = Math.max(...max); let width = values.width; let adj = 0; if (!values.dynamic) { adj = center - parseInt((min + max) / 2); } else { width = max - min; adj = -min; } for (let item of col) { item.x += adj; } widths.push(width); } return { 'options': values, 'widths': widths, 'columns': cols }; }; /** * Array of plane 4 code points for kinds of symbols: writing, location, and punctuation. * @alias swu.kind * @type {array} */ const kind = [0x40001, 0x4efa1, 0x4f2a1]; /** * Array of plane 4 code points for categories of symbols: hand, movement, dynamics, head, trunk & limb, location, and punctuation. * @alias swu.category * @type {array} */ const category = [0x40001, 0x461e1, 0x4bca1, 0x4bfa1, 0x4e8e1, 0x4efa1, 0x4f2a1]; /** * Array of plane 4 code points for the 30 symbol groups. * @alias swu.group * @type {array} */ 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]; /** * Object of symbol ranges with starting and ending code points on plane 4. * * { all, writing, hand, movement, dynamic, head, hcenter, vcenter, trunk, limb, location, punctuation } * @alias swu.ranges * @type {object} */ const ranges = { 'all': [0x40001, 0x4f480], 'writing': [0x40001, 0x4efa0], 'hand': [0x40001, 0x461e0], 'movement': [0x461e1, 0x4bca0], 'dynamic': [0x4bca1, 0x4bfa0], 'head': [0x4bfa1, 0x4e8e0], 'hcenter': [0x4bfa1, 0x4e8e0], 'vcenter': [0x4bfa1, 0x4ec40], 'trunk': [0x4e8e1, 0x4ec40], 'limb': [0x4ec41, 0x4efa0], 'location': [0x4efa1, 0x4f2a0], 'punctuation': [0x4f2a1, 0x4f480] }; /** * Function to test if symbol is of a certain type. * @function swu.isType * @param {string} swuSym - an SWU symbol character * @param {string} type - the name of a symbol range * @returns {boolean} is symbol of specified type * @example * swu.isType('񀀁', 'hand') * * return true */ const isType = (swuSym, type) => { const parsed = parse$1.symbol(swuSym); if (parsed.symbol) { const code = swu2code(parsed.symbol); const range = ranges[type]; if (range) { return range[0] <= code && range[1] >= code; } } return false; }; /** * Array of colors associated with the seven symbol categories. * @alias swu.colors * @type {array} */ const colors = ['#0000CC', '#CC0000', '#FF0099', '#006600', '#000000', '#884411', '#FF9900']; /** * Function that returns the standardized color for a symbol. * @function swu.colorize * @param {string} swuSym - an SWU symbol character * @returns {string} name of standardized color for symbol * @example * swu.colorize('񀀁') * * return '#0000CC' */ const colorize = swuSym => { const parsed = parse$1.symbol(swuSym); let color = '#000000'; if (parsed.symbol) { const code = swu2code(parsed.symbol); const index = category.findIndex(val => val > code); color = colors[index < 0 ? 6 : index - 1]; } return color; }; /** The swu module contains functions for handling SignWriitng in Unicode (SWU) characters. * [SWU characters definition](https://tools.ietf.org/id/draft-slevinski-formal-signwriting-09.html#name-signwriting-in-unicode-swu) * @module swu */ var index$1 = /*#__PURE__*/Object.freeze({ __proto__: null, re: re$2, parse: parse$1, encode: encode, decode: decode, pair: pair, compose: compose$1, info: info, columnDefaults: columnDefaults, columnDefaultsMerge: columnDefaultsMerge, columns: columns, kind: kind, category: category, group: group, ranges: ranges, isType: isType, colors: colors, colorize: colorize }); /** * Object of regular expressions for SWU query strings * * { base, coord, var, symbol, range, item, list, prefix, signbox, full } * @alias swuquery.re * @type {object} */ let re = { 'base': '(?:(?:\uD8C0[\uDC01-\uDFFF])|(?:[\uD8C1-\uD8FC][\uDC00-\uDFFF])|(?:\uD8FD[\uDC00-\uDC80]))', 'coord': '(?:(?:\uD836[\uDC0C-\uDDFF]){2})?', 'var': 'V[0-9]+' }; re.symbol = `${re.base}f?r?`; re.range = `R${re.base}${re.base}`; re.item = `(?:${re.symbol}|${re.range})`; re.list = `${re.item}(?:o${re.item})*`; re.prefix = `(?:A(?:${re.list})+)?T`; re.signbox = `(?:${re.list}${re.coord})*`; re.full = `Q(${re.prefix})?(${re.signbox})?(${re.var})?(-?)`; const parsePrefix = text => { return { required: true, parts: text == 'T' ? undefined : text.match(new RegExp(`(${re.list})`, 'g')).map(part => { if (part.includes('o')) { return ['or'].concat(part.match(new RegExp(`(${re.item})`, 'g')).map(part => part[0] != 'R' ? part : [part.slice(1, 3), part.slice(3, 5)])); } else { return part[0] != 'R' ? part : [part.slice(1, 3), part.slice(3, 5)]; } }) }; }; const parseSignbox = text => { return text.match(new RegExp(`(${re.list}${re.coord})`, 'g')).map(part => { let coord, front; coord = part.match(new RegExp(`${re$2.coord}`)); if (coord) { coord = swu2coord(coord[0]); front = part.slice(0, -4); } else { coord = undefined; front = part; } if (front.includes('o')) { return { or: front.split('o').map(part => { if (!part.includes('R')) { return part; } else { return [part.slice(1, 3), part.slice(3, 5)]; } }), coord, coord }; } else if (!front.includes('R')) { return { symbol: front, coord: coord }; } else { return { range: [front.slice(1, 3), front.slice(3, 5)], coord: coord }; } }); }; /** * Function to parse SWU query string to object * @function swuquery.parse * @param {string} swuQueryString - an SWU query string * @returns {object} elements of an SWU query string * @example * swuquery.parse('QA񀀁R񀀁񆆑񆇡T񆀁R񀀁񀇱𝤆𝤆V5-') * * return { * query: true, * prefix: { * required: true, * parts: [ * '񀀁', * ['񀀁', '񆆑'], * '񆇡' * ] * }, * signbox: [ * { symbol: '񆀁' }, * { * range: ['񀀁', '񀇱'], * coord: [500, 500] * } * ], * variance: 5, * style: true * } */ const parse = swuQueryString => { const query = typeof swuQueryString === 'string' ? swuQueryString.match(new RegExp(`^${re.full}`)) : undefined; return { 'query': query ? true : undefined, 'prefix': query && query[1] ? parsePrefix(query[1]) : undefined, 'signbox': query && query[2] ? parseSignbox(query[2]) : undefined, 'variance': query && query[3] ? parseInt(query[3].slice(1)) : undefined, 'style': query && query[4] ? true : undefined }; }; /** * Function to compose SWU query string from object * @function swuquery.compose * @param {object} swuQueryObject - an object of style options * @param {boolean} swuQueryObject.query - required true for SWU query object * @param {object} swuQueryObject.prefix - an object for prefix elements * @param {boolean} swuQueryObject.prefix.required - true if sorting prefix is required * @param {(string|string[]|(string|string[])[])[]} swuQueryObject.prefix.parts - array of symbol strings, range arrays, and OR arrays of strings and range arrays * @param {({symbol:string,coord:number[]}|{range:string[],coord:number[]}|{or:(string|string[])[],coord:number[]})[]} swuQueryObject.signbox - array of objects for symbols, ranges, and list of symbols or ranges, with optional coordinates * @param {number} swuQueryObject.variance - amount that x or y coordinates can vary and find a match, defaults to 20 * @param {boolean} swuQueryObject.style - boolean value for including style string in matches * @returns {string} SWU query string * @example * swuquery.compose({ * query: true, * prefix: { * required: true, * parts: [ * '񀀁', * ['񀀁', '񆆑'], * '񆇡' * ] * }, * signbox: [ * { symbol: '񆀁' }, * { * range: ['񀀁', '񀇱'], * coord: [500, 500] * } * ], * variance: 5, * style: true * }) * * return 'QA񀀁R񀀁񆆑񆇡T񆀁R񀀁񀇱𝤆𝤆V5-' */ const compose = swuQueryObject => { if (!swuQueryObject || !swuQueryObject.query) { return undefined; } let query = 'Q'; if (swuQueryObject.prefix && swuQueryObject.prefix.required) { if (Array.isArray(swuQueryObject.prefix.parts)) { query += 'A'; query += swuQueryObject.prefix.parts.map(part => { if (typeof part === 'string') { return part; } else { if (Array.isArray(part) && part.length == 2) { return `R${part[0]}${part[1]}`; } else if (Array.isArray(part) && part.length > 2 && part[0] == 'or') { part.shift(); return part.map(part => { if (typeof part === 'string') { return part; } else { if (Array.isArray(part) && part.length == 2) { return `R${part[0]}${part[1]}`; } } }).join('o'); } } }).join(''); } query += 'T'; } if (Array.isArray(swuQueryObject.signbox)) { query += swuQueryObject.signbox.map(part => { let out; if (part.or) { out = part.or.map(item => { if (typeof item === 'string') { return item; } else { if (Array.isArray(item) && item.length == 2) { return `R${item[0]}${item[1]}`; } } }).join('o'); } else if (part.symbol) { out = part.symbol; } else { if (part.range && Array.isArray(part.range) && part.range.length == 2) { out = `R${part.range[0]}${part.range[1]}`; } } return out + (Array.isArray(part.coord) && part.coord.length == 2 ? coord2swu(part.coord) : ''); }).join(''); } query += swuQueryObject.style ? '-' : ''; query = query.match(new RegExp(`^${re.full}`))[0]; return query; }; /** * Function to convert an SWU sign to a query string * * For the flags parameter, use one or more of the following. * - A: exact symbol in temporal prefix * - a: general symbol in temporal prefix * - S: exact symbol in spatial signbox * - s: general symbol in spatial signbox * - L: spatial signbox symbol at location * @function swuquery.swu2query * @param {string} swuSign - SWU sign * @param {string} flags - flags for query string creation * @returns {string} SWU query string * @example * swuquery.swu2query('𝠀񀀒񀀚񋚥񋛩𝠃𝤟𝤩񋛩𝣵𝤐񀀒𝤇𝣤񋚥𝤐𝤆񀀚𝣮𝣭', 'ASL') * * return 'QA񀀒񀀚񋚥񋛩T񋛩𝣵𝤐񀀒𝤇𝣤񋚥𝤐𝤆񀀚𝣮𝣭' */ const swu2query = (swuSign, flags) => { let query = ''; const parsed = parse$1.sign(swuSign); if (parsed.box) { const A_flag = flags.indexOf('A') > -1; const a_flag = flags.indexOf('a') > -1; const S_flag = flags.indexOf('S') > -1; const s_flag = flags.indexOf('s') > -1; const L_flag = flags.indexOf('L') > -1; if (A_flag || a_flag || S_flag || s_flag) { if ((A_flag || a_flag) && parsed.sequence) { query += 'A'; query += parsed.sequence.map(sym => sym + (a_flag ? 'fr' : '')).join(''); query += 'T'; } if ((S_flag || s_flag) && parsed.spatials) { query += parsed.spatials.map(spatial => spatial.symbol + (s_flag ? 'fr' : '') + (L_flag ? coord2swu(spatial.coord) : '')).join(''); } } return query ? "Q" + query : undefined; } else { return undefined; } }; /** * Function to transform a range of SWU characters to a regular expression * @function swuquery.range * @param {string} min - an SWU character * @param {string} max - an SWU character * @returns {string} a regular expression that matches a range of SWU characters * @example * swuquery.range('񀀁', '񀇡') * * return '\uD8C0[\uDC01-\uDDE1]' * @example * swuquery.range('𝣔', '𝤸') * * return '\uD836[\uDCD4-\uDD38]' */ const range = (min, max) => { if (min > max) return ''; let pattern = ''; let cnt; let re = []; min = pair(min); max = pair(max); if (min.length != 2 && max.length != 2) return ''; // HEAD // min[0] with range of min[1] to (DFFF or max[1]) if (min[0] == max[0]) { if (min[1] == max[1]) { pattern = '\\u' + min[0] + '\\u' + min[1]; re.push(pattern); } else { pattern = '\\u' + min[0] + '[\\u' + min[1] + '-\\u' + max[1] + ']'; re.push(pattern); } } else { if (min[1] == "DFFF") { pattern = '\\u' + min[0] + '\\uDFFF'; } else { pattern = '\\u' + min[0] + '[\\u' + min[1] + '-\\uDFFF]'; } re.push(pattern); // BODY // range of (min[0] +1) to (max[0] -1) with all DC00-DFFF let diff = parseInt(max[0], 16) - parseInt(min[0], 16); if (diff == 2) { pattern = '\\u' + (parseInt(min[0], 16) + 1).toString(16).toUpperCase(); pattern += '[\\uDC00-\\uDFFF]'; re.push(pattern); } if (diff > 2) { pattern = '['; pattern += '\\u' + (parseInt(min[0], 16) + 1).toString(16).toUpperCase(); pattern += '-\\u' + (parseInt(max[0], 16) - 1).toString(16).toUpperCase(); pattern += '][\\uDC00-\\uDFFF]'; re.push(pattern); } // TAIL // max[0] with range of DC00 to max[1] if (max[1] == "DC00") { pattern = '\\u' + max[0] + '\\uDC00'; } else { pattern = '\\u' + max[0] + '[\\uDC00-\\u' + max[1] + ']'; } re.push(pattern); } cnt = re.length; if (cnt == 1) { pattern = re[0]; } else { pattern = re.join(')|('); pattern = '((' + pattern + '))'; } return decode(pattern); }; /** * Function to transform an SWU symbol with fill and rotation flags to a regular expression * @function swuquery.symbolRanges * @param {string} symbolFR - an SWU character with optional flags of 'f' for any fill and 'r' for any rotation * @returns {string} a regular expression that matches one or more ranges of SWU symbols * @example Match an exact symbol * swuquery.symbolRanges('񀀁') * * return '\uD8C0\uDC01'); * @example Match a symbol with any fill * swuquery.symbolRanges('񀀁f') * * return '(\uD8C0\uDC01|\uD8C0\uDC11|\uD8C0\uDC21|\uD8C0\uDC31|\uD8C0\uDC41|\uD8C0\uDC51)' * @example Match a symbol with any rotation * swuquery.symbolRanges('񀀁r') * * return '\uD8C0[\uDC01-\uDC10]' * @example Match a symbol with any fill or rotation * swuquery.symbolRanges('񀀁fr') * * return '\uD8C0[\uDC01-\uDC60]' */ const symbolRanges = symbolFR => { let match = symbolFR.match(new RegExp(re.symbol)); if (match) { let sym = match[0].slice(0, 2); let key = swu2key(sym); let base = key.slice(0, 4); let start, end; if (match[0].slice(-2) == 'fr') { start = key2swu(base + "00"); end = key2swu(base + "5f"); return range(start, end); } else if (match[0].slice(-1) == 'r') { start = key2swu(key.slice(0, 5) + '0'); end = key2swu(key.slice(0, 5) + 'f'); return range(start, end); } else if (match[0].slice(-1) == 'f') { let list = [0, 1, 2, 3, 4, 5].map(function (f) { return key2swu(base + f + key.slice(-1)); }); return "(" + list.join("|") + ")"; } else { return sym; } } else { return ''; } }; const regexRange = symRange => { from = swu2key(symRange.slice(1, 3)); to = swu2key(symRange.slice(-2)); from = key2swu(from.slice(0, 4) + '00'); to = key2swu(to.slice(0, 4) + '5f'); return range(from, to); }; //needs rewritten, but it works /** * Function to transform an SWU query string to one or more regular expressions * @function swuquery.regex * @param {string} query - an SWU query string * @returns {string[]} an array of one or more regular expressions * @example * swuquery.regex('QA񀀒T') * * return [ * '(\uD836\uDC00\uD8C0\uDC12((?:(?:\uD8C0[\uDC01-\uDFFF])|(?:[\uD8C1-\uD8FC][\uDC00-\uDFFF])|(?:\uD8FD[\uDC00-\uDC80])))*)\uD836[\uDC01-\uDC04](?:\uD836[\uDC0C-\uDDFF]){2}((?:(?:\uD8C0[\uDC01-\uDFFF])|(?:[\uD8C1-\uD8FC][\uDC00-\uDFFF])|(?:\uD8FD[\uDC00-\uDC80]))(?:\uD836[\uDC0C-\uDDFF]){2})*' * ] */ const regex = query => { query = query.match(new RegExp(`^${re.full}`))[0]; if (!query) { return ''; } let matches; let matchesOr; let matched; let orList; let i; let j; let coord; let segment; let x; let y; let fuzz = 20; let re_sym = re$2.symbol; let re_coord = re$2.coord; let re_signbox = re$2.box; let re_seq = re$2.sort; let re_word = re_signbox + re_coord + '(' + re_sym + re_coord + ')*'; let re_sortable = '(' + re_seq + '(' + re_sym + ')+)'; let q_var = '(V[0-9]+)'; let q_style = '(' + re$3.full + ')?'; let q_sortable; if (query == 'Q') { return [re$2.sign]; } if (query == 'Q-') { return [re$2.sign + "(" + re$3.full + ")?"]; } if (query == 'QT') { return [re$2.sortable]; } if (query == 'QT-') { return [re$2.sortable + "(" + re$3.full + ")?"]; } let segments = []; let sortable = query.indexOf('T') + 1; if (sortable) { q_sortable = '(' + re$2.sort; let qat = query.slice(0, sortable); query = query.replace(qat, ''); if (qat == 'QT') { q_sortable += '(' + re_sym + ')+)'; } else { matches = qat.match(new RegExp('(' + re.list + ')', 'g')); if (matches) { for (i = 0; i < matches.length; i += 1) { orList = []; matchesOr = matches[i].match(new RegExp('(' + re.symbol + '|' + re.range + ')', 'g')); if (matchesOr) { for (j = 0; j < matchesOr.length; j += 1) { matched = matchesOr[j].match(new RegExp(re.symbol)); if (matched) { orList.push(symbolRanges(matched[0])); } else { orList.push(regexRange(matchesOr[j])); } } if (orList.length == 1) { q_sortable += orList[0]; } else { q_sortable += '(' + orList.join('|') + ')'; } } } q_sortable += '(' + re$2.symbol + ')*)'; } } } //get the variance matches = query.match(new RegExp(q_var, 'g')); if (matches) { fuzz = matches.toString().slice(1) * 1; } //this gets all symbols and ranges with or without location matches = query.match(new RegExp(re.list + re.coord, 'g')); if (matches) { for (i = 0; i < matches.length; i += 1) { orList = []; matchesOr = matches[i].match(new RegExp('(' + re.symbol + '|' + re.range + ')', 'g')); if (matchesOr) { for (j = 0; j < matchesOr.length; j += 1) { matched = matchesOr[j].match(new RegExp(re.symbol)); if (matched) { orList.push(symbolRanges(matched[0])); } else { orList.push(regexRange(matchesOr[j])); } } if (orList.length == 1) { segment = orList[0]; } else { segment = '(' + orList.join('|') + ')'; } } coord = matches[i].match(new RegExp(`${re$2.coord}`)); if (coord) { coord = swu2coord(coord[0]); x = coord[0]; y = coord[1]; segment += range(num2swu(x - fuzz), num2swu(x + fuzz)); segment += range(num2swu(y - fuzz), num2swu(y + fuzz)); } else { segment += re$2.coord; } // add to general swu word segment = re_word + segment + '(' + re_sym + re_coord + ')*'; if (sortable) { segment = q_sortable + segment; } else { segment = re_sortable + "?" + segment; } if (query.indexOf('-') > 0) { segment += q_style; } segments.push(segment); } } if (!segments.length) { if (query.indexOf('-') > 0) { segment += q_style; } segments.push(q_sortable + re_word); } return segments; }; /** * Function that uses a query string to match signs from a string of text. * @function swuquery.results * @param {string} query - an SWU query string * @param {string} text - a string of text containing multiple signs * @returns {string[]} an array of SWU signs * @example * swuquery.results('QA񀀒T','𝠀񀀒񀀚񋚥񋛩𝠃𝤟𝤩񋛩𝣵𝤐񀀒𝤇𝣤񋚥𝤐𝤆񀀚𝣮𝣭 𝠀񂇢񂇈񆙡񋎥񋎵𝠃𝤛𝤬񂇈𝤀𝣺񂇢𝤄𝣻񋎥𝤄𝤗񋎵𝤃𝣟񆙡𝣱𝣸 𝠀񅨑񀀙񆉁𝠃𝤙𝤞񀀙𝣷𝤀񅨑𝣼𝤀񆉁𝣳𝣮') * * return [ * '𝠀񀀒񀀚񋚥񋛩𝠃𝤟𝤩񋛩𝣵𝤐񀀒𝤇𝣤񋚥𝤐𝤆񀀚𝣮𝣭' * ] */ const results = (query, text) => { if (!text) { return []; } let pattern; let matches; let parts; let words; let res = regex(query); if (!res) { return []; } let i; for (i = 0; i < res.length; i += 1) { pattern = res[i]; matches = text.match(new RegExp(pattern, 'g')); if (matches) { text = matches.join(' '); } else { text = ''; } } if (text) { parts = text.split(' '); words = parts.filter(function (element) { return element in parts ? false : parts[element] = true; }, {}); } else { words = []; } return words; }; //needs rewritten, but it works /** * Function that uses an SWU query string to match signs from multiple lines of text. * @function swuquery.lines * @param {string} query - an SWU query string * @param {string} text - multiple lines of text, each starting with an SWU sign * @returns {string[]} an array of lines of text, each starting with an SWU sign * @example * swuquery.lines('QA񀀒T',`𝠀񀀒񀀚񋚥񋛩𝠃𝤟𝤩񋛩𝣵𝤐񀀒𝤇𝣤񋚥𝤐𝤆񀀚𝣮𝣭 line one * 𝠀񂇢񂇈񆙡񋎥񋎵𝠃𝤛𝤬񂇈𝤀𝣺񂇢𝤄𝣻񋎥𝤄𝤗񋎵𝤃𝣟񆙡𝣱𝣸 line two * 𝠀񅨑񀀙񆉁𝠃𝤙𝤞񀀙𝣷𝤀񅨑𝣼𝤀񆉁𝣳𝣮 line three`) * * return [ * '𝠀񀀒񀀚񋚥񋛩𝠃𝤟𝤩񋛩𝣵𝤐񀀒𝤇𝣤񋚥𝤐𝤆񀀚𝣮𝣭 line one' * ] */ const lines = (query, text) => { if (!text) { return []; } let pattern; let matches; let parts; let words; let res = regex(query); if (!res) { return []; } let i; for (i = 0; i < res.length; i += 1) { pattern = res[i]; pattern = '^' + pattern + '.*'; matches = text.match(new RegExp(pattern, 'mg')); if (matches) { text = matches.join("\n"); } else { text = ''; } } if (text) { parts = text.split("\n"); words = parts.filter(function (element) { return element in parts ? false : parts[element] = true; }, {}); } else { words = []; } return words; }; /** The swuquery module contains functions for handling the SWU query language. * [Query Language definition](https://tools.ietf.org/id/draft-slevinski-formal-signwriting-09.html#name-query-language) * @module swuquery */ var index = /*#__PURE__*/Object.freeze({ __proto__: null, re: re, parse: parse, compose: compose, swu2query: swu2query, range: range, symbolRanges: symbolRanges, regex: regex, results: results, lines: lines }); exports.convert = index$4; exports.fsw = index$3; exports.fswquery = index$2; exports.style = index$5; exports.swu = index$1; exports.swuquery = index; /* support ongoing development on https://patreon.com/signwriting */