fe-supervisor
Version:
The frontend monitor project
1,297 lines (1,294 loc) • 58 kB
JavaScript
(function(g,f){typeof exports==='object'&&typeof module!=='undefined'?f(exports):typeof define==='function'&&define.amd?define(['exports'],f):(g=g||self,f(g.$sv={}));}(this,function(exports){'use strict';/**
* @param {AnyEventName} event event name
* @param {function} fn event callback
* @param {object} dom event dom
* @param {ListenerOption} option option contain captrue, passive, once
*/
var addListener = (function () {
if (typeof window === 'undefined')
return function () { };
if (!window.addEventListener) {
return function (event, fn, dom) {
var eventDOM = dom || window;
eventDOM.attachEvent("on" + event, fn);
};
}
return function (event, fn, dom, option) {
if (option === void 0) { option = {}; }
var eventDOM = dom || window;
var _a = option.capture, capture = _a === void 0 ? false : _a, _b = option.passive, passive = _b === void 0 ? false : _b, _c = option.once, once = _c === void 0 ? false : _c;
eventDOM.addEventListener(event, fn, {
capture: capture,
passive: passive,
once: once
});
};
})();/**
* copy text to clipboard method
* @param {string} text the content for copy
* @param {function} cb the callback for copy result
* @return {void}
*/
function __awaiter(thisArg, _arguments, P, generator) {
return new (P || (P = Promise))(function (resolve, reject) {
function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
function step(result) { result.done ? resolve(result.value) : new P(function (resolve) { resolve(result.value); }).then(fulfilled, rejected); }
step((generator = generator.apply(thisArg, _arguments || [])).next());
});
}
function __generator(thisArg, body) {
var _ = { label: 0, sent: function() { if (t[0] & 1) throw t[1]; return t[1]; }, trys: [], ops: [] }, f, y, t, g;
return g = { next: verb(0), "throw": verb(1), "return": verb(2) }, typeof Symbol === "function" && (g[Symbol.iterator] = function() { return this; }), g;
function verb(n) { return function (v) { return step([n, v]); }; }
function step(op) {
if (f) throw new TypeError("Generator is already executing.");
while (_) try {
if (f = 1, y && (t = op[0] & 2 ? y["return"] : op[0] ? y["throw"] || ((t = y["return"]) && t.call(y), 0) : y.next) && !(t = t.call(y, op[1])).done) return t;
if (y = 0, t) op = [op[0] & 2, t.value];
switch (op[0]) {
case 0: case 1: t = op; break;
case 4: _.label++; return { value: op[1], done: false };
case 5: _.label++; y = op[1]; op = [0]; continue;
case 7: op = _.ops.pop(); _.trys.pop(); continue;
default:
if (!(t = _.trys, t = t.length > 0 && t[t.length - 1]) && (op[0] === 6 || op[0] === 2)) { _ = 0; continue; }
if (op[0] === 3 && (!t || (op[1] > t[0] && op[1] < t[3]))) { _.label = op[1]; break; }
if (op[0] === 6 && _.label < t[1]) { _.label = t[1]; t = op; break; }
if (t && _.label < t[2]) { _.label = t[2]; _.ops.push(op); break; }
if (t[2]) _.ops.pop();
_.trys.pop(); continue;
}
op = body.call(thisArg, _);
} catch (e) { op = [6, e]; y = 0; } finally { f = t = 0; }
if (op[0] & 5) throw op[1]; return { value: op[0] ? op[1] : void 0, done: true };
}
}function compatCheck(type) {
var res = true;
try {
switch (type) {
case 'generator':
var genFn = function () {
return __generator(this, function (_a) {
switch (_a.label) {
case 0: return [4 /*yield*/, true];
case 1:
_a.sent();
return [2 /*return*/];
}
});
};
var gen = genFn();
gen.next();
break;
case 'promise':
Promise.resolve(true);
break;
case 'async':
var asyncFn = function () {
return __awaiter(this, void 0, void 0, function () {
var res;
return __generator(this, function (_a) {
switch (_a.label) {
case 0: return [4 /*yield*/, Promise.resolve(true).then(function (res) { return res; })];
case 1:
res = _a.sent();
return [2 /*return*/, res];
}
});
});
};
asyncFn();
break;
}
}
catch (error) {
res = false;
}
return res;
}/**
* get cookie by appoint name and cookie str
* @param {string} name
* @param {string} cookie
* @return {string | null}
*/
function getTs() {
return (Date.now && +Date.now()) || new Date().getTime();
}/**
* get element type
* @param {any} ele the target element
* @return {string} the element type
*/
function getUA(u) {
if (!u)
return false;
function check(pattern) {
return (pattern).test(u);
}
function parseIPhoneX(u) {
var model = '';
if (/(iPhone\/)?iPhone\s?X/.test(u)) { // 通过UA判断
model = 'iPhoneX';
}
else { // 浏览器中无法通过UA检测出iphonex系列
if (/iPhone/.test(u)) { // 通过ua加屏幕宽高判断
// https://developer.apple.com/design/human-interface-guidelines/ios/visual-design/adaptivity-and-layout/
// iPhone X @3x 1125px × 2436px => 375px x 812px
// iPhone XS @3x 1125px × 2436px => 375px x 812px
// iPhone XS Max @3x 1242px × 2688px => 414px x 896px
// iPhone XR @2x 828px × 1792px => 414px x 896px
// Portrait/Landscape
if (typeof screen !== 'undefined') {
var screenHeight = screen.height;
var screenWidth = screen.width;
if (screenHeight === 812 || screenWidth === 812) { // iPhoneX/iPhoneXS
model = 'iPhoneX';
}
else if (screenHeight === 896 || screenWidth === 896) { // iPhoneXR/iPhoneXSMax
model = 'iPhoneXR';
if (devicePixelRatio) {
if (devicePixelRatio === 3) {
model = 'iPhoneXSMax';
}
}
}
}
}
}
return model;
}
var UA = {
isAndroid: check(/android|linux|adr/i),
isIOS: check(/\(i[^;]+;( U;)? CPU.+Mac OS X/i),
isWindows: check(/window/i),
isMac: check(/mac os x/i),
isIPad: check(/iPad/i),
isMobile: check(/AppleWebKit.*Mobile.*|Mobile/i),
isWebKit: check(/webkit\W/i),
isChrome: check(/webkit\W.*(chrome|chromium)\W/i),
isFirefox: check(/mozilla.*\Wfirefox\W/i),
isGecko: check(/mozilla(?!.*webkit).*\Wgecko\W/i),
is360se: check(/360/i),
isIE: (function () {
if (typeof window !== 'undefined' && navigator.appName === 'Microsoft Internet Explorer') {
return true;
}
else if (check(/\bTrident\b/)) {
return true;
}
else {
return false;
}
})(),
isEdge: check(/\bEdge\b/i),
isOpera: check(/opera.*\Wpresto\W|OPR/i),
isSafari: check(/webkit\W(?!.*chrome).*safari\W/i),
isUCBrowser: check(/ucbrowser/i),
isBaiduBrowser: check(/bidubrowser/i),
isSougouBrowser: check(/metasr/i),
isLiebaoBrowser: check(/lbbrowser/i),
isWeixin: check(/micromessenger/i),
isSinaWeibo: check(/weibo/i),
isQQ: check(/qq/i),
isQQWeibo: check(/tencentmicroblog/i),
isFacebook: check(/fban/i),
isTwitter: check(/twitter/i),
isInstagram: check(/instagram/i),
isIphoneX: parseIPhoneX(u) == 'iPhoneX' ? true : false,
isIPhoneXR: parseIPhoneX(u) == 'iPhoneXR' ? true : false,
isIPhoneXSMax: parseIPhoneX(u) == 'iPhoneXSMax' ? true : false,
};
return UA;
}var _this = undefined;
var gen = function () {
return __generator(this, function (_a) {
switch (_a.label) {
case 0: return [4 /*yield*/];
case 1:
_a.sent();
return [2 /*return*/];
}
});
};
var typeMap = {
string: '',
number: 1,
boolean: true,
null: null,
undefined: undefined,
symbol: Symbol(1),
array: [],
object: {},
regexp: /regexp/,
date: new Date(),
function: function () { },
promise: Promise.resolve(void (0)),
generatorfunction: gen,
generator: gen(),
asyncfunction: function () { return __awaiter(_this, void 0, void 0, function () { return __generator(this, function (_a) {
return [2 /*return*/];
}); }); }
};
/**
* judgement ele type
* @param {string} type the string of ele type for judgement
* @param {any} ele the target element
* @return {boolean} whether or not ele pair with type
*/
function isType(type) {
return function (ele) {
if (typeof ele !== 'object')
return typeof ele === type.toLowerCase();
var len = Object.prototype.toString.call(ele).length - 1;
return Object.prototype.toString.call(ele).slice(8, len).toLowerCase() === type.toLowerCase();
};
}var logLevelSet;
(function (logLevelSet) {
logLevelSet[logLevelSet["detail"] = 0] = "detail";
logLevelSet[logLevelSet["info"] = 1] = "info";
logLevelSet[logLevelSet["warn"] = 2] = "warn";
logLevelSet[logLevelSet["error"] = 3] = "error";
logLevelSet[logLevelSet["silent"] = 4] = "silent";
})(logLevelSet || (logLevelSet = {}));
var parseUrl = (function () {
if (typeof window === 'undefined')
return function (url) { return url; };
var HTTP_PORT = '80';
var HTTPS_PORT = '443';
var DEFAULT_PORT = RegExp(':(' + HTTP_PORT + '|' + HTTPS_PORT + ')$');
var a = document.createElement('a');
var cache = {};
/**
* Parses the given url and returns an object mimicing a `Location` object.
* @param {string} url The url to parse.
* @return {!Object} An object with the same properties as a `Location`.
*/
return function parse_url(url) {
// All falsy values (as well as ".") should map to the current URL.
url = (!url || url == '.') ? location.href : url;
if (cache[url])
return cache[url];
a.href = url;
// When parsing file relative paths (e.g. `../index.html`), IE will correctly
// resolve the `href` property but will keep the `..` in the `path` property.
// It will also not include the `host` or `hostname` properties. Furthermore,
// IE will sometimes return no protocol or just a colon, especially for things
// like relative protocol URLs (e.g. "//google.com").
// To workaround all of these issues, we reparse with the full URL from the
// `href` property.
if (url.charAt(0) == '.' || url.charAt(0) == '/')
return parse_url(a.href);
// Don't include default ports.
var port = (a.port == HTTP_PORT || a.port == HTTPS_PORT) ? '' : a.port;
// PhantomJS sets the port to "0" when using the file: protocol.
port = port == '0' ? '' : port;
// Sometimes IE incorrectly includes a port for default ports
// (e.g. `:80` or `:443`) even when no port is specified in the URL.
// http://bit.ly/1rQNoMg
var host = a.host.replace(DEFAULT_PORT, '');
// Not all browser support `origin` so we have to build it.
var origin = a.origin ? a.origin : a.protocol + '//' + host;
// Sometimes IE doesn't include the leading slash for pathname.
// http://bit.ly/1rQNoMg
var pathname = a.pathname.charAt(0) == '/' ? a.pathname : '/' + a.pathname;
return cache[url] = {
hash: a.hash,
host: host,
hostname: a.hostname,
href: a.href,
origin: origin,
pathname: pathname,
port: port,
protocol: a.protocol,
search: a.search,
};
};
})();/**
* @param {event} event event object
*/
var removeListener = (function () {
if (typeof window === 'undefined')
return function () { };
if (!window.removeEventListener) {
return function (event, fn, dom) {
var eventDOM = dom || window;
eventDOM.detachEvent("on" + event, fn);
};
}
return function (event, fn, dom, useCapture) {
if (useCapture === void 0) { useCapture = false; }
var eventDOM = dom || window;
eventDOM.removeEventListener(event, fn, useCapture);
};
})();/**
* @param {event} event event object
*/
var storage = (function () {
if (typeof window === 'undefined' || !window.sessionStorage || !window.localStorage)
return {
get: function () { return null; },
set: function () { return false; },
clear: function () { return false; }
};
var handleStorage = {
sessionStorage: {
set: function (key, val) {
var res;
try {
window.sessionStorage.setItem(key, val);
res = true;
}
catch (err) {
res = false;
}
return res;
},
get: function (key) {
var res;
try {
res = window.sessionStorage.getItem(key);
}
catch (err) {
res = null;
}
return res;
},
clear: function (key) {
var res;
try {
window.sessionStorage.removeItem(key);
res = true;
}
catch (err) {
res = false;
}
return res;
}
},
localStorage: {
set: function (key, val) {
var res;
try {
window.localStorage.setItem(key, val);
res = true;
}
catch (err) {
res = false;
}
return res;
},
get: function (key) {
var res;
try {
res = window.localStorage.getItem(key);
}
catch (err) {
res = null;
}
return res;
},
clear: function (key) {
var res;
try {
window.localStorage.removeItem(key);
res = true;
}
catch (err) {
res = false;
}
return res;
}
},
cookie: {
set: function (key, val, option) {
var res;
try {
var _a = option || {}, _b = _a.domain, domain = _b === void 0 ? '' : _b, _c = _a.path, path = _c === void 0 ? '/' : _c, _d = _a.expires, expires = _d === void 0 ? 0 : _d, _e = _a.secure, secure = _e === void 0 ? false : _e;
var ts = void 0;
if (expires >= 0) {
ts = new Date();
ts.setTime(ts.getTime() + expires * 1000);
}
document.cookie = key + "=" + val + ";domain=" + domain + ";path=" + path + ";expires=" + (ts ? ts.toUTCString() : '') + ";" + (secure ? 'secure' : '');
res = true;
}
catch (err) {
res = false;
}
return res;
},
get: function (key) {
var reg = new RegExp('(^| )' + key + '=([^;]*)(;|$)');
var cookArr = document.cookie.match(reg);
if (cookArr && cookArr[2]) {
return unescape(cookArr[2]);
}
return null;
},
clear: function (key, domain) {
document.cookie = key + "=\"\";domain=" + domain + ";max-age=-1";
var res = handleStorage.cookie.get(key);
return !res;
}
},
get: function (key, storeType) {
try {
if (typeof storeType === 'string' && handleStorage[storeType]) {
return handleStorage[storeType].get(key);
}
return this.cookie.get(key) || this.localStorage.get(key) || this.sessionStorage.get(key);
}
catch (e) {
return null;
}
},
set: function (key, val, storeType, option) {
var res;
try {
if (typeof storeType === 'string' && handleStorage[storeType]) {
res = handleStorage[storeType].set(key, val, option);
}
else if (option) {
res = handleStorage.cookie.set(key, val, option);
}
else {
res = handleStorage.localStorage.set(key, val);
}
}
catch (e) {
res = false;
}
return res;
},
clear: function (key, storeType, domain) {
var res;
try {
if (typeof storeType === 'string' && handleStorage[storeType]) {
res = handleStorage[storeType].clear(key, domain);
}
else if (domain) {
res = handleStorage.cookie.clear(key, domain);
}
else {
res = handleStorage.localStorage.clear(key) && handleStorage.sessionStorage.clear(key);
}
}
catch (e) {
res = false;
}
return res;
}
};
return handleStorage;
})();var longTaskThreshold = 50;
var singleTaskThreshold = longTaskThreshold / 2;
var notGF = function () {
console.warn('Please pass the [Generator Function]');
return false;
};
var timeslice = (function () {
var supportAPI = typeof window !== 'undefined' && window.performance;
return function (genF) {
if (!genF || typeof genF !== 'function')
return notGF;
var gen = genF();
if (typeof gen.next !== 'function')
return notGF;
return new Promise(function (resolve, reject) {
function next() {
try {
var start = (supportAPI && performance.now()) || getTs();
var res = null;
do {
res = gen.next();
} while (!res.done && ((supportAPI && performance.now()) || getTs()) - start < singleTaskThreshold);
if (res.done) {
resolve(res.value);
return;
}
setTimeout(next);
}
catch (err) {
reject(err);
}
}
next();
});
};
})();function uuid() {
function S4() {
return (((1 + Math.random()) * 0x10000) | 0).toString(16).substring(1);
}
return (S4() + S4() + '-' + S4() + '-' + S4() + '-' + S4() + '-' + S4() + S4() + S4());
}function notSupport() {
console.warn('Your browser not support the [Performance] API!');
return false;
}
function notSupportPromisify() {
console.warn('Your browser not support the [Performance] API!');
return Promise.resolve(false);
}class Observer {
constructor(target, callback) {
this.cache = [];
this.observer = this.init(target, callback);
}
init(target, callback) {
if (!target) {
console.warn('Please pass a vaild HTMLElement');
return null;
}
let observer;
try {
observer = new MutationObserver(callback);
observer.observe(target, {
'childList': true,
'subtree': true,
'attributes': true,
'attributeFilter': ['src', 'href']
});
}
catch (error) {
return null;
}
return observer;
}
cancel() {
this.observer && this.observer.disconnect();
}
setCache(cache) {
this.cache.push(cache);
}
getCache() {
return this.cache;
}
clearCache() {
this.cache.splice(0);
}
}var ExceptionTypes;
(function (ExceptionTypes) {
ExceptionTypes[ExceptionTypes["js"] = 0] = "js";
ExceptionTypes[ExceptionTypes["api"] = 1] = "api";
ExceptionTypes[ExceptionTypes["source"] = 2] = "source";
})(ExceptionTypes || (ExceptionTypes = {}));
const jsErrors = [];
const apiErrors = [];
const sourceErrors = [];
const errorTag = 'fe-supervisor-error';
function getErrors(type) {
const typeIndex = type ? ExceptionTypes[type] : undefined;
let errors;
switch (typeIndex) {
case ExceptionTypes['js']:
errors = jsErrors;
break;
case ExceptionTypes['api']:
errors = apiErrors;
break;
case ExceptionTypes['source']:
errors = sourceErrors;
break;
default:
errors = { jsErrors, apiErrors, sourceErrors };
}
return errors;
}
const HandleException = {
getErrors,
setErrors(error) {
const { type } = error;
const typeIndex = ExceptionTypes[type];
switch (typeIndex) {
case ExceptionTypes['js']:
const callstack = new Error().stack;
jsErrors.push({ ...error, code_site: callstack });
break;
case ExceptionTypes['api']:
apiErrors.push(error);
break;
case ExceptionTypes['source']:
sourceErrors.push(error);
break;
}
},
clearError(type) {
const typeIndex = type ? ExceptionTypes[type] : undefined;
let res = true;
try {
switch (typeIndex) {
case ExceptionTypes['js']:
jsErrors.splice(0);
break;
case ExceptionTypes['api']:
apiErrors.splice(0);
break;
case ExceptionTypes['source']:
sourceErrors.splice(0);
break;
default:
jsErrors.splice(0);
apiErrors.splice(0);
sourceErrors.splice(0);
}
}
catch (err) {
res = false;
}
return res;
},
async dupliError(type, msg) {
let res = false;
const errs = HandleException.getErrors(type);
const len = (errs && errs.length) || 0;
if (compatCheck('generator')) {
function* gen() {
for (let i = 0; i < len; i++) {
const item = errs[i];
if (item.msg && item.msg === msg) {
res = true;
return;
}
yield;
}
}
await timeslice(gen);
return res;
}
else {
for (let i = 0; i < len; i++) {
const item = errs[i];
if (item.msg && item.msg === msg) {
res = true;
break;
}
}
return res;
}
}
};async function catchError(type, msg) {
if (!type)
return;
const isDuplication = await HandleException.dupliError(type, msg);
if (!isDuplication) {
HandleException.setErrors({
ts: getTs(),
type,
url: location.href,
msg
});
}
}const markCache = [];
const measureCache = [];
const marks = [];
const measures = [];
const getMemory = (function () {
if (typeof window === 'undefined' || !window.performance)
return notSupport;
return function () {
let limit, used, total, usedRatio;
try {
const p = window.performance;
const m = p.memory || {};
limit = m.jsHeapSizeLimit || 1;
used = m.usedJSHeapSize || 0;
total = m.totalJSHeapSize || 1;
usedRatio = +Number.prototype.toFixed.call(used / total, 3);
}
catch (error) {
const msg = `[SV - getMemory]: ${JSON.stringify(error, Object.getOwnPropertyNames(error))}`;
catchError('js', msg);
}
finally {
return {
limit,
memory: usedRatio || 'N/A',
used: usedRatio ? used : 'N/A',
total: usedRatio ? total : 'N/A'
};
}
};
})();
const getTiming = (function () {
if (typeof window === 'undefined' || !window.performance)
return notSupport;
return function () {
let wscreen, fscreen, network, network_prev, network_redirect, network_dns, network_tcp, network_request, network_response, network_interact, dom_loading, dom_interact, dom_ready, dom_load, dom_complete, js_ready, js_load, js_complete, render_ready, render_load, total;
wscreen = fscreen = network = network_prev = network_redirect = network_dns = network_tcp = network_request = network_response = network_interact = dom_loading = dom_interact = dom_ready = dom_load = dom_complete = js_ready = js_load = js_complete = render_ready = render_load = total = 'N/A';
try {
const p = window.performance;
const t = p.timing || {};
wscreen = timingFilter(t.domLoading - t.navigationStart);
fscreen = timingFilter(t.domContentLoadedEventStart - t.navigationStart);
network = timingFilter(t.responseEnd - t.navigationStart);
network_prev = timingFilter(t.fetchStart - t.navigationStart);
network_redirect = timingFilter(t.redirectEnd - t.redirectStart);
network_dns = timingFilter(t.domainLookupEnd - t.domainLookupStart);
network_tcp = timingFilter(t.connectEnd - t.connectStart);
network_request = timingFilter(t.responseStart - t.requestStart);
network_response = timingFilter(t.responseEnd - t.responseStart);
network_interact = timingFilter(t.responseEnd - t.requestStart);
dom_loading = timingFilter(t.domLoading - t.responseEnd);
dom_interact = timingFilter(t.domInteractive - t.domLoading);
dom_ready = timingFilter(t.domContentLoadedEventStart - t.domLoading);
dom_load = timingFilter(t.domComplete - t.domLoading);
dom_complete = timingFilter(t.domComplete - t.navigationStart);
js_ready = timingFilter(t.domContentLoadedEventStart - t.domInteractive);
js_load = timingFilter(t.domContentLoadedEventEnd - t.domInteractive);
js_complete = timingFilter(t.domContentLoadedEventEnd - t.navigationStart);
render_ready = timingFilter(t.domContentLoadedEventStart - t.domLoading);
render_load = timingFilter(t.loadEventEnd - t.domLoading);
total = timingFilter(t.loadEventEnd - t.navigationStart);
}
catch (error) {
const msg = `[SV - getTiming]: ${JSON.stringify(error, Object.getOwnPropertyNames(error))}`;
catchError('js', msg);
}
finally {
return {
wscreen,
fscreen,
network,
network_prev,
network_redirect,
network_dns,
network_tcp,
network_request,
network_response,
network_interact,
dom_loading,
dom_interact,
dom_ready,
dom_load,
dom_complete,
js_ready,
js_load,
js_complete,
render_ready,
render_load,
total
};
}
};
})();
const getSource = (function () {
if (typeof window === 'undefined' || !window.performance)
return notSupportPromisify;
return async function (config) {
const api_random = [];
const api_timeout = [];
const api_appoint = [];
const source_random = [];
const source_timeout = [];
const source_appoint = [];
const others = [];
try {
const { apiRatio = 0.1, sourceRatio = 0.1, apis = '', sources = '', timeout = 2000, whitelist = {}, paintTiming = false } = config || {};
const w_a = whitelist.api || '';
const w_s = whitelist.source || '';
const p = window.performance;
const s = (p.getEntries && p.getEntries()) || [];
const threshold = timeout;
function sortSource(item) {
const entryType = item.entryType || '';
const type = item.initiatorType || '';
const data = {
name: item.name,
duration: +Number.prototype.toFixed.call(item.duration, 2),
type
};
if (type === 'xmlhttprequest' || type === 'fetchrequest') {
if (w_a) {
if (isType('string')(w_a)) {
if (data.name === w_a)
return;
}
else if (isType('array')(w_a)) {
const w_a_len = (w_a && w_a.length) || 0;
for (let j = 0; j < w_a_len; j++) {
if (data.name === w_a[j])
return;
}
}
}
randomRatio(apiRatio) && api_random.push(data);
data.duration >= threshold && api_timeout.push(data);
if (isType('string')(apis)) {
data.name === apis && api_appoint.push(data);
}
else if (isType('array')(apis)) {
apis.some(v => {
if (v === data.name) {
api_appoint.push(data);
return true;
}
return false;
});
}
}
else if (type === 'script' || type === 'css' || type === 'img' || type === 'link' || type === 'css') {
if (w_s) {
if (isType('string')(w_s)) {
if (data.name === w_s)
return;
}
else if (isType('array')(w_s)) {
const w_s_len = (w_s && w_s.length) || 0;
for (let m = 0; m < w_s_len; m++) {
if (data.name === w_s[m])
return;
}
}
}
randomRatio(sourceRatio) && source_random.push(data);
data.duration >= threshold && source_timeout.push(data);
if (isType('string')(sources)) {
type === sources && source_appoint.push(data);
}
else if (isType('array')(sources)) {
sources.some(v => {
if (v === type) {
source_appoint.push(data);
return true;
}
return false;
});
}
else if (isType('object')(sources)) {
for (const k in sources) {
if (k === type) {
sources[k].some(v => {
if (v === data.name) {
source_appoint.push(data);
return true;
}
return false;
});
}
}
}
}
else {
if (paintTiming && entryType === 'paint') {
const { startTime } = item;
let type = '';
if (data.name === 'first-paint' || data.name === 'first-contentful-paint') {
switch (data.name) {
case 'first-paint':
type = 'FP';
break;
case 'first-contentful-paint':
type = 'FCP';
break;
}
others.push({
...data,
type,
startTime
});
}
}
}
}
if (compatCheck('generator')) {
function* gen() {
const len = s.length;
for (let i = 0; i < len; i++) {
sortSource(s[i]);
yield;
}
}
await timeslice(gen);
}
else {
const len = s.length;
for (let i = 0; i < len; i++) {
sortSource(s[i]);
}
}
}
catch (error) {
const msg = `[SV - getSource]: ${JSON.stringify(error, Object.getOwnPropertyNames(error))}`;
catchError('js', msg);
}
finally {
return {
api_random,
api_timeout,
api_appoint,
source_random,
source_timeout,
source_appoint,
others
};
}
};
})();
const getExecTiming = (function () {
if (typeof window === 'undefined' || !window.performance)
return notSupportPromisify;
return async function () {
const exec = [];
try {
const p = window.performance;
let measures = [];
if (p.mark && p.measure) {
measures = (p.getEntriesByType && p.getEntriesByType('measure')) || (p.getEntries && p.getEntries()) || [];
}
else {
measures = measureCache;
}
function sortExec(item) {
if (item.entryType === 'measure') {
exec.push({
name: item.name,
duration: +Number.prototype.toFixed.call(item.duration, 3)
});
}
}
if (compatCheck('generator')) {
function* gen() {
const len = (measures && measures.length) || 0;
for (let i = 0; i < len; i++) {
sortExec(measures[i]);
}
yield;
}
await timeslice(gen);
}
else {
const len = (measures && measures.length) || 0;
for (let i = 0; i < len; i++) {
sortExec(measures[i]);
}
}
}
catch (error) {
const msg = `[SV - getExecTiming]: ${JSON.stringify(error, Object.getOwnPropertyNames(error))}`;
catchError('js', msg);
}
finally {
return {
exec
};
}
};
})();
const getPerformanceData = (function () {
if (typeof window === 'undefined' || !window.performance)
return notSupportPromisify;
return async function (config) {
let memory, timings, sources, execTiming;
try {
const memo = getMemory();
memory = memo && memo.memory;
timings = getTiming() || {};
sources = await getSource(config).then(data => data);
execTiming = await getExecTiming().then(data => data);
}
catch (error) {
const msg = `[SV - getPerformanceData]: ${JSON.stringify(error, Object.getOwnPropertyNames(error))}`;
catchError('js', msg);
}
finally {
return {
memory: memory || 'N/A',
...timings,
...sources,
...execTiming
};
}
};
})();
const mark = (function () {
if (typeof window === 'undefined' || !window.performance)
return notSupport;
return function (tag) {
let res = true;
try {
const p = window.performance;
const tagS = `${tag}_start`;
const tagE = `${tag}_end`;
if (!~marks.indexOf(tag)) {
if (p.mark) {
p.mark(tagS);
}
else {
compatibleMark(tagS);
}
marks.push(tag);
}
else if (!~measures.indexOf(tag)) {
if (p.mark && p.measure) {
p.mark(tagE);
p.measure(`${tag}`, tagS, tagE);
}
else {
compatibleMark(tagE);
compatibleMeasure(`${tag}`, tagS, tagE);
}
measures.push(tag);
}
else {
res = false;
console.warn(`Cannot repeat tag the mark: ${tag}`);
}
}
catch (error) {
res = false;
const msg = `[SV - mark]: ${JSON.stringify(error, Object.getOwnPropertyNames(error))}`;
catchError('js', msg);
}
finally {
return res;
}
};
})();
const clearPerformance = (function () {
if (typeof window === 'undefined' || !window.performance)
return notSupport;
return function (clearType) {
try {
const isClearSource = !clearType || clearType === 'source' || clearType === 'all';
const isClearMark = !clearType || clearType === 'mark' || clearType === 'all';
const isClearMeasure = !clearType || clearType === 'measure' || clearType === 'all';
const p = window.performance;
if (isClearMark) {
p.clearMarks && p.clearMarks();
marks.splice(0);
markCache.splice(0);
}
if (isClearMeasure) {
p.clearMeasures && p.clearMeasures();
measures.splice(0);
measureCache.splice(0);
}
isClearSource && p.clearResourceTimings && p.clearResourceTimings();
return true;
}
catch (error) {
const msg = `[SV - clearPerformance]: ${JSON.stringify(error, Object.getOwnPropertyNames(error))}`;
catchError('js', msg);
return false;
}
};
})();
const observeSource = (function () {
if (typeof window === 'undefined' || !window.performance)
return notSupport;
return function (target, callback, option) {
let { sourceType = 'img', timeout = 2000, whitelist = {} } = option || {};
sourceType = isType('string')(sourceType) ? [sourceType.toLowerCase()] : [...sourceType.map(type => type.toLowerCase())];
getSourceByDom(target);
const observer = new Observer(target, async function (mutationRecords) {
let spendTime = 0;
let frequence = 200;
const len = (mutationRecords && mutationRecords.length) || 0;
const sourceAddr = {};
for (let i = 0; i < len; i++) {
const item = mutationRecords[i];
const recordType = item.type;
switch (recordType) {
case 'childList':
const addNodes = item.addedNodes;
await timeslice(iterationDOM(addNodes, sourceAddr));
break;
case 'attributes':
const attrName = item.attributeName;
const target = item.target;
const type = target.tagName && target.tagName.toLowerCase() || 'img';
if (attrName) {
!sourceAddr[type] && (sourceAddr[type] = []);
sourceAddr[type].push(target[attrName]);
}
break;
}
}
timerQuery();
function timerQuery() {
setTimeout(async function () {
const sourceData = await getSource({
apiRatio: 0,
sourceRatio: 0,
sources: sourceAddr,
whitelist
}).then(data => data.source_appoint);
let addrLen = 0;
for (const k in sourceAddr) {
addrLen += sourceAddr[k].length;
}
if ((sourceData && sourceData.length === addrLen) || spendTime >= timeout) {
return callback && callback(sourceData);
}
else {
spendTime += frequence;
timerQuery();
}
}, frequence);
}
});
async function getSourceByDom(dom, isAsync) {
let data = [];
try {
const type = dom.nodeName.toLowerCase();
const sourceAddr = {};
if (~sourceType.indexOf(type)) {
const sourceSrc = dom.src || dom.href || '';
!sourceAddr[type] && (sourceAddr[type] = []);
sourceSrc && sourceAddr[type].push(sourceSrc);
}
const doms = dom.children;
if (doms && doms.length > 0) {
await timeslice(iterationDOM(doms, sourceAddr));
}
data = await getSource({
apiRatio: 0,
sourceRatio: 0,
sources: sourceAddr,
whitelist
}).then(data => data.source_appoint);
!isAsync && callback && callback(data);
}
catch (error) {
const msg = `[SV - observeSource_getSourceByDom]: ${JSON.stringify(error, Object.getOwnPropertyNames(error))}`;
catchError('js', msg);
}
finally {
return data;
}
}
function iterationDOM(doms, sourceAddr) {
function sortDOM(doms, sourceStore) {
const len = (doms && doms.length) || 0;
for (let i = 0; i < len; i++) {
const dom = doms[i];
const type = dom.nodeName.toLowerCase();
if (~sourceType.indexOf(type)) {
const sourceSrc = dom.src || dom.href || '';
!sourceStore[type] && (sourceStore[type] = []);
sourceSrc && sourceStore[type].push(sourceSrc);
}
const children = dom.children;
const childLen = (children && children.length) || 0;
if (childLen > 0) {
sortDOM(children, sourceStore);
}
}
}
if (compatCheck('generator')) {
return function* () {
try {
const len = (doms && doms.length) || 0;
for (let i = 0; i < len; i++) {
const dom = doms[i];
const type = dom.nodeName.toLowerCase();
if (~sourceType.indexOf(type)) {
const sourceSrc = dom.src || dom.href || '';
!sourceAddr[type] && (sourceAddr[type] = []);
sourceSrc && sourceAddr[type].push(sourceSrc);
}
const children = dom.children;
const childLen = (children && children.length) || 0;
if (childLen > 0) {
yield timeslice(iterationDOM(children, sourceAddr));
}
yield;
}
}
catch (error) {
const msg = `[SV - observeSource_iterationDOM]: ${JSON.stringify(error, Object.getOwnPropertyNames(error))}`;
catchError('js', msg);
}
};
}
else {
sortDOM(doms, sourceAddr);
}
}
return observer;
};
})();
function compatibleMark(tag) {
const ts = +((window.performance.now && ('' + window.performance.now())) || getTs());
markCache.push({ tag, ts });
}
function compatibleMeasure(tag, tagStart, tagEnd) {
const len = (markCache && markCache.length) || 0;
let startTime = 0;
let endTime = 0;
for (let i = 0; i < len; i++) {
const item = markCache[i];
if (item.tag === tagStart)
startTime = item.ts;
if (item.tag === tagEnd)
endTime = item.ts;
}
measureCache.push({ entryType: 'measure', name: tag, duration: Math.abs(endTime - startTime) });
}
function timingFilter(timing) {
if (timing === null || timing === undefined || isNaN(timing))
return 'N/A';
return timing > 0 ? timing : 0;
}
function randomRatio(ratio) {
if (ratio === 0)
return false;
if (Math.random() <= ratio)
return true;
return false;
}
class SV {
constructor(config) {
this.config = config;
}
updateConfig(newConfig) {
this.config = { ...this.config, ...newConfig };
}
getMemory() {
return getMemory();
}
getTiming() {
return getTiming();
}
async getSource() {
return await getSource(this.config);
}
async getExecTiming() {
return await getExecTiming();
}
async getPerformanceData() {
return getPerformanceData(this.config);
}
clearPerformance(clearType) {
return clearPerformance(clearType);
}
observeSource(target, callback, option) {
if (this.config && this.config.timeout) {
option = { timeout: this.config.timeout, ...option };
}
return observeSource(target, callback, option);
}
}const getEnvInfo = (function () {
if (typeof window === 'undefined')
return function () {
console.warn('Please call in browser environment');
return false;
};
return function () {
const ua = navigator.userAgent;
const scr = window.screen;
const page_url = location.href;
const referer = document.referrer;
const info = {
ts: getTs(),
os: '',
browser: '',
screen_size: (scr.width || 0) + 'x' + (scr.height || 0),
page_url,
referer,
device: ('ontouchstart' in window ? 'mobile' : 'pc'),
ua
};
const { isMobile, isAndroid, isIOS, isWindows, isMac, isWeixin, isSinaWeibo, isQQWeibo, isFacebook, isTwitter, isInstagram, isChrome, isFirefox, isGecko, isIE, isEdge, isOpera, isSafari, is360se, isQQ, isUCBrowser, isBaiduBrowser, isSougouBrowser, isLiebaoBrowser, isWebKit } = getUA(ua);
if (isMobile) {
if (isAndroid) {
info.os = 'android';
}