743 lines
23 KiB
JavaScript
743 lines
23 KiB
JavaScript
"use strict";
|
|
|
|
/**
|
|
* @class ErrorsManager
|
|
* @constructor
|
|
* @param {?(Object.<string, any|null>|Array.<any|null>|string)} [inputs = null]
|
|
* @returns {void}
|
|
* @access public
|
|
* @static
|
|
*/
|
|
export const ErrorsManager = (function(){
|
|
|
|
/**
|
|
* @constructs ErrorsManager
|
|
* @param {?(Object.<string, any|null>|Array.<any|null>|string)} [inputs = null]
|
|
* @returns {void}
|
|
* @access private
|
|
* @static
|
|
*/
|
|
const ErrorsManager = function(inputs = null){
|
|
|
|
/** @type {ErrorsManager} */
|
|
const self = this,
|
|
/** @type {Array.<string>} */
|
|
alphabet = [],
|
|
/** @type {Object.<string, number>} */
|
|
dictionary = {};
|
|
/** @type {number} */
|
|
let error = 0,
|
|
/** @type {number} */
|
|
base,
|
|
/** @type {number} */
|
|
power;
|
|
|
|
/**
|
|
* @returns {void}
|
|
* @access private
|
|
*/
|
|
const constructor = () => {
|
|
|
|
self.set_alphabet(
|
|
ErrorsManager.get("alphabet", inputs, ErrorsManager.ALPHABET),
|
|
ErrorsManager.get("base", inputs, 64)
|
|
);
|
|
|
|
};
|
|
|
|
/**
|
|
* @param {?(string|Array.<string>)} [new_alphabet = null]
|
|
* @param {!number} [new_base = 64]
|
|
* @returns {number}
|
|
* @access public
|
|
*/
|
|
this.set_alphabet = (new_alphabet = null, new_base = 64) => {
|
|
|
|
/** @type {number} */
|
|
let original_length;
|
|
|
|
error = 0;
|
|
// alphabet.splice(0, alphabet.length);
|
|
alphabet.length = 0;
|
|
|
|
if(ErrorsManager.is_null_or_undefined(new_alphabet))
|
|
alphabet.push(...ErrorsManager.ALPHABET);
|
|
else if(ErrorsManager.is_string(new_alphabet))
|
|
alphabet.push(...new_alphabet.split(""));
|
|
else if(ErrorsManager.is_array(new_alphabet))
|
|
alphabet.push(...new_alphabet);
|
|
else{
|
|
error |= 1 << 2;
|
|
alphabet.push(...ErrorsManager.ALPHABET);
|
|
};
|
|
|
|
original_length = alphabet.length;
|
|
|
|
for(let i = alphabet.length - 1; i >= 0; i--)
|
|
alphabet.indexOf(alphabet[i]) != i && alphabet.splice(i, 1);
|
|
|
|
if(alphabet.length < 2){
|
|
error |= 1 << 3;
|
|
alphabet.splice(0, alphabet.length);
|
|
alphabet.push(...ErrorsManager.ALPHABET);
|
|
};
|
|
if(alphabet.length != original_length)
|
|
error |= 1 << 4;
|
|
if(alphabet.length > 128)
|
|
error |= 1 << 5;
|
|
|
|
error |= (
|
|
new_base < 2 ? 1 << 0 :
|
|
new_base > 128 ? 1 << 1 :
|
|
new_base >= alphabet.length ? 1 << 2 :
|
|
0) << 6;
|
|
|
|
if(!(error >> 6))
|
|
alphabet.splice(new_base, alphabet.length - new_base);
|
|
|
|
alphabet.splice(base = 2 ** (power = Math.log2(alphabet.slice(0, 128).length) >> 0));
|
|
|
|
for(const key in dictionary)
|
|
delete dictionary[key];
|
|
|
|
for(let i = alphabet.length - 1; i >= 0; i--)
|
|
dictionary[alphabet[i]] = i;
|
|
|
|
return error;
|
|
};
|
|
|
|
/**
|
|
* @returns {string}
|
|
* @access public
|
|
*/
|
|
this.get_alphabet = () => alphabet.join("");
|
|
|
|
/**
|
|
* @param {!(string|Array.<number>|number)} code
|
|
* @return {Array.<number>}
|
|
* @access public
|
|
*/
|
|
this.to_array = code => {
|
|
if(ErrorsManager.is_string(code))
|
|
return code.split("").map(character => dictionary[character]);
|
|
if(ErrorsManager.is_array(code))
|
|
return code;
|
|
if(ErrorsManager.is_integer(code)){
|
|
|
|
/** @type {Array.<number>} */
|
|
const hexas = [];
|
|
|
|
while(code){
|
|
hexas.push(code % base);
|
|
code = code / base >> 0;
|
|
};
|
|
|
|
return hexas;
|
|
};
|
|
return [];
|
|
};
|
|
|
|
/**
|
|
* @param {!(string|Array.<number>|number)} code
|
|
* @return {string}
|
|
* @access public
|
|
*/
|
|
this.to_string = code => {
|
|
if(ErrorsManager.is_string(code))
|
|
return code;
|
|
if(ErrorsManager.is_array(code))
|
|
return code.map(number => alphabet[number]).join("");
|
|
if(ErrorsManager.is_integer(code)){
|
|
|
|
/** @type {string} */
|
|
let string = "";
|
|
|
|
while(code){
|
|
string += alphabet[code % base];
|
|
code = code / base >> 0;
|
|
};
|
|
|
|
return string;
|
|
};
|
|
return "";
|
|
};
|
|
|
|
/**
|
|
* @param {!(string|Array.<number>|number)} code
|
|
* @return {number}
|
|
* @access public
|
|
*/
|
|
this.to_integer = code => {
|
|
if(ErrorsManager.is_string(code))
|
|
return code.split("").reduce((integer, character) => integer * base + dictionary[character], 0);
|
|
if(ErrorsManager.is_array(code))
|
|
return code.reduce((integer, number) => integer * base + number, 0);
|
|
if(ErrorsManager.is_integer(code))
|
|
return code;
|
|
return 0;
|
|
};
|
|
|
|
/**
|
|
* @param {!(string|Array.<number>|number)} code
|
|
* @return {string}
|
|
* @access public
|
|
*/
|
|
this.to_string_binary = code => {
|
|
if(ErrorsManager.is_string(code))
|
|
return code.split("").map(character => dictionary[character].toString(2).padStart(power, "0")).join("");
|
|
if(ErrorsManager.is_array(code))
|
|
return code.map(number => number.toString(2).padStart(power, "0")).join("");
|
|
if(ErrorsManager.is_integer(code)){
|
|
|
|
/** @type {string} */
|
|
let string = "";
|
|
|
|
while(code){
|
|
string = (code % base).toString(2).padStart(power, "0") + string;
|
|
code = code / base >> 0;
|
|
};
|
|
|
|
return string;
|
|
};
|
|
return "";
|
|
};
|
|
|
|
/**
|
|
* @param {!(string|Array.<number>|number)} code
|
|
* @return {number}
|
|
* @access public
|
|
*/
|
|
this.get_bits = code => {
|
|
if(ErrorsManager.is_string(code))
|
|
return (code.length - 1) * power + Math.ceil(Math.log2(dictionary[code[code.length - 1]] + 1));
|
|
if(ErrorsManager.is_array(code))
|
|
return (code.length - 1) * power + Math.ceil(Math.log2(code[code.length - 1] + 1));
|
|
if(ErrorsManager.is_integer(code))
|
|
return Math.ceil(Math.log2(code + 1));
|
|
return 0;
|
|
};
|
|
|
|
/**
|
|
* @param {!Array.<number>} code
|
|
* @param {!number} from
|
|
* @param {!number} bits
|
|
* @returns {![number, number]}
|
|
* @access private
|
|
*/
|
|
const get_from_bits_array = (code, from, bits) => {
|
|
|
|
if(from < 0){
|
|
from = self.get_bits(code) + from;
|
|
if(from < 0)
|
|
from = 0;
|
|
};
|
|
if(bits < 0){
|
|
from += bits;
|
|
bits *= -1;
|
|
if(from < 0){
|
|
bits += from;
|
|
from = 0;
|
|
};
|
|
};
|
|
|
|
return [from, bits];
|
|
};
|
|
|
|
/**
|
|
* @param {!(string|Array.<number>|number)} code
|
|
* @param {!number} from
|
|
* @param {!number} bits
|
|
* @returns {![number, number]}
|
|
* @access public
|
|
*/
|
|
this.get_from_bits = (code, from, bits) => (
|
|
ErrorsManager.is_string(code) ? get_from_bits_array(code.split("").map(character => dictionary[character]), from, bits) :
|
|
ErrorsManager.is_array(code) ? get_from_bits_array(code, from, bits) :
|
|
ErrorsManager.is_integer(code) ? get_from_bits_array(self.to_array(code), from, bits) :
|
|
[from, bits]);
|
|
|
|
/**
|
|
* @param {!(string|Array.<number>|number)} code
|
|
* @returns {string|Array.<number>|number|null}
|
|
* @access public
|
|
*/
|
|
this.clean = code => {
|
|
if(ErrorsManager.is_string(code)){
|
|
|
|
/** @type {number} */
|
|
let l = code.length;
|
|
|
|
for(; l > 0 && code[l - 1] == alphabet[0]; l --);
|
|
|
|
return (
|
|
l == code.length ? code :
|
|
!l ? alphabet[0] :
|
|
code.slice(0, l));
|
|
};
|
|
if(ErrorsManager.is_array(code)){
|
|
|
|
/** @type {number} */
|
|
let l = code.length;
|
|
|
|
for(; l > 0 && code[l - 1] == 0; l --);
|
|
|
|
return (
|
|
l == code.length ? code :
|
|
!l ? [0] :
|
|
code.slice(0, l));
|
|
};
|
|
if(ErrorsManager.is_integer(code))
|
|
return code;
|
|
return null;
|
|
};
|
|
|
|
/**
|
|
* @param {!(string|Array.<number>|number)} code
|
|
* @param {!number} from
|
|
* @param {!number} bits
|
|
* @returns {!(string|Array.<number>|number|null)}
|
|
* @access public
|
|
*/
|
|
this.get_range = (code, from, bits) => {
|
|
if(ErrorsManager.is_array(code)){
|
|
|
|
/** @type {Array.<number>} */
|
|
const hexas = [];
|
|
|
|
[from, bits] = self.get_from_bits(code, from, bits);
|
|
|
|
if(bits == 0)
|
|
bits = self.get_bits(code) - from;
|
|
if(bits <= 0)
|
|
return [0];
|
|
|
|
hexas.push(...code);
|
|
|
|
if(from > 0){
|
|
|
|
/** @type {number} */
|
|
const shift = from % power,
|
|
/** @type {number} */
|
|
mask = ~-base;
|
|
|
|
hexas = hexas.slice(from / power >> 0);
|
|
if(shift != 0 && hexas.length){
|
|
|
|
/** @type {number} */
|
|
const l = hexas.length - 1;
|
|
|
|
for(let i = 0; i < l; i ++)
|
|
hexas[i] = ((hexas[i] >> shift) | (hexas[i + 1] << power - shift)) & mask;
|
|
hexas[l] >>= shift;
|
|
|
|
};
|
|
|
|
}
|
|
if(bits > 0){
|
|
|
|
/** @type {number} */
|
|
const shift = bits % power,
|
|
/** @type {number} */
|
|
l = Math.ceil(bits / power);
|
|
|
|
hexas = hexas.slice(l, hexas.length - l);
|
|
if(shift && hexas.length)
|
|
hexas[hexas.length - 1] &= ~(-1 << shift);
|
|
|
|
};
|
|
|
|
return self.clean(hexas);
|
|
};
|
|
if(ErrorsManager.is_string(code))
|
|
return self.to_string(self.get_range(self.to_array(code), from, bits));
|
|
if(ErrorsManager.is_integer(code)){
|
|
|
|
[from, bits] = self.get_from_bits(code, from, bits);
|
|
|
|
from > 0 && (code = (code >> from) & ~-(1 << 31 - from));
|
|
|
|
if(bits <= 0 || bits >= 31)
|
|
return code;
|
|
|
|
from + bits > 31 && (bits = 31 - from);
|
|
|
|
return code & ~-(1 << bits);
|
|
};
|
|
return null;
|
|
};
|
|
|
|
/**
|
|
* @param {!(string|Array.<string>|number)} code
|
|
* @param {!Array.<string>} messages
|
|
* @param {?Object.<number, number>} [blocks = null]
|
|
* @returns {Array.<[number, string]>}
|
|
* @access public
|
|
*/
|
|
this.process = (code, messages, blocks = null) => {
|
|
|
|
/** @type {Array.<[number, string]>} */
|
|
const response = [],
|
|
/** @type {number} */
|
|
l = (code = self.to_array(code)).length;
|
|
/** @type {number} */
|
|
let k = 0;
|
|
|
|
blocks || (blocks = {});
|
|
|
|
for(let i = 0; i < l;)
|
|
if(blocks[i] !== undefined){
|
|
|
|
/** @type {number} */
|
|
const logarithm = Math.log2(blocks[i]) >> 0;
|
|
/** @type {number} */
|
|
let _k = self.to_integer(self.get_range(code, i, logarithm ? logarithm : logarithm = 1));
|
|
|
|
if(_k > 0){
|
|
_k = k + _k;
|
|
response.push([k, messages[k] || "error_message_" + k]);
|
|
};
|
|
|
|
k += blocks[i];
|
|
i += logarithm;
|
|
|
|
}else{
|
|
code[i / power >> 0] & (1 << i % power) && response.push([k, messages[k] || "error_message_" + k]);
|
|
k ++;
|
|
i ++;
|
|
};
|
|
|
|
return response;
|
|
};
|
|
|
|
/**
|
|
* @param {!(string|Array.<number>|number)} code
|
|
* @param {!number} bits
|
|
* @return {string|Array.<number>|number|null}
|
|
* @access public
|
|
*/
|
|
this.bitwise = (code, bits) => {
|
|
if(ErrorsManager.is_array(code)){
|
|
if(!code.length || !bits)
|
|
return code;
|
|
|
|
/** @type {number} */
|
|
const shift = Math.abs(bits) % power,
|
|
/** @type {number} */
|
|
mask = ~-base,
|
|
/** @type {Array.<number>} */
|
|
hexas = [...code];
|
|
|
|
if(bits < 0){
|
|
|
|
hexas.splice(0, -bits / power >> 0);
|
|
|
|
if(shift != 0 && hexas.length){
|
|
|
|
/** @type {number} */
|
|
const l = hexas.length - 1;
|
|
|
|
for(let i = 0; i < l; i ++)
|
|
hexas[i] = (hexas[i] >> shift | (hexas[i + 1] << power - shift)) & mask;
|
|
hexas[l] >>= shift;
|
|
|
|
};
|
|
|
|
}else{
|
|
|
|
if(shift != 0){
|
|
|
|
/** @type {number} */
|
|
const last_hexa = hexas[hexas.length - 1] << shift;
|
|
|
|
for(let i = hexas.length - 1; i > 0; i --)
|
|
hexas[i] = ((hexas[i] << shift) | (hexas[i - 1] >> power - shift)) & mask;
|
|
hexas[0] = (hexas[0] << shift) & mask;
|
|
|
|
if(last_hexa)
|
|
hexas.push(last_hexa);
|
|
|
|
};
|
|
|
|
for(let i = bits / power >> 0; i > 0; i --)
|
|
hexas.unshift(0);
|
|
|
|
};
|
|
|
|
return self.clean(hexas);
|
|
};
|
|
if(ErrorsManager.is_string(code))
|
|
return self.to_string(self.bitwise(self.to_array(code), bits));
|
|
if(ErrorsManager.is_integer(code)){};
|
|
return null;
|
|
};
|
|
|
|
/**
|
|
* @param {!(string|Array.<number>|number)} code
|
|
* @param {!number} from
|
|
* @param {!number} bits
|
|
* @param {!boolean} reversed
|
|
* @returns {!(string|Array.<number>|number|null)}
|
|
* @access public
|
|
*/
|
|
this.reset = (code, from, bits = 0, reversed = false) => {
|
|
if(ErrorsManager.is_array(code)){
|
|
|
|
/** @type {Array.<number>} */
|
|
const hexas = [...code];
|
|
/** @type {[number, number, number]} */
|
|
let l, from_mask, to_mask;
|
|
|
|
[from, bits] = self.get_from_bits(hexas, from, bits);
|
|
|
|
/** @type {number} */
|
|
const hexa_from = from / power >> 0,
|
|
/** @type {number} */
|
|
hexas_to = Math.ceil((from + bits) / power);
|
|
|
|
if(reversed){
|
|
|
|
l = from % power;
|
|
from_mask = ~-(1 << power) << l;
|
|
to_mask = ~-(1 << (from + bits) % power);
|
|
|
|
for(let i = 0, l = hexas.length; i < l; i ++)
|
|
if(i < hexa_from || i > hexas_to)
|
|
hexas[i] = 0;
|
|
|
|
if(hexa_from == hexa_to)
|
|
hexas[hexa_to] &= from_mask | to_mask;
|
|
else{
|
|
hexas[hexa_from] &= from_mask;
|
|
if(hexa_to < hexas.length)
|
|
hexas[hexa_to] &= to_mask;
|
|
};
|
|
|
|
}else{
|
|
|
|
l = (from + bits) % power;
|
|
from_mask = ~-(1 << from % power);
|
|
to_mask = ~-(1 << l) << l;
|
|
|
|
if(hexa_from == hexa_to)
|
|
hexas[hexa_to] &= from_mask | to_mask;
|
|
else{
|
|
hexas[hexa_from] &= from_mask;
|
|
for(let i = hexa_from + 1; i < hexas_to && i < hexas.length; i ++)
|
|
hexas[i] = 0;
|
|
if(hexa_to < hexas.length)
|
|
hexas[hexa_to] &= to_mask;
|
|
};
|
|
|
|
};
|
|
|
|
return self.clean(hexas);
|
|
};
|
|
if(ErrorsManager.is_string(code))
|
|
return self.to_string(self.reset(self.to_array(code), from, bits, reversed));
|
|
if(ErrorsManager.is_integer(code)){
|
|
|
|
[from, bits] = self.get_from_bits(code, from, bits);
|
|
from + bits > 31 && (bits = 31 - from);
|
|
|
|
return code & (
|
|
reversed ? ~-(1 << bits) << from :
|
|
(~-(1 << self.get_bits(code)) << from + bits) | ~-(1 << from));
|
|
};
|
|
return null;
|
|
};
|
|
|
|
/**
|
|
* @param {!(string|Array.<number>|number)} code
|
|
* @param {!number} from
|
|
* @param {!number} bits
|
|
* @return {boolean|null}
|
|
* @access public
|
|
*/
|
|
this.has = (code, from = 0, bits = 0) => (
|
|
ErrorsManager.is_string(code) ? self.get_range(code, from, bits).split("").some(character => character != alphabet[0]) :
|
|
ErrorsManager.is_array(code) ? self.get_range(code, from, bits).some(number => number != 0) :
|
|
ErrorsManager.is_integer(code) ? !!self.get_range(code, from, bits) :
|
|
null);
|
|
|
|
/**
|
|
* @param {!(string|Array.<number>|number)} error
|
|
* @param {!(string|Array.<number>|number)} code
|
|
* @param {!number} from
|
|
* @param {!number} bits
|
|
* @return {string|Array.<number>|number|null}
|
|
* @access public
|
|
*/
|
|
this.set = (error, code, from = 0, bits = 0) => {
|
|
if(ErrorsManager.is_array(code)){
|
|
|
|
let l,
|
|
m = error.length,
|
|
n = code.length;
|
|
const results = [];
|
|
|
|
bits && (m = (error = self.reset(error, from, bits)).length);
|
|
from && (n = (code = self.bitwise(code, from)).length);
|
|
l = m > n ? m : n;
|
|
|
|
for(let i = 0; i < l; i ++)
|
|
results.push(
|
|
(i < m ? error[i] : 0) |
|
|
(i < n ? code[i] : 0)
|
|
);
|
|
|
|
return self.clean(results);
|
|
};
|
|
if(ErrorsManager.is_string(code))
|
|
return self.to_string(self.set(self.to_array(error), self.to_array(code), from, bits));
|
|
if(ErrorsManager.is_integer(code)){
|
|
|
|
bits && (error = self.reset(error, from, bits));
|
|
from && (code = self.bitwise(code, from));
|
|
|
|
return error | code;
|
|
};
|
|
return null;
|
|
};
|
|
|
|
constructor();
|
|
|
|
};
|
|
|
|
/** @type {Array.<string>} */
|
|
ErrorsManager.ALPHABET = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/=".split("");
|
|
|
|
/** @type {Array.<string>} */
|
|
ErrorsManager.ERRORS_MESSAGES = [
|
|
"invalid_alphabet",
|
|
"invalid_base",
|
|
"invalid_alphabet_type",
|
|
"too_short_alphabet",
|
|
"repeated_characters_in_alphabet",
|
|
"too_long_alphabet",
|
|
"base_lower_than_2",
|
|
"base_greater_than_128",
|
|
"base_greater_than_alphabet"
|
|
];
|
|
|
|
/** @type {RegExp} */
|
|
ErrorsManager.RE_KEY = /^[a-z_][a-z0-9_]*$/i;
|
|
|
|
/**
|
|
* @param {?any} item
|
|
* @returns {boolean}
|
|
* @access public
|
|
* @static
|
|
*/
|
|
ErrorsManager.is_array = item => item instanceof Array;
|
|
|
|
/**
|
|
* @param {?any} item
|
|
* @returns {boolean}
|
|
* @access public
|
|
* @static
|
|
*/
|
|
ErrorsManager.is_dictionary = item => item instanceof Object && item.constructor === Object;
|
|
|
|
/**
|
|
* @param {?any} item
|
|
* @returns {boolean}
|
|
* @access public
|
|
* @static
|
|
*/
|
|
ErrorsManager.is_string = item => typeof item == "string";
|
|
|
|
/**
|
|
* @param {?any} item
|
|
* @returns {boolean}
|
|
* @access public
|
|
* @static
|
|
*/
|
|
ErrorsManager.is_key = item => ErrorsManager.is_string(item) && ErrorsManager.RE_KEY.test(item);
|
|
|
|
/**
|
|
* @param {?any} item
|
|
* @returns {boolean}
|
|
* @access public
|
|
* @static
|
|
*/
|
|
ErrorsManager.is_integer = item => typeof item == "number" && item >> 0 == item;
|
|
|
|
/**
|
|
* @param {?any} item
|
|
* @returns {boolean}
|
|
* @access public
|
|
* @static
|
|
*/
|
|
ErrorsManager.is_null_or_undefined = item => item === undefined || item === null;
|
|
|
|
/**
|
|
* @param {?any} inputs
|
|
* @returns {Array.<string>}
|
|
* @access public
|
|
* @static
|
|
*/
|
|
ErrorsManager.get_keys = inputs => {
|
|
|
|
/** @type {Array.<string>} */
|
|
const keys = [];
|
|
|
|
if(ErrorsManager.is_key(inputs))
|
|
keys.push(inputs);
|
|
else if(ErrorsManager.is_array(inputs))
|
|
for(const item of inputs)
|
|
ErrorsManager.get_keys(item).forEach(key => {
|
|
keys.includes(key) || keys.push(key);
|
|
})
|
|
|
|
return keys;
|
|
};
|
|
|
|
/**
|
|
* @param {?any} inputs
|
|
* @returns {Array.<Object.<string, any|null>>}
|
|
* @access public
|
|
* @static
|
|
*/
|
|
ErrorsManager.get_dictionaries = inputs => {
|
|
|
|
/** @type {Array.<Object.<string, any|null>>} */
|
|
const dictionaries = [];
|
|
|
|
if(ErrorsManager.is_dictionary(inputs))
|
|
dictionaries.push(inputs);
|
|
else if(ErrorsManager.is_array(inputs))
|
|
for(const item of inputs)
|
|
dictionaries.push(...ErrorsManager.get_dictionaries(item));
|
|
|
|
return dictionaries;
|
|
};
|
|
|
|
/**
|
|
* @param {!(string|Array.<string>)} keys
|
|
* @param {?any} inputs
|
|
* @param {?any} [_default = null]
|
|
* @returns {any|null}
|
|
* @access public
|
|
* @static
|
|
*/
|
|
ErrorsManager.get = (keys, inputs, _default = null) => {
|
|
if((keys = ErrorsManager.get_keys(keys)).length)
|
|
for(const dictionary of ErrorsManager.get_dictionaries(inputs))
|
|
for(const key of keys)
|
|
if(key in dictionary)
|
|
return dictionary[key];
|
|
return _default;
|
|
};
|
|
|
|
/**
|
|
* @param {!(string|Array.<any|null>)} items
|
|
* @returns {string|Array.<any|null>}
|
|
* @access public
|
|
* @static
|
|
*/
|
|
ErrorsManager.unique = items => (
|
|
ErrorsManager.is_array(items) ? items.filter((item, i) => items.indexOf(item) == i) :
|
|
items.split("").filter((item, i) => items.indexOf(item) == i).join(""));
|
|
|
|
return ErrorsManager;
|
|
})(); |