(function () {
"use strict";
//region Imports
//endregion
//region String
var rxBR2NL = /
/gi;
var rxP2NL = /<\/p>/gi;
var rxStripHTML = /<\S[^><]*>/g;
/**
* Probably don't ever need this - HTMLElement.innerText does the job......
* @param val
* @returns {string}
* @function external:String.stripHTML
*/
String.stripHTML = function(val) {
return (val) ? val.replace(rxBR2NL, '\n').replace(rxP2NL, '\n').replace(rxStripHTML, '') : '';
};
var rxSpaces = / +/g;
/**
* Strip all spaces from a string
* @param s
* @returns {XML|string|void}
*/
String.stripSpaces = function(s) {
return s.replace(rxSpaces, '');
}
var rxUT = /(>|<|&)/g;
/**
* Internal Regex helper
* @param $0
* @param $1
* @returns {string}
*/
function unescapeText($0, $1) {
switch($1) {
case '>': return '>';
case '<': return '<';
case '&': return '&';
default:
return '';
}
}
/**
* Unescape text
* @param v the string to unescape
* @returns {string}
* @function external:String.unescapeText
*/
String.unescapeText = function(v) {
return v ? v.replace(rxUT, unescapeText) : '';
};
//escape html entities helper
var reET = /[<>"']/g;
var encodeHTMLRules = { "&": "&", "<": "<", ">": ">", '"': '"', "'": ''', "/": '/' };
/**
* Internal Regex helper
* @param $0
* @returns {string}
*/
function escapeText($0) {
return encodeHTMLRules[$0];
}
/**
* Escape text
* @param v the string to escape
* @returns {string}
* @function external:String.escapeText
*/
String.escapeText = function(v) {
return v ? v.toString().replace(reET, escapeText) : '';
};
/**
* Generate a uuid for a given string
* @param {string} input
* @returns {string}
* @function external:String.hashCode
*/
String.hashCode = function(input) {
var hval = 0x811c9dc5;
// Strips unicode bits, only the lower 8 bits of the values are used
for (var i = 0; i < input.length; i++) {
hval = hval ^ (input.charCodeAt(i) & 0xFF);
hval += (hval << 1) + (hval << 4) + (hval << 7) + (hval << 8) + (hval << 24);
}
return hval >>> 0;
};
/**
* Create a message digest (hash)
* @param {string} message
* @returns {Promise}
*/
String.digestMessage = async function (message) {
//TODO:check for code that only runs on HTTPS
if(location.protocol === 'https') {
const msgUint8 = new TextEncoder().encode(message); // encode as (utf-8) Uint8Array
const hashBuffer = await crypto.subtle.digest('SHA-256', msgUint8); // hash the message
const hashArray = Array.from(new Uint8Array(hashBuffer)); // convert buffer to byte array
const hashHex = hashArray.map(b => b.toString(16).padStart(2, '0')).join(''); // convert bytes to hex string
return hashHex;
}else return message;//some sites are running HTTP!!!!!!!!!!!!!!!!!!!
}
/**
* Convert a string to an integer
* @param {string} value
* @returns {number}
* @function external:String.toInt
*/
String.toInt = function(value) {
return parseInt(value, 10);
};
var rxNumeric = /[^0-9\.]+/g;
/**
* Strips non-numeric characters from a string
* @param value
* @returns {string}
*/
String.toNumeric = function(value) {
return value.replace(rxNumeric, '');
};
/**
* Prefix a value ie prefix(task.TaskID, task.level, ' ');
* @param {string} v the value
* @param {string} p the pad char/string
* @param {number} n the count
* @returns {string}
* @function external:String.prefix
*/
String.prefix = function(v, p, n) {
return (v) ? String.repeat(p, n) + v : '';
};
/**
* Returns a string v repeated n times.
* @param {string} v
* @param {number} n defaults to 0
* @returns {string}
* @function external:String.repeat
*/
String.repeat = function(v, n) {
return (new Array((n || 0) + 1).join(v));
};
/**
* pad number where required with leading zeros
* @param n the number to pad
* @param c the character count
* @returns {string}
* @function external:String.padZeros
*/
String.padZeros = function(n, c) {
return ('000000000000' + n).substr(-c);
};
/**
* Pads the string in the center with specified character
* @param {string} str
* @param {number} len
* @param {string} ch
* @returns {string}
* @function external:String.pad
*/
String.pad = function(str, len, ch) {
ch = ch || ' ';
if(str.length >= len) return str;
len = len - str.length;
var left = new Array(Math.ceil(len / 2) + 1).join(ch);
var right = new Array(Math.floor(len / 2) + 1).join(ch);
return left + str + right;
};
/**
* Left pads the string.
* @param {string} str
* @param {number} len
* @param {string} ch
* @returns {string}
* @function external:String.padLeft
*/
String.padLeft = function(str, len, ch) {
ch = ch || ' ';
if(str.length >= len) return str;
return (new Array(len - str.length + 1).join(ch) + str);
};
/**
* Right pads the string.
* @param {string} str
* @param {number} len
* @param {string} ch
* @returns {string}
* @function external:String.padRight
*/
String.padRight = function(str, len, ch) {
ch = ch || ' ';
if(str.length >= len) return (str);
return (str + new Array(len - str.length + 1).join(ch));
};
/**
* CaseInsensitive search of whether a string contains another
* @param {string} str
* @param {string} test
* @returns {boolean}
* @function external:String.contains
*/
String.contains = function(str, test) {
return str && test ? str.toString().toLowerCase().includes(test.toString().toLowerCase()) : false;
};
/**
* Test if a string end with a test string
* RJE rewrote this
* @param {string} str the str to search
* @param {string} test the string to test or search for
* @param {number} [position] Search within this string as if this string were only this long;
* defaults to this string's actual length, clamped within the range established by this string's length
* @returns {boolean}
* @function external:String.endsWith
*/
String.endsWith = function(str, test, position) {
return str.endsWith(test, position);
};
/**
* Ensure a string ends with a test string (useful for paths in URLs and files). Returns the string with "test" appended on
* RJE rewrote this
* @param {string} str the str to search
* @param {string} test the string to test or search for; defaults to '/'
* @param {number} [position] Search within this string as if this string were only this long;
* defaults to this string's actual length, clamped within the range established by this string's length
* @returns {string}
* @function external:String.forceEndsWith
*/
String.forceEndsWith = function(str, test, position) {
if(str) {
test = test || '/';
return str.endsWith(test, position) ? str : str + test;
} else {
return '';
}
};
/**
* Substitute placeholders with string values
* @example
* alert(substitute("${0} ... ${1} ... ${0}", ["A", "B"]));
* @param {String} str The string containing the placeholders
* @param {(Array|Object)} arr The array or object literal of values to substitute
* @returns {string}
* @function external:String.substitute
*/
String.substitute = function(str, arr) {
if(arguments.length > 2) arr = window.Utils.convertArgs(arguments, 1);
var i, re;
for(i in arr) {
re = new RegExp('\\$\\{' + i + '\\}', 'g');
str = str.replace(re, arr[i]);
}
return str;
};
String.prototype.substitute = function() {
var args = arguments;
return this.replace(/{(\d+)}/g, function(match, i) {
return args[i] === undefined || args[i] === null ? match : args[i];
});
};
/**
* Replace all occurrences of search in str with replace
* @param {string} str the string to be modified
* @param {string} search the search string
* @param {string} replace the replacement string
* @param {boolean} ignoreCase if true the search is case insensitive
* @returns {string}
* @function external:String.replaceAll
*/
String.replaceAll = function (str, search, replace, ignoreCase) {
// if we pass str = undefined, str.split crashes
if (str === undefined) {
str = '';
}
if(ignoreCase) {
var result = [];
var _string = str.toLowerCase();
var _search = search.toLowerCase();
var start = 0, match, len = _search.length;
while((match = _string.indexOf(_search, start)) >= 0) {
result.push(str.slice(start, match));
start = match + len;
}
result.push(str.slice(start));
return result.join(replace);
} else {
return str.toString().split(search).join(replace);
}
};
String.replaceAt = function(str, index, character) {
return str.substr(0, index) + character + str.substr(index + character.length);
};
var rxCamel = /([A-Z])/g;
var rxDash = /\W+(.)/g;
/**
* Convert camelCase to dash
* @param {string} str
* @returns {string}
* @function external:String.camelToDash
*/
String.camelToDash = function(str) {
return str.replace(rxCamel, camelDashHelper);
};
/**
* Internal helper
* @param $1
* @returns {string}
*/
function camelDashHelper($1) {
return "-" + $1.toLowerCase();
}
/**
* Convert camelCase to space
* @param {string} str
* @returns {string}
* @function external:String.camelToSpace
*/
String.camelToSpace = function(str) {
return str.replace(rxCamel, camelSpaceHelper);
};
/**
* Internal helper
* @param $1
* @returns {string}
*/
function camelSpaceHelper($1) {
return ' ' + $1.toLowerCase();
}
/**
* Convert dash to camelCase
* @param {string} str
* @returns {string}
* @function external:String.dashToCamel
*/
String.dashToCamel = function(str) {
return str.replace(rxDash, dashHelper);
};
/**
* Internal Helper
* @param x
* @param chr
* @returns {string}
*/
function dashHelper(x, chr) {
return chr.toUpperCase();
}
/**
*
* @param str
* @param delimiter
* @param qualifier
* @param escape
* @param lineDelimiter
* @returns {Array}
* @function external:String.parseCSV
*/
String.parseCSV = function(str, delimiter, qualifier, escape, lineDelimiter) { //try to parse no matter what
delimiter = delimiter || ',';
escape = escape || '\\';
qualifier = qualifier || '"';
var i = 0, fieldBuffer = [], fields = [], len = str.length, inField = false;
var ca = function(i) { return str.charAt(i) };
if(lineDelimiter) var rows = [];
if(!qualifier)
inField = true;
while(i < len) {
var current = ca(i);
switch(current) {
case escape:
if(inField && ((escape !== qualifier) || ca(i + 1) === qualifier)) {
i += 1;
fieldBuffer.push(ca(i));
break;
}
if(escape !== qualifier) break;
case qualifier:
inField = !inField;
break;
case delimiter:
if(inField && qualifier)
fieldBuffer.push(current);
else {
fields.push(fieldBuffer.join(''));
fieldBuffer.length = 0;
}
break;
case lineDelimiter:
if(inField) {
fieldBuffer.push(current);
} else {
if(rows) {
fields.push(fieldBuffer.join(''));
rows.push(fields);
fields = [];
fieldBuffer.length = 0;
}
}
break;
default:
if(inField)
fieldBuffer.push(current);
break;
}
i += 1;
}
fields.push(fieldBuffer.join(''));
if(rows) {
rows.push(fields);
return rows;
}
return fields;
};
/**
* Shortens the provided string (if necessary) to a string of length at most
* |maxLength|.
* @param {string} original The original string.
* @param {number} maxLength The maximum length allowed for the string.
* @return {string} The original string if its length does not exceed
* |maxLength|. Otherwise the first |maxLength| - 1 characters with '...'
* appended.
*/
String.elide = function(original, maxLength) {
if(original.length <= maxLength)
return original;
return original.substring(0, maxLength - 1) + '\u2026';
}
//endregion
//region HTMLStrip
const stripHTML = document.createElement('div');
const cache = {};
const timer = setInterval(clearCache, 30*60*1000)
/**
* Periodically clear the cahce
* @param {number} timerID
*/
function clearCache(timerID){
var keys = Object.keys(cache);
for (var i = 0, len = keys.length - 25; i < len; i++) {
delete cache[keys[i]];
}
}
/**
* Extract the text from the html string
* @param {string} html
* @returns {string}
*/
function getText(html, hash){
stripHTML.innerHTML = html;
var text = stripHTML.innerText;
cache[hash] = text;
return text;
}
/**
* Extract text from a html string
* @param {string} html
* @returns {*|string}
* @memberOf #Utils
*/
String.textContent = function textContent(html){
var hash = (html) ? String.hashCode(html) : '';
return cache[hash] || getText(html, hash);
}
//endregion
})();