|
/** |
|
* Slice an array every n element |
|
* @example |
|
* array_slice_every_n([1,2,3,4,5,6,7], 3) |
|
* //=> [[1,4,7],[2,5],[3,6]] |
|
*/ |
|
function array_slice_every_n(array, n) { |
|
let subs = Array(n); |
|
const min_len = Math.floor(array.length / n); |
|
const rem = array.length - n * min_len; |
|
for (let m = 0; m < n; m++) { |
|
subs[m] = Array(min_len + (rem - m > 0 ? 1 : 0)); |
|
} |
|
for (let i = 0; i < array.length; i++) { |
|
subs[i % n][Math.floor(i / n)] = array[i]; |
|
} |
|
return subs; |
|
} |
|
|
|
function array_separate_odd_and_pair_indices(array) { |
|
const [odds, pairs] = array_slice_every_n(array, 2); |
|
return { odds, pairs }; |
|
} |
|
|
|
function increment_wrap({low: lo, high: hi, index: i}) { |
|
return i >= hi ? lo : i + 1; |
|
} |
|
|
|
/** |
|
* Interleave arrays elements into one array |
|
* @example |
|
* arrays_interleave([1,2,3,4], "abcdefgh".split(''), "ABCDE".split('')) |
|
* //=> [1,"a","A" 2,"b","B",3,"c","C",4,"d","D","e","E","f","g", "h"] |
|
* @param {...[]} arrays |
|
*/ |
|
function arrays_interleave(array_a, array_b) { |
|
var arrays = Array.prototype.slice.call(arguments, 0); |
|
var result = []; |
|
for (let i = 0; arrays.length; i = increment_wrap({low: 0, high: arrays.length - 1, index: i})) { |
|
if (arrays[i].length === 0) { |
|
arrays.splice(i, 1); |
|
i = i - 1; |
|
} else { |
|
result.push(arrays[i].shift()); |
|
} |
|
} |
|
return result; |
|
} |
|
|
|
function escapeHTML(text) { |
|
const replacements = [ |
|
{ match: /&/g, subst: "&" }, |
|
{ match: /</g, subst: "<" }, |
|
{ match: />/g, subst: ">" }, |
|
{ match: /"/g, subst: """ }, |
|
{ match: /'/g, subst: "'" }, |
|
{ match: /\//g, subst: "/" }, |
|
]; |
|
return replacements.reduce((p, { match, subst }) => p.replace(match, subst), text); |
|
} |
|
|
|
/** |
|
* @param {string} options.text - Raw HTML input |
|
* @param {string|string[]} options.tagNames - Name of the tag(s) to exclude or include in the escaping |
|
* depending on the value of `exclude` |
|
* @param {boolean} [options.exclude=false] - If true: escape all but the selected tags, else escape only the |
|
* selected tags |
|
* @return {string} escaped HTML |
|
*/ |
|
function escapeHTMLTags({ text, tags: tagNames, exclude }) { |
|
const tagsRegexp = (tagNames) ? |
|
`(?:${(typeof tagNames === 'string' ? [tagNames] : tagNames).join('|')})` : |
|
'(?:\\w+)'; |
|
const regexp = new RegExp(`(<\\s*/?\\s*${tagsRegexp}[^>]*\\s*>)`, 'ig'); |
|
|
|
/* Split text into selected tags and the rest: [text, tag, text, tag, ...] */ |
|
let { odds: chunks, pairs: tags } = array_separate_odd_and_pair_indices(text.split(regexp)); |
|
|
|
if (exclude) { /* If in exclude tags mode, we escape all the rest */ |
|
chunks = chunks.map(escapeHTML); |
|
} else { /* else escape only the selected tags */ |
|
tags = tags.map(escapeHTML); |
|
} |
|
|
|
return arrays_interleave(chunks, tags).join(""); |
|
} |