PageSourceSearch

https://www.merchantmaverick.com/wp-content/mm-filters/wNumb.js

js merchantmaverick.com collected 2026-09-24 08:34:03 UTC 8,785 bytes, 357 lines download raw bytes

1(function (factory) {
2
3    if ( typeof define === 'function' && define.amd ) {
4
5        // AMD. Register as an anonymous module.
6        define([], factory);
7
8    } else if ( typeof exports === 'object' ) {
9
10        // Node/CommonJS
11        module.exports = factory();
12
13    } else {
14
15        // Browser globals
16        window.wNumb = factory();
17    }
18
19}(function(){
20
21	'use strict';
22
23var FormatOptions = [
24	'decimals',
25	'thousand',
26	'mark',
27	'prefix',
28	'suffix',
29	'encoder',
30	'decoder',
31	'negativeBefore',
32	'negative',
33	'edit',
34	'undo'
35];
36
37// General
38
39	// Reverse a string
40	function strReverse ( a ) {
41		return a.split('').reverse().join('');
42	}
43
44	// Check if a string starts with a specified prefix.
45	function strStartsWith ( input, match ) {
46		return input.substring(0, match.length) === match;
47	}
48
49	// Check is a string ends in a specified suffix.
50	function strEndsWith ( input, match ) {
51		return input.slice(-1 * match.length) === match;
52	}
53
54	// Throw an error if formatting options are incompatible.
55	function throwEqualError( F, a, b ) {
56		if ( (F[a] || F[b]) && (F[a] === F[b]) ) {
57			throw new Error(a);
58		}
59	}
60
61	// Check if a number is finite and not NaN
62	function isValidNumber ( input ) {
63		return typeof input === 'number' && isFinite( input );
64	}
65
66	// Provide rounding-accurate toFixed method.
67	// Borrowed: http://stackoverflow.com/a/21323330/775265
68	function toFixed ( value, exp ) {
69		value = value.toString().split('e');
70		value = Math.round(+(value[0] + 'e' + (value[1] ? (+value[1] + exp) : exp)));
71		value = value.toString().split('e');
72		return (+(value[0] + 'e' + (value[1] ? (+value[1] - exp) : -exp))).toFixed(exp);
73	}
74
75
76// Formatting
77
78	// Accept a number as input, output formatted string.
79	function formatTo ( decimals, thousand, mark, prefix, suffix, encoder, decoder, negativeBefore, negative, edit, undo, input ) {
80
81		var originalInput = input, inputIsNegative, inputPieces, inputBase, inputDecimals = '', output = '';
82
83		// Apply user encoder to the input.
84		// Expected outcome: number.
85		if ( encoder ) {
86			input = encoder(input);
87		}
88
89		// Stop if no valid number was provided, the number is infinite or NaN.
90		if ( !isValidNumber(input) ) {
91			return false;
92		}
93
94		// Rounding away decimals might cause a value of -0
95		// when using very small ranges. Remove those cases.
96		if ( decimals !== false && parseFloat(input.toFixed(decimals)) === 0 ) {
97			input = 0;
98		}
99
100		// Formatting is done on absolute numbers,
101		// decorated by an optional negative symbol.
102		if ( input < 0 ) {
103			inputIsNegative = true;
104			input = Math.abs(input);
105		}
106
107		// Reduce the number of decimals to the specified option.
108		if ( decimals !== false ) {
109			input = toFixed( input, decimals );
110		}
111
112		// Transform the number into a string, so it can be split.
113		input = input.toString();
114
115		// Break the number on the decimal separator.
116		if ( input.indexOf('.') !== -1 ) {
117			inputPieces = input.split('.');
118
119			inputBase = inputPieces[0];
120
121			if ( mark ) {
122				inputDecimals = mark + inputPieces[1];
123			}
124
125		} else {
126
127		// If it isn't split, the entire number will do.
128			inputBase = input;
129		}
130
131		// Group numbers in sets of three.
132		if ( thousand ) {
133			inputBase = strReverse(inputBase).match(/.{1,3}/g);
134			inputBase = strReverse(inputBase.join( strReverse( thousand ) ));
135		}
136
137		// If the number is negative, prefix with negation symbol.
138		if ( inputIsNegative && negativeBefore ) {
139			output += negativeBefore;
140		}
141
142		// Prefix the number
143		if ( prefix ) {
144			output += prefix;
145		}
146
147		// Normal negative option comes after the prefix. Defaults to '-'.
148		if ( inputIsNegative && negative ) {
149			output += negative;
150		}
151
152		// Append the actual number.
153		output += inputBase;
154		output += inputDecimals;
155
156		// Apply the suffix.
157		if ( suffix ) {
158			output += suffix;
159		}
160
161		// Run the output through a user-specified post-formatter.
162		if ( edit ) {
163			output = edit ( output, originalInput );
164		}
165
166		// All done.
167		return output;
168	}
169
170	// Accept a sting as input, output decoded number.
171	function formatFrom ( decimals, thousand, mark, prefix, suffix, encoder, decoder, negativeBefore, negative, edit, undo, input ) {
172
173		var originalInput = input, inputIsNegative, output = '';
174
175		// User defined pre-decoder. Result must be a non empty string.
176		if ( undo ) {
177			input = undo(input);
178		}
179
180		// Test the input. Can't be empty.
181		if ( !input || typeof input !== 'string' ) {
182			return false;
183		}
184
185		// If the string starts with the negativeBefore value: remove it.
186		// Remember is was there, the number is negative.
187		if ( negativeBefore && strStartsWith(input, negativeBefore) ) {
188			input = input.replace(negativeBefore, '');
189			inputIsNegative = true;
190		}
191
192		// Repeat the same procedure for the prefix.
193		if ( prefix && strStartsWith(input, prefix) ) {
194			input = input.replace(prefix, '');
195		}
196
197		// And again for negative.
198		if ( negative && strStartsWith(input, negative) ) {
199			input = input.replace(negative, '');
200			inputIsNegative = true;
201		}
202
203		// Remove the suffix.
204		// https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/String/slice
205		if ( suffix && strEndsWith(input, suffix) ) {
206			input = input.slice(0, -1 * suffix.length);
207		}
208
209		// Remove the thousand grouping.
210		if ( thousand ) {
211			input = input.split(thousand).join('');
212		}
213
214		// Set the decimal separator back to period.
215		if ( mark ) {
216			input = input.replace(mark, '.');
217		}
218
219		// Prepend the negative symbol.
220		if ( inputIsNegative ) {
221			output += '-';
222		}
223
224		// Add the number
225		output += input;
226
227		// Trim all non-numeric characters (allow '.' and '-');
228		output = output.replace(/[^0-9\.\-.]/g, '');
229
230		// The value contains no parse-able number.
231		if ( output === '' ) {
232			return false;
233		}
234
235		// Covert to number.
236		output = Number(output);
237
238		// Run the user-specified post-decoder.
239		if ( decoder ) {
240			output = decoder(output);
241		}
242
243		// Check is the output is valid, otherwise: return false.
244		if ( !isValidNumber(output) ) {
245			return false;
246		}
247
248		return output;
249	}
250
251
252// Framework
253
254	// Validate formatting options
255	function validate ( inputOptions ) {
256
257		var i, optionName, optionValue,
258			filteredOptions = {};
259
260		if ( inputOptions['suffix'] === undefined ) {
261			inputOptions['suffix'] = inputOptions['postfix'];
262		}
263
264		for ( i = 0; i < FormatOptions.length; i+=1 ) {
265
266			optionName = FormatOptions[i];
267			optionValue = inputOptions[optionName];
268
269			if ( optionValue === undefined ) {
270
271				// Only default if negativeBefore isn't set.
272				if ( optionName === 'negative' && !filteredOptions.negativeBefore ) {
273					filteredOptions[optionName] = '-';
274				// Don't set a default for mark when 'thousand' is set.
275				} else if ( optionName === 'mark' && filteredOptions.thousand !== '.' ) {
276					filteredOptions[optionName] = '.';
277				} else {
278					filteredOptions[optionName] = false;
279				}
280
281			// Floating points in JS are stable up to 7 decimals.
282			} else if ( optionName === 'decimals' ) {
283				if ( optionValue >= 0 && optionValue < 8 ) {
284					filteredOptions[optionName] = optionValue;
285				} else {
286					throw new Error(optionName);
287				}
288
289			// These options, when provided, must be functions.
290			} else if ( optionName === 'encoder' || optionName === 'decoder' || optionName === 'edit' || optionName === 'undo' ) {
291				if ( typeof optionValue === 'function' ) {
292					filteredOptions[optionName] = optionValue;
293				} else {
294					throw new Error(optionName);
295				}
296
297			// Other options are strings.
298			} else {
299
300				if ( typeof optionValue === 'string' ) {
301					filteredOptions[optionName] = optionValue;
302				} else {
303					throw new Error(optionName);
304				}
305			}
306		}
307
308		// Some values can't be extracted from a
309		// string if certain combinations are present.
310		throwEqualError(filteredOptions, 'mark', 'thousand');
311		throwEqualError(filteredOptions, 'prefix', 'negative');
312		throwEqualError(filteredOptions, 'prefix', 'negativeBefore');
313
314		return filteredOptions;
315	}
316
317	// Pass all options as function arguments
318	function passAll ( options, method, input ) {
319		var i, args = [];
320
321		// Add all options in order of FormatOptions
322		for ( i = 0; i < FormatOptions.length; i+=1 ) {
323			args.push(options[FormatOptions[i]]);
324		}
325
326		// Append the input, then call the method, presenting all
327		// options as arguments.
328		args.push(input);
329		return method.apply('', args);
330	}
331
332	function wNumb ( options ) {
333
334		if ( !(this instanceof wNumb) ) {
335			return new wNumb ( options );
336		}
337
338		if ( typeof options !== "object" ) {
339			return;
340		}
341
342		options = validate(options);
343
344		// Call 'formatTo' with proper arguments.
345		this.to = function ( input ) {
346			return passAll(options, formatTo, input);
347		};
348
349		// Call 'formatFrom' with proper arguments.
350		this.from = function ( input ) {
351			return passAll(options, formatFrom, input);
352		};
353	}
354
355	return wNumb;
356
357}));

Line numbers count LF bytes from the start of the resource, as the search results do. Vendor segments are library code the classifier recognised; they are stored but not indexed. Bytes are shown as Latin1 characters, one per byte.