Source: string.js

"use strict";

/**
 * Provides an API for enabling extra string functions.
 *
 * @module string
 *
 * @author Henry Brown
 */


/**
 * A helper function that's used to get the number of repeats of padValue needed
 *  to pad string's length to padLength without exceeding padLength while only
 *  repeating whole portions of padValue.
 *
 * @example
 * //returns 2 because the length of "freddyblabla" is 12, and additional
 * // repetitions of bla would cause us to exceed the specified padLength of 13
 * padCount("freddy", "bla", 13);
 */
module.exports.padCount = function(string, padValue, padLength) {
  return Math.floor(Math.max(0, padLength - string.length) / padValue.length);
};

/**
 * Enables all of the string functions including:
 *  - {@link module:string.padLeft  padLeft}
 *  - {@link module:string.padRight padRight}
 *  - {@link module:string.prepend  prepend}
 *  - {@link module:string.indent   indent}
 *
 * @throws {Error}        If String has already implemented any of the above
 *                        functions
 */
module.exports.enableAll = function() {
  let s = require("./string.js");

  s.enablePadLeft();
  s.enablePadRight();
  s.enablePrepend();
  s.enableIndent();
};

/**
 * Pads the left hand side of this string with copies of padValue until another
 *  would cause the string's length to exceed padLength.
 *
 * @param {String} padValue   The value to repeat
 * @param {String} padLength  The max length of the string after padding
 *
 * @returns {String}          A padded string
 *
 * @example
 * //returns "00007"
 * "7".padLeft("0", 5)
 */
module.exports.padLeft = function(padValue, padLength) {
  if (padLength < 0) {
    throw new Error("Invalid pad length");
  } else if (typeof this !== "string") {
    throw new Error("This function can only be used on a string object after" +
      "calling require(\"node-extra-funcs\").string.enablePadLeft();");
  }

  return String(padValue.repeat(require("./string")
    .padCount(this, padValue, padLength)) + this);
};

/**
 * Attaches the {@link module:string.padLeft padLeft} function to the String
 *  prototypes so that it can be used on any String objects in the application.
 *
 * @throws {Error}            If String has already implemented padLeft.
 */
module.exports.enablePadLeft = function() {
  if (String.prototype.padLeft === undefined) {
    this.enabledPadLeft = true;

    String.prototype.padLeft = require("./string").padLeft;
  } else {
    if (this.enabledPadLeft !== true) {
      throw new Error("Attempted to redefine padLeft function");
    }
  }
};

/**
 * Detaches the {@link module:string.padLeft padLeft} function from the String
 *  prototypes.
 */
module.exports.disablePadLeft = function() {
  if (this.enabledPadLeft === true) {
    delete String.prototype.padLeft;
  }
};

/**
 * Pads the right hand side of this string with copies of padValue until another
 *  would cause the string's length to exceed padLength.
 *
 * @param {String} padValue   The value to repeat
 * @param {String} padLength  The max length of the string after padding
 *
 * @returns {String}          A padded string
 *
 * @example
 * //returns "Noooo"
 * "N".padRight("o", 5)
 */
module.exports.padRight = function(padValue, padLength) {
  if (padLength < 0) {
    throw new Error("Invalid pad length");
  }

  return String(this + padValue.repeat(require("./string")
    .padCount(this, padValue, padLength)));
};

/**
 * Attaches the {@link module:string.padRight padRight} function to the String
 *  prototypes so that it can be used on any String objects in the application.
 *
 * @throws {Error}            If String has already implemented padRight.
 */
module.exports.enablePadRight = function() {
  if (String.prototype.padRight === undefined) {
    this.enabledPadRight = true;

    String.prototype.padRight = require("./string").padRight;
  } else {
    if (this.enabledPadRight !== true) {
      throw new Error("Attempted to redefine padRight function");
    }
  }
};

/**
 * Detaches the {@link module:string.padRight padRight} function from the String
 *  prototypes.
 */
module.exports.disablePadRight = function() {
  if (this.enabledPadRight === true) {
    delete String.prototype.padRight;
  }
};

/**
 * Concatenates the prefix parameter and this string together.
 *
 * @param {String} prefix     The value to prepend.
 *
 * @returns {String}          The concatenated string.
 *
 * @example
 * //returns "prefix"
 * "fix".prepend("pre");
 */
module.exports.prepend = function(prefix) {
  return String(prefix + this);
};

/**
 * Attaches the {@link module:string.prepend prepend} function to the String
 *  prototypes so that it can be used on any String objects in the application.
 *
 * @throws {Error}            If String has already implemented prepend.
 */
module.exports.enablePrepend = function() {
  if (String.prototype.prepend === undefined) {
    this.enabledPrepend = true;

    String.prototype.prepend = require("./string").prepend;
  } else {
    if (this.enabledPrepend !== true) {
      throw new Error("Attempted to redefine prepend function");
    }
  }
};

/**
 * Detaches the {@link module:string.prepend prepend} function from the String
 *  prototypes.
 */
module.exports.disablePrepend = function() {
  if (this.enabledPrepend === true) {
    delete String.prototype.prepend;
  }
};

/**
 * Indents this string by a number of spaces characters equal to the
 *  indentSize parameter.
 *
 * @param {Number} indentSize The number of spaces to indent each line
 *
 * @returns {String}          The indented string.
 *
 * @example
 * //outputs:
 * //    {
 * //      "name": "Fred",
 * //      "role": "Superhero",
 * //      "mascot": "Platypus"
 * //    }
 * console.log(JSON.stringify({
 *   "name": "Fred",
 *   "role": "Superhero",
 *   "mascot": "Platypus"
 * }, null, 2).indent(4));
 */
module.exports.indent = function(indentSize) {
  /* prepend a new line in order to indent the first line as well */
  return ("\n" + this)
  /* replace all consecutive end of line characters with \n and indentSize
      spaces */
    .replace(/[\r\n]+/g, "\n" + " ".repeat(indentSize))
  /* pulls off the first newline (which we prepended in order to indent the
      first line.) */
    .slice(1);
};

/**
 * Attaches the {@link module:string.indent indent} function to the String
 *  prototypes so that it can be used on any String objects in the application.
 *
 * @throws {Error}            If String has already implemented indent.
 */
module.exports.enableIndent = function() {
  if (String.prototype.indent === undefined) {
    this.enabledIndent = true;

    String.prototype.indent = require("./string").indent;
  } else {
    if (this.enabledIndent !== true) {
      throw new Error("Attempted to redefine indent function");
    }
  }
};

/**
 * Detaches the {@link module:string.indent indent} function from the String
 *  prototypes.
 */
module.exports.disableIndent = function() {
  if (this.enabledIndent === true) {
    delete String.prototype.indent;
  }
};