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.