/** * Sutton SignWriting Core Module v1.4.2 (https://github.com/sutton-signwriting/core) * Author: Steve Slevinski (https://SteveSlevinski.me) * fswquery.cjs is released under the MIT License. */ 'use strict'; Object.defineProperty(exports, '__esModule', { value: true }); /** * 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$2 = { 'base': '[123][0-9a-f]{2}', 'coord': '(?:[0-9]{3}x[0-9]{3})?', 'var': 'V[0-9]+' }; re$2.symbol = `S${re$2.base}[0-5u][0-9a-fu]`; re$2.range = `R${re$2.base}t${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 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$1 = { '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$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 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)); 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] == 'S' ? part : part.slice(1).split('t'))); } else { return part[0] == 'S' ? part : part.slice(1).split('t'); } }) }; }; const parseSignbox = text => { return text.match(new RegExp(`(${re$2.list}${re$2.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$1 = fswQueryString => { const query = typeof fswQueryString === 'string' ? fswQueryString.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 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 = 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$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 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$1.symbol})(${re$1.coord})?(${re.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$1.prefix})?(${re$1.signbox})(${re.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$1.sign}(${re.full})?|${re$1.spatial}(${re.full})?)`; const matches = fswText.match(new RegExp(regex, 'g')); return matches ? [...matches] : []; } }; /** * 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.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 = (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 = symRange => { let from = symRange.slice(1, 4); let to = symRange.slice(5, 8); return 'S' + range(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 = query => { query = query.match(new RegExp(`^${re$2.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.full + ')?'; var q_sortable; if (query == 'Q') { return [re$1.prefix + "?" + re$1.signbox]; } if (query == 'Q-') { return [re$1.prefix + "?" + re$1.signbox + q_style]; } if (query == 'QT') { return [re$1.prefix + re$1.signbox]; } if (query == 'QT-') { return [re$1.prefix + re$1.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$1.symbol + ')+)'; } 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(regexSymbol(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(re$2.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(regexSymbol(matched[0])); } else { orList.push(regexRange(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(x - fuzz, x + fuzz); segment += 'x'; segment += range(y - fuzz, y + fuzz); } else { segment += re$1.coord; } // add to general fsw word segment = re$1.signbox + segment + '(' + re$1.symbol + re$1.coord + ')*'; if (sortable) { segment = q_sortable + segment; } else { segment = re$1.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$1.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 = (query, text) => { if (!text) { return []; } let pattern; let matches; let parts; let words; let re = regex(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 = (query, text) => { if (!text) { return []; } let pattern; let matches; let parts; let words; let re = regex(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; }; exports.compose = compose; exports.fsw2query = fsw2query; exports.lines = lines; exports.parse = parse$1; exports.range = range; exports.re = re$2; exports.regex = regex; exports.results = results; /* support ongoing development on https://patreon.com/signwriting */