UNPKG

5.9 kBJavaScriptView Raw
1import { isBigNumber, isString, typeOf } from './is'
2import { format as formatNumber } from './number'
3import { format as formatBigNumber } from './bignumber/formatter'
4
5/**
6 * Check if a text ends with a certain string.
7 * @param {string} text
8 * @param {string} search
9 */
10export function endsWith (text, search) {
11 const start = text.length - search.length
12 const end = text.length
13 return (text.substring(start, end) === search)
14}
15
16/**
17 * Format a value of any type into a string.
18 *
19 * Usage:
20 * math.format(value)
21 * math.format(value, precision)
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] Formatting options. See
46 * lib/utils/number:format for a
47 * description of the available
48 * options.
49 * @return {string} str
50 */
51export function format (value, options) {
52 if (typeof value === 'number') {
53 return formatNumber(value, options)
54 }
55
56 if (isBigNumber(value)) {
57 return formatBigNumber(value, options)
58 }
59
60 // note: we use unsafe duck-typing here to check for Fractions, this is
61 // ok here since we're only invoking toString or concatenating its values
62 if (looksLikeFraction(value)) {
63 if (!options || options.fraction !== 'decimal') {
64 // output as ratio, like '1/3'
65 return (value.s * value.n) + '/' + value.d
66 } else {
67 // output as decimal, like '0.(3)'
68 return value.toString()
69 }
70 }
71
72 if (Array.isArray(value)) {
73 return formatArray(value, options)
74 }
75
76 if (isString(value)) {
77 return '"' + value + '"'
78 }
79
80 if (typeof value === 'function') {
81 return value.syntax ? String(value.syntax) : 'function'
82 }
83
84 if (value && typeof value === 'object') {
85 if (typeof value.format === 'function') {
86 return value.format(options)
87 } else if (value && value.toString(options) !== {}.toString()) {
88 // this object has a non-native toString method, use that one
89 return value.toString(options)
90 } else {
91 const entries = Object.keys(value).map(key => {
92 return '"' + key + '": ' + format(value[key], options)
93 })
94
95 return '{' + entries.join(', ') + '}'
96 }
97 }
98
99 return String(value)
100}
101
102/**
103 * Stringify a value into a string enclosed in double quotes.
104 * Unescaped double quotes and backslashes inside the value are escaped.
105 * @param {*} value
106 * @return {string}
107 */
108export function stringify (value) {
109 const text = String(value)
110 let escaped = ''
111 let i = 0
112 while (i < text.length) {
113 let c = text.charAt(i)
114
115 if (c === '\\') {
116 escaped += c
117 i++
118
119 c = text.charAt(i)
120 if (c === '' || '"\\/bfnrtu'.indexOf(c) === -1) {
121 escaped += '\\' // no valid escape character -> escape it
122 }
123 escaped += c
124 } else if (c === '"') {
125 escaped += '\\"'
126 } else {
127 escaped += c
128 }
129 i++
130 }
131
132 return '"' + escaped + '"'
133}
134
135/**
136 * Escape special HTML characters
137 * @param {*} value
138 * @return {string}
139 */
140export function escape (value) {
141 let text = String(value)
142 text = text.replace(/&/g, '&amp;')
143 .replace(/"/g, '&quot;')
144 .replace(/'/g, '&#39;')
145 .replace(/</g, '&lt;')
146 .replace(/>/g, '&gt;')
147
148 return text
149}
150
151/**
152 * Recursively format an n-dimensional matrix
153 * Example output: "[[1, 2], [3, 4]]"
154 * @param {Array} array
155 * @param {Object | number | Function} [options] Formatting options. See
156 * lib/utils/number:format for a
157 * description of the available
158 * options.
159 * @returns {string} str
160 */
161function formatArray (array, options) {
162 if (Array.isArray(array)) {
163 let str = '['
164 const len = array.length
165 for (let i = 0; i < len; i++) {
166 if (i !== 0) {
167 str += ', '
168 }
169 str += formatArray(array[i], options)
170 }
171 str += ']'
172 return str
173 } else {
174 return format(array, options)
175 }
176}
177
178/**
179 * Check whether a value looks like a Fraction (unsafe duck-type check)
180 * @param {*} value
181 * @return {boolean}
182 */
183function looksLikeFraction (value) {
184 return (value &&
185 typeof value === 'object' &&
186 typeof value.s === 'number' &&
187 typeof value.n === 'number' &&
188 typeof value.d === 'number') || false
189}
190
191/**
192 * Compare two strings
193 * @param {string} x
194 * @param {string} y
195 * @returns {number}
196 */
197export function compareText (x, y) {
198 // we don't want to convert numbers to string, only accept string input
199 if (!isString(x)) {
200 throw new TypeError('Unexpected type of argument in function compareText ' +
201 '(expected: string or Array or Matrix, actual: ' + typeOf(x) + ', index: 0)')
202 }
203 if (!isString(y)) {
204 throw new TypeError('Unexpected type of argument in function compareText ' +
205 '(expected: string or Array or Matrix, actual: ' + typeOf(y) + ', index: 1)')
206 }
207
208 return (x === y)
209 ? 0
210 : (x > y ? 1 : -1)
211}