simple-squiggle

A restricted subset of Squiggle
Log | Files | Refs | README

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, '&amp;').replace(/"/g, '&quot;').replace(/'/g, '&#39;').replace(/</g, '&lt;').replace(/>/g, '&gt;');
    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 }