text: (function (factory) {
    if (typeof define === "function" && define.amd) {
      // AMD. Register as an anonymous module.
      define([], factory);
    } else if (typeof exports === "object") {
      // Node/CommonJS
      module.exports = factory();
    } else {
      // Browser globals
      window.wNumb = factory();
    }
  })(function () {
    "use strict";
  
    var FormatOptions = [
      "decimals",
      "thousand",
      "mark",
      "prefix",
      "suffix",
      "encoder",
      "decoder",
      "negativeBefore",
      "negative",
      "edit",
      "undo",
    ];
  
    // General
  
    // Reverse a string
    function strReverse(a) {
      return a.split("").reverse().join("");
    }
  
    // Check if a string starts with a specified prefix.
    function strStartsWith(input, match) {
      return input.substring(0, match.length) === match;
    }
  
    // Check is a string ends in a specified suffix.
    function strEndsWith(input, match) {
      return input.slice(-1 * match.length) === match;
    }
  
    // Throw an error if formatting options are incompatible.
    function throwEqualError(F, a, b) {
      if ((F[a] || F[b]) && F[a] === F[b]) {
        throw new Error(a);
      }
    }
  
    // Check if a number is finite and not NaN
    function isValidNumber(input) {
      return typeof input === "number" && isFinite(input);
    }
  
    // Provide rounding-accurate toFixed method.
    // Borrowed: http://stackoverflow.com/a/21323330/775265
    function toFixed(value, exp) {
      value = value.toString().split("e");
      value = Math.round(+(value[0] + "e" + (value[1] ? +value[1] + exp : exp)));
      value = value.toString().split("e");
      return (+(value[0] + "e" + (value[1] ? +value[1] - exp : -exp))).toFixed(
        exp
      );
    }
  
    // Formatting
  
    // Accept a number as input, output formatted string.
    function formatTo(
      decimals,
      thousand,
      mark,
      prefix,
      suffix,
      encoder,
      decoder,
      negativeBefore,
      negative,
      edit,
      undo,
      input
    ) {
      var originalInput = input,
        inputIsNegative,
        inputPieces,
        inputBase,
        inputDecimals = "",
        output = "";
  
      // Apply user encoder to the input.
      // Expected outcome: number.
      if (encoder) {
        input = encoder(input);
      }
  
      // Stop if no valid number was provided, the number is infinite or NaN.
      if (!isValidNumber(input)) {
        return false;
      }
  
      // Rounding away decimals might cause a value of -0
      // when using very small ranges. Remove those cases.
      if (decimals !== false && parseFloat(input.toFixed(decimals)) === 0) {
        input = 0;
      }
  
      // Formatting is done on absolute numbers,
      // decorated by an optional negative symbol.
      if (input < 0) {
        inputIsNegative = true;
        input = Math.abs(input);
      }
  
      // Reduce the number of decimals to the specified option.
      if (decimals !== false) {
        input = toFixed(input, decimals);
      }
  
      // Transform the number into a string, so it can be split.
      input = input.toString();
  
      // Break the number on the decimal separator.
      if (input.indexOf(".") !== -1) {
        inputPieces = input.split(".");
  
        inputBase = inputPieces[0];
  
        if (mark) {
          inputDecimals = mark + inputPieces[1];
        }
      } else {
        // If it isn't split, the entire number will do.
        inputBase = input;
      }
  
      // Group numbers in sets of three.
      if (thousand) {
        inputBase = strReverse(inputBase).match(/.{1,3}/g);
        inputBase = strReverse(inputBase.join(strReverse(thousand)));
      }
  
      // If the number is negative, prefix with negation symbol.
      if (inputIsNegative && negativeBefore) {
        output += negativeBefore;
      }
  
      // Prefix the number
      if (prefix) {
        output += prefix;
      }
  
      // Normal negative option comes after the prefix. Defaults to '-'.
      if (inputIsNegative && negative) {
        output += negative;
      }
  
      // Append the actual number.
      output += inputBase;
      output += inputDecimals;
  
      // Apply the suffix.
      if (suffix) {
        output += suffix;
      }
  
      // Run the output through a user-specified post-formatter.
      if (edit) {
        output = edit(output, originalInput);
      }
  
      // All done.
      return output;
    }
  
    // Accept a sting as input, output decoded number.
    function formatFrom(
      decimals,
      thousand,
      mark,
      prefix,
      suffix,
      encoder,
      decoder,
      negativeBefore,
      negative,
      edit,
      undo,
      input
    ) {
      var originalInput = input,
        inputIsNegative,
        output = "";
  
      // User defined pre-decoder. Result must be a non empty string.
      if (undo) {
        input = undo(input);
      }
  
      // Test the input. Can't be empty.
      if (!input || typeof input !== "string") {
        return false;
      }
  
      // If the string starts with the negativeBefore value: remove it.
      // Remember is was there, the number is negative.
      if (negativeBefore && strStartsWith(input, negativeBefore)) {
        input = input.replace(negativeBefore, "");
        inputIsNegative = true;
      }
  
      // Repeat the same procedure for the prefix.
      if (prefix && strStartsWith(input, prefix)) {
        input = input.replace(prefix, "");
      }
  
      // And again for negative.
      if (negative && strStartsWith(input, negative)) {
        input = input.replace(negative, "");
        inputIsNegative = true;
      }
  
      // Remove the suffix.
      // https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/String/slice
      if (suffix && strEndsWith(input, suffix)) {
        input = input.slice(0, -1 * suffix.length);
      }
  
      // Remove the thousand grouping.
      if (thousand) {
        input = input.split(thousand).join("");
      }
  
      // Set the decimal separator back to period.
      if (mark) {
        input = input.replace(mark, ".");
      }
  
      // Prepend the negative symbol.
      if (inputIsNegative) {
        output += "-";
      }
  
      // Add the number
      output += input;
  
      // Trim all non-numeric characters (allow '.' and '-');
      output = output.replace(/[^0-9\.\-.]/g, "");
  
      // The value contains no parse-able number.
      if (output === "") {
        return false;
      }
  
      // Covert to number.
      output = Number(output);
  
      // Run the user-specified post-decoder.
      if (decoder) {
        output = decoder(output);
      }
  
      // Check is the output is valid, otherwise: return false.
      if (!isValidNumber(output)) {
        return false;
      }
  
      return output;
    }
  
    // Framework
  
    // Validate formatting options
    function validate(inputOptions) {
      var i,
        optionName,
        optionValue,
        filteredOptions = {};
  
      if (inputOptions["suffix"] === undefined) {
        inputOptions["suffix"] = inputOptions["postfix"];
      }
  
      for (i = 0; i < FormatOptions.length; i += 1) {
        optionName = FormatOptions[i];
        optionValue = inputOptions[optionName];
  
        if (optionValue === undefined) {
          // Only default if negativeBefore isn't set.
          if (optionName === "negative" && !filteredOptions.negativeBefore) {
            filteredOptions[optionName] = "-";
            // Don't set a default for mark when 'thousand' is set.
          } else if (optionName === "mark" && filteredOptions.thousand !== ".") {
            filteredOptions[optionName] = ".";
          } else {
            filteredOptions[optionName] = false;
          }
  
          // Floating points in JS are stable up to 7 decimals.
        } else if (optionName === "decimals") {
          if (optionValue >= 0 && optionValue < 8) {
            filteredOptions[optionName] = optionValue;
          } else {
            throw new Error(optionName);
          }
  
          // These options, when provided, must be functions.
        } else if (
          optionName === "encoder" ||
          optionName === "decoder" ||
          optionName === "edit" ||
          optionName === "undo"
        ) {
          if (typeof optionValue === "function") {
            filteredOptions[optionName] = optionValue;
          } else {
            throw new Error(optionName);
          }
  
          // Other options are strings.
        } else {
          if (typeof optionValue === "string") {
            filteredOptions[optionName] = optionValue;
          } else {
            throw new Error(optionName);
          }
        }
      }
  
      // Some values can't be extracted from a
      // string if certain combinations are present.
      throwEqualError(filteredOptions, "mark", "thousand");
      throwEqualError(filteredOptions, "prefix", "negative");
      throwEqualError(filteredOptions, "prefix", "negativeBefore");
  
      return filteredOptions;
    }
  
    // Pass all options as function arguments
    function passAll(options, method, input) {
      var i,
        args = [];
  
      // Add all options in order of FormatOptions
      for (i = 0; i < FormatOptions.length; i += 1) {
        args.push(options[FormatOptions[i]]);
      }
  
      // Append the input, then call the method, presenting all
      // options as arguments.
      args.push(input);
      return method.apply("", args);
    }
  
    function wNumb(options) {
      if (!(this instanceof wNumb)) {
        return new wNumb(options);
      }
  
      if (typeof options !== "object") {
        return;
      }
  
      options = validate(options);
  
      // Call 'formatTo' with proper arguments.
      this.to = function (input) {
        return passAll(options, formatTo, input);
      };
  
      // Call 'formatFrom' with proper arguments.
      this.from = function (input) {
        return passAll(options, formatFrom, input);
      };
    }
  
    return wNumb;
  });
  