@sutton-signwriting/core
Version:
a javascript package for node and browsers that supports general processing of the Sutton SignWriting script
957 lines (922 loc) • 30.8 kB
JavaScript
/**
* Sutton SignWriting Core Module v2.0.0 (https://github.com/sutton-signwriting/core)
* Author: Steve Slevinski (https://SteveSlevinski.me)
* swuquery.cjs is released under the MIT License.
*/
;
Object.defineProperty(exports, '__esModule', { value: true });
/**
* Object of regular expressions for SWU query strings
*
* @alias swuquery.re
* @type {object}
* @property {string} null - the null symbol
* @property {string} base - SWU symbol
* @property {string} coord - SWU coordinate of X and Y number characters
* @property {string} var - variance string for searching sign box
* @property {string} symbol - SWU symbol character with ignore fill and rotation flags
* @property {string} nullorsymbol - null or a symbol
* @property {string} range - SWU range starting with 'R'
* @property {string} item - SWU symbol or range query string
* @property {string} list - several SWU symbols and SWU ranges as a logical OR for searching
* @property {string} prefix - a sequential list of SWU symbol characters with nulls starting with SWU 'A' character
* @property {string} signbox - several groups of SWU lists, each group having a coordinate
* @property {string} full - a query string to search prefix in order and the signbox with variance
*/
let re$2 = {
'null': '\uD8C0\uDC00',
'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.nullorsymbol = `(?:${re$2.null}|${re$2.symbol})`;
re$2.range = `R${re$2.base}${re$2.base}`;
re$2.item = `(?:${re$2.null}|${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} null - the null symbol
* @property {string} symbol - a symbol
* @property {string} nullorsymbol - null or a symbol
* @property {string} sort - the sorting marker
* @property {string} prefix - a sorting marker followed by one or more symbols with nulls
* @property {string} box - a signbox marker
* @property {string} coord - a coordinate
* @property {string} spatial - a symbol followed by a coordinate
* @property {string} signbox - a signbox marker, max coordinate and zero or more spatial symbols
* @property {string} sign - an optional prefix followed by a signbox
* @property {string} sortable - a mandatory prefix followed by a signbox
*/
let re$1 = {
'null': '\uD8C0\uDC00',
'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.nullorsymbol = `(?:${re$1.null}|${re$1.symbol})`;
re$1.prefix = `(?:${re$1.sort}(?:${re$1.nullorsymbol})+)`;
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 => {
if (swuSym === "") {
return "S00000";
}
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 => {
if (key === "S00000") {
return code2swu(0x40000);
}
return 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 {QueryObject} elements of an SWU query string
* @example
* swuquery.parse('QARTR𝤆𝤆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 {QueryObject} swuQueryObject - an object of query options
* @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 'QARTR𝤆𝤆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 {SymbolObject} 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 {SignObject} 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 {string[]} 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 'QAT𝣵𝤐𝤇𝣤𝤐𝤆𝣮𝣭'
*/
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;
}
};
//needs rewritten, but it works
/**
* 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);
};
//needs rewritten, but it works
/**
* 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 <caption>Match an exact symbol</caption>
* swuquery.symbolRanges('')
*
* return '\uD8C0\uDC01');
* @example <caption>Match a symbol with any fill</caption>
* swuquery.symbolRanges('f')
*
* return '(\uD8C0\uDC01|\uD8C0\uDC11|\uD8C0\uDC21|\uD8C0\uDC31|\uD8C0\uDC41|\uD8C0\uDC51)'
* @example <caption>Match a symbol with any rotation</caption>
* swuquery.symbolRanges('r')
*
* return '\uD8C0[\uDC01-\uDC10]'
* @example <caption>Match a symbol with any fill or rotation</caption>
* 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 => {
let from = swu2key(symRange.slice(1, 3));
let 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('QAT')
*
* 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_word = re$1.box + re$1.coord + '(?:' + re$1.symbol + re$1.coord + ')*';
let re_sortable = '(?:' + re$1.sort + re$1.nullorsymbol + '+)';
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$1.nullorsymbol + '+)';
} 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.item, 'g'));
if (matchesOr) {
for (j = 0; j < matchesOr.length; j += 1) {
matched = matchesOr[j].match(new RegExp(re$2.range));
if (matched) {
orList.push(regexRange(matchesOr[j]));
} else {
orList.push(symbolRanges(matchesOr[j]));
}
}
if (orList.length == 1) {
q_sortable += orList[0];
} else {
q_sortable += '(?:' + orList.join('|') + ')';
}
}
}
q_sortable += re$1.nullorsymbol + '*)';
}
}
}
//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.range + '|' + re$2.symbol + ')', 'g'));
if (matchesOr) {
for (j = 0; j < matchesOr.length; j += 1) {
matched = matchesOr[j].match(new RegExp(re$2.range));
if (matched) {
orList.push(regexRange(matchesOr[j]));
} else {
orList.push(symbolRanges(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$1.symbol + re$1.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;
};
//needs rewritten, but it works
/**
* 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('QAT','𝠀𝠃𝤟𝤩𝣵𝤐𝤇𝣤𝤐𝤆𝣮𝣭 𝠀𝠃𝤛𝤬𝤀𝣺𝤄𝣻𝤄𝤗𝤃𝣟𝣱𝣸 𝠀𝠃𝤙𝤞𝣷𝤀𝣼𝤀𝣳𝣮')
*
* 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('QAT',`𝠀𝠃𝤟𝤩𝣵𝤐𝤇𝣤𝤐𝤆𝣮𝣭 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 */
/* https://patreon.com/signwriting */
/* https://donate.sutton-signwriting.io */