string.js 6.1 KB

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