/** * Sutton SignWriting Core Module v1.4.2 (https://github.com/sutton-signwriting/core) * Author: Steve Slevinski (https://SteveSlevinski.me) * swuquery.cjs is released under the MIT License. */ 'use strict'; Object.defineProperty(exports, '__esModule', { value: true }); /** * 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$2 = { 'base': '(?:(?:\uD8C0[\uDC01-\uDFFF])|(?:[\uD8C1-\uD8FC][\uDC00-\uDFFF])|(?:\uD8FD[\uDC00-\uDC80]))', 'coord': '(?:(?:\uD836[\uDC0C-\uDDFF]){2})?', 'var': 'V[0-9]+' }; re$2.symbol = `${re$2.base}f?r?`; re$2.range = `R${re$2.base}${re$2.base}`; re$2.item = `(?:${re$2.symbol}|${re$2.range})`; re$2.list = `${re$2.item}(?:o${re$2.item})*`; re$2.prefix = `(?:A(?:${re$2.list})+)?T`; re$2.signbox = `(?:${re$2.list}${re$2.coord})*`; re$2.full = `Q(${re$2.prefix})?(${re$2.signbox})?(${re$2.var})?(-?)`; /** * 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$1 = { 'symbol': '(?:(?:\uD8C0[\uDC01-\uDFFF])|(?:[\uD8C1-\uD8FC][\uDC00-\uDFFF])|(?:\uD8FD[\uDC00-\uDC80]))', 'coord': '(?:\uD836[\uDC0C-\uDDFF]){2}', 'sort': '\uD836\uDC00', 'box': '\uD836[\uDC01-\uDC04]' }; re$1.prefix = `(?:${re$1.sort}(?:${re$1.symbol})+)`; re$1.spatial = `${re$1.symbol}${re$1.coord}`; re$1.signbox = `${re$1.box}${re$1.coord}(?:${re$1.spatial})*`; re$1.sign = `${re$1.prefix}?${re$1.signbox}`; re$1.sortable = `${re$1.prefix}${re$1.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 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 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 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)); const parsePrefix = text => { return { required: true, parts: text == 'T' ? undefined : text.match(new RegExp(`(${re$2.list})`, 'g')).map(part => { if (part.includes('o')) { return ['or'].concat(part.match(new RegExp(`(${re$2.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$2.list}${re$2.coord})`, 'g')).map(part => { let coord, front; coord = part.match(new RegExp(`${re$1.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$1 = swuQueryString => { const query = typeof swuQueryString === 'string' ? swuQueryString.match(new RegExp(`^${re$2.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$2.full}`))[0]; return query; }; /** * 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 = { '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.colorbase = `(?:${re.colorhex}|${re.colorname})`; re.color = `_${re.colorbase}_`; re.colors = `_${re.colorbase}(?:,${re.colorbase})?_`; re.background = `G${re.color}`; re.detail = `D${re.colors}`; re.detailsym = `D[0-9]{2}${re.colors}`; re.classes = `${re.classbase}(?: ${re.classbase})*`; re.full = `-(${re.colorize})?(${re.padding})?(${re.background})?(${re.detail})?(${re.zoom})?(?:-((?:${re.detailsym})*))?(?:-(${re.classes})?!(?:(${re.id})!)?)?`; const parse = { /** * 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$1.symbol})(${re$1.coord})?(${re.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$1.prefix})?(${re$1.signbox})(${re.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$1.sign}(${re.full})?|${re$1.spatial}(${re.full})?)`; const matches = swuText.match(new RegExp(regex, 'g')); return matches ? [...matches] : []; } }; /** * 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()]; /** * 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.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$2.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$2.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$1.symbol; let re_coord = re$1.coord; let re_signbox = re$1.box; let re_seq = re$1.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.full + ')?'; let q_sortable; if (query == 'Q') { return [re$1.sign]; } if (query == 'Q-') { return [re$1.sign + "(" + re.full + ")?"]; } if (query == 'QT') { return [re$1.sortable]; } if (query == 'QT-') { return [re$1.sortable + "(" + re.full + ")?"]; } let segments = []; let sortable = query.indexOf('T') + 1; if (sortable) { q_sortable = '(' + re$1.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$2.list + ')', 'g')); if (matches) { for (i = 0; i < matches.length; i += 1) { orList = []; matchesOr = matches[i].match(new RegExp('(' + re$2.symbol + '|' + re$2.range + ')', 'g')); if (matchesOr) { for (j = 0; j < matchesOr.length; j += 1) { matched = matchesOr[j].match(new RegExp(re$2.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$1.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$2.list + re$2.coord, 'g')); if (matches) { for (i = 0; i < matches.length; i += 1) { orList = []; matchesOr = matches[i].match(new RegExp('(' + re$2.symbol + '|' + re$2.range + ')', 'g')); if (matchesOr) { for (j = 0; j < matchesOr.length; j += 1) { matched = matchesOr[j].match(new RegExp(re$2.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$1.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$1.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; }; exports.compose = compose; exports.lines = lines; exports.parse = parse$1; exports.range = range; exports.re = re$2; exports.regex = regex; exports.results = results; exports.swu2query = swu2query; exports.symbolRanges = symbolRanges; /* support ongoing development on https://patreon.com/signwriting */