UNPKG

coach-core

Version:
230 lines (211 loc) 6.87 kB
'use strict'; const util = { /** * Make your URL absolute. * @memberof util * @param {String} url The URL to convert to absolute. * @returns {String} the absolute URL including host/protocol. */ getAbsoluteURL: function (url) { const a = window.document.createElement('a'); a.href = url; return a.href; }, /** * Get the hostname from a URL * @memberof util * @param {String} url The URL. * @returns {String} the hostname from the URL. */ getHostname: function (url) { const a = window.document.createElement('a'); a.href = url; return a.hostname; }, /** * Checks if an element exist in an array. * * @memberof util * @param {String} element the element. * @param {Array} array the array. * @returns {Boolean} true if the element exist. */ exists: function (element, array) { return array.some(function (e) { return e === element; }); }, /** * Returns an array filter function for finding tags with an attribute specified value, matching case insensitive. * @param attributeName the name of the attribute to look for (html attribute values are always case insensitive). * @param attributeValue the value to match against, ignoring case. * @returns {Function} function that can be passed to Array#filter */ caseInsensitiveAttributeValueFilter: function ( attributeName, attributeValue ) { return function (item) { const attribute = item.getAttribute(attributeName) || ''; if (attribute.toLowerCase() === attributeValue.toLowerCase()) { return item; } return undefined; }; }, /** * Is the connection used for the main document using HTTP/2? * Works in Chrome, Firefox, Edge and other browsers that supports Resource Timing * API v2 (not Safari yet). * @memberof util * @returns {Boolean} true if the connection is HTTP/2 */ isHTTP2: function () { const type = util.getConnectionType().toLowerCase(); return type === 'h2' || type.startsWith('spdy'); }, /** * Is the connection used for the main document using HTTP/3? * @memberof util * @returns {Boolean} true if the connection is HTTP/3 */ isHTTP3: function () { const type = util.getConnectionType().toLowerCase(); return type.startsWith('h3'); }, /** * Get the connection type used for the main document. Works in Chrome, Firefox, * Edge and + browsers that support Resource Timing * API v2. * @memberof util * @returns {String} http/1 or h2 for http 1 and 2 respectively. 'unknown' if browser lacks api to determine it. */ getConnectionType: function () { // it's easy in Chrome if ( window.performance.getEntriesByType('navigation') && window.performance.getEntriesByType('navigation')[0] && window.performance.getEntriesByType('navigation')[0].nextHopProtocol ) { return window.performance.getEntriesByType('navigation')[0] .nextHopProtocol; } else if ( window.performance && window.performance.getEntriesByType && window.performance.getEntriesByType('resource') ) { // if you support resource timing v2 // it's kind of easy too const resources = window.performance.getEntriesByType('resource'); // now we "only" need to know if it is v2 if (resources.length > 1 && resources[0].nextHopProtocol) { // if it's the same domain, say it's OK const host = document.domain; for (let i = 0, len = resources.length; i < len; i++) { if (host === util.getHostname(resources[i].name)) { return resources[i].nextHopProtocol; } } } } return 'unknown'; }, /** * Get JavaScript requests that are loaded synchronously. All URLs are absolute. * @memberof util * @param {Object} parentElement the parent element that has all the scripts. * @returns {Array} an array with the URL to each JavaScript file that is loaded synchronously. */ getSynchJSFiles: function (parentElement) { const scripts = Array.prototype.slice.call( parentElement.getElementsByTagName('script') ); return scripts .filter(function (s) { return !s.async && s.src && !s.defer; }) .map(function (s) { return util.getAbsoluteURL(s.src); }); }, /** * Get JavaScript requests that are loaded asynchronously. All URLs are absolute. * @memberof util * @param {Object} parentElement the parent element that has all the scripts. * @returns {Array} an array with the URL to each JavaScript file that are loaded asynchronously. */ getAsynchJSFiles: function (parentElement) { const scripts = Array.prototype.slice.call( parentElement.getElementsByTagName('script') ); return scripts .filter(function (s) { return s.async && s.src; }) .map(function (s) { return util.getAbsoluteURL(s.src); }); }, /** * Get Resource Hints hrefs by type * @memberof util * @param {String} type the name of the Resources hint: dns-prefetch, preconnect, prefetch, prerender * @returns {Array} an array of matching hrefs */ getResourceHintsHrefs: function (type) { const links = Array.prototype.slice.call( window.document.head.getElementsByTagName('link') ); return links .filter(function (link) { return link.rel === type; }) .map(function (link) { return link.href; }); }, /** * Get CSS requests. All URLs are absolute. * @memberof util * @param {Object} parentElement the parent element that has all the scripts. * @returns {Array} an array with the URL to each CSS file that is loaded synchronously. */ getCSSFiles: function (parentElement) { const links = Array.prototype.slice.call( parentElement.getElementsByTagName('link') ); return links .filter(function (link) { // make sure we skip data: return link.rel === 'stylesheet' && !link.href.startsWith('data:'); }) .map(function (link) { return util.getAbsoluteURL(link.href); }); }, plural: function (number, text) { if (number > 1) { text += 's'; } return `${number} ${text}`; }, /** * Get the size of an asset. Will try to use the Resource Timing V2. If that's * not available or the asset size is unknown we report 0. **/ getTransferSize: function (url) { const entries = window.performance.getEntriesByName(url, 'resource'); if (entries.length === 1 && typeof entries[0].transferSize === 'number') { return entries[0].transferSize; } else { return 0; } }, ms(ms) { if (ms < 1000) { return ms + ' ms'; } else { return Number(ms / 1000).toFixed(3) + ' s'; } } };