123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152 |
- /**
- * 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;
|