string.js (6296B)
1 import { isBigNumber, isString, typeOf } from './is.js'; 2 import { format as formatNumber } from './number.js'; 3 import { format as formatBigNumber } from './bignumber/formatter.js'; 4 /** 5 * Check if a text ends with a certain string. 6 * @param {string} text 7 * @param {string} search 8 */ 9 10 export function endsWith(text, search) { 11 var start = text.length - search.length; 12 var end = text.length; 13 return text.substring(start, end) === search; 14 } 15 /** 16 * Format a value of any type into a string. 17 * 18 * Usage: 19 * math.format(value) 20 * math.format(value, precision) 21 * math.format(value, options) 22 * 23 * When value is a function: 24 * 25 * - When the function has a property `syntax`, it returns this 26 * syntax description. 27 * - In other cases, a string `'function'` is returned. 28 * 29 * When `value` is an Object: 30 * 31 * - When the object contains a property `format` being a function, this 32 * function is invoked as `value.format(options)` and the result is returned. 33 * - When the object has its own `toString` method, this method is invoked 34 * and the result is returned. 35 * - In other cases the function will loop over all object properties and 36 * return JSON object notation like '{"a": 2, "b": 3}'. 37 * 38 * Example usage: 39 * math.format(2/7) // '0.2857142857142857' 40 * math.format(math.pi, 3) // '3.14' 41 * math.format(new Complex(2, 3)) // '2 + 3i' 42 * math.format('hello') // '"hello"' 43 * 44 * @param {*} value Value to be stringified 45 * @param {Object | number | Function} [options] 46 * Formatting options. See src/utils/number.js:format for a 47 * description of the available options controlling number output. 48 * This generic "format" also supports the option property `truncate: NN` 49 * giving the maximum number NN of characters to return (if there would 50 * have been more, they are deleted and replaced by an ellipsis). 51 * @return {string} str 52 */ 53 54 export function format(value, options) { 55 var result = _format(value, options); 56 57 if (options && typeof options === 'object' && 'truncate' in options && result.length > options.truncate) { 58 return result.substring(0, options.truncate - 3) + '...'; 59 } 60 61 return result; 62 } 63 64 function _format(value, options) { 65 if (typeof value === 'number') { 66 return formatNumber(value, options); 67 } 68 69 if (isBigNumber(value)) { 70 return formatBigNumber(value, options); 71 } // note: we use unsafe duck-typing here to check for Fractions, this is 72 // ok here since we're only invoking toString or concatenating its values 73 74 75 if (looksLikeFraction(value)) { 76 if (!options || options.fraction !== 'decimal') { 77 // output as ratio, like '1/3' 78 return value.s * value.n + '/' + value.d; 79 } else { 80 // output as decimal, like '0.(3)' 81 return value.toString(); 82 } 83 } 84 85 if (Array.isArray(value)) { 86 return formatArray(value, options); 87 } 88 89 if (isString(value)) { 90 return '"' + value + '"'; 91 } 92 93 if (typeof value === 'function') { 94 return value.syntax ? String(value.syntax) : 'function'; 95 } 96 97 if (value && typeof value === 'object') { 98 if (typeof value.format === 'function') { 99 return value.format(options); 100 } else if (value && value.toString(options) !== {}.toString()) { 101 // this object has a non-native toString method, use that one 102 return value.toString(options); 103 } else { 104 var entries = Object.keys(value).map(key => { 105 return '"' + key + '": ' + format(value[key], options); 106 }); 107 return '{' + entries.join(', ') + '}'; 108 } 109 } 110 111 return String(value); 112 } 113 /** 114 * Stringify a value into a string enclosed in double quotes. 115 * Unescaped double quotes and backslashes inside the value are escaped. 116 * @param {*} value 117 * @return {string} 118 */ 119 120 121 export function stringify(value) { 122 var text = String(value); 123 var escaped = ''; 124 var i = 0; 125 126 while (i < text.length) { 127 var c = text.charAt(i); 128 129 if (c === '\\') { 130 escaped += c; 131 i++; 132 c = text.charAt(i); 133 134 if (c === '' || '"\\/bfnrtu'.indexOf(c) === -1) { 135 escaped += '\\'; // no valid escape character -> escape it 136 } 137 138 escaped += c; 139 } else if (c === '"') { 140 escaped += '\\"'; 141 } else { 142 escaped += c; 143 } 144 145 i++; 146 } 147 148 return '"' + escaped + '"'; 149 } 150 /** 151 * Escape special HTML characters 152 * @param {*} value 153 * @return {string} 154 */ 155 156 export function escape(value) { 157 var text = String(value); 158 text = text.replace(/&/g, '&').replace(/"/g, '"').replace(/'/g, ''').replace(/</g, '<').replace(/>/g, '>'); 159 return text; 160 } 161 /** 162 * Recursively format an n-dimensional matrix 163 * Example output: "[[1, 2], [3, 4]]" 164 * @param {Array} array 165 * @param {Object | number | Function} [options] Formatting options. See 166 * lib/utils/number:format for a 167 * description of the available 168 * options. 169 * @returns {string} str 170 */ 171 172 function formatArray(array, options) { 173 if (Array.isArray(array)) { 174 var str = '['; 175 var len = array.length; 176 177 for (var i = 0; i < len; i++) { 178 if (i !== 0) { 179 str += ', '; 180 } 181 182 str += formatArray(array[i], options); 183 } 184 185 str += ']'; 186 return str; 187 } else { 188 return format(array, options); 189 } 190 } 191 /** 192 * Check whether a value looks like a Fraction (unsafe duck-type check) 193 * @param {*} value 194 * @return {boolean} 195 */ 196 197 198 function looksLikeFraction(value) { 199 return value && typeof value === 'object' && typeof value.s === 'number' && typeof value.n === 'number' && typeof value.d === 'number' || false; 200 } 201 /** 202 * Compare two strings 203 * @param {string} x 204 * @param {string} y 205 * @returns {number} 206 */ 207 208 209 export function compareText(x, y) { 210 // we don't want to convert numbers to string, only accept string input 211 if (!isString(x)) { 212 throw new TypeError('Unexpected type of argument in function compareText ' + '(expected: string or Array or Matrix, actual: ' + typeOf(x) + ', index: 0)'); 213 } 214 215 if (!isString(y)) { 216 throw new TypeError('Unexpected type of argument in function compareText ' + '(expected: string or Array or Matrix, actual: ' + typeOf(y) + ', index: 1)'); 217 } 218 219 return x === y ? 0 : x > y ? 1 : -1; 220 }