JavaScript Application Development API for Oracle Visual Builder Cloud Service - Classic Applications

Source: core/js/api/utils/StringUtils.js

define([
], function() {

    'use strict';

    var StringUtils = function() {
        AbcsLib.throwStaticClassError();
    };

    /**
     * In the given {@link template} replace all occurences in of {@link toReplace} parameter
     * with the string given in {@link replacement} parameter.
     *
     * @param {String} template - String where we want to replace all occurences
     * @param {String} toReplace - String we want to replace
     * @param {String} replacement - String for replacement
     * @returns {String} - String with replaced occurences
     */
    StringUtils.replaceAll = function(template, toReplace, replacement) {
        return template.replace(new RegExp(this.escapeRegExp(toReplace), 'g'), replacement);
    };

    /**
     * Resolves the given template with the map of key-value pairs. See #replaceAll().
     *
     * @param {string} template
     * @param {Object} map - key - value map;
     * @param {string[]} delimiters - two items array of the keys delimiters, can be undefined
     * @returns {string} the resolved template code
     */
    StringUtils.resolveTemplate = function(template, map, delimiters) {
        var self = this;
        var resolved = template;
        for (var key in map) {
            var toReplace = delimiters ? delimiters[0] + key + delimiters[1] : key;
            resolved = self.replaceAll(resolved, toReplace, map[key]);
        }
        return resolved;
    };

    /**
     * Simple java-like format function. It replaces occurences of "{0}", "{1}"
     * etc. with n-th value with the n-th argument.
     * <p>
     * <b>This method can't format numbers and similar features like the common
     * java's format method yet - feel free to improve it.</b>
     *
     * @param {string} template string to be replaced with values
     * @param {...string} values array of values used in replace method;
     * @returns {string} the string with applied all values
     */
    StringUtils.format = function(/*template, values*/) {
        var s = arguments[0];
        for (var i = 0; i < arguments.length - 1; i++) {
            var reg = new RegExp('\\{' + i + '\\}', 'gm');
            s = s.replace(reg, arguments[i + 1]);
        }

        return s;
    };

    /**
     * Escapes a String for use in a regular expression.
     * @param {string} string the string to escape
     * @returns {string} the escaped version
     */
    StringUtils.escapeRegExp = function(string) {
        return string.replace(/([.*+?^=!:${}()|\[\]\/\\])/g, '\\$1');
    };

    /**
     * Checks if the given String is empty or not.
     *
     * <p>
     * Returns {@constant true} only if the String is either not defined or if it's empty.
     * Whitespaces are considered as part of the String.
     * </p>
     *
     * @param {String} string
     * @returns {Boolean} true if it's undefined/null or empty
     */
    StringUtils.isEmpty = function(string) {
        return !string || string.length === 0;
    };

    /**
     * Checks if the given String is empty or not.
     *
     * <p>
     * Similar to {@link StringUtils.isEmpty) with a small difference because this
     * one is also ignoring all whitespaces and thus String with spaces only is considered
     * as an empty one.
     * </p>
     *
     * @param {String} string
     * @returns {Boolean} true if it's undefined/null or empty
     */
    StringUtils.isTrimEmpty = function(string) {
        return !string || string.length === 0 || !string.trim();
    };

    StringUtils.startsWith = function(string, prefix) {
        return string.indexOf(prefix) === 0;
    };

    StringUtils.startsWithIgnoreCase = function (string, prefix) {
        return StringUtils._matchIgnoreCase('^', '', string, prefix);
    };

    /**
     * Checks whether the given string starts with upper case or not.
     *
     * @param {String} string
     * @returns {Boolean} - true if the given string starts with an upper case, false otherwise
     */
    StringUtils.startsWithUpperCase = function(string) {
        var first = string.charAt(0);
        return first === first.toUpperCase() && first !== first.toLowerCase();
    };

    StringUtils.endsWith = function(string, suffix) {
        return string.indexOf(suffix, string.length - suffix.length) !== -1;
    };

    StringUtils.endsWithIgnoreCase = function (string, suffix) {
        return StringUtils._matchIgnoreCase('', '$', string, suffix);
    };

    StringUtils.hash = function(string) {
        var hash = 0, char;
        if (string.length !== 0) {
            for (var i = 0; i < string.length; i++) {
                char = string.charCodeAt(i);
                hash = ((hash << 5) - hash) + char;
                hash |= 0; // Convert to 32bit integer
            }
        }
        return hash;
    };

    StringUtils.contains = function(string, subString) {
        return string.indexOf(subString) > -1;
    };

    StringUtils.containsIgnoreCase = function (string, subString) {
        return StringUtils._matchIgnoreCase('', '', string, subString);
    };

    StringUtils._matchIgnoreCase = function (regexpStart, regexpEnd, string, subString) {
        var exp = regexpStart + StringUtils.escapeRegExp(subString) + regexpEnd;
        return new RegExp(exp, 'i').test(string);
    };

    /**
     * Escape string (e.g. user input) to be used as literal inside a regular
     * expression.
     *
     * @param {string} str
     *
     * @returns {string}
     */
    StringUtils.escapeRegExp = function (str) {
        if (AbcsLib.hasValue(str)) {
            // Expression from:
            // https://developer.mozilla.org/en/docs/Web/JavaScript/Guide/Regular_Expressions.
            return ('' + str).replace(/[.*+?^${}()|[\]\\]/g, '\\$&');
        } else {
            return '';
        }
    };

    /**
     * Checks if the given value contains at least one occurrence of one of the given arguments.
     *
     * @param {String} value
     * @param {String[]} subItems
     * @returns {Boolean} true if there is at least one occurrence, false otherwise
     */
    StringUtils.containsOneOf = function(value, subItems) {
        if (subItems && subItems.length > 0) {
            for (var i = 0; i < subItems.length; i++) {
                if (StringUtils.contains(value, subItems[i])) {
                    return true;
                }
            }
        }
        return false;
    };

    /**
     * Indicates whether string1 object is "equal to" string2 object.
     *
     * <p>
     * Both parameters are firstly typed to string,  and then compared using '===' operator.
     * </p>
     *
     * @param {Object} string1
     * @param {Object} string2
     * @returns {Boolean}
     */
    StringUtils.equals = function(string1, string2) {
        return (new String(string1).valueOf() === new String(string2).valueOf());
    };

    StringUtils.equalsIgnoreCase = function (string1, string2) {
        return StringUtils._matchIgnoreCase('^', '$', string1, string2);
    };

    StringUtils._ENTITIES = {
        '&': '&amp;',
        '<': '&lt;',
        '>': '&gt;',
        '"': '&quot;',
        '\'': '&#39;',
        '/': '&#x2F;'
    };

    /**
     * Escapes HTML entities.
     *
     * @param {string} code
     * @returns {string}
     */
    StringUtils.escapeHtml = function (code) {
        return String(code).replace(/[&<>"'\/]/g, function (s) {
            return StringUtils._ENTITIES[s];
        });
    };

    StringUtils._JS_STRING_ESCAPING = {
      '\'': '\\\'',
      '\"': '\\\"',
      '\\': '\\\\',
      '\n': '\\n'
    };

    /**
     * Escape a value that will be put into JavaScript string literal. It is
     * quite common for values that are stored in View Properties and used
     * in component templates.
     *
     * <p>
     *   <b>Recommendation:</b> Values should be stored as raw JavaScript
     *   objects and escaped just before rendering. Component templates support
     *   variables with escaping marks, e.g.:
     *   <code>
     *      &lt;div data-bind="text: '$myVar/stringJS$'" &gt;
     *   </code>. Please, if possible, use this approach instead of calling this
     *   method directly.
     * </p>
     * <p>
     *   <b>Warning:</b> Once you have started storing an escaped value, you
     *   cannot switch to using escaping marks in templates, as previously
     *   stored values would be escaped twice. Such change would be backward
     *   incompatible!!!
     * </p>
     *
     * @param {string} value Value to be escaped.
     *
     * @returns {string} Escaped value.
     */
    StringUtils.escapeStringJS = function(value) {
        // Escape apostrophes (single quotes) and double quotes in
        // strings that are going to be placed inside JavaScript
        // literals. Added newlines for BUFP-6964.
        var val = ('' + value).replace(/['"\\\n]/g, function (s) {
             return StringUtils._JS_STRING_ESCAPING[s];
        });
        return StringUtils.escapeHtml(val);
    };

    /**
     * Escape a value that will be used as JavaScript object (or array) literal
     * in Knockout binding. It is common for objects that are stored in
     * View Properties and used in component templates.
     *
     * <p>
     *   <b>Recommendation:</b> Values should be stored as raw JavaScript
     *   objects and escaped just before rendering. Component templates support
     *   variables with escaping marks, e.g.:
     *   <code>
     *      &lt;div data-bind="myStructuredData: '$myVar/objectJS$'" &gt;
     *   </code>. Please, if possible, use this approach instead of calling this
     *   method directly.
     * </p>
     * <p>
     *   <b>Warning:</b> Once you have started storing an stringified and
     *   escaped value, you cannot switch to using escaping marks in templates,
     *   as previously stored values would be serialized and escaped twice. Such
     *   change would be backward incompatible!!!
     * </p>
     *
     * @param {object} value Value to be stringified and escaped.
     *
     * @returns {string} Escaped value.
     */
    StringUtils.escapeObjectJS = function(value) {
        var val = AbcsLib.stringify(value);
        return StringUtils.escapeHtml(val);
    };

    /**
     * Escape a translatable value. The translatable value could be one of the
     * following:
     *
     * <ul>
     *   <li>A translatable JavaScript object, by which we mean a JavaScript object
     *       with a <b/>toTranslatableString</b> function that returns a JavaScript
     *       expression that binds at run-time to a language-dependent string.
     *   </li>
     *   <li>A string literal; though not as flexible as translatable JavaScript
     *       objects, handling string literals ensures that legacy values for
     *       translatable values are also handled correctly.
     *   </li>
     * </ul>
     *
     * Either case will return a value that is safe to be inserted into a
     * Knockout binding.
     *
     * <p>
     *   Component templates support variables with escaping marks, e.g.:
     *   <code>
     *      &lt;div data-bind="text: '$myVar/i18n$'" &gt;
     *   </code>.
     *   Please, if possible, use this approach instead of calling this
     *   method directly.
     * </p>
     *
     * @param {object} value Value to be stringified and escaped.
     * @param {Object} [legacyStringEscapingFn=StringUtils.escapeStringJS] the fallback function to be
     *                 used when escaping (legacy) strings
     *
     * @returns {string} Escaped value.
     */
    StringUtils.escapeTranslatable = function(value, legacyStringEscapingFn) {
        var stringEscapingFn = legacyStringEscapingFn ? legacyStringEscapingFn : StringUtils.escapeStringJS;
        if (!value) {
            return '\'\'';
        } else if (typeof value.toTranslatableString === 'function') {
            return value.toTranslatableString();
        } else {
            return '\'' + stringEscapingFn(value) + '\'';
        }
    };

    StringUtils.escapeHelp = function (value) {
        var isEmpty = !AbcsLib.hasValue(value) || (value.isEmptyStringWrapper && value.isEmptyStringWrapper());
        return isEmpty ? 'null' : StringUtils.escapeTranslatable(value);
    };

    StringUtils.escapeHelpURL = function (value) {
        var isEmpty = !AbcsLib.hasValue(value) || value === '';
        return isEmpty ? 'null' : '\'' + StringUtils.escapeStringJS(value) + '\'';
    };

    /**
     * Remove HTML tags from string.
     * @param {string} str html Input string.
     * @return {string}
     */

    StringUtils.stripHTML = function (str) {
        return str.replace(/(<([^>]+)>)/ig, '');
    };

    /**
     * Removes all non-alphabetical characters from the given string and replaces
     * them with '_'. This will usually be used when putting the string into
     * generated code as a valid JS identifier.
     *
     * @param {String} identifier
     * @returns {String}
     */
    StringUtils.toValidIdentifier = function (identifier) {
        return identifier.replace(/[^\w\d]+/g, '_');
    };

    /**
     * Use as second argument to StringUtils.toCamelCase if you want
     * CamelCase.
     */
    StringUtils.CAMELCASE_FIRST_UPPER = 'UPPER';

    /**
     * Use as second argument to StringUtils.toCamelCase if you want
     * camelCase.
     */
    StringUtils.CAMELCASE_FIRST_LOWER = 'LOWER';

    /**
     * Convert a String into camelCase
     * @param {String} str The string to convert
     * @param {String} firstCase The case required for the first letter.  Use
     * StringUtils.CAMELCASE_FIRST_UPPER or StringUtils.CAMELCASE_FIRST_LOWER.
     * @returns {String} The converted string
     */
    StringUtils.toCamelCase = function (str, firstCase) {
        return str.replace(/(?:^\w|[A-Z]|\b\w|\s+)/g, function(match, index) {
            if (+match === 0) {
                return '';
            } else if (index === 0) {
                if (firstCase === StringUtils.CAMELCASE_FIRST_UPPER) {
                    return match.toUpperCase();
                } else if (firstCase === StringUtils.CAMELCASE_FIRST_LOWER) {
                    return match.toLowerCase();
                } else {
                    return match;
                }
            } else {
                return match.toUpperCase();
            }
        });
    };

    return StringUtils;

});