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 = {
'&': '&',
'<': '<',
'>': '>',
'"': '"',
'\'': ''',
'/': '/'
};
/**
* 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>
* <div data-bind="text: '$myVar/stringJS$'" >
* </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>
* <div data-bind="myStructuredData: '$myVar/objectJS$'" >
* </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>
* <div data-bind="text: '$myVar/i18n$'" >
* </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;
});