luhn-generator
Version:
A generator of numbers that passes the validation of Luhn algorithm or Luhn formula, also known as the 'modulus 10' or 'mod 10' algorithm
153 lines (133 loc) • 3.85 kB
JavaScript
/**
* Parse resource object for a given node from a specified attribute
* @method urlPropsFromAttribute
* @param {HTMLElement} node given node
* @param {String} attribute attribute of the node from which resource should be parsed
* @returns {Object}
*/
function urlPropsFromAttribute(node, attribute) {
if (!node.hasAttribute(attribute)) {
return undefined;
}
const nodeName = node.nodeName.toUpperCase();
let parser = node;
/**
* Note:
* The need to create a parser, is to keep this function generic, to be able to parse resource from element(s) like `iframe` with `src` attribute,
*
* Also, when `a` or `area` is nested inside an svg document,
* they do not have url properties as a HTML Node, hence the check for `ownerSVGElement`
*/
if (!['A', 'AREA'].includes(nodeName) || node.ownerSVGElement) {
parser = document.createElement('a');
parser.href = node.getAttribute(attribute);
}
/**
* Curate `https` and `ftps` to `http` and `ftp` as they will resolve to same resource
*/
const protocol = [`https:`, `ftps:`].includes(parser.protocol)
? parser.protocol.replace(/s:$/, ':')
: parser.protocol;
/**
* certain browser (in this case IE10 & 11)
* does not resolve pathname with a beginning slash, thence prepending with a beginning slash
*/
const parserPathname = /^\//.test(parser.pathname)
? parser.pathname
: `/${parser.pathname}`;
const { pathname, filename } = getPathnameOrFilename(parserPathname);
return {
protocol,
hostname: parser.hostname,
port: getPort(parser.port),
pathname: /\/$/.test(pathname) ? pathname : `${pathname}/`,
search: getSearchPairs(parser.search),
hash: getHashRoute(parser.hash),
filename
};
}
/**
* Resolve given port excluding default port(s)
* @param {String} port port
* @returns {String}
*/
function getPort(port) {
const excludePorts = [
`443`, // default `https` port
`80`
];
return !excludePorts.includes(port) ? port : ``;
}
/**
* Resolve if a given pathname has filename & resolve the same as parts
* @method getPathnameOrFilename
* @param {String} pathname pathname part of a given uri
* @returns {Array<Object>}
*/
function getPathnameOrFilename(pathname) {
const filename = pathname.split('/').pop();
if (!filename || filename.indexOf('.') === -1) {
return {
pathname,
filename: ``
};
}
return {
// remove `filename` from `pathname`
pathname: pathname.replace(filename, ''),
// ignore filename when index.*
filename: /index./.test(filename) ? `` : filename
};
}
/**
* Parse a given query string to key/value pairs sorted alphabetically
* @param {String} searchStr search string
* @returns {Object}
*/
function getSearchPairs(searchStr) {
const query = {};
if (!searchStr || !searchStr.length) {
return query;
}
// `substring` to remove `?` at the beginning of search string
const pairs = searchStr.substring(1).split(`&`);
if (!pairs || !pairs.length) {
return query;
}
for (let index = 0; index < pairs.length; index++) {
const pair = pairs[index];
const [key, value = ''] = pair.split(`=`);
query[decodeURIComponent(key)] = decodeURIComponent(value);
}
return query;
}
/**
* Interpret a given hash
* if `hash`
* -> is `hashbang` -or- `hash` is followed by `slash`
* -> it resolves to a different resource
* @method getHashRoute
* @param {String} hash hash component of a parsed uri
* @returns {String}
*/
function getHashRoute(hash) {
if (!hash) {
return ``;
}
/**
* Check for any conventionally-formatted hashbang that may be present
* eg: `#, #/, #!, #!/`
*/
const hashRegex = /#!?\/?/g;
const hasMatch = hash.match(hashRegex);
if (!hasMatch) {
return ``;
}
// do not resolve inline link as hash
const [matchedStr] = hasMatch;
if (matchedStr === '#') {
return ``;
}
return hash;
}
export default urlPropsFromAttribute;