Viewing File: /usr/local/cpanel/share/libraries/cjt2/src/filters/wrapFilter.js
/*
# cjt/filters/wrapFilter.js Copyright(c) 2020 cPanel, L.L.C.
# All rights reserved.
# copyright@cpanel.net http://cpanel.net
# This code is subject to the cPanel license. Unauthorized copying is prohibited
*/
/* global define: false */
define(
[
"angular",
"cjt/util/html",
"ngSanitize"
],
function(angular, HTML) {
/**
* Check if the object is a number
* @note Keeping this here to keep dependencies light.
* @private
* @method isNumber
* @param {object} obj Any object to test.
* @return {Boolean} true if the obj is a number, false otherwise
*/
function isNumber(obj) {
return !isNaN(parseFloat(obj));
}
/**
* Return the value unchanged.
* @note Used in place of the encoder when the value arrived already escaped.
*
* @private
* @method _identity
* @param {String} value Any value.
* @return {String} The same value.
*/
function _identity(value) {
return value;
}
/**
* Checks if the value is a TrustedValueHolder and extracts the value if it is.
* This is useful when something earlier in the chain returns a $secDelegate.trustAs()
* wrapped value.
*
* @private
* @method _getTrustedValue
* @param {Any} value This is any var you're trying to extract a trusted value from
* @return {String|Undefined} If it's detected as a TrustedValueHolder, it returns the value
*/
function _getTrustedValue(valHolder) {
var val = angular.isObject(valHolder) && // Some primitive wrappers override Object.prototype.valueOf, so this
// makes the next comparison more accurate and doubles as a guard.
valHolder.valueOf !== Object.prototype.valueOf && // We need to make sure it's not just the inherited valueOf method.
angular.isFunction(valHolder.valueOf);
return val ? valHolder.valueOf() : void 0;
}
var module = angular.module("cjt2.filters.wrap", [
"ngSanitize"
]);
/**
* Filter that injects hidden whitespace into strings where the match rule exists.
* By default it does this on periods. You can provide your own regex pattern to match
* other characters or patterns.
*
* @example
*
* Default: [Separates on period (.)]
* <div>{{ "abc.tld" | wrap }}</div> => <div>abc.<wbr><span class="wbr"></span>tld</div>
*
* With a custom regex pattern:
* <div>{{ "ABD1:ABD2" | wrap:':' }}</div> => <div>ABD1:<wbr><span class="wbr"></span>ABD2</div>
*
* With a custom complex regex pattern:
* <div>{{ "ABD1:ABD2.DDDD" | wrap:'[:.]' }}</div> => <div>ABD1:<wbr><span class="wbr"></span>ABD2.<wbr><span class="wbr"></span>DDDD</div>
*
* With a secondary wrap on words that exceed a max length: (note: wrapLimit must be > 2)
*
* <div>{{ "DDDD.01234567890123" | wrap:'[.]':10 }}</div> => <div>DDDD.<wbr><span class="wbr"></span>0123456789<wbr><span class="wbr"></span>0123</div>
*/
module.filter("wrap", ["$sceDelegate", "$sce", function($sceDelegate, $sce) {
var template = "<wbr><span class=\"wbr\"></span>";
var trim = new RegExp(template + "$");
var cache = {};
return function(value, match, wrapLimit) {
// If it's not a string, we'll try and fetch the trusted value
var wasTrusted = false;
if (typeof value !== "string") {
value = _getTrustedValue(value);
// Whatever trusted this has already escaped the text and
// inserted markup of its own, so escaping it again would
// double-encode the text and mangle that markup.
wasTrusted = true;
}
// If value is still falsy at this point it's time to give up
if (!value) {
return "";
}
// Setup up defaults
match = (!match ? "[.]" : match);
wrapLimit = (parseInt(wrapLimit, 10) || 0);
var ruleId = match + wrapLimit;
// Wrap the match rule in a capture
var expression = cache[ruleId];
if (!expression) {
// The expression is not cached, so create a new cache entry for it
if (typeof (wrapLimit) !== "undefined" && isNumber(wrapLimit) && wrapLimit > 1) {
// -----------------------------------------------------------------------------------
// Notes:
// 1) We only want to take on this overhead if there is a wrapLimit
// 2) we use wrapLimit - 1 since the regex match the range and one more word character
// -----------------------------------------------------------------------------------
expression = new RegExp("((?:\\w{1," + (wrapLimit - 1) + "})\\w|" + match + ")", "g");
} else {
expression = new RegExp("(" + match + ")", "g");
}
cache[ruleId] = expression;
}
// ---------------------------------------------------------------------------------------
// Adjust the string. The insertion points are offsets into the raw value, so each run of
// text has to be escaped as the template is placed after it. Escaping the finished string
// would escape our own markup, and escaping the value up front would let the template land
// inside an entity -- "&" becoming "&<wbr>...;" renders as "&" plus a stray ";".
// ---------------------------------------------------------------------------------------
var encode = wasTrusted ? _identity : HTML.encode;
var out = "";
var end = 0;
var found;
expression.lastIndex = 0; // The expression is cached, so it is shared between calls.
while ((found = expression.exec(value)) !== null) {
out += encode(value.slice(end, found.index)) + encode(found[0]) + template;
end = expression.lastIndex;
if (found[0] === "") {
expression.lastIndex++; // A zero-length match would not advance on its own.
}
}
value = out + encode(value.slice(end));
if (wrapLimit) {
// Workaround, since the expression for limits matches at the end of the
// string too. I could not find an obvious way to prevent that match.
value = value.replace(trim, "");
}
return $sceDelegate.trustAs($sce.HTML, value);
};
}]);
}
);
Back to Directory