UNPKG

@polygonjs/polygonjs

Version:

node-based WebGL 3D engine https://polygonjs.com

301 lines (281 loc) 8.85 kB
import {isNumberValid} from './Type'; import {arrayPushItems, arrayUniq, range} from './ArrayUtils'; const ATTRIB_NAMES_SEPARATOR = /[, ]/; //[',', ' '] // const LETTERS = 'abcdefghijklmnopqrstuvwxyz' // const LETTERS_UPPERCASE = LETTERS.toUpperCase() // const NUMBERS = '0123645789' // const ALL = LETTERS + LETTERS_UPPERCASE + NUMBERS const TAIL_DIGIT_MATCH_REGEXP = /\d+$/; const LEADING_ZEROS_MATCH_REGEXP = /^0+/; // const DIGIT_PREDEDED_BY_UNDERSCOPE = /_\d$/ const INDICES_LIST_SEPARATOR = /,| /; const ZERO = '0'; const SPACE = ' '; const RANGE_SEPARATOR = '-'; // https://stackoverflow.com/questions/41856126/regexp-optional-dot-in-a-decimal-number const NUM_REGEXP = /^-?\d+\.?\d*$/; enum BooleanString { TRUE = 'true', FALSE = 'false', } export function stringIsBoolean(word: string): boolean { return word == BooleanString.TRUE || word == BooleanString.FALSE; } export function stringToBoolean(word: string): boolean { return word == BooleanString.TRUE; } export function stringIsNumber(word: string): boolean { return NUM_REGEXP.test(word); } export function sanitizeName(word: string): string { word = word.replace(/[^A-Za-z0-9]/g, '_'); word = word.replace(/^[0-9]/, '_'); // replace first char if not a letter return word; } let _tmp: string[] = []; export function stringToAttribNames(word: string, target: string[]): string[] { const elements = word.split(ATTRIB_NAMES_SEPARATOR); _tmp.length = 0; for (const element of elements) { const trimmed = element.trim(); if (trimmed.length > 0) { _tmp.push(trimmed); } } arrayUniq(_tmp, target); return target; } export function stringTailDigits(word: string): number { const match = word.match(TAIL_DIGIT_MATCH_REGEXP); if (match) { return parseInt(match[0]); } else { return 0; } } export function stringIncrement(word: string): string { const match = word.match(TAIL_DIGIT_MATCH_REGEXP); if (match) { let numbers_as_str = match[0]; let zeros_prefix: string = ''; const leading_zeros_match = numbers_as_str.match(LEADING_ZEROS_MATCH_REGEXP); if (leading_zeros_match) { zeros_prefix = leading_zeros_match[0]; } const digits = parseInt(numbers_as_str); if (digits == 0) { if (zeros_prefix.length > 0) { if (zeros_prefix[zeros_prefix.length - 1] == ZERO) { zeros_prefix = zeros_prefix.slice(0, -1); } } } const prefix = word.substring(0, word.length - match[0].length); return `${prefix}${zeros_prefix}${digits + 1}`; } else { return `${word}1`; } } export function stringPluralize(word: string): string { const last_char = word[word.length - 1]; if (last_char !== 's') { return `${word}s`; } else { return word; } } export function stringCamelCase(str: string): string { const elements = str.replace(/_/g, ' ').split(' '); let newWord = ''; for (let i = 0; i < elements.length; i++) { let element = elements[i].toLowerCase(); if (i > 0) { element = stringUpperFirst(element); } newWord += element; } return newWord; // inspired from https://blog.bitsrc.io/5-string-manipulation-libraries-for-javascript-5de27e48ee62 // return str.replace(/_/g, ' ').replace(/(?:^\w|[A-Z0-9]|\b\w|\s+)/g, function (match, index) { // console.log('match', match, index); // if (+match === 0) return ''; // or if (/\s+/.test(match)) for white spaces // return index === 0 ? match.toLowerCase() : match.toUpperCase(); // }); } export function stringUpperFirst(word: string): string { if (word.length == 0) { return word; } const newString = word[0].toUpperCase() + word.substring(1); return newString; } export function stringTitleize(word: string): string { const elements = word.split(/\s|_/g); const newElements = elements.map(stringUpperFirst); return newElements.join(' '); } export function precision(val: number, decimals: number = 2): string { decimals = Math.max(decimals, 0); const elements = `${val}`.split('.'); if (decimals <= 0) { return elements[0]; } let frac = elements[1]; if (frac !== undefined) { if (frac.length > decimals) { frac = frac.substring(0, decimals); } frac = frac.padEnd(decimals, '0'); return `${elements[0]}.${frac}`; } else { const string_to_pad = `${val}.`; const pad = string_to_pad.length + decimals; return string_to_pad.padEnd(pad, '0'); } } export function ensureFloat(num: number): string { // const integer = Math.floor(num) // const delta = num - integer // if(delta) const num_as_string = `${num}`; const dot_pos = num_as_string.indexOf('.'); if (dot_pos >= 0) { return num_as_string; } else { return `${num_as_string}.0`; } } export function ensureInteger(num: number): string { const num_as_string = `${num}`; const dot_pos = num_as_string.indexOf('.'); if (dot_pos >= 0) { return num_as_string.split('.')[0]; } else { return num_as_string; } } // let _elements: string[] = []; // let _inclusionFilters: string[] = []; // // https://stackoverflow.com/questions/26246601/wildcard-string-comparison-in-javascript#32402438 export function stringMatchMask(word: string, mask: string) { if (mask === '*') { return true; } if (word == mask) { return true; } const elements = mask.split(SPACE); // const elements = _elements; const exclusionFilters: string[] = []; // _inclusionFilters.length = 0; for (const element of elements) { if (element.startsWith('^')) { exclusionFilters.push(element.substring(1)); } else { // _inclusionFilters.push(element); } } // const exclusionFilters = elements // .filter((element) => element.startsWith('^')) // .map((element) => element.substring(1)); for (const exclusionFilter of exclusionFilters) { const match = stringMatchMask(word, exclusionFilter); if (match) { return false; } } if (elements.length > 1) { for (const element of elements) { if (stringMatchMask(word, element)) { return true; } } return false; } // "." => Find a single character, except newline or line terminator // ".*" => Matches any string that contains zero or more characters mask = mask.split('*').join('.*'); // ? needs to be escaped, otherwise it will be used inside the regex mask = mask.split('?').join('\\?'); // "^" => Matches any string with the following at the beginning of it // "$" => Matches any string with that in front at the end of it mask = `^${mask}$`; try { // Create a regular expression object for matching string const regex = new RegExp(mask); // Returns true if it finds a match, otherwise it returns false return regex.test(word); } catch (err) { // we need to wrap in a try catch in case it would create an invalid regex return false; } } export function stringMatchesOneMask(word: string, masks: string[]): boolean { for (const mask of masks) { if (stringMatchMask(word, mask)) { return true; } } return false; } let _indices: number[] = []; let _subIndices: number[] = []; export function stringToIndices(indicesString: string, target: number[]): number[] { target.length = 0; const elements = indicesString.split(INDICES_LIST_SEPARATOR); if (elements.length > 1) { _indices.length = 0; for (const element of elements) { stringToIndices(element, _subIndices); arrayPushItems(_subIndices, _indices); } // const indices: number[] = elements.flatMap(stringToIndices); // const uniqIndices: number[] = []; arrayUniq(_indices, target); return target.sort((a, b) => a - b); } else { const element = elements[0]; if (element) { if (element.indexOf(RANGE_SEPARATOR) > 0) { const rangeElements = element.split(RANGE_SEPARATOR); const rangeStart = rangeElements[0]; const rangeEnd = rangeElements[1]; const rangeStartI = parseInt(rangeStart); const rangeEndI = parseInt(rangeEnd); if (isNumberValid(rangeStartI) && isNumberValid(rangeEndI)) { return range(rangeStartI, rangeEndI + 1, 1, target); } } else { const parsed = parseInt(element); if (isNumberValid(parsed)) { target.push(parsed); return target; } } } } return target; } export function stringEscapeLineBreaks(word: string): string { return word.replace(/(\r\n|\n|\r)/gm, '\\n'); } export class CoreString { static isBoolean = stringIsBoolean; static toBoolean = stringToBoolean; static isNumber = stringIsNumber; static tailDigits = stringTailDigits; static increment = stringIncrement; static pluralize = stringPluralize; static camelCase = stringCamelCase; static upperFirst = stringUpperFirst; static titleize = stringTitleize; static precision = precision; static ensureFloat = ensureFloat; static ensureInteger = ensureInteger; static matchMask = stringMatchMask; static matchesOneMask = stringMatchesOneMask; static attribNames = stringToAttribNames; static indices = stringToIndices; static escapeLineBreaks = stringEscapeLineBreaks; static sanitizeName = sanitizeName; }