@fingerprintjs/fingerprintjs
Advanced tools
Comparing version 3.0.0 to 3.0.1
/** | ||
* FingerprintJS v3.0.0 - Copyright (c) FingerprintJS, Inc, 2020 (https://fingerprintjs.com) | ||
* FingerprintJS v3.0.1 - Copyright (c) FingerprintJS, Inc, 2020 (https://fingerprintjs.com) | ||
* Licensed under the MIT (http://www.opensource.org/licenses/mit-license.php) license. | ||
@@ -139,3 +139,4 @@ * | ||
var c2 = [0x4cf5ad43, 0x2745937f]; | ||
for (var i = 0; i < bytes; i = i + 16) { | ||
var i; | ||
for (i = 0; i < bytes; i = i + 16) { | ||
k1 = [ | ||
@@ -247,8 +248,8 @@ (key.charCodeAt(i + 4) & 0xff) | | ||
var version = "3.0.0"; | ||
var version = "3.0.1"; | ||
function requestIdleCallbackIfAvailable(fallbackTimeout) { | ||
return new Promise((resolve) => { | ||
return new Promise(function (resolve) { | ||
if (window.requestIdleCallback) { | ||
window.requestIdleCallback(() => resolve()); | ||
window.requestIdleCallback(function () { return resolve(); }); | ||
} | ||
@@ -268,3 +269,3 @@ else { | ||
function includes(haystack, needle) { | ||
for (let i = 0, l = haystack.length; i < l; ++i) { | ||
for (var i = 0, l = haystack.length; i < l; ++i) { | ||
if (haystack[i] === needle) { | ||
@@ -291,65 +292,82 @@ return true; | ||
} | ||
/** | ||
* Be careful, NaN can return | ||
*/ | ||
function toFloat(value) { | ||
if (typeof value === 'number') { | ||
return value; | ||
} | ||
return parseFloat(value); | ||
} | ||
function countTruthy(values) { | ||
return values.reduce((sum, value) => sum + (value ? 1 : 0), 0); | ||
return values.reduce(function (sum, value) { return sum + (value ? 1 : 0); }, 0); | ||
} | ||
const n = navigator; | ||
const w = window; | ||
var n = navigator; | ||
var w = window; | ||
function isAudioParam(value) { | ||
return value && typeof value.setValueAtTime === 'function'; | ||
} | ||
// Inspired by and based on https://github.com/cozylife/audio-fingerprint | ||
function getAudioFingerprint() { | ||
return tslib.__awaiter(this, void 0, void 0, function* () { | ||
// On iOS 11, audio context can only be used in response to user interaction. | ||
// We require users to explicitly enable audio fingerprinting on iOS 11. | ||
// See https://stackoverflow.com/questions/46363048/onaudioprocess-not-called-on-ios11#46534088 | ||
if (n.userAgent.match(/OS 11.+Version\/11.+Safari/)) { | ||
// See comment for excludeUserAgent and https://stackoverflow.com/questions/46363048/onaudioprocess-not-called-on-ios11#46534088 | ||
return -1; | ||
} | ||
const AudioContext = w.OfflineAudioContext || w.webkitOfflineAudioContext; | ||
if (!AudioContext) { | ||
return -2; | ||
} | ||
const context = new AudioContext(1, 44100, 44100); | ||
const oscillator = context.createOscillator(); | ||
oscillator.type = 'triangle'; | ||
oscillator.frequency.setValueAtTime(10000, context.currentTime); | ||
const compressor = context.createDynamicsCompressor(); | ||
for (const [param, value] of [ | ||
['threshold', -50], | ||
['knee', 40], | ||
['ratio', 12], | ||
['reduction', -20], | ||
['attack', 0], | ||
['release', 0.25], | ||
]) { | ||
if (typeof compressor[param].setValueAtTime === 'function') { | ||
compressor[param].setValueAtTime(value, context.currentTime); | ||
return tslib.__awaiter(this, void 0, void 0, function () { | ||
var AudioContext, context, oscillator, compressor, _i, _a, _b, name_1, value, param; | ||
return tslib.__generator(this, function (_c) { | ||
// On iOS 11, audio context can only be used in response to user interaction. | ||
// We require users to explicitly enable audio fingerprinting on iOS 11. | ||
// See https://stackoverflow.com/questions/46363048/onaudioprocess-not-called-on-ios11#46534088 | ||
if (n.userAgent.match(/OS 11.+Version\/11.+Safari/)) { | ||
// See comment for excludeUserAgent and https://stackoverflow.com/questions/46363048/onaudioprocess-not-called-on-ios11#46534088 | ||
return [2 /*return*/, -1]; | ||
} | ||
} | ||
oscillator.connect(compressor); | ||
compressor.connect(context.destination); | ||
oscillator.start(0); | ||
context.startRendering(); | ||
return new Promise((resolve) => { | ||
const audioTimeoutId = setTimeout(() => { | ||
context.oncomplete = () => { }; | ||
resolve(-3); | ||
}, 1000); | ||
context.oncomplete = (event) => { | ||
let afp; | ||
try { | ||
clearTimeout(audioTimeoutId); | ||
afp = event.renderedBuffer | ||
.getChannelData(0) | ||
.slice(4500, 5000) | ||
.reduce((acc, val) => acc + Math.abs(val), 0); | ||
oscillator.disconnect(); | ||
compressor.disconnect(); | ||
AudioContext = w.OfflineAudioContext || w.webkitOfflineAudioContext; | ||
if (!AudioContext) { | ||
return [2 /*return*/, -2]; | ||
} | ||
context = new AudioContext(1, 44100, 44100); | ||
oscillator = context.createOscillator(); | ||
oscillator.type = 'triangle'; | ||
oscillator.frequency.setValueAtTime(10000, context.currentTime); | ||
compressor = context.createDynamicsCompressor(); | ||
for (_i = 0, _a = [ | ||
['threshold', -50], | ||
['knee', 40], | ||
['ratio', 12], | ||
['reduction', -20], | ||
['attack', 0], | ||
['release', 0.25], | ||
]; _i < _a.length; _i++) { | ||
_b = _a[_i], name_1 = _b[0], value = _b[1]; | ||
param = compressor[name_1]; | ||
if (isAudioParam(param)) { | ||
param.setValueAtTime(value, context.currentTime); | ||
} | ||
catch (error) { | ||
resolve(-4); | ||
return; | ||
} | ||
resolve(afp); | ||
}; | ||
} | ||
oscillator.connect(compressor); | ||
compressor.connect(context.destination); | ||
oscillator.start(0); | ||
context.startRendering(); | ||
return [2 /*return*/, new Promise(function (resolve) { | ||
var audioTimeoutId = setTimeout(function () { | ||
context.oncomplete = null; | ||
resolve(-3); | ||
}, 1000); | ||
context.oncomplete = function (event) { | ||
var afp; | ||
try { | ||
clearTimeout(audioTimeoutId); | ||
afp = event.renderedBuffer | ||
.getChannelData(0) | ||
.slice(4500, 5000) | ||
.reduce(function (acc, val) { return acc + Math.abs(val); }, 0); | ||
oscillator.disconnect(); | ||
compressor.disconnect(); | ||
} | ||
catch (error) { | ||
resolve(-4); | ||
return; | ||
} | ||
resolve(afp); | ||
}; | ||
})]; | ||
}); | ||
@@ -359,8 +377,13 @@ }); | ||
const d = document; | ||
// a font will be compared against all the three default fonts. | ||
// and if it doesn't match all 3 then that font is not available. | ||
const baseFonts = ['monospace', 'sans-serif', 'serif']; | ||
const fontList = [ | ||
// this is android-specific font from "Roboto" family | ||
var d = document; | ||
// We use m or w because these two characters take up the maximum width. | ||
// And we use a LLi so that the same matching fonts can get separated. | ||
var testString = 'mmMwWLliI0O&1'; | ||
// We test using 48px font size, we may use any size. I guess larger the better. | ||
var testSize = '48px'; | ||
// A font will be compared against all the three default fonts. | ||
// And if it doesn't match all 3 then that font is not available. | ||
var baseFonts = ['monospace', 'sans-serif', 'serif']; | ||
var fontList = [ | ||
// This is android-specific font from "Roboto" family | ||
'sans-serif-thin', | ||
@@ -419,3 +442,4 @@ 'ARNO PRO', | ||
]; | ||
const fontResetStyles = { | ||
var fontSpanStyle = { | ||
// CSS font reset to reset external styles | ||
fontStyle: 'normal', | ||
@@ -433,46 +457,37 @@ fontWeight: 'normal', | ||
wordSpacing: 'normal', | ||
// We need this css as in some weird browser this span elements shows up for a microSec which creates | ||
// a bad user experience | ||
position: 'absolute', | ||
left: '-9999px', | ||
fontSize: testSize, | ||
}; | ||
// we use m or w because these two characters take up the maximum width. | ||
// And we use a LLi so that the same matching fonts can get separated | ||
const testString = 'mmMwWLliI0O&1'; | ||
// we test using 48px font size, we may use any size. I guess larger the better. | ||
const testSize = '48px'; | ||
// kudos to http://www.lalit.org/lab/javascript-css-font-detect/ | ||
function getFonts() { | ||
const h = d.body; | ||
var h = d.body; | ||
// div to load spans for the base fonts | ||
const baseFontsDiv = d.createElement('div'); | ||
var baseFontsDiv = d.createElement('div'); | ||
// div to load spans for the fonts to detect | ||
const fontsDiv = d.createElement('div'); | ||
const defaultWidth = {}; | ||
const defaultHeight = {}; | ||
var fontsDiv = d.createElement('div'); | ||
var defaultWidth = {}; | ||
var defaultHeight = {}; | ||
// creates a span where the fonts will be loaded | ||
const createSpan = () => { | ||
const s = d.createElement('span'); | ||
Object.assign(s.style, | ||
// css font reset to reset external styles | ||
fontResetStyles, | ||
/* | ||
* We need this css as in some weird browser this | ||
* span elements shows up for a microSec which creates a | ||
* bad user experience | ||
*/ | ||
{ | ||
position: 'absolute', | ||
left: '-9999px', | ||
fontSize: testSize, | ||
}); | ||
s.textContent = testString; | ||
return s; | ||
var createSpan = function () { | ||
var span = d.createElement('span'); | ||
span.textContent = testString; | ||
for (var _i = 0, _a = Object.keys(fontSpanStyle); _i < _a.length; _i++) { | ||
var prop = _a[_i]; | ||
span.style[prop] = fontSpanStyle[prop]; | ||
} | ||
return span; | ||
}; | ||
// creates a span and load the font to detect and a base font for fallback | ||
const createSpanWithFonts = (fontToDetect, baseFont) => { | ||
const s = createSpan(); | ||
s.style.fontFamily = `'${fontToDetect}',${baseFont}`; | ||
var createSpanWithFonts = function (fontToDetect, baseFont) { | ||
var s = createSpan(); | ||
s.style.fontFamily = "'" + fontToDetect + "'," + baseFont; | ||
return s; | ||
}; | ||
// creates spans for the base fonts and adds them to baseFontsDiv | ||
const initializeBaseFontsSpans = () => { | ||
return baseFonts.map((baseFont) => { | ||
const s = createSpan(); | ||
var initializeBaseFontsSpans = function () { | ||
return baseFonts.map(function (baseFont) { | ||
var s = createSpan(); | ||
s.style.fontFamily = baseFont; | ||
@@ -484,11 +499,15 @@ baseFontsDiv.appendChild(s); | ||
// creates spans for the fonts to detect and adds them to fontsDiv | ||
const initializeFontsSpans = () => { | ||
var initializeFontsSpans = function () { | ||
// Stores {fontName : [spans for that font]} | ||
const spans = {}; | ||
for (const font of fontList) { | ||
spans[font] = baseFonts.map((baseFont) => { | ||
const s = createSpanWithFonts(font, baseFont); | ||
var spans = {}; | ||
var _loop_1 = function (font) { | ||
spans[font] = baseFonts.map(function (baseFont) { | ||
var s = createSpanWithFonts(font, baseFont); | ||
fontsDiv.appendChild(s); | ||
return s; | ||
}); | ||
}; | ||
for (var _i = 0, fontList_1 = fontList; _i < fontList_1.length; _i++) { | ||
var font = fontList_1[_i]; | ||
_loop_1(font); | ||
} | ||
@@ -498,12 +517,14 @@ return spans; | ||
// checks if a font is available | ||
const isFontAvailable = (fontSpans) => { | ||
return baseFonts.some(((baseFont, baseFontIndex) => (fontSpans[baseFontIndex].offsetWidth !== defaultWidth[baseFont] || | ||
fontSpans[baseFontIndex].offsetHeight !== defaultHeight[baseFont]))); | ||
var isFontAvailable = function (fontSpans) { | ||
return baseFonts.some(function (baseFont, baseFontIndex) { | ||
return fontSpans[baseFontIndex].offsetWidth !== defaultWidth[baseFont] || | ||
fontSpans[baseFontIndex].offsetHeight !== defaultHeight[baseFont]; | ||
}); | ||
}; | ||
// create spans for base fonts | ||
const baseFontsSpans = initializeBaseFontsSpans(); | ||
var baseFontsSpans = initializeBaseFontsSpans(); | ||
// add the spans to the DOM | ||
h.appendChild(baseFontsDiv); | ||
// get the default width for the three base fonts | ||
for (let index = 0, length = baseFonts.length; index < length; index++) { | ||
for (var index = 0, length_1 = baseFonts.length; index < length_1; index++) { | ||
defaultWidth[baseFonts[index]] = baseFontsSpans[index].offsetWidth; // width for the default font | ||
@@ -513,8 +534,8 @@ defaultHeight[baseFonts[index]] = baseFontsSpans[index].offsetHeight; // height for the default font | ||
// create spans for fonts to detect | ||
const fontsSpans = initializeFontsSpans(); | ||
var fontsSpans = initializeFontsSpans(); | ||
// add all the spans to the DOM | ||
h.appendChild(fontsDiv); | ||
// check available fonts | ||
const available = []; | ||
for (let i = 0, l = fontList.length; i < l; i++) { | ||
var available = []; | ||
for (var i = 0, l = fontList.length; i < l; i++) { | ||
if (isFontAvailable(fontsSpans[fontList[i]])) { | ||
@@ -530,15 +551,129 @@ available.push(fontList[i]); | ||
/* | ||
* Functions to help with browser features | ||
*/ | ||
var w$1 = window; | ||
var n$1 = navigator; | ||
var d$1 = document; | ||
/** | ||
* Checks whether the browser is based on Trident (the Internet Explorer engine) without using user-agent. | ||
* | ||
* Warning for package users: | ||
* This function is out of Semantic Versioning, i.e. can change unexpectedly. Usage is at your own risk. | ||
*/ | ||
function isTrident() { | ||
// The properties are checked to be in IE 10, IE 11 and not to be in other browsers in October 2020 | ||
return (countTruthy([ | ||
'MSCSSMatrix' in w$1, | ||
'msSetImmediate' in w$1, | ||
'msIndexedDB' in w$1, | ||
'msMaxTouchPoints' in n$1, | ||
'msPointerEnabled' in n$1, | ||
]) >= 4); | ||
} | ||
/** | ||
* Checks whether the browser is based on EdgeHTML (the pre-Chromium Edge engine) without using user-agent. | ||
* | ||
* Warning for package users: | ||
* This function is out of Semantic Versioning, i.e. can change unexpectedly. Usage is at your own risk. | ||
*/ | ||
function isEdgeHTML() { | ||
// Based on research in October 2020 | ||
return (countTruthy(['msWriteProfilerMark' in w$1, 'MSStream' in w$1, 'msLaunchUri' in n$1, 'msSaveBlob' in n$1]) >= 3 && | ||
!isTrident()); | ||
} | ||
/** | ||
* Checks whether the browser is based on Chromium without using user-agent. | ||
* | ||
* Warning for package users: | ||
* This function is out of Semantic Versioning, i.e. can change unexpectedly. Usage is at your own risk. | ||
*/ | ||
function isChromium() { | ||
// Based on research in October 2020. Tested to detect Chromium 42-86. | ||
return (countTruthy([ | ||
'webkitPersistentStorage' in n$1, | ||
'webkitTemporaryStorage' in n$1, | ||
n$1.vendor.indexOf('Google') === 0, | ||
'webkitResolveLocalFileSystemURL' in w$1, | ||
'BatteryManager' in w$1, | ||
'webkitMediaStream' in w$1, | ||
'webkitSpeechGrammar' in w$1, | ||
]) >= 5); | ||
} | ||
/** | ||
* Checks whether the browser is based on mobile or desktop Safari without using user-agent. | ||
* All iOS browsers use WebKit (the Safari engine). | ||
* | ||
* Warning for package users: | ||
* This function is out of Semantic Versioning, i.e. can change unexpectedly. Usage is at your own risk. | ||
*/ | ||
function isWebKit() { | ||
// Based on research in September 2020 | ||
return (countTruthy([ | ||
'ApplePayError' in w$1, | ||
'CSSPrimitiveValue' in w$1, | ||
'Counter' in w$1, | ||
n$1.vendor.indexOf('Apple') === 0, | ||
'getStorageUpdates' in n$1, | ||
'WebKitMediaKeys' in w$1, | ||
]) >= 4); | ||
} | ||
/** | ||
* Checks whether the WebKit browser is a desktop Safari. | ||
* | ||
* Warning for package users: | ||
* This function is out of Semantic Versioning, i.e. can change unexpectedly. Usage is at your own risk. | ||
*/ | ||
function isDesktopSafari() { | ||
return 'safari' in w$1; | ||
} | ||
/** | ||
* Checks whether the browser is based on Gecko (Firefox engine) without using user-agent. | ||
* | ||
* Warning for package users: | ||
* This function is out of Semantic Versioning, i.e. can change unexpectedly. Usage is at your own risk. | ||
*/ | ||
function isGecko() { | ||
var _a; | ||
// Based on research in September 2020 | ||
return (countTruthy([ | ||
'buildID' in n$1, | ||
((_a = d$1.documentElement) === null || _a === void 0 ? void 0 : _a.style) && 'MozAppearance' in d$1.documentElement.style, | ||
'MediaRecorderErrorEvent' in w$1, | ||
'mozInnerScreenX' in w$1, | ||
'CSSMozDocumentRule' in w$1, | ||
'CanvasCaptureMediaStream' in w$1, | ||
]) >= 4); | ||
} | ||
/** | ||
* Checks whether the browser is based on Chromium version ā„86 without using user-agent. | ||
* It doesn't check that the browser is based on Chromium, there is a separate function for this. | ||
*/ | ||
function isChromium86OrNewer() { | ||
// Checked in Chrome 85 vs Chrome 86 both on desktop and Android | ||
return (countTruthy([ | ||
!('MediaSettingsRange' in w$1), | ||
'RTCEncodedAudioFrame' in w$1, | ||
'' + w$1.Intl === '[object Intl]', | ||
'' + w$1.Reflect === '[object Reflect]', | ||
]) >= 3); | ||
} | ||
function getPlugins() { | ||
if (isTrident()) { | ||
return []; | ||
} | ||
if (!navigator.plugins) { | ||
return undefined; | ||
} | ||
const plugins = []; | ||
var plugins = []; | ||
// Safari 10 doesn't support iterating navigator.plugins with for...of | ||
for (let i = 0; i < navigator.plugins.length; ++i) { | ||
const plugin = navigator.plugins[i]; | ||
for (var i = 0; i < navigator.plugins.length; ++i) { | ||
var plugin = navigator.plugins[i]; | ||
if (!plugin) { | ||
continue; | ||
} | ||
const mimeTypes = []; | ||
for (const mimeType of plugin) { | ||
var mimeTypes = []; | ||
for (var j = 0; j < plugin.length; ++j) { | ||
var mimeType = plugin[j]; | ||
mimeTypes.push({ | ||
@@ -552,3 +687,3 @@ type: mimeType.type, | ||
description: plugin.description, | ||
mimeTypes, | ||
mimeTypes: mimeTypes, | ||
}); | ||
@@ -560,3 +695,3 @@ } | ||
function makeCanvasContext() { | ||
const canvas = document.createElement('canvas'); | ||
var canvas = document.createElement('canvas'); | ||
canvas.width = 240; | ||
@@ -577,3 +712,3 @@ canvas.height = 140; | ||
function getCanvasFingerprint() { | ||
const [canvas, context] = makeCanvasContext(); | ||
var _a = makeCanvasContext(), canvas = _a[0], context = _a[1]; | ||
if (!isSupported(canvas, context)) { | ||
@@ -587,3 +722,3 @@ return { winding: false, data: '' }; | ||
context.rect(2, 2, 6, 6); | ||
const winding = !context.isPointInPath(5, 5, 'evenodd'); | ||
var winding = !context.isPointInPath(5, 5, 'evenodd'); | ||
context.textBaseline = 'alphabetic'; | ||
@@ -599,3 +734,3 @@ context.fillStyle = '#f60'; | ||
// context.fillText("CwēØm fjordbank \ud83d\ude03 gly", 2, 15) | ||
const printedText = 'Cwm fjordbank \ud83d\ude03 gly'; | ||
var printedText = 'Cwm fjordbank \ud83d\ude03 gly'; | ||
context.fillText(printedText, 2, 15); | ||
@@ -632,9 +767,9 @@ context.fillStyle = 'rgba(102, 204, 0, 0.2)'; | ||
return { | ||
winding, | ||
data: save(canvas) | ||
winding: winding, | ||
data: save(canvas), | ||
}; | ||
} | ||
const n$1 = navigator; | ||
const w$1 = window; | ||
var n$2 = navigator; | ||
var w$2 = window; | ||
/** | ||
@@ -648,9 +783,9 @@ * This is a crude and primitive touch screen detection. It's not possible to currently reliably detect the availability | ||
function getTouchSupport() { | ||
let maxTouchPoints = 0; | ||
let touchEvent; | ||
if (n$1.maxTouchPoints !== undefined) { | ||
maxTouchPoints = toInt(n$1.maxTouchPoints); | ||
var maxTouchPoints = 0; | ||
var touchEvent; | ||
if (n$2.maxTouchPoints !== undefined) { | ||
maxTouchPoints = toInt(n$2.maxTouchPoints); | ||
} | ||
else if (n$1.msMaxTouchPoints !== undefined) { | ||
maxTouchPoints = n$1.msMaxTouchPoints; | ||
else if (n$2.msMaxTouchPoints !== undefined) { | ||
maxTouchPoints = n$2.msMaxTouchPoints; | ||
} | ||
@@ -664,7 +799,7 @@ try { | ||
} | ||
const touchStart = 'ontouchstart' in w$1; | ||
var touchStart = 'ontouchstart' in w$2; | ||
return { | ||
maxTouchPoints, | ||
touchEvent, | ||
touchStart, | ||
maxTouchPoints: maxTouchPoints, | ||
touchEvent: touchEvent, | ||
touchStart: touchStart, | ||
}; | ||
@@ -677,85 +812,6 @@ } | ||
/* | ||
* Functions to help with browser features | ||
*/ | ||
const w$2 = window; | ||
const n$2 = navigator; | ||
const d$1 = document; | ||
/** | ||
* Checks whether the browser is Internet Explorer or pre-Chromium Edge without using user-agent. | ||
* | ||
* Warning for package users: | ||
* This function is out of Semantic Versioning, i.e. can change unexpectedly. Usage is at your own risk. | ||
*/ | ||
function isIEOrOldEdge() { | ||
// The properties are checked to be in IE 10, IE 11 and Edge 18 and not to be in other browsers | ||
return countTruthy([ | ||
'msWriteProfilerMark' in w$2, | ||
'msLaunchUri' in n$2, | ||
'msSaveBlob' in n$2, | ||
]) >= 2; | ||
} | ||
/** | ||
* Checks whether the browser is based on Chromium without using user-agent. | ||
* | ||
* Warning for package users: | ||
* This function is out of Semantic Versioning, i.e. can change unexpectedly. Usage is at your own risk. | ||
*/ | ||
function isChromium() { | ||
// Based on research in September 2020 | ||
return countTruthy([ | ||
'userActivation' in n$2, | ||
'mediaSession' in n$2, | ||
n$2.vendor.indexOf('Google') === 0, | ||
'BackgroundFetchManager' in w$2, | ||
'BatteryManager' in w$2, | ||
'webkitMediaStream' in w$2, | ||
'webkitSpeechGrammar' in w$2, | ||
]) >= 5; | ||
} | ||
/** | ||
* Checks whether the WebKit browser is a desktop Safari. | ||
* | ||
* Warning for package users: | ||
* This function is out of Semantic Versioning, i.e. can change unexpectedly. Usage is at your own risk. | ||
*/ | ||
function isDesktopSafari() { | ||
return 'safari' in w$2; | ||
} | ||
/** | ||
* Checks whether the browser is based on Gecko (Firefox engine) without using user-agent. | ||
* | ||
* Warning for package users: | ||
* This function is out of Semantic Versioning, i.e. can change unexpectedly. Usage is at your own risk. | ||
*/ | ||
function isGecko() { | ||
var _a; | ||
// Based on research in September 2020 | ||
return countTruthy([ | ||
'buildID' in n$2, | ||
((_a = d$1.documentElement) === null || _a === void 0 ? void 0 : _a.style) && 'MozAppearance' in d$1.documentElement.style, | ||
'MediaRecorderErrorEvent' in w$2, | ||
'mozInnerScreenX' in w$2, | ||
'CSSMozDocumentRule' in w$2, | ||
'CanvasCaptureMediaStream' in w$2, | ||
]) >= 4; | ||
} | ||
/** | ||
* Checks whether the browser is based on Chromium version ā„86 without using user-agent. | ||
* It doesn't check that the browser is based on Chromium, there is a separate function for this. | ||
*/ | ||
function isChromium86OrNewer() { | ||
// Checked in Chrome 85 vs Chrome 86 both on desktop and Android | ||
return countTruthy([ | ||
!('MediaSettingsRange' in w$2), | ||
!('PhotoCapabilities' in w$2), | ||
'RTCEncodedAudioFrame' in w$2, | ||
('' + w$2.Intl) === '[object Intl]', | ||
]) >= 2; | ||
} | ||
const n$3 = navigator; | ||
var n$3 = navigator; | ||
function getLanguages() { | ||
const result = []; | ||
const language = n$3.language || n$3.userLanguage || n$3.browserLanguage || n$3.systemLanguage; | ||
var result = []; | ||
var language = n$3.language || n$3.userLanguage || n$3.browserLanguage || n$3.systemLanguage; | ||
if (language !== undefined) { | ||
@@ -772,3 +828,3 @@ result.push([language]); | ||
else if (typeof n$3.languages === 'string') { | ||
const languages = n$3.languages; | ||
var languages = n$3.languages; | ||
if (languages) { | ||
@@ -789,7 +845,7 @@ result.push(languages.split(',')); | ||
const w$3 = window; | ||
var w$3 = window; | ||
function getScreenResolution() { | ||
// Some browsers return screen resolution as strings, e.g. "1200", instead of a number, e.g. 1200. | ||
// I suspect it's done by certain plugins that randomize browser properties to prevent fingerprinting. | ||
const dimensions = [toInt(w$3.screen.width), toInt(w$3.screen.height)]; | ||
var dimensions = [toInt(w$3.screen.width), toInt(w$3.screen.height)]; | ||
dimensions.sort().reverse(); | ||
@@ -799,3 +855,3 @@ return dimensions; | ||
const w$4 = window; | ||
var w$4 = window; | ||
function getAvailableScreenResolution() { | ||
@@ -805,3 +861,3 @@ if (w$4.screen.availWidth && w$4.screen.availHeight) { | ||
// I suspect it's done by certain plugins that randomize browser properties to prevent fingerprinting. | ||
const dimensions = [toInt(w$4.screen.availWidth), toInt(w$4.screen.availHeight)]; | ||
var dimensions = [toInt(w$4.screen.availWidth), toInt(w$4.screen.availHeight)]; | ||
dimensions.sort().reverse(); | ||
@@ -816,3 +872,3 @@ return dimensions; | ||
// sometimes hardware concurrency is a string | ||
const concurrency = toInt(navigator.hardwareConcurrency); | ||
var concurrency = toInt(navigator.hardwareConcurrency); | ||
return isNaN(concurrency) ? 1 : concurrency; | ||
@@ -826,6 +882,13 @@ } | ||
function getTimezoneOffset() { | ||
return new Date().getTimezoneOffset(); | ||
var currentYear = new Date().getFullYear(); | ||
// The timezone offset may change over time due to daylight saving time (DST) shifts. | ||
// The non-DST timezone offset is used as the result timezone offset. | ||
// Since the DST season differs in the northern and the southern hemispheres, | ||
// both January and July timezones offsets are considered. | ||
return Math.max( | ||
// `getTimezoneOffset` returns a number as a string in some unidentified cases | ||
toFloat(new Date(currentYear, 0, 1).getTimezoneOffset()), toFloat(new Date(currentYear, 6, 1).getTimezoneOffset())); | ||
} | ||
const w$5 = window; | ||
var w$5 = window; | ||
function getTimezone() { | ||
@@ -863,3 +926,3 @@ var _a; | ||
// visitor identifier in normal and private modes. | ||
if (isIEOrOldEdge()) { | ||
if (isTrident() || isEdgeHTML()) { | ||
return undefined; | ||
@@ -923,3 +986,3 @@ } | ||
const d$2 = document; | ||
var d$2 = document; | ||
/** | ||
@@ -933,2 +996,6 @@ * navigator.cookieEnabled cannot detect custom or nuanced cookie blocking configurations. For example, when blocking | ||
function areCookiesEnabled() { | ||
// Taken from here: https://github.com/Modernizr/Modernizr/blob/master/feature-detects/cookies.js | ||
// navigator.cookieEnabled cannot detect custom or nuanced cookie blocking configurations. For example, when blocking | ||
// cookies via the Advanced Privacy Settings in IE9, it always returns true. And there have been issues in the past | ||
// with site-specific exceptions. Don't rely on it. | ||
// try..catch because some in situations `document.cookie` is exposed but throws a | ||
@@ -940,3 +1007,3 @@ // SecurityError if you try to access it; e.g. documents created from data URIs | ||
d$2.cookie = 'cookietest=1'; | ||
const result = d$2.cookie.indexOf('cookietest=') !== -1; | ||
var result = d$2.cookie.indexOf('cookietest=') !== -1; | ||
// Delete cookie | ||
@@ -957,3 +1024,3 @@ d$2.cookie = 'cookietest=1; expires=Thu, 01-Jan-1970 00:00:01 GMT'; | ||
*/ | ||
const sources = { | ||
var sources = { | ||
// Expected errors and default values must be handled inside the functions | ||
@@ -997,22 +1064,42 @@ osCpu: getOsCpu, | ||
function getComponents(sources, sourceOptions, excludeSources) { | ||
return tslib.__awaiter(this, void 0, void 0, function* () { | ||
let timestamp = Date.now(); | ||
const components = {}; | ||
for (const sourceKey of Object.keys(sources)) { | ||
if (!excludes(excludeSources, sourceKey)) { | ||
continue; | ||
return tslib.__awaiter(this, void 0, void 0, function () { | ||
var timestamp, components, _i, _a, sourceKey, result, error_1, nextTimestamp; | ||
var _b; | ||
return tslib.__generator(this, function (_c) { | ||
switch (_c.label) { | ||
case 0: | ||
timestamp = Date.now(); | ||
components = {}; | ||
_i = 0, _a = Object.keys(sources); | ||
_c.label = 1; | ||
case 1: | ||
if (!(_i < _a.length)) return [3 /*break*/, 7]; | ||
sourceKey = _a[_i]; | ||
if (!excludes(excludeSources, sourceKey)) { | ||
return [3 /*break*/, 6]; | ||
} | ||
result = void 0; | ||
_c.label = 2; | ||
case 2: | ||
_c.trys.push([2, 4, , 5]); | ||
_b = {}; | ||
return [4 /*yield*/, sources[sourceKey](sourceOptions)]; | ||
case 3: | ||
result = (_b.value = _c.sent(), _b); | ||
return [3 /*break*/, 5]; | ||
case 4: | ||
error_1 = _c.sent(); | ||
result = error_1 && typeof error_1 === 'object' && 'message' in error_1 ? { error: error_1 } : { error: { message: error_1 } }; | ||
return [3 /*break*/, 5]; | ||
case 5: | ||
nextTimestamp = Date.now(); | ||
components[sourceKey] = tslib.__assign(tslib.__assign({}, result), { duration: nextTimestamp - timestamp }); // TypeScript has beaten me here | ||
timestamp = nextTimestamp; | ||
_c.label = 6; | ||
case 6: | ||
_i++; | ||
return [3 /*break*/, 1]; | ||
case 7: return [2 /*return*/, components]; | ||
} | ||
let result; | ||
let nextTimestamp; | ||
try { | ||
result = { value: yield sources[sourceKey](sourceOptions) }; | ||
} | ||
catch (error) { | ||
result = error && typeof error === 'object' && 'message' in error ? { error } : { error: { message: error } }; | ||
} | ||
nextTimestamp = Date.now(); | ||
components[sourceKey] = Object.assign(Object.assign({}, result), { duration: nextTimestamp - timestamp }); // TypeScript has beaten me here | ||
timestamp = nextTimestamp; | ||
} | ||
return components; | ||
}); | ||
}); | ||
@@ -1028,7 +1115,8 @@ } | ||
function componentsToCanonicalString(components) { | ||
let result = ''; | ||
for (const componentKey of Object.keys(components)) { | ||
const component = components[componentKey]; | ||
const value = component.error ? 'error' : JSON.stringify(component.value); | ||
result += `${result ? '|' : ''}${componentKey.replace(/([:|\\])/g, '\\$1')}:${value}`; | ||
var result = ''; | ||
for (var _i = 0, _a = Object.keys(components); _i < _a.length; _i++) { | ||
var componentKey = _a[_i]; | ||
var component = components[componentKey]; | ||
var value = component.error ? 'error' : JSON.stringify(component.value); | ||
result += "" + (result ? '|' : '') + componentKey.replace(/([:|\\])/g, '\\$1') + ":" + value; | ||
} | ||
@@ -1038,6 +1126,6 @@ return result; | ||
function componentsToDebugString(components) { | ||
return JSON.stringify(components, (_key, value) => { | ||
return JSON.stringify(components, function (_key, value) { | ||
var _a; | ||
if (value instanceof Error) { | ||
return Object.assign(Object.assign({}, value), { message: value.message, stack: (_a = value.stack) === null || _a === void 0 ? void 0 : _a.split('\n') }); | ||
return tslib.__assign(tslib.__assign({}, value), { message: value.message, stack: (_a = value.stack) === null || _a === void 0 ? void 0 : _a.split('\n') }); | ||
} | ||
@@ -1055,6 +1143,6 @@ return value; | ||
function makeLazyGetResult(components) { | ||
let visitorIdCache; | ||
var visitorIdCache; | ||
// A plain class isn't used because its getters and setters aren't enumerable. | ||
return { | ||
components, | ||
components: components, | ||
get visitorId() { | ||
@@ -1075,34 +1163,51 @@ if (visitorIdCache === undefined) { | ||
*/ | ||
class OpenAgent { | ||
var OpenAgent = /** @class */ (function () { | ||
function OpenAgent() { | ||
} | ||
/** | ||
* @inheritDoc | ||
*/ | ||
get(options = {}) { | ||
return tslib.__awaiter(this, void 0, void 0, function* () { | ||
const components = yield getBuiltinComponents(); | ||
const result = makeLazyGetResult(components); | ||
if (options.debug) { | ||
console.log(`Copy the text below to get the debug data: | ||
\`\`\` | ||
version: ${version} | ||
getOptions: ${JSON.stringify(options, undefined, 2)} | ||
visitorId: ${result.visitorId} | ||
components: ${componentsToDebugString(components)} | ||
\`\`\``); | ||
} | ||
return result; | ||
OpenAgent.prototype.get = function (options) { | ||
if (options === void 0) { options = {}; } | ||
return tslib.__awaiter(this, void 0, void 0, function () { | ||
var components, result; | ||
return tslib.__generator(this, function (_a) { | ||
switch (_a.label) { | ||
case 0: return [4 /*yield*/, getBuiltinComponents()]; | ||
case 1: | ||
components = _a.sent(); | ||
result = makeLazyGetResult(components); | ||
if (options.debug) { | ||
// console.log is ok here because it's under a debug clause | ||
// eslint-disable-next-line no-console | ||
console.log("Copy the text below to get the debug data:\n\n```\nversion: " + version + "\ngetOptions: " + JSON.stringify(options, undefined, 2) + "\nvisitorId: " + result.visitorId + "\ncomponents: " + componentsToDebugString(components) + "\n```"); | ||
} | ||
return [2 /*return*/, result]; | ||
} | ||
}); | ||
}); | ||
} | ||
} | ||
}; | ||
return OpenAgent; | ||
}()); | ||
/** | ||
* Builds an instance of Agent and waits a delay required for a proper operation. | ||
*/ | ||
function load({ delayFallback = 50 } = {}) { | ||
return tslib.__awaiter(this, void 0, void 0, function* () { | ||
// A delay is required to ensure consistent entropy components. | ||
// See https://github.com/fingerprintjs/fingerprintjs/issues/254 | ||
// and https://github.com/fingerprintjs/fingerprintjs/issues/307 | ||
yield requestIdleCallbackIfAvailable(delayFallback); | ||
return new OpenAgent(); | ||
function load(_a) { | ||
var _b = (_a === void 0 ? {} : _a).delayFallback, delayFallback = _b === void 0 ? 50 : _b; | ||
return tslib.__awaiter(this, void 0, void 0, function () { | ||
return tslib.__generator(this, function (_c) { | ||
switch (_c.label) { | ||
case 0: | ||
// A delay is required to ensure consistent entropy components. | ||
// See https://github.com/fingerprintjs/fingerprintjs/issues/254 | ||
// and https://github.com/fingerprintjs/fingerprintjs/issues/307 | ||
return [4 /*yield*/, requestIdleCallbackIfAvailable(delayFallback)]; | ||
case 1: | ||
// A delay is required to ensure consistent entropy components. | ||
// See https://github.com/fingerprintjs/fingerprintjs/issues/254 | ||
// and https://github.com/fingerprintjs/fingerprintjs/issues/307 | ||
_c.sent(); | ||
return [2 /*return*/, new OpenAgent()]; | ||
} | ||
}); | ||
}); | ||
@@ -1113,6 +1218,6 @@ } | ||
// It should contain all the public exported values. | ||
var index = { load, hashComponents, componentsToDebugString }; | ||
var index = { load: load, hashComponents: hashComponents, componentsToDebugString: componentsToDebugString }; | ||
// The exports below are for private usage. They may change unexpectedly. Use them at your own risk. | ||
/** Not documented, out of Semantic Versioning, usage is at your own risk */ | ||
const murmurX64Hash128 = x64hash128; | ||
var murmurX64Hash128 = x64hash128; | ||
@@ -1125,5 +1230,7 @@ exports.componentsToDebugString = componentsToDebugString; | ||
exports.isDesktopSafari = isDesktopSafari; | ||
exports.isEdgeHTML = isEdgeHTML; | ||
exports.isGecko = isGecko; | ||
exports.isIEOrOldEdge = isIEOrOldEdge; | ||
exports.isTrident = isTrident; | ||
exports.isWebKit = isWebKit; | ||
exports.load = load; | ||
exports.murmurX64Hash128 = murmurX64Hash128; |
/** | ||
* FingerprintJS v3.0.0 - Copyright (c) FingerprintJS, Inc, 2020 (https://fingerprintjs.com) | ||
* FingerprintJS v3.0.1 - Copyright (c) FingerprintJS, Inc, 2020 (https://fingerprintjs.com) | ||
* Licensed under the MIT (http://www.opensource.org/licenses/mit-license.php) license. | ||
@@ -247,3 +247,3 @@ * | ||
/** | ||
* Checks whether the browser is Internet Explorer or pre-Chromium Edge without using user-agent. | ||
* Checks whether the browser is based on Trident (the Internet Explorer engine) without using user-agent. | ||
* | ||
@@ -253,4 +253,11 @@ * Warning for package users: | ||
*/ | ||
declare function isIEOrOldEdge(): boolean; | ||
declare function isTrident(): boolean; | ||
/** | ||
* Checks whether the browser is based on EdgeHTML (the pre-Chromium Edge engine) without using user-agent. | ||
* | ||
* Warning for package users: | ||
* This function is out of Semantic Versioning, i.e. can change unexpectedly. Usage is at your own risk. | ||
*/ | ||
declare function isEdgeHTML(): boolean; | ||
/** | ||
* Checks whether the browser is based on Chromium without using user-agent. | ||
@@ -263,2 +270,10 @@ * | ||
/** | ||
* Checks whether the browser is based on mobile or desktop Safari without using user-agent. | ||
* All iOS browsers use WebKit (the Safari engine). | ||
* | ||
* Warning for package users: | ||
* This function is out of Semantic Versioning, i.e. can change unexpectedly. Usage is at your own risk. | ||
*/ | ||
declare function isWebKit(): boolean; | ||
/** | ||
* Checks whether the WebKit browser is a desktop Safari. | ||
@@ -287,2 +302,2 @@ * | ||
export default _default; | ||
export { Agent, BuiltinComponents, Component, GetOptions, GetResult, LoadOptions, SourcesToComponents, UnknownComponents, componentsToDebugString, getComponents, hashComponents, isChromium, isDesktopSafari, isGecko, isIEOrOldEdge, load, murmurX64Hash128 }; | ||
export { Agent, BuiltinComponents, Component, GetOptions, GetResult, LoadOptions, SourcesToComponents, UnknownComponents, componentsToDebugString, getComponents, hashComponents, isChromium, isDesktopSafari, isEdgeHTML, isGecko, isTrident, isWebKit, load, murmurX64Hash128 }; |
/** | ||
* FingerprintJS v3.0.0 - Copyright (c) FingerprintJS, Inc, 2020 (https://fingerprintjs.com) | ||
* FingerprintJS v3.0.1 - Copyright (c) FingerprintJS, Inc, 2020 (https://fingerprintjs.com) | ||
* Licensed under the MIT (http://www.opensource.org/licenses/mit-license.php) license. | ||
@@ -9,3 +9,3 @@ * | ||
import { __awaiter } from 'tslib'; | ||
import { __awaiter, __generator, __assign } from 'tslib'; | ||
@@ -136,3 +136,4 @@ /* | ||
var c2 = [0x4cf5ad43, 0x2745937f]; | ||
for (var i = 0; i < bytes; i = i + 16) { | ||
var i; | ||
for (i = 0; i < bytes; i = i + 16) { | ||
k1 = [ | ||
@@ -244,8 +245,8 @@ (key.charCodeAt(i + 4) & 0xff) | | ||
var version = "3.0.0"; | ||
var version = "3.0.1"; | ||
function requestIdleCallbackIfAvailable(fallbackTimeout) { | ||
return new Promise((resolve) => { | ||
return new Promise(function (resolve) { | ||
if (window.requestIdleCallback) { | ||
window.requestIdleCallback(() => resolve()); | ||
window.requestIdleCallback(function () { return resolve(); }); | ||
} | ||
@@ -265,3 +266,3 @@ else { | ||
function includes(haystack, needle) { | ||
for (let i = 0, l = haystack.length; i < l; ++i) { | ||
for (var i = 0, l = haystack.length; i < l; ++i) { | ||
if (haystack[i] === needle) { | ||
@@ -288,65 +289,82 @@ return true; | ||
} | ||
/** | ||
* Be careful, NaN can return | ||
*/ | ||
function toFloat(value) { | ||
if (typeof value === 'number') { | ||
return value; | ||
} | ||
return parseFloat(value); | ||
} | ||
function countTruthy(values) { | ||
return values.reduce((sum, value) => sum + (value ? 1 : 0), 0); | ||
return values.reduce(function (sum, value) { return sum + (value ? 1 : 0); }, 0); | ||
} | ||
const n = navigator; | ||
const w = window; | ||
var n = navigator; | ||
var w = window; | ||
function isAudioParam(value) { | ||
return value && typeof value.setValueAtTime === 'function'; | ||
} | ||
// Inspired by and based on https://github.com/cozylife/audio-fingerprint | ||
function getAudioFingerprint() { | ||
return __awaiter(this, void 0, void 0, function* () { | ||
// On iOS 11, audio context can only be used in response to user interaction. | ||
// We require users to explicitly enable audio fingerprinting on iOS 11. | ||
// See https://stackoverflow.com/questions/46363048/onaudioprocess-not-called-on-ios11#46534088 | ||
if (n.userAgent.match(/OS 11.+Version\/11.+Safari/)) { | ||
// See comment for excludeUserAgent and https://stackoverflow.com/questions/46363048/onaudioprocess-not-called-on-ios11#46534088 | ||
return -1; | ||
} | ||
const AudioContext = w.OfflineAudioContext || w.webkitOfflineAudioContext; | ||
if (!AudioContext) { | ||
return -2; | ||
} | ||
const context = new AudioContext(1, 44100, 44100); | ||
const oscillator = context.createOscillator(); | ||
oscillator.type = 'triangle'; | ||
oscillator.frequency.setValueAtTime(10000, context.currentTime); | ||
const compressor = context.createDynamicsCompressor(); | ||
for (const [param, value] of [ | ||
['threshold', -50], | ||
['knee', 40], | ||
['ratio', 12], | ||
['reduction', -20], | ||
['attack', 0], | ||
['release', 0.25], | ||
]) { | ||
if (typeof compressor[param].setValueAtTime === 'function') { | ||
compressor[param].setValueAtTime(value, context.currentTime); | ||
return __awaiter(this, void 0, void 0, function () { | ||
var AudioContext, context, oscillator, compressor, _i, _a, _b, name_1, value, param; | ||
return __generator(this, function (_c) { | ||
// On iOS 11, audio context can only be used in response to user interaction. | ||
// We require users to explicitly enable audio fingerprinting on iOS 11. | ||
// See https://stackoverflow.com/questions/46363048/onaudioprocess-not-called-on-ios11#46534088 | ||
if (n.userAgent.match(/OS 11.+Version\/11.+Safari/)) { | ||
// See comment for excludeUserAgent and https://stackoverflow.com/questions/46363048/onaudioprocess-not-called-on-ios11#46534088 | ||
return [2 /*return*/, -1]; | ||
} | ||
} | ||
oscillator.connect(compressor); | ||
compressor.connect(context.destination); | ||
oscillator.start(0); | ||
context.startRendering(); | ||
return new Promise((resolve) => { | ||
const audioTimeoutId = setTimeout(() => { | ||
context.oncomplete = () => { }; | ||
resolve(-3); | ||
}, 1000); | ||
context.oncomplete = (event) => { | ||
let afp; | ||
try { | ||
clearTimeout(audioTimeoutId); | ||
afp = event.renderedBuffer | ||
.getChannelData(0) | ||
.slice(4500, 5000) | ||
.reduce((acc, val) => acc + Math.abs(val), 0); | ||
oscillator.disconnect(); | ||
compressor.disconnect(); | ||
AudioContext = w.OfflineAudioContext || w.webkitOfflineAudioContext; | ||
if (!AudioContext) { | ||
return [2 /*return*/, -2]; | ||
} | ||
context = new AudioContext(1, 44100, 44100); | ||
oscillator = context.createOscillator(); | ||
oscillator.type = 'triangle'; | ||
oscillator.frequency.setValueAtTime(10000, context.currentTime); | ||
compressor = context.createDynamicsCompressor(); | ||
for (_i = 0, _a = [ | ||
['threshold', -50], | ||
['knee', 40], | ||
['ratio', 12], | ||
['reduction', -20], | ||
['attack', 0], | ||
['release', 0.25], | ||
]; _i < _a.length; _i++) { | ||
_b = _a[_i], name_1 = _b[0], value = _b[1]; | ||
param = compressor[name_1]; | ||
if (isAudioParam(param)) { | ||
param.setValueAtTime(value, context.currentTime); | ||
} | ||
catch (error) { | ||
resolve(-4); | ||
return; | ||
} | ||
resolve(afp); | ||
}; | ||
} | ||
oscillator.connect(compressor); | ||
compressor.connect(context.destination); | ||
oscillator.start(0); | ||
context.startRendering(); | ||
return [2 /*return*/, new Promise(function (resolve) { | ||
var audioTimeoutId = setTimeout(function () { | ||
context.oncomplete = null; | ||
resolve(-3); | ||
}, 1000); | ||
context.oncomplete = function (event) { | ||
var afp; | ||
try { | ||
clearTimeout(audioTimeoutId); | ||
afp = event.renderedBuffer | ||
.getChannelData(0) | ||
.slice(4500, 5000) | ||
.reduce(function (acc, val) { return acc + Math.abs(val); }, 0); | ||
oscillator.disconnect(); | ||
compressor.disconnect(); | ||
} | ||
catch (error) { | ||
resolve(-4); | ||
return; | ||
} | ||
resolve(afp); | ||
}; | ||
})]; | ||
}); | ||
@@ -356,8 +374,13 @@ }); | ||
const d = document; | ||
// a font will be compared against all the three default fonts. | ||
// and if it doesn't match all 3 then that font is not available. | ||
const baseFonts = ['monospace', 'sans-serif', 'serif']; | ||
const fontList = [ | ||
// this is android-specific font from "Roboto" family | ||
var d = document; | ||
// We use m or w because these two characters take up the maximum width. | ||
// And we use a LLi so that the same matching fonts can get separated. | ||
var testString = 'mmMwWLliI0O&1'; | ||
// We test using 48px font size, we may use any size. I guess larger the better. | ||
var testSize = '48px'; | ||
// A font will be compared against all the three default fonts. | ||
// And if it doesn't match all 3 then that font is not available. | ||
var baseFonts = ['monospace', 'sans-serif', 'serif']; | ||
var fontList = [ | ||
// This is android-specific font from "Roboto" family | ||
'sans-serif-thin', | ||
@@ -416,3 +439,4 @@ 'ARNO PRO', | ||
]; | ||
const fontResetStyles = { | ||
var fontSpanStyle = { | ||
// CSS font reset to reset external styles | ||
fontStyle: 'normal', | ||
@@ -430,46 +454,37 @@ fontWeight: 'normal', | ||
wordSpacing: 'normal', | ||
// We need this css as in some weird browser this span elements shows up for a microSec which creates | ||
// a bad user experience | ||
position: 'absolute', | ||
left: '-9999px', | ||
fontSize: testSize, | ||
}; | ||
// we use m or w because these two characters take up the maximum width. | ||
// And we use a LLi so that the same matching fonts can get separated | ||
const testString = 'mmMwWLliI0O&1'; | ||
// we test using 48px font size, we may use any size. I guess larger the better. | ||
const testSize = '48px'; | ||
// kudos to http://www.lalit.org/lab/javascript-css-font-detect/ | ||
function getFonts() { | ||
const h = d.body; | ||
var h = d.body; | ||
// div to load spans for the base fonts | ||
const baseFontsDiv = d.createElement('div'); | ||
var baseFontsDiv = d.createElement('div'); | ||
// div to load spans for the fonts to detect | ||
const fontsDiv = d.createElement('div'); | ||
const defaultWidth = {}; | ||
const defaultHeight = {}; | ||
var fontsDiv = d.createElement('div'); | ||
var defaultWidth = {}; | ||
var defaultHeight = {}; | ||
// creates a span where the fonts will be loaded | ||
const createSpan = () => { | ||
const s = d.createElement('span'); | ||
Object.assign(s.style, | ||
// css font reset to reset external styles | ||
fontResetStyles, | ||
/* | ||
* We need this css as in some weird browser this | ||
* span elements shows up for a microSec which creates a | ||
* bad user experience | ||
*/ | ||
{ | ||
position: 'absolute', | ||
left: '-9999px', | ||
fontSize: testSize, | ||
}); | ||
s.textContent = testString; | ||
return s; | ||
var createSpan = function () { | ||
var span = d.createElement('span'); | ||
span.textContent = testString; | ||
for (var _i = 0, _a = Object.keys(fontSpanStyle); _i < _a.length; _i++) { | ||
var prop = _a[_i]; | ||
span.style[prop] = fontSpanStyle[prop]; | ||
} | ||
return span; | ||
}; | ||
// creates a span and load the font to detect and a base font for fallback | ||
const createSpanWithFonts = (fontToDetect, baseFont) => { | ||
const s = createSpan(); | ||
s.style.fontFamily = `'${fontToDetect}',${baseFont}`; | ||
var createSpanWithFonts = function (fontToDetect, baseFont) { | ||
var s = createSpan(); | ||
s.style.fontFamily = "'" + fontToDetect + "'," + baseFont; | ||
return s; | ||
}; | ||
// creates spans for the base fonts and adds them to baseFontsDiv | ||
const initializeBaseFontsSpans = () => { | ||
return baseFonts.map((baseFont) => { | ||
const s = createSpan(); | ||
var initializeBaseFontsSpans = function () { | ||
return baseFonts.map(function (baseFont) { | ||
var s = createSpan(); | ||
s.style.fontFamily = baseFont; | ||
@@ -481,11 +496,15 @@ baseFontsDiv.appendChild(s); | ||
// creates spans for the fonts to detect and adds them to fontsDiv | ||
const initializeFontsSpans = () => { | ||
var initializeFontsSpans = function () { | ||
// Stores {fontName : [spans for that font]} | ||
const spans = {}; | ||
for (const font of fontList) { | ||
spans[font] = baseFonts.map((baseFont) => { | ||
const s = createSpanWithFonts(font, baseFont); | ||
var spans = {}; | ||
var _loop_1 = function (font) { | ||
spans[font] = baseFonts.map(function (baseFont) { | ||
var s = createSpanWithFonts(font, baseFont); | ||
fontsDiv.appendChild(s); | ||
return s; | ||
}); | ||
}; | ||
for (var _i = 0, fontList_1 = fontList; _i < fontList_1.length; _i++) { | ||
var font = fontList_1[_i]; | ||
_loop_1(font); | ||
} | ||
@@ -495,12 +514,14 @@ return spans; | ||
// checks if a font is available | ||
const isFontAvailable = (fontSpans) => { | ||
return baseFonts.some(((baseFont, baseFontIndex) => (fontSpans[baseFontIndex].offsetWidth !== defaultWidth[baseFont] || | ||
fontSpans[baseFontIndex].offsetHeight !== defaultHeight[baseFont]))); | ||
var isFontAvailable = function (fontSpans) { | ||
return baseFonts.some(function (baseFont, baseFontIndex) { | ||
return fontSpans[baseFontIndex].offsetWidth !== defaultWidth[baseFont] || | ||
fontSpans[baseFontIndex].offsetHeight !== defaultHeight[baseFont]; | ||
}); | ||
}; | ||
// create spans for base fonts | ||
const baseFontsSpans = initializeBaseFontsSpans(); | ||
var baseFontsSpans = initializeBaseFontsSpans(); | ||
// add the spans to the DOM | ||
h.appendChild(baseFontsDiv); | ||
// get the default width for the three base fonts | ||
for (let index = 0, length = baseFonts.length; index < length; index++) { | ||
for (var index = 0, length_1 = baseFonts.length; index < length_1; index++) { | ||
defaultWidth[baseFonts[index]] = baseFontsSpans[index].offsetWidth; // width for the default font | ||
@@ -510,8 +531,8 @@ defaultHeight[baseFonts[index]] = baseFontsSpans[index].offsetHeight; // height for the default font | ||
// create spans for fonts to detect | ||
const fontsSpans = initializeFontsSpans(); | ||
var fontsSpans = initializeFontsSpans(); | ||
// add all the spans to the DOM | ||
h.appendChild(fontsDiv); | ||
// check available fonts | ||
const available = []; | ||
for (let i = 0, l = fontList.length; i < l; i++) { | ||
var available = []; | ||
for (var i = 0, l = fontList.length; i < l; i++) { | ||
if (isFontAvailable(fontsSpans[fontList[i]])) { | ||
@@ -527,15 +548,129 @@ available.push(fontList[i]); | ||
/* | ||
* Functions to help with browser features | ||
*/ | ||
var w$1 = window; | ||
var n$1 = navigator; | ||
var d$1 = document; | ||
/** | ||
* Checks whether the browser is based on Trident (the Internet Explorer engine) without using user-agent. | ||
* | ||
* Warning for package users: | ||
* This function is out of Semantic Versioning, i.e. can change unexpectedly. Usage is at your own risk. | ||
*/ | ||
function isTrident() { | ||
// The properties are checked to be in IE 10, IE 11 and not to be in other browsers in October 2020 | ||
return (countTruthy([ | ||
'MSCSSMatrix' in w$1, | ||
'msSetImmediate' in w$1, | ||
'msIndexedDB' in w$1, | ||
'msMaxTouchPoints' in n$1, | ||
'msPointerEnabled' in n$1, | ||
]) >= 4); | ||
} | ||
/** | ||
* Checks whether the browser is based on EdgeHTML (the pre-Chromium Edge engine) without using user-agent. | ||
* | ||
* Warning for package users: | ||
* This function is out of Semantic Versioning, i.e. can change unexpectedly. Usage is at your own risk. | ||
*/ | ||
function isEdgeHTML() { | ||
// Based on research in October 2020 | ||
return (countTruthy(['msWriteProfilerMark' in w$1, 'MSStream' in w$1, 'msLaunchUri' in n$1, 'msSaveBlob' in n$1]) >= 3 && | ||
!isTrident()); | ||
} | ||
/** | ||
* Checks whether the browser is based on Chromium without using user-agent. | ||
* | ||
* Warning for package users: | ||
* This function is out of Semantic Versioning, i.e. can change unexpectedly. Usage is at your own risk. | ||
*/ | ||
function isChromium() { | ||
// Based on research in October 2020. Tested to detect Chromium 42-86. | ||
return (countTruthy([ | ||
'webkitPersistentStorage' in n$1, | ||
'webkitTemporaryStorage' in n$1, | ||
n$1.vendor.indexOf('Google') === 0, | ||
'webkitResolveLocalFileSystemURL' in w$1, | ||
'BatteryManager' in w$1, | ||
'webkitMediaStream' in w$1, | ||
'webkitSpeechGrammar' in w$1, | ||
]) >= 5); | ||
} | ||
/** | ||
* Checks whether the browser is based on mobile or desktop Safari without using user-agent. | ||
* All iOS browsers use WebKit (the Safari engine). | ||
* | ||
* Warning for package users: | ||
* This function is out of Semantic Versioning, i.e. can change unexpectedly. Usage is at your own risk. | ||
*/ | ||
function isWebKit() { | ||
// Based on research in September 2020 | ||
return (countTruthy([ | ||
'ApplePayError' in w$1, | ||
'CSSPrimitiveValue' in w$1, | ||
'Counter' in w$1, | ||
n$1.vendor.indexOf('Apple') === 0, | ||
'getStorageUpdates' in n$1, | ||
'WebKitMediaKeys' in w$1, | ||
]) >= 4); | ||
} | ||
/** | ||
* Checks whether the WebKit browser is a desktop Safari. | ||
* | ||
* Warning for package users: | ||
* This function is out of Semantic Versioning, i.e. can change unexpectedly. Usage is at your own risk. | ||
*/ | ||
function isDesktopSafari() { | ||
return 'safari' in w$1; | ||
} | ||
/** | ||
* Checks whether the browser is based on Gecko (Firefox engine) without using user-agent. | ||
* | ||
* Warning for package users: | ||
* This function is out of Semantic Versioning, i.e. can change unexpectedly. Usage is at your own risk. | ||
*/ | ||
function isGecko() { | ||
var _a; | ||
// Based on research in September 2020 | ||
return (countTruthy([ | ||
'buildID' in n$1, | ||
((_a = d$1.documentElement) === null || _a === void 0 ? void 0 : _a.style) && 'MozAppearance' in d$1.documentElement.style, | ||
'MediaRecorderErrorEvent' in w$1, | ||
'mozInnerScreenX' in w$1, | ||
'CSSMozDocumentRule' in w$1, | ||
'CanvasCaptureMediaStream' in w$1, | ||
]) >= 4); | ||
} | ||
/** | ||
* Checks whether the browser is based on Chromium version ā„86 without using user-agent. | ||
* It doesn't check that the browser is based on Chromium, there is a separate function for this. | ||
*/ | ||
function isChromium86OrNewer() { | ||
// Checked in Chrome 85 vs Chrome 86 both on desktop and Android | ||
return (countTruthy([ | ||
!('MediaSettingsRange' in w$1), | ||
'RTCEncodedAudioFrame' in w$1, | ||
'' + w$1.Intl === '[object Intl]', | ||
'' + w$1.Reflect === '[object Reflect]', | ||
]) >= 3); | ||
} | ||
function getPlugins() { | ||
if (isTrident()) { | ||
return []; | ||
} | ||
if (!navigator.plugins) { | ||
return undefined; | ||
} | ||
const plugins = []; | ||
var plugins = []; | ||
// Safari 10 doesn't support iterating navigator.plugins with for...of | ||
for (let i = 0; i < navigator.plugins.length; ++i) { | ||
const plugin = navigator.plugins[i]; | ||
for (var i = 0; i < navigator.plugins.length; ++i) { | ||
var plugin = navigator.plugins[i]; | ||
if (!plugin) { | ||
continue; | ||
} | ||
const mimeTypes = []; | ||
for (const mimeType of plugin) { | ||
var mimeTypes = []; | ||
for (var j = 0; j < plugin.length; ++j) { | ||
var mimeType = plugin[j]; | ||
mimeTypes.push({ | ||
@@ -549,3 +684,3 @@ type: mimeType.type, | ||
description: plugin.description, | ||
mimeTypes, | ||
mimeTypes: mimeTypes, | ||
}); | ||
@@ -557,3 +692,3 @@ } | ||
function makeCanvasContext() { | ||
const canvas = document.createElement('canvas'); | ||
var canvas = document.createElement('canvas'); | ||
canvas.width = 240; | ||
@@ -574,3 +709,3 @@ canvas.height = 140; | ||
function getCanvasFingerprint() { | ||
const [canvas, context] = makeCanvasContext(); | ||
var _a = makeCanvasContext(), canvas = _a[0], context = _a[1]; | ||
if (!isSupported(canvas, context)) { | ||
@@ -584,3 +719,3 @@ return { winding: false, data: '' }; | ||
context.rect(2, 2, 6, 6); | ||
const winding = !context.isPointInPath(5, 5, 'evenodd'); | ||
var winding = !context.isPointInPath(5, 5, 'evenodd'); | ||
context.textBaseline = 'alphabetic'; | ||
@@ -596,3 +731,3 @@ context.fillStyle = '#f60'; | ||
// context.fillText("CwēØm fjordbank \ud83d\ude03 gly", 2, 15) | ||
const printedText = 'Cwm fjordbank \ud83d\ude03 gly'; | ||
var printedText = 'Cwm fjordbank \ud83d\ude03 gly'; | ||
context.fillText(printedText, 2, 15); | ||
@@ -629,9 +764,9 @@ context.fillStyle = 'rgba(102, 204, 0, 0.2)'; | ||
return { | ||
winding, | ||
data: save(canvas) | ||
winding: winding, | ||
data: save(canvas), | ||
}; | ||
} | ||
const n$1 = navigator; | ||
const w$1 = window; | ||
var n$2 = navigator; | ||
var w$2 = window; | ||
/** | ||
@@ -645,9 +780,9 @@ * This is a crude and primitive touch screen detection. It's not possible to currently reliably detect the availability | ||
function getTouchSupport() { | ||
let maxTouchPoints = 0; | ||
let touchEvent; | ||
if (n$1.maxTouchPoints !== undefined) { | ||
maxTouchPoints = toInt(n$1.maxTouchPoints); | ||
var maxTouchPoints = 0; | ||
var touchEvent; | ||
if (n$2.maxTouchPoints !== undefined) { | ||
maxTouchPoints = toInt(n$2.maxTouchPoints); | ||
} | ||
else if (n$1.msMaxTouchPoints !== undefined) { | ||
maxTouchPoints = n$1.msMaxTouchPoints; | ||
else if (n$2.msMaxTouchPoints !== undefined) { | ||
maxTouchPoints = n$2.msMaxTouchPoints; | ||
} | ||
@@ -661,7 +796,7 @@ try { | ||
} | ||
const touchStart = 'ontouchstart' in w$1; | ||
var touchStart = 'ontouchstart' in w$2; | ||
return { | ||
maxTouchPoints, | ||
touchEvent, | ||
touchStart, | ||
maxTouchPoints: maxTouchPoints, | ||
touchEvent: touchEvent, | ||
touchStart: touchStart, | ||
}; | ||
@@ -674,85 +809,6 @@ } | ||
/* | ||
* Functions to help with browser features | ||
*/ | ||
const w$2 = window; | ||
const n$2 = navigator; | ||
const d$1 = document; | ||
/** | ||
* Checks whether the browser is Internet Explorer or pre-Chromium Edge without using user-agent. | ||
* | ||
* Warning for package users: | ||
* This function is out of Semantic Versioning, i.e. can change unexpectedly. Usage is at your own risk. | ||
*/ | ||
function isIEOrOldEdge() { | ||
// The properties are checked to be in IE 10, IE 11 and Edge 18 and not to be in other browsers | ||
return countTruthy([ | ||
'msWriteProfilerMark' in w$2, | ||
'msLaunchUri' in n$2, | ||
'msSaveBlob' in n$2, | ||
]) >= 2; | ||
} | ||
/** | ||
* Checks whether the browser is based on Chromium without using user-agent. | ||
* | ||
* Warning for package users: | ||
* This function is out of Semantic Versioning, i.e. can change unexpectedly. Usage is at your own risk. | ||
*/ | ||
function isChromium() { | ||
// Based on research in September 2020 | ||
return countTruthy([ | ||
'userActivation' in n$2, | ||
'mediaSession' in n$2, | ||
n$2.vendor.indexOf('Google') === 0, | ||
'BackgroundFetchManager' in w$2, | ||
'BatteryManager' in w$2, | ||
'webkitMediaStream' in w$2, | ||
'webkitSpeechGrammar' in w$2, | ||
]) >= 5; | ||
} | ||
/** | ||
* Checks whether the WebKit browser is a desktop Safari. | ||
* | ||
* Warning for package users: | ||
* This function is out of Semantic Versioning, i.e. can change unexpectedly. Usage is at your own risk. | ||
*/ | ||
function isDesktopSafari() { | ||
return 'safari' in w$2; | ||
} | ||
/** | ||
* Checks whether the browser is based on Gecko (Firefox engine) without using user-agent. | ||
* | ||
* Warning for package users: | ||
* This function is out of Semantic Versioning, i.e. can change unexpectedly. Usage is at your own risk. | ||
*/ | ||
function isGecko() { | ||
var _a; | ||
// Based on research in September 2020 | ||
return countTruthy([ | ||
'buildID' in n$2, | ||
((_a = d$1.documentElement) === null || _a === void 0 ? void 0 : _a.style) && 'MozAppearance' in d$1.documentElement.style, | ||
'MediaRecorderErrorEvent' in w$2, | ||
'mozInnerScreenX' in w$2, | ||
'CSSMozDocumentRule' in w$2, | ||
'CanvasCaptureMediaStream' in w$2, | ||
]) >= 4; | ||
} | ||
/** | ||
* Checks whether the browser is based on Chromium version ā„86 without using user-agent. | ||
* It doesn't check that the browser is based on Chromium, there is a separate function for this. | ||
*/ | ||
function isChromium86OrNewer() { | ||
// Checked in Chrome 85 vs Chrome 86 both on desktop and Android | ||
return countTruthy([ | ||
!('MediaSettingsRange' in w$2), | ||
!('PhotoCapabilities' in w$2), | ||
'RTCEncodedAudioFrame' in w$2, | ||
('' + w$2.Intl) === '[object Intl]', | ||
]) >= 2; | ||
} | ||
const n$3 = navigator; | ||
var n$3 = navigator; | ||
function getLanguages() { | ||
const result = []; | ||
const language = n$3.language || n$3.userLanguage || n$3.browserLanguage || n$3.systemLanguage; | ||
var result = []; | ||
var language = n$3.language || n$3.userLanguage || n$3.browserLanguage || n$3.systemLanguage; | ||
if (language !== undefined) { | ||
@@ -769,3 +825,3 @@ result.push([language]); | ||
else if (typeof n$3.languages === 'string') { | ||
const languages = n$3.languages; | ||
var languages = n$3.languages; | ||
if (languages) { | ||
@@ -786,7 +842,7 @@ result.push(languages.split(',')); | ||
const w$3 = window; | ||
var w$3 = window; | ||
function getScreenResolution() { | ||
// Some browsers return screen resolution as strings, e.g. "1200", instead of a number, e.g. 1200. | ||
// I suspect it's done by certain plugins that randomize browser properties to prevent fingerprinting. | ||
const dimensions = [toInt(w$3.screen.width), toInt(w$3.screen.height)]; | ||
var dimensions = [toInt(w$3.screen.width), toInt(w$3.screen.height)]; | ||
dimensions.sort().reverse(); | ||
@@ -796,3 +852,3 @@ return dimensions; | ||
const w$4 = window; | ||
var w$4 = window; | ||
function getAvailableScreenResolution() { | ||
@@ -802,3 +858,3 @@ if (w$4.screen.availWidth && w$4.screen.availHeight) { | ||
// I suspect it's done by certain plugins that randomize browser properties to prevent fingerprinting. | ||
const dimensions = [toInt(w$4.screen.availWidth), toInt(w$4.screen.availHeight)]; | ||
var dimensions = [toInt(w$4.screen.availWidth), toInt(w$4.screen.availHeight)]; | ||
dimensions.sort().reverse(); | ||
@@ -813,3 +869,3 @@ return dimensions; | ||
// sometimes hardware concurrency is a string | ||
const concurrency = toInt(navigator.hardwareConcurrency); | ||
var concurrency = toInt(navigator.hardwareConcurrency); | ||
return isNaN(concurrency) ? 1 : concurrency; | ||
@@ -823,6 +879,13 @@ } | ||
function getTimezoneOffset() { | ||
return new Date().getTimezoneOffset(); | ||
var currentYear = new Date().getFullYear(); | ||
// The timezone offset may change over time due to daylight saving time (DST) shifts. | ||
// The non-DST timezone offset is used as the result timezone offset. | ||
// Since the DST season differs in the northern and the southern hemispheres, | ||
// both January and July timezones offsets are considered. | ||
return Math.max( | ||
// `getTimezoneOffset` returns a number as a string in some unidentified cases | ||
toFloat(new Date(currentYear, 0, 1).getTimezoneOffset()), toFloat(new Date(currentYear, 6, 1).getTimezoneOffset())); | ||
} | ||
const w$5 = window; | ||
var w$5 = window; | ||
function getTimezone() { | ||
@@ -860,3 +923,3 @@ var _a; | ||
// visitor identifier in normal and private modes. | ||
if (isIEOrOldEdge()) { | ||
if (isTrident() || isEdgeHTML()) { | ||
return undefined; | ||
@@ -920,3 +983,3 @@ } | ||
const d$2 = document; | ||
var d$2 = document; | ||
/** | ||
@@ -930,2 +993,6 @@ * navigator.cookieEnabled cannot detect custom or nuanced cookie blocking configurations. For example, when blocking | ||
function areCookiesEnabled() { | ||
// Taken from here: https://github.com/Modernizr/Modernizr/blob/master/feature-detects/cookies.js | ||
// navigator.cookieEnabled cannot detect custom or nuanced cookie blocking configurations. For example, when blocking | ||
// cookies via the Advanced Privacy Settings in IE9, it always returns true. And there have been issues in the past | ||
// with site-specific exceptions. Don't rely on it. | ||
// try..catch because some in situations `document.cookie` is exposed but throws a | ||
@@ -937,3 +1004,3 @@ // SecurityError if you try to access it; e.g. documents created from data URIs | ||
d$2.cookie = 'cookietest=1'; | ||
const result = d$2.cookie.indexOf('cookietest=') !== -1; | ||
var result = d$2.cookie.indexOf('cookietest=') !== -1; | ||
// Delete cookie | ||
@@ -954,3 +1021,3 @@ d$2.cookie = 'cookietest=1; expires=Thu, 01-Jan-1970 00:00:01 GMT'; | ||
*/ | ||
const sources = { | ||
var sources = { | ||
// Expected errors and default values must be handled inside the functions | ||
@@ -994,22 +1061,42 @@ osCpu: getOsCpu, | ||
function getComponents(sources, sourceOptions, excludeSources) { | ||
return __awaiter(this, void 0, void 0, function* () { | ||
let timestamp = Date.now(); | ||
const components = {}; | ||
for (const sourceKey of Object.keys(sources)) { | ||
if (!excludes(excludeSources, sourceKey)) { | ||
continue; | ||
return __awaiter(this, void 0, void 0, function () { | ||
var timestamp, components, _i, _a, sourceKey, result, error_1, nextTimestamp; | ||
var _b; | ||
return __generator(this, function (_c) { | ||
switch (_c.label) { | ||
case 0: | ||
timestamp = Date.now(); | ||
components = {}; | ||
_i = 0, _a = Object.keys(sources); | ||
_c.label = 1; | ||
case 1: | ||
if (!(_i < _a.length)) return [3 /*break*/, 7]; | ||
sourceKey = _a[_i]; | ||
if (!excludes(excludeSources, sourceKey)) { | ||
return [3 /*break*/, 6]; | ||
} | ||
result = void 0; | ||
_c.label = 2; | ||
case 2: | ||
_c.trys.push([2, 4, , 5]); | ||
_b = {}; | ||
return [4 /*yield*/, sources[sourceKey](sourceOptions)]; | ||
case 3: | ||
result = (_b.value = _c.sent(), _b); | ||
return [3 /*break*/, 5]; | ||
case 4: | ||
error_1 = _c.sent(); | ||
result = error_1 && typeof error_1 === 'object' && 'message' in error_1 ? { error: error_1 } : { error: { message: error_1 } }; | ||
return [3 /*break*/, 5]; | ||
case 5: | ||
nextTimestamp = Date.now(); | ||
components[sourceKey] = __assign(__assign({}, result), { duration: nextTimestamp - timestamp }); // TypeScript has beaten me here | ||
timestamp = nextTimestamp; | ||
_c.label = 6; | ||
case 6: | ||
_i++; | ||
return [3 /*break*/, 1]; | ||
case 7: return [2 /*return*/, components]; | ||
} | ||
let result; | ||
let nextTimestamp; | ||
try { | ||
result = { value: yield sources[sourceKey](sourceOptions) }; | ||
} | ||
catch (error) { | ||
result = error && typeof error === 'object' && 'message' in error ? { error } : { error: { message: error } }; | ||
} | ||
nextTimestamp = Date.now(); | ||
components[sourceKey] = Object.assign(Object.assign({}, result), { duration: nextTimestamp - timestamp }); // TypeScript has beaten me here | ||
timestamp = nextTimestamp; | ||
} | ||
return components; | ||
}); | ||
}); | ||
@@ -1025,7 +1112,8 @@ } | ||
function componentsToCanonicalString(components) { | ||
let result = ''; | ||
for (const componentKey of Object.keys(components)) { | ||
const component = components[componentKey]; | ||
const value = component.error ? 'error' : JSON.stringify(component.value); | ||
result += `${result ? '|' : ''}${componentKey.replace(/([:|\\])/g, '\\$1')}:${value}`; | ||
var result = ''; | ||
for (var _i = 0, _a = Object.keys(components); _i < _a.length; _i++) { | ||
var componentKey = _a[_i]; | ||
var component = components[componentKey]; | ||
var value = component.error ? 'error' : JSON.stringify(component.value); | ||
result += "" + (result ? '|' : '') + componentKey.replace(/([:|\\])/g, '\\$1') + ":" + value; | ||
} | ||
@@ -1035,6 +1123,6 @@ return result; | ||
function componentsToDebugString(components) { | ||
return JSON.stringify(components, (_key, value) => { | ||
return JSON.stringify(components, function (_key, value) { | ||
var _a; | ||
if (value instanceof Error) { | ||
return Object.assign(Object.assign({}, value), { message: value.message, stack: (_a = value.stack) === null || _a === void 0 ? void 0 : _a.split('\n') }); | ||
return __assign(__assign({}, value), { message: value.message, stack: (_a = value.stack) === null || _a === void 0 ? void 0 : _a.split('\n') }); | ||
} | ||
@@ -1052,6 +1140,6 @@ return value; | ||
function makeLazyGetResult(components) { | ||
let visitorIdCache; | ||
var visitorIdCache; | ||
// A plain class isn't used because its getters and setters aren't enumerable. | ||
return { | ||
components, | ||
components: components, | ||
get visitorId() { | ||
@@ -1072,34 +1160,51 @@ if (visitorIdCache === undefined) { | ||
*/ | ||
class OpenAgent { | ||
var OpenAgent = /** @class */ (function () { | ||
function OpenAgent() { | ||
} | ||
/** | ||
* @inheritDoc | ||
*/ | ||
get(options = {}) { | ||
return __awaiter(this, void 0, void 0, function* () { | ||
const components = yield getBuiltinComponents(); | ||
const result = makeLazyGetResult(components); | ||
if (options.debug) { | ||
console.log(`Copy the text below to get the debug data: | ||
\`\`\` | ||
version: ${version} | ||
getOptions: ${JSON.stringify(options, undefined, 2)} | ||
visitorId: ${result.visitorId} | ||
components: ${componentsToDebugString(components)} | ||
\`\`\``); | ||
} | ||
return result; | ||
OpenAgent.prototype.get = function (options) { | ||
if (options === void 0) { options = {}; } | ||
return __awaiter(this, void 0, void 0, function () { | ||
var components, result; | ||
return __generator(this, function (_a) { | ||
switch (_a.label) { | ||
case 0: return [4 /*yield*/, getBuiltinComponents()]; | ||
case 1: | ||
components = _a.sent(); | ||
result = makeLazyGetResult(components); | ||
if (options.debug) { | ||
// console.log is ok here because it's under a debug clause | ||
// eslint-disable-next-line no-console | ||
console.log("Copy the text below to get the debug data:\n\n```\nversion: " + version + "\ngetOptions: " + JSON.stringify(options, undefined, 2) + "\nvisitorId: " + result.visitorId + "\ncomponents: " + componentsToDebugString(components) + "\n```"); | ||
} | ||
return [2 /*return*/, result]; | ||
} | ||
}); | ||
}); | ||
} | ||
} | ||
}; | ||
return OpenAgent; | ||
}()); | ||
/** | ||
* Builds an instance of Agent and waits a delay required for a proper operation. | ||
*/ | ||
function load({ delayFallback = 50 } = {}) { | ||
return __awaiter(this, void 0, void 0, function* () { | ||
// A delay is required to ensure consistent entropy components. | ||
// See https://github.com/fingerprintjs/fingerprintjs/issues/254 | ||
// and https://github.com/fingerprintjs/fingerprintjs/issues/307 | ||
yield requestIdleCallbackIfAvailable(delayFallback); | ||
return new OpenAgent(); | ||
function load(_a) { | ||
var _b = (_a === void 0 ? {} : _a).delayFallback, delayFallback = _b === void 0 ? 50 : _b; | ||
return __awaiter(this, void 0, void 0, function () { | ||
return __generator(this, function (_c) { | ||
switch (_c.label) { | ||
case 0: | ||
// A delay is required to ensure consistent entropy components. | ||
// See https://github.com/fingerprintjs/fingerprintjs/issues/254 | ||
// and https://github.com/fingerprintjs/fingerprintjs/issues/307 | ||
return [4 /*yield*/, requestIdleCallbackIfAvailable(delayFallback)]; | ||
case 1: | ||
// A delay is required to ensure consistent entropy components. | ||
// See https://github.com/fingerprintjs/fingerprintjs/issues/254 | ||
// and https://github.com/fingerprintjs/fingerprintjs/issues/307 | ||
_c.sent(); | ||
return [2 /*return*/, new OpenAgent()]; | ||
} | ||
}); | ||
}); | ||
@@ -1110,8 +1215,8 @@ } | ||
// It should contain all the public exported values. | ||
var index = { load, hashComponents, componentsToDebugString }; | ||
var index = { load: load, hashComponents: hashComponents, componentsToDebugString: componentsToDebugString }; | ||
// The exports below are for private usage. They may change unexpectedly. Use them at your own risk. | ||
/** Not documented, out of Semantic Versioning, usage is at your own risk */ | ||
const murmurX64Hash128 = x64hash128; | ||
var murmurX64Hash128 = x64hash128; | ||
export default index; | ||
export { componentsToDebugString, getComponents, hashComponents, isChromium, isDesktopSafari, isGecko, isIEOrOldEdge, load, murmurX64Hash128 }; | ||
export { componentsToDebugString, getComponents, hashComponents, isChromium, isDesktopSafari, isEdgeHTML, isGecko, isTrident, isWebKit, load, murmurX64Hash128 }; |
724
dist/fp.js
/** | ||
* FingerprintJS v3.0.0 - Copyright (c) FingerprintJS, Inc, 2020 (https://fingerprintjs.com) | ||
* FingerprintJS v3.0.1 - Copyright (c) FingerprintJS, Inc, 2020 (https://fingerprintjs.com) | ||
* Licensed under the MIT (http://www.opensource.org/licenses/mit-license.php) license. | ||
@@ -136,3 +136,4 @@ * | ||
var c2 = [0x4cf5ad43, 0x2745937f]; | ||
for (var i = 0; i < bytes; i = i + 16) { | ||
var i; | ||
for (i = 0; i < bytes; i = i + 16) { | ||
k1 = [ | ||
@@ -259,2 +260,13 @@ (key.charCodeAt(i + 4) & 0xff) | | ||
var __assign = function() { | ||
__assign = Object.assign || function __assign(t) { | ||
for (var s, i = 1, n = arguments.length; i < n; i++) { | ||
s = arguments[i]; | ||
for (var p in s) if (Object.prototype.hasOwnProperty.call(s, p)) t[p] = s[p]; | ||
} | ||
return t; | ||
}; | ||
return __assign.apply(this, arguments); | ||
}; | ||
function __awaiter(thisArg, _arguments, P, generator) { | ||
@@ -270,8 +282,36 @@ function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); } | ||
var version = "3.0.0"; | ||
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 }; | ||
} | ||
} | ||
var version = "3.0.1"; | ||
function requestIdleCallbackIfAvailable(fallbackTimeout) { | ||
return new Promise((resolve) => { | ||
return new Promise(function (resolve) { | ||
if (window.requestIdleCallback) { | ||
window.requestIdleCallback(() => resolve()); | ||
window.requestIdleCallback(function () { return resolve(); }); | ||
} | ||
@@ -291,3 +331,3 @@ else { | ||
function includes(haystack, needle) { | ||
for (let i = 0, l = haystack.length; i < l; ++i) { | ||
for (var i = 0, l = haystack.length; i < l; ++i) { | ||
if (haystack[i] === needle) { | ||
@@ -314,65 +354,82 @@ return true; | ||
} | ||
/** | ||
* Be careful, NaN can return | ||
*/ | ||
function toFloat(value) { | ||
if (typeof value === 'number') { | ||
return value; | ||
} | ||
return parseFloat(value); | ||
} | ||
function countTruthy(values) { | ||
return values.reduce((sum, value) => sum + (value ? 1 : 0), 0); | ||
return values.reduce(function (sum, value) { return sum + (value ? 1 : 0); }, 0); | ||
} | ||
const n = navigator; | ||
const w = window; | ||
var n = navigator; | ||
var w = window; | ||
function isAudioParam(value) { | ||
return value && typeof value.setValueAtTime === 'function'; | ||
} | ||
// Inspired by and based on https://github.com/cozylife/audio-fingerprint | ||
function getAudioFingerprint() { | ||
return __awaiter(this, void 0, void 0, function* () { | ||
// On iOS 11, audio context can only be used in response to user interaction. | ||
// We require users to explicitly enable audio fingerprinting on iOS 11. | ||
// See https://stackoverflow.com/questions/46363048/onaudioprocess-not-called-on-ios11#46534088 | ||
if (n.userAgent.match(/OS 11.+Version\/11.+Safari/)) { | ||
// See comment for excludeUserAgent and https://stackoverflow.com/questions/46363048/onaudioprocess-not-called-on-ios11#46534088 | ||
return -1; | ||
} | ||
const AudioContext = w.OfflineAudioContext || w.webkitOfflineAudioContext; | ||
if (!AudioContext) { | ||
return -2; | ||
} | ||
const context = new AudioContext(1, 44100, 44100); | ||
const oscillator = context.createOscillator(); | ||
oscillator.type = 'triangle'; | ||
oscillator.frequency.setValueAtTime(10000, context.currentTime); | ||
const compressor = context.createDynamicsCompressor(); | ||
for (const [param, value] of [ | ||
['threshold', -50], | ||
['knee', 40], | ||
['ratio', 12], | ||
['reduction', -20], | ||
['attack', 0], | ||
['release', 0.25], | ||
]) { | ||
if (typeof compressor[param].setValueAtTime === 'function') { | ||
compressor[param].setValueAtTime(value, context.currentTime); | ||
return __awaiter(this, void 0, void 0, function () { | ||
var AudioContext, context, oscillator, compressor, _i, _a, _b, name_1, value, param; | ||
return __generator(this, function (_c) { | ||
// On iOS 11, audio context can only be used in response to user interaction. | ||
// We require users to explicitly enable audio fingerprinting on iOS 11. | ||
// See https://stackoverflow.com/questions/46363048/onaudioprocess-not-called-on-ios11#46534088 | ||
if (n.userAgent.match(/OS 11.+Version\/11.+Safari/)) { | ||
// See comment for excludeUserAgent and https://stackoverflow.com/questions/46363048/onaudioprocess-not-called-on-ios11#46534088 | ||
return [2 /*return*/, -1]; | ||
} | ||
} | ||
oscillator.connect(compressor); | ||
compressor.connect(context.destination); | ||
oscillator.start(0); | ||
context.startRendering(); | ||
return new Promise((resolve) => { | ||
const audioTimeoutId = setTimeout(() => { | ||
context.oncomplete = () => { }; | ||
resolve(-3); | ||
}, 1000); | ||
context.oncomplete = (event) => { | ||
let afp; | ||
try { | ||
clearTimeout(audioTimeoutId); | ||
afp = event.renderedBuffer | ||
.getChannelData(0) | ||
.slice(4500, 5000) | ||
.reduce((acc, val) => acc + Math.abs(val), 0); | ||
oscillator.disconnect(); | ||
compressor.disconnect(); | ||
AudioContext = w.OfflineAudioContext || w.webkitOfflineAudioContext; | ||
if (!AudioContext) { | ||
return [2 /*return*/, -2]; | ||
} | ||
context = new AudioContext(1, 44100, 44100); | ||
oscillator = context.createOscillator(); | ||
oscillator.type = 'triangle'; | ||
oscillator.frequency.setValueAtTime(10000, context.currentTime); | ||
compressor = context.createDynamicsCompressor(); | ||
for (_i = 0, _a = [ | ||
['threshold', -50], | ||
['knee', 40], | ||
['ratio', 12], | ||
['reduction', -20], | ||
['attack', 0], | ||
['release', 0.25], | ||
]; _i < _a.length; _i++) { | ||
_b = _a[_i], name_1 = _b[0], value = _b[1]; | ||
param = compressor[name_1]; | ||
if (isAudioParam(param)) { | ||
param.setValueAtTime(value, context.currentTime); | ||
} | ||
catch (error) { | ||
resolve(-4); | ||
return; | ||
} | ||
resolve(afp); | ||
}; | ||
} | ||
oscillator.connect(compressor); | ||
compressor.connect(context.destination); | ||
oscillator.start(0); | ||
context.startRendering(); | ||
return [2 /*return*/, new Promise(function (resolve) { | ||
var audioTimeoutId = setTimeout(function () { | ||
context.oncomplete = null; | ||
resolve(-3); | ||
}, 1000); | ||
context.oncomplete = function (event) { | ||
var afp; | ||
try { | ||
clearTimeout(audioTimeoutId); | ||
afp = event.renderedBuffer | ||
.getChannelData(0) | ||
.slice(4500, 5000) | ||
.reduce(function (acc, val) { return acc + Math.abs(val); }, 0); | ||
oscillator.disconnect(); | ||
compressor.disconnect(); | ||
} | ||
catch (error) { | ||
resolve(-4); | ||
return; | ||
} | ||
resolve(afp); | ||
}; | ||
})]; | ||
}); | ||
@@ -382,8 +439,13 @@ }); | ||
const d = document; | ||
// a font will be compared against all the three default fonts. | ||
// and if it doesn't match all 3 then that font is not available. | ||
const baseFonts = ['monospace', 'sans-serif', 'serif']; | ||
const fontList = [ | ||
// this is android-specific font from "Roboto" family | ||
var d = document; | ||
// We use m or w because these two characters take up the maximum width. | ||
// And we use a LLi so that the same matching fonts can get separated. | ||
var testString = 'mmMwWLliI0O&1'; | ||
// We test using 48px font size, we may use any size. I guess larger the better. | ||
var testSize = '48px'; | ||
// A font will be compared against all the three default fonts. | ||
// And if it doesn't match all 3 then that font is not available. | ||
var baseFonts = ['monospace', 'sans-serif', 'serif']; | ||
var fontList = [ | ||
// This is android-specific font from "Roboto" family | ||
'sans-serif-thin', | ||
@@ -442,3 +504,4 @@ 'ARNO PRO', | ||
]; | ||
const fontResetStyles = { | ||
var fontSpanStyle = { | ||
// CSS font reset to reset external styles | ||
fontStyle: 'normal', | ||
@@ -456,46 +519,37 @@ fontWeight: 'normal', | ||
wordSpacing: 'normal', | ||
// We need this css as in some weird browser this span elements shows up for a microSec which creates | ||
// a bad user experience | ||
position: 'absolute', | ||
left: '-9999px', | ||
fontSize: testSize, | ||
}; | ||
// we use m or w because these two characters take up the maximum width. | ||
// And we use a LLi so that the same matching fonts can get separated | ||
const testString = 'mmMwWLliI0O&1'; | ||
// we test using 48px font size, we may use any size. I guess larger the better. | ||
const testSize = '48px'; | ||
// kudos to http://www.lalit.org/lab/javascript-css-font-detect/ | ||
function getFonts() { | ||
const h = d.body; | ||
var h = d.body; | ||
// div to load spans for the base fonts | ||
const baseFontsDiv = d.createElement('div'); | ||
var baseFontsDiv = d.createElement('div'); | ||
// div to load spans for the fonts to detect | ||
const fontsDiv = d.createElement('div'); | ||
const defaultWidth = {}; | ||
const defaultHeight = {}; | ||
var fontsDiv = d.createElement('div'); | ||
var defaultWidth = {}; | ||
var defaultHeight = {}; | ||
// creates a span where the fonts will be loaded | ||
const createSpan = () => { | ||
const s = d.createElement('span'); | ||
Object.assign(s.style, | ||
// css font reset to reset external styles | ||
fontResetStyles, | ||
/* | ||
* We need this css as in some weird browser this | ||
* span elements shows up for a microSec which creates a | ||
* bad user experience | ||
*/ | ||
{ | ||
position: 'absolute', | ||
left: '-9999px', | ||
fontSize: testSize, | ||
}); | ||
s.textContent = testString; | ||
return s; | ||
var createSpan = function () { | ||
var span = d.createElement('span'); | ||
span.textContent = testString; | ||
for (var _i = 0, _a = Object.keys(fontSpanStyle); _i < _a.length; _i++) { | ||
var prop = _a[_i]; | ||
span.style[prop] = fontSpanStyle[prop]; | ||
} | ||
return span; | ||
}; | ||
// creates a span and load the font to detect and a base font for fallback | ||
const createSpanWithFonts = (fontToDetect, baseFont) => { | ||
const s = createSpan(); | ||
s.style.fontFamily = `'${fontToDetect}',${baseFont}`; | ||
var createSpanWithFonts = function (fontToDetect, baseFont) { | ||
var s = createSpan(); | ||
s.style.fontFamily = "'" + fontToDetect + "'," + baseFont; | ||
return s; | ||
}; | ||
// creates spans for the base fonts and adds them to baseFontsDiv | ||
const initializeBaseFontsSpans = () => { | ||
return baseFonts.map((baseFont) => { | ||
const s = createSpan(); | ||
var initializeBaseFontsSpans = function () { | ||
return baseFonts.map(function (baseFont) { | ||
var s = createSpan(); | ||
s.style.fontFamily = baseFont; | ||
@@ -507,11 +561,15 @@ baseFontsDiv.appendChild(s); | ||
// creates spans for the fonts to detect and adds them to fontsDiv | ||
const initializeFontsSpans = () => { | ||
var initializeFontsSpans = function () { | ||
// Stores {fontName : [spans for that font]} | ||
const spans = {}; | ||
for (const font of fontList) { | ||
spans[font] = baseFonts.map((baseFont) => { | ||
const s = createSpanWithFonts(font, baseFont); | ||
var spans = {}; | ||
var _loop_1 = function (font) { | ||
spans[font] = baseFonts.map(function (baseFont) { | ||
var s = createSpanWithFonts(font, baseFont); | ||
fontsDiv.appendChild(s); | ||
return s; | ||
}); | ||
}; | ||
for (var _i = 0, fontList_1 = fontList; _i < fontList_1.length; _i++) { | ||
var font = fontList_1[_i]; | ||
_loop_1(font); | ||
} | ||
@@ -521,12 +579,14 @@ return spans; | ||
// checks if a font is available | ||
const isFontAvailable = (fontSpans) => { | ||
return baseFonts.some(((baseFont, baseFontIndex) => (fontSpans[baseFontIndex].offsetWidth !== defaultWidth[baseFont] || | ||
fontSpans[baseFontIndex].offsetHeight !== defaultHeight[baseFont]))); | ||
var isFontAvailable = function (fontSpans) { | ||
return baseFonts.some(function (baseFont, baseFontIndex) { | ||
return fontSpans[baseFontIndex].offsetWidth !== defaultWidth[baseFont] || | ||
fontSpans[baseFontIndex].offsetHeight !== defaultHeight[baseFont]; | ||
}); | ||
}; | ||
// create spans for base fonts | ||
const baseFontsSpans = initializeBaseFontsSpans(); | ||
var baseFontsSpans = initializeBaseFontsSpans(); | ||
// add the spans to the DOM | ||
h.appendChild(baseFontsDiv); | ||
// get the default width for the three base fonts | ||
for (let index = 0, length = baseFonts.length; index < length; index++) { | ||
for (var index = 0, length_1 = baseFonts.length; index < length_1; index++) { | ||
defaultWidth[baseFonts[index]] = baseFontsSpans[index].offsetWidth; // width for the default font | ||
@@ -536,8 +596,8 @@ defaultHeight[baseFonts[index]] = baseFontsSpans[index].offsetHeight; // height for the default font | ||
// create spans for fonts to detect | ||
const fontsSpans = initializeFontsSpans(); | ||
var fontsSpans = initializeFontsSpans(); | ||
// add all the spans to the DOM | ||
h.appendChild(fontsDiv); | ||
// check available fonts | ||
const available = []; | ||
for (let i = 0, l = fontList.length; i < l; i++) { | ||
var available = []; | ||
for (var i = 0, l = fontList.length; i < l; i++) { | ||
if (isFontAvailable(fontsSpans[fontList[i]])) { | ||
@@ -553,15 +613,129 @@ available.push(fontList[i]); | ||
/* | ||
* Functions to help with browser features | ||
*/ | ||
var w$1 = window; | ||
var n$1 = navigator; | ||
var d$1 = document; | ||
/** | ||
* Checks whether the browser is based on Trident (the Internet Explorer engine) without using user-agent. | ||
* | ||
* Warning for package users: | ||
* This function is out of Semantic Versioning, i.e. can change unexpectedly. Usage is at your own risk. | ||
*/ | ||
function isTrident() { | ||
// The properties are checked to be in IE 10, IE 11 and not to be in other browsers in October 2020 | ||
return (countTruthy([ | ||
'MSCSSMatrix' in w$1, | ||
'msSetImmediate' in w$1, | ||
'msIndexedDB' in w$1, | ||
'msMaxTouchPoints' in n$1, | ||
'msPointerEnabled' in n$1, | ||
]) >= 4); | ||
} | ||
/** | ||
* Checks whether the browser is based on EdgeHTML (the pre-Chromium Edge engine) without using user-agent. | ||
* | ||
* Warning for package users: | ||
* This function is out of Semantic Versioning, i.e. can change unexpectedly. Usage is at your own risk. | ||
*/ | ||
function isEdgeHTML() { | ||
// Based on research in October 2020 | ||
return (countTruthy(['msWriteProfilerMark' in w$1, 'MSStream' in w$1, 'msLaunchUri' in n$1, 'msSaveBlob' in n$1]) >= 3 && | ||
!isTrident()); | ||
} | ||
/** | ||
* Checks whether the browser is based on Chromium without using user-agent. | ||
* | ||
* Warning for package users: | ||
* This function is out of Semantic Versioning, i.e. can change unexpectedly. Usage is at your own risk. | ||
*/ | ||
function isChromium() { | ||
// Based on research in October 2020. Tested to detect Chromium 42-86. | ||
return (countTruthy([ | ||
'webkitPersistentStorage' in n$1, | ||
'webkitTemporaryStorage' in n$1, | ||
n$1.vendor.indexOf('Google') === 0, | ||
'webkitResolveLocalFileSystemURL' in w$1, | ||
'BatteryManager' in w$1, | ||
'webkitMediaStream' in w$1, | ||
'webkitSpeechGrammar' in w$1, | ||
]) >= 5); | ||
} | ||
/** | ||
* Checks whether the browser is based on mobile or desktop Safari without using user-agent. | ||
* All iOS browsers use WebKit (the Safari engine). | ||
* | ||
* Warning for package users: | ||
* This function is out of Semantic Versioning, i.e. can change unexpectedly. Usage is at your own risk. | ||
*/ | ||
function isWebKit() { | ||
// Based on research in September 2020 | ||
return (countTruthy([ | ||
'ApplePayError' in w$1, | ||
'CSSPrimitiveValue' in w$1, | ||
'Counter' in w$1, | ||
n$1.vendor.indexOf('Apple') === 0, | ||
'getStorageUpdates' in n$1, | ||
'WebKitMediaKeys' in w$1, | ||
]) >= 4); | ||
} | ||
/** | ||
* Checks whether the WebKit browser is a desktop Safari. | ||
* | ||
* Warning for package users: | ||
* This function is out of Semantic Versioning, i.e. can change unexpectedly. Usage is at your own risk. | ||
*/ | ||
function isDesktopSafari() { | ||
return 'safari' in w$1; | ||
} | ||
/** | ||
* Checks whether the browser is based on Gecko (Firefox engine) without using user-agent. | ||
* | ||
* Warning for package users: | ||
* This function is out of Semantic Versioning, i.e. can change unexpectedly. Usage is at your own risk. | ||
*/ | ||
function isGecko() { | ||
var _a; | ||
// Based on research in September 2020 | ||
return (countTruthy([ | ||
'buildID' in n$1, | ||
((_a = d$1.documentElement) === null || _a === void 0 ? void 0 : _a.style) && 'MozAppearance' in d$1.documentElement.style, | ||
'MediaRecorderErrorEvent' in w$1, | ||
'mozInnerScreenX' in w$1, | ||
'CSSMozDocumentRule' in w$1, | ||
'CanvasCaptureMediaStream' in w$1, | ||
]) >= 4); | ||
} | ||
/** | ||
* Checks whether the browser is based on Chromium version ā„86 without using user-agent. | ||
* It doesn't check that the browser is based on Chromium, there is a separate function for this. | ||
*/ | ||
function isChromium86OrNewer() { | ||
// Checked in Chrome 85 vs Chrome 86 both on desktop and Android | ||
return (countTruthy([ | ||
!('MediaSettingsRange' in w$1), | ||
'RTCEncodedAudioFrame' in w$1, | ||
'' + w$1.Intl === '[object Intl]', | ||
'' + w$1.Reflect === '[object Reflect]', | ||
]) >= 3); | ||
} | ||
function getPlugins() { | ||
if (isTrident()) { | ||
return []; | ||
} | ||
if (!navigator.plugins) { | ||
return undefined; | ||
} | ||
const plugins = []; | ||
var plugins = []; | ||
// Safari 10 doesn't support iterating navigator.plugins with for...of | ||
for (let i = 0; i < navigator.plugins.length; ++i) { | ||
const plugin = navigator.plugins[i]; | ||
for (var i = 0; i < navigator.plugins.length; ++i) { | ||
var plugin = navigator.plugins[i]; | ||
if (!plugin) { | ||
continue; | ||
} | ||
const mimeTypes = []; | ||
for (const mimeType of plugin) { | ||
var mimeTypes = []; | ||
for (var j = 0; j < plugin.length; ++j) { | ||
var mimeType = plugin[j]; | ||
mimeTypes.push({ | ||
@@ -575,3 +749,3 @@ type: mimeType.type, | ||
description: plugin.description, | ||
mimeTypes, | ||
mimeTypes: mimeTypes, | ||
}); | ||
@@ -583,3 +757,3 @@ } | ||
function makeCanvasContext() { | ||
const canvas = document.createElement('canvas'); | ||
var canvas = document.createElement('canvas'); | ||
canvas.width = 240; | ||
@@ -600,3 +774,3 @@ canvas.height = 140; | ||
function getCanvasFingerprint() { | ||
const [canvas, context] = makeCanvasContext(); | ||
var _a = makeCanvasContext(), canvas = _a[0], context = _a[1]; | ||
if (!isSupported(canvas, context)) { | ||
@@ -610,3 +784,3 @@ return { winding: false, data: '' }; | ||
context.rect(2, 2, 6, 6); | ||
const winding = !context.isPointInPath(5, 5, 'evenodd'); | ||
var winding = !context.isPointInPath(5, 5, 'evenodd'); | ||
context.textBaseline = 'alphabetic'; | ||
@@ -622,3 +796,3 @@ context.fillStyle = '#f60'; | ||
// context.fillText("CwēØm fjordbank \ud83d\ude03 gly", 2, 15) | ||
const printedText = 'Cwm fjordbank \ud83d\ude03 gly'; | ||
var printedText = 'Cwm fjordbank \ud83d\ude03 gly'; | ||
context.fillText(printedText, 2, 15); | ||
@@ -655,9 +829,9 @@ context.fillStyle = 'rgba(102, 204, 0, 0.2)'; | ||
return { | ||
winding, | ||
data: save(canvas) | ||
winding: winding, | ||
data: save(canvas), | ||
}; | ||
} | ||
const n$1 = navigator; | ||
const w$1 = window; | ||
var n$2 = navigator; | ||
var w$2 = window; | ||
/** | ||
@@ -671,9 +845,9 @@ * This is a crude and primitive touch screen detection. It's not possible to currently reliably detect the availability | ||
function getTouchSupport() { | ||
let maxTouchPoints = 0; | ||
let touchEvent; | ||
if (n$1.maxTouchPoints !== undefined) { | ||
maxTouchPoints = toInt(n$1.maxTouchPoints); | ||
var maxTouchPoints = 0; | ||
var touchEvent; | ||
if (n$2.maxTouchPoints !== undefined) { | ||
maxTouchPoints = toInt(n$2.maxTouchPoints); | ||
} | ||
else if (n$1.msMaxTouchPoints !== undefined) { | ||
maxTouchPoints = n$1.msMaxTouchPoints; | ||
else if (n$2.msMaxTouchPoints !== undefined) { | ||
maxTouchPoints = n$2.msMaxTouchPoints; | ||
} | ||
@@ -687,7 +861,7 @@ try { | ||
} | ||
const touchStart = 'ontouchstart' in w$1; | ||
var touchStart = 'ontouchstart' in w$2; | ||
return { | ||
maxTouchPoints, | ||
touchEvent, | ||
touchStart, | ||
maxTouchPoints: maxTouchPoints, | ||
touchEvent: touchEvent, | ||
touchStart: touchStart, | ||
}; | ||
@@ -700,85 +874,6 @@ } | ||
/* | ||
* Functions to help with browser features | ||
*/ | ||
const w$2 = window; | ||
const n$2 = navigator; | ||
const d$1 = document; | ||
/** | ||
* Checks whether the browser is Internet Explorer or pre-Chromium Edge without using user-agent. | ||
* | ||
* Warning for package users: | ||
* This function is out of Semantic Versioning, i.e. can change unexpectedly. Usage is at your own risk. | ||
*/ | ||
function isIEOrOldEdge() { | ||
// The properties are checked to be in IE 10, IE 11 and Edge 18 and not to be in other browsers | ||
return countTruthy([ | ||
'msWriteProfilerMark' in w$2, | ||
'msLaunchUri' in n$2, | ||
'msSaveBlob' in n$2, | ||
]) >= 2; | ||
} | ||
/** | ||
* Checks whether the browser is based on Chromium without using user-agent. | ||
* | ||
* Warning for package users: | ||
* This function is out of Semantic Versioning, i.e. can change unexpectedly. Usage is at your own risk. | ||
*/ | ||
function isChromium() { | ||
// Based on research in September 2020 | ||
return countTruthy([ | ||
'userActivation' in n$2, | ||
'mediaSession' in n$2, | ||
n$2.vendor.indexOf('Google') === 0, | ||
'BackgroundFetchManager' in w$2, | ||
'BatteryManager' in w$2, | ||
'webkitMediaStream' in w$2, | ||
'webkitSpeechGrammar' in w$2, | ||
]) >= 5; | ||
} | ||
/** | ||
* Checks whether the WebKit browser is a desktop Safari. | ||
* | ||
* Warning for package users: | ||
* This function is out of Semantic Versioning, i.e. can change unexpectedly. Usage is at your own risk. | ||
*/ | ||
function isDesktopSafari() { | ||
return 'safari' in w$2; | ||
} | ||
/** | ||
* Checks whether the browser is based on Gecko (Firefox engine) without using user-agent. | ||
* | ||
* Warning for package users: | ||
* This function is out of Semantic Versioning, i.e. can change unexpectedly. Usage is at your own risk. | ||
*/ | ||
function isGecko() { | ||
var _a; | ||
// Based on research in September 2020 | ||
return countTruthy([ | ||
'buildID' in n$2, | ||
((_a = d$1.documentElement) === null || _a === void 0 ? void 0 : _a.style) && 'MozAppearance' in d$1.documentElement.style, | ||
'MediaRecorderErrorEvent' in w$2, | ||
'mozInnerScreenX' in w$2, | ||
'CSSMozDocumentRule' in w$2, | ||
'CanvasCaptureMediaStream' in w$2, | ||
]) >= 4; | ||
} | ||
/** | ||
* Checks whether the browser is based on Chromium version ā„86 without using user-agent. | ||
* It doesn't check that the browser is based on Chromium, there is a separate function for this. | ||
*/ | ||
function isChromium86OrNewer() { | ||
// Checked in Chrome 85 vs Chrome 86 both on desktop and Android | ||
return countTruthy([ | ||
!('MediaSettingsRange' in w$2), | ||
!('PhotoCapabilities' in w$2), | ||
'RTCEncodedAudioFrame' in w$2, | ||
('' + w$2.Intl) === '[object Intl]', | ||
]) >= 2; | ||
} | ||
const n$3 = navigator; | ||
var n$3 = navigator; | ||
function getLanguages() { | ||
const result = []; | ||
const language = n$3.language || n$3.userLanguage || n$3.browserLanguage || n$3.systemLanguage; | ||
var result = []; | ||
var language = n$3.language || n$3.userLanguage || n$3.browserLanguage || n$3.systemLanguage; | ||
if (language !== undefined) { | ||
@@ -795,3 +890,3 @@ result.push([language]); | ||
else if (typeof n$3.languages === 'string') { | ||
const languages = n$3.languages; | ||
var languages = n$3.languages; | ||
if (languages) { | ||
@@ -812,7 +907,7 @@ result.push(languages.split(',')); | ||
const w$3 = window; | ||
var w$3 = window; | ||
function getScreenResolution() { | ||
// Some browsers return screen resolution as strings, e.g. "1200", instead of a number, e.g. 1200. | ||
// I suspect it's done by certain plugins that randomize browser properties to prevent fingerprinting. | ||
const dimensions = [toInt(w$3.screen.width), toInt(w$3.screen.height)]; | ||
var dimensions = [toInt(w$3.screen.width), toInt(w$3.screen.height)]; | ||
dimensions.sort().reverse(); | ||
@@ -822,3 +917,3 @@ return dimensions; | ||
const w$4 = window; | ||
var w$4 = window; | ||
function getAvailableScreenResolution() { | ||
@@ -828,3 +923,3 @@ if (w$4.screen.availWidth && w$4.screen.availHeight) { | ||
// I suspect it's done by certain plugins that randomize browser properties to prevent fingerprinting. | ||
const dimensions = [toInt(w$4.screen.availWidth), toInt(w$4.screen.availHeight)]; | ||
var dimensions = [toInt(w$4.screen.availWidth), toInt(w$4.screen.availHeight)]; | ||
dimensions.sort().reverse(); | ||
@@ -839,3 +934,3 @@ return dimensions; | ||
// sometimes hardware concurrency is a string | ||
const concurrency = toInt(navigator.hardwareConcurrency); | ||
var concurrency = toInt(navigator.hardwareConcurrency); | ||
return isNaN(concurrency) ? 1 : concurrency; | ||
@@ -849,6 +944,13 @@ } | ||
function getTimezoneOffset() { | ||
return new Date().getTimezoneOffset(); | ||
var currentYear = new Date().getFullYear(); | ||
// The timezone offset may change over time due to daylight saving time (DST) shifts. | ||
// The non-DST timezone offset is used as the result timezone offset. | ||
// Since the DST season differs in the northern and the southern hemispheres, | ||
// both January and July timezones offsets are considered. | ||
return Math.max( | ||
// `getTimezoneOffset` returns a number as a string in some unidentified cases | ||
toFloat(new Date(currentYear, 0, 1).getTimezoneOffset()), toFloat(new Date(currentYear, 6, 1).getTimezoneOffset())); | ||
} | ||
const w$5 = window; | ||
var w$5 = window; | ||
function getTimezone() { | ||
@@ -886,3 +988,3 @@ var _a; | ||
// visitor identifier in normal and private modes. | ||
if (isIEOrOldEdge()) { | ||
if (isTrident() || isEdgeHTML()) { | ||
return undefined; | ||
@@ -946,3 +1048,3 @@ } | ||
const d$2 = document; | ||
var d$2 = document; | ||
/** | ||
@@ -956,2 +1058,6 @@ * navigator.cookieEnabled cannot detect custom or nuanced cookie blocking configurations. For example, when blocking | ||
function areCookiesEnabled() { | ||
// Taken from here: https://github.com/Modernizr/Modernizr/blob/master/feature-detects/cookies.js | ||
// navigator.cookieEnabled cannot detect custom or nuanced cookie blocking configurations. For example, when blocking | ||
// cookies via the Advanced Privacy Settings in IE9, it always returns true. And there have been issues in the past | ||
// with site-specific exceptions. Don't rely on it. | ||
// try..catch because some in situations `document.cookie` is exposed but throws a | ||
@@ -963,3 +1069,3 @@ // SecurityError if you try to access it; e.g. documents created from data URIs | ||
d$2.cookie = 'cookietest=1'; | ||
const result = d$2.cookie.indexOf('cookietest=') !== -1; | ||
var result = d$2.cookie.indexOf('cookietest=') !== -1; | ||
// Delete cookie | ||
@@ -980,3 +1086,3 @@ d$2.cookie = 'cookietest=1; expires=Thu, 01-Jan-1970 00:00:01 GMT'; | ||
*/ | ||
const sources = { | ||
var sources = { | ||
// Expected errors and default values must be handled inside the functions | ||
@@ -1020,22 +1126,42 @@ osCpu: getOsCpu, | ||
function getComponents(sources, sourceOptions, excludeSources) { | ||
return __awaiter(this, void 0, void 0, function* () { | ||
let timestamp = Date.now(); | ||
const components = {}; | ||
for (const sourceKey of Object.keys(sources)) { | ||
if (!excludes(excludeSources, sourceKey)) { | ||
continue; | ||
return __awaiter(this, void 0, void 0, function () { | ||
var timestamp, components, _i, _a, sourceKey, result, error_1, nextTimestamp; | ||
var _b; | ||
return __generator(this, function (_c) { | ||
switch (_c.label) { | ||
case 0: | ||
timestamp = Date.now(); | ||
components = {}; | ||
_i = 0, _a = Object.keys(sources); | ||
_c.label = 1; | ||
case 1: | ||
if (!(_i < _a.length)) return [3 /*break*/, 7]; | ||
sourceKey = _a[_i]; | ||
if (!excludes(excludeSources, sourceKey)) { | ||
return [3 /*break*/, 6]; | ||
} | ||
result = void 0; | ||
_c.label = 2; | ||
case 2: | ||
_c.trys.push([2, 4, , 5]); | ||
_b = {}; | ||
return [4 /*yield*/, sources[sourceKey](sourceOptions)]; | ||
case 3: | ||
result = (_b.value = _c.sent(), _b); | ||
return [3 /*break*/, 5]; | ||
case 4: | ||
error_1 = _c.sent(); | ||
result = error_1 && typeof error_1 === 'object' && 'message' in error_1 ? { error: error_1 } : { error: { message: error_1 } }; | ||
return [3 /*break*/, 5]; | ||
case 5: | ||
nextTimestamp = Date.now(); | ||
components[sourceKey] = __assign(__assign({}, result), { duration: nextTimestamp - timestamp }); // TypeScript has beaten me here | ||
timestamp = nextTimestamp; | ||
_c.label = 6; | ||
case 6: | ||
_i++; | ||
return [3 /*break*/, 1]; | ||
case 7: return [2 /*return*/, components]; | ||
} | ||
let result; | ||
let nextTimestamp; | ||
try { | ||
result = { value: yield sources[sourceKey](sourceOptions) }; | ||
} | ||
catch (error) { | ||
result = error && typeof error === 'object' && 'message' in error ? { error } : { error: { message: error } }; | ||
} | ||
nextTimestamp = Date.now(); | ||
components[sourceKey] = Object.assign(Object.assign({}, result), { duration: nextTimestamp - timestamp }); // TypeScript has beaten me here | ||
timestamp = nextTimestamp; | ||
} | ||
return components; | ||
}); | ||
}); | ||
@@ -1051,7 +1177,8 @@ } | ||
function componentsToCanonicalString(components) { | ||
let result = ''; | ||
for (const componentKey of Object.keys(components)) { | ||
const component = components[componentKey]; | ||
const value = component.error ? 'error' : JSON.stringify(component.value); | ||
result += `${result ? '|' : ''}${componentKey.replace(/([:|\\])/g, '\\$1')}:${value}`; | ||
var result = ''; | ||
for (var _i = 0, _a = Object.keys(components); _i < _a.length; _i++) { | ||
var componentKey = _a[_i]; | ||
var component = components[componentKey]; | ||
var value = component.error ? 'error' : JSON.stringify(component.value); | ||
result += "" + (result ? '|' : '') + componentKey.replace(/([:|\\])/g, '\\$1') + ":" + value; | ||
} | ||
@@ -1061,6 +1188,6 @@ return result; | ||
function componentsToDebugString(components) { | ||
return JSON.stringify(components, (_key, value) => { | ||
return JSON.stringify(components, function (_key, value) { | ||
var _a; | ||
if (value instanceof Error) { | ||
return Object.assign(Object.assign({}, value), { message: value.message, stack: (_a = value.stack) === null || _a === void 0 ? void 0 : _a.split('\n') }); | ||
return __assign(__assign({}, value), { message: value.message, stack: (_a = value.stack) === null || _a === void 0 ? void 0 : _a.split('\n') }); | ||
} | ||
@@ -1078,6 +1205,6 @@ return value; | ||
function makeLazyGetResult(components) { | ||
let visitorIdCache; | ||
var visitorIdCache; | ||
// A plain class isn't used because its getters and setters aren't enumerable. | ||
return { | ||
components, | ||
components: components, | ||
get visitorId() { | ||
@@ -1098,34 +1225,51 @@ if (visitorIdCache === undefined) { | ||
*/ | ||
class OpenAgent { | ||
var OpenAgent = /** @class */ (function () { | ||
function OpenAgent() { | ||
} | ||
/** | ||
* @inheritDoc | ||
*/ | ||
get(options = {}) { | ||
return __awaiter(this, void 0, void 0, function* () { | ||
const components = yield getBuiltinComponents(); | ||
const result = makeLazyGetResult(components); | ||
if (options.debug) { | ||
console.log(`Copy the text below to get the debug data: | ||
\`\`\` | ||
version: ${version} | ||
getOptions: ${JSON.stringify(options, undefined, 2)} | ||
visitorId: ${result.visitorId} | ||
components: ${componentsToDebugString(components)} | ||
\`\`\``); | ||
} | ||
return result; | ||
OpenAgent.prototype.get = function (options) { | ||
if (options === void 0) { options = {}; } | ||
return __awaiter(this, void 0, void 0, function () { | ||
var components, result; | ||
return __generator(this, function (_a) { | ||
switch (_a.label) { | ||
case 0: return [4 /*yield*/, getBuiltinComponents()]; | ||
case 1: | ||
components = _a.sent(); | ||
result = makeLazyGetResult(components); | ||
if (options.debug) { | ||
// console.log is ok here because it's under a debug clause | ||
// eslint-disable-next-line no-console | ||
console.log("Copy the text below to get the debug data:\n\n```\nversion: " + version + "\ngetOptions: " + JSON.stringify(options, undefined, 2) + "\nvisitorId: " + result.visitorId + "\ncomponents: " + componentsToDebugString(components) + "\n```"); | ||
} | ||
return [2 /*return*/, result]; | ||
} | ||
}); | ||
}); | ||
} | ||
} | ||
}; | ||
return OpenAgent; | ||
}()); | ||
/** | ||
* Builds an instance of Agent and waits a delay required for a proper operation. | ||
*/ | ||
function load({ delayFallback = 50 } = {}) { | ||
return __awaiter(this, void 0, void 0, function* () { | ||
// A delay is required to ensure consistent entropy components. | ||
// See https://github.com/fingerprintjs/fingerprintjs/issues/254 | ||
// and https://github.com/fingerprintjs/fingerprintjs/issues/307 | ||
yield requestIdleCallbackIfAvailable(delayFallback); | ||
return new OpenAgent(); | ||
function load(_a) { | ||
var _b = (_a === void 0 ? {} : _a).delayFallback, delayFallback = _b === void 0 ? 50 : _b; | ||
return __awaiter(this, void 0, void 0, function () { | ||
return __generator(this, function (_c) { | ||
switch (_c.label) { | ||
case 0: | ||
// A delay is required to ensure consistent entropy components. | ||
// See https://github.com/fingerprintjs/fingerprintjs/issues/254 | ||
// and https://github.com/fingerprintjs/fingerprintjs/issues/307 | ||
return [4 /*yield*/, requestIdleCallbackIfAvailable(delayFallback)]; | ||
case 1: | ||
// A delay is required to ensure consistent entropy components. | ||
// See https://github.com/fingerprintjs/fingerprintjs/issues/254 | ||
// and https://github.com/fingerprintjs/fingerprintjs/issues/307 | ||
_c.sent(); | ||
return [2 /*return*/, new OpenAgent()]; | ||
} | ||
}); | ||
}); | ||
@@ -1136,6 +1280,6 @@ } | ||
// It should contain all the public exported values. | ||
var index = { load, hashComponents, componentsToDebugString }; | ||
var index = { load: load, hashComponents: hashComponents, componentsToDebugString: componentsToDebugString }; | ||
// The exports below are for private usage. They may change unexpectedly. Use them at your own risk. | ||
/** Not documented, out of Semantic Versioning, usage is at your own risk */ | ||
const murmurX64Hash128 = x64hash128; | ||
var murmurX64Hash128 = x64hash128; | ||
@@ -1148,4 +1292,6 @@ exports.componentsToDebugString = componentsToDebugString; | ||
exports.isDesktopSafari = isDesktopSafari; | ||
exports.isEdgeHTML = isEdgeHTML; | ||
exports.isGecko = isGecko; | ||
exports.isIEOrOldEdge = isIEOrOldEdge; | ||
exports.isTrident = isTrident; | ||
exports.isWebKit = isWebKit; | ||
exports.load = load; | ||
@@ -1152,0 +1298,0 @@ exports.murmurX64Hash128 = murmurX64Hash128; |
/** | ||
* FingerprintJS v3.0.0 - Copyright (c) FingerprintJS, Inc, 2020 (https://fingerprintjs.com) | ||
* FingerprintJS v3.0.1 - Copyright (c) FingerprintJS, Inc, 2020 (https://fingerprintjs.com) | ||
* Licensed under the MIT (http://www.opensource.org/licenses/mit-license.php) license. | ||
@@ -9,2 +9,2 @@ * | ||
var FingerprintJS=function(t){"use strict";function e(t,e){t=[t[0]>>>16,65535&t[0],t[1]>>>16,65535&t[1]],e=[e[0]>>>16,65535&e[0],e[1]>>>16,65535&e[1]];var n=[0,0,0,0];return n[3]+=t[3]+e[3],n[2]+=n[3]>>>16,n[3]&=65535,n[2]+=t[2]+e[2],n[1]+=n[2]>>>16,n[2]&=65535,n[1]+=t[1]+e[1],n[0]+=n[1]>>>16,n[1]&=65535,n[0]+=t[0]+e[0],n[0]&=65535,[n[0]<<16|n[1],n[2]<<16|n[3]]}function n(t,e){t=[t[0]>>>16,65535&t[0],t[1]>>>16,65535&t[1]],e=[e[0]>>>16,65535&e[0],e[1]>>>16,65535&e[1]];var n=[0,0,0,0];return n[3]+=t[3]*e[3],n[2]+=n[3]>>>16,n[3]&=65535,n[2]+=t[2]*e[3],n[1]+=n[2]>>>16,n[2]&=65535,n[2]+=t[3]*e[2],n[1]+=n[2]>>>16,n[2]&=65535,n[1]+=t[1]*e[3],n[0]+=n[1]>>>16,n[1]&=65535,n[1]+=t[2]*e[2],n[0]+=n[1]>>>16,n[1]&=65535,n[1]+=t[3]*e[1],n[0]+=n[1]>>>16,n[1]&=65535,n[0]+=t[0]*e[3]+t[1]*e[2]+t[2]*e[1]+t[3]*e[0],n[0]&=65535,[n[0]<<16|n[1],n[2]<<16|n[3]]}function o(t,e){return 32===(e%=64)?[t[1],t[0]]:e<32?[t[0]<<e|t[1]>>>32-e,t[1]<<e|t[0]>>>32-e]:(e-=32,[t[1]<<e|t[0]>>>32-e,t[0]<<e|t[1]>>>32-e])}function r(t,e){return 0===(e%=64)?t:e<32?[t[0]<<e|t[1]>>>32-e,t[1]<<e]:[t[1]<<e-32,0]}function i(t,e){return[t[0]^e[0],t[1]^e[1]]}function a(t){return t=i(t,[0,t[0]>>>1]),t=i(t=n(t,[4283543511,3981806797]),[0,t[0]>>>1]),t=i(t=n(t,[3301882366,444984403]),[0,t[0]>>>1])}function c(t,c){c=c||0;for(var s=(t=t||"").length%16,u=t.length-s,l=[0,c],d=[0,c],f=[0,0],h=[0,0],g=[2277735313,289559509],m=[1291169091,658871167],p=0;p<u;p+=16)f=[255&t.charCodeAt(p+4)|(255&t.charCodeAt(p+5))<<8|(255&t.charCodeAt(p+6))<<16|(255&t.charCodeAt(p+7))<<24,255&t.charCodeAt(p)|(255&t.charCodeAt(p+1))<<8|(255&t.charCodeAt(p+2))<<16|(255&t.charCodeAt(p+3))<<24],h=[255&t.charCodeAt(p+12)|(255&t.charCodeAt(p+13))<<8|(255&t.charCodeAt(p+14))<<16|(255&t.charCodeAt(p+15))<<24,255&t.charCodeAt(p+8)|(255&t.charCodeAt(p+9))<<8|(255&t.charCodeAt(p+10))<<16|(255&t.charCodeAt(p+11))<<24],f=o(f=n(f,g),31),l=e(l=o(l=i(l,f=n(f,m)),27),d),l=e(n(l,[0,5]),[0,1390208809]),h=o(h=n(h,m),33),d=e(d=o(d=i(d,h=n(h,g)),31),l),d=e(n(d,[0,5]),[0,944331445]);switch(f=[0,0],h=[0,0],s){case 15:h=i(h,r([0,t.charCodeAt(p+14)],48));case 14:h=i(h,r([0,t.charCodeAt(p+13)],40));case 13:h=i(h,r([0,t.charCodeAt(p+12)],32));case 12:h=i(h,r([0,t.charCodeAt(p+11)],24));case 11:h=i(h,r([0,t.charCodeAt(p+10)],16));case 10:h=i(h,r([0,t.charCodeAt(p+9)],8));case 9:h=n(h=i(h,[0,t.charCodeAt(p+8)]),m),d=i(d,h=n(h=o(h,33),g));case 8:f=i(f,r([0,t.charCodeAt(p+7)],56));case 7:f=i(f,r([0,t.charCodeAt(p+6)],48));case 6:f=i(f,r([0,t.charCodeAt(p+5)],40));case 5:f=i(f,r([0,t.charCodeAt(p+4)],32));case 4:f=i(f,r([0,t.charCodeAt(p+3)],24));case 3:f=i(f,r([0,t.charCodeAt(p+2)],16));case 2:f=i(f,r([0,t.charCodeAt(p+1)],8));case 1:f=n(f=i(f,[0,t.charCodeAt(p)]),g),l=i(l,f=n(f=o(f,31),m))}return l=e(l=i(l,[0,t.length]),d=i(d,[0,t.length])),d=e(d,l),l=e(l=a(l),d=a(d)),d=e(d,l),("00000000"+(l[0]>>>0).toString(16)).slice(-8)+("00000000"+(l[1]>>>0).toString(16)).slice(-8)+("00000000"+(d[0]>>>0).toString(16)).slice(-8)+("00000000"+(d[1]>>>0).toString(16)).slice(-8)}function s(t,e,n,o){return new(n||(n=Promise))((function(r,i){function a(t){try{s(o.next(t))}catch(e){i(e)}}function c(t){try{s(o.throw(t))}catch(e){i(e)}}function s(t){var e;t.done?r(t.value):(e=t.value,e instanceof n?e:new n((function(t){t(e)}))).then(a,c)}s((o=o.apply(t,e||[])).next())}))}function u(t){return"number"==typeof t?0|t:parseInt(t)}function l(t){return t.reduce(((t,e)=>t+(e?1:0)),0)}const d=navigator,f=window;const h=document,g=["monospace","sans-serif","serif"],m=["sans-serif-thin","ARNO PRO","Agency FB","Arabic Typesetting","Arial Unicode MS","AvantGarde Bk BT","BankGothic Md BT","Batang","Bitstream Vera Sans Mono","Calibri","Century","Century Gothic","Clarendon","EUROSTILE","Franklin Gothic","Futura Bk BT","Futura Md BT","GOTHAM","Gill Sans","HELV","Haettenschweiler","Helvetica Neue","Humanst521 BT","Leelawadee","Letter Gothic","Levenim MT","Lucida Bright","Lucida Sans","Menlo","MS Mincho","MS Outlook","MS Reference Specialty","MS UI Gothic","MT Extra","MYRIAD PRO","Marlett","Meiryo UI","Microsoft Uighur","Minion Pro","Monotype Corsiva","PMingLiU","Pristina","SCRIPTINA","Segoe UI Light","Serifa","SimHei","Small Fonts","Staccato222 BT","TRAJAN PRO","Univers CE 55 Medium","Vrinda","ZWAdobeF"],p={fontStyle:"normal",fontWeight:"normal",letterSpacing:"normal",lineBreak:"auto",lineHeight:"normal",textTransform:"none",textAlign:"left",textDecoration:"none",textShadow:"none",whiteSpace:"normal",wordBreak:"normal",wordSpacing:"normal"};function v(t){return t.toDataURL()}const y=navigator,C=window;const S=window,w=navigator,A=document;function b(){return l(["msWriteProfilerMark"in S,"msLaunchUri"in w,"msSaveBlob"in w])>=2}function M(){return l(["userActivation"in w,"mediaSession"in w,0===w.vendor.indexOf("Google"),"BackgroundFetchManager"in S,"BatteryManager"in S,"webkitMediaStream"in S,"webkitSpeechGrammar"in S])>=5}const T=navigator;const k=window;const O=window;const P=window;const x=document;const I={osCpu:function(){return navigator.oscpu},languages:function(){const t=[],e=T.language||T.userLanguage||T.browserLanguage||T.systemLanguage;if(void 0!==e&&t.push([e]),Array.isArray(T.languages))M()&&l([!("MediaSettingsRange"in S),!("PhotoCapabilities"in S),"RTCEncodedAudioFrame"in S,""+S.Intl=="[object Intl]"])>=2||t.push(T.languages);else if("string"==typeof T.languages){const e=T.languages;e&&t.push(e.split(","))}return t},colorDepth:function(){return window.screen.colorDepth},deviceMemory:function(){return navigator.deviceMemory},screenResolution:function(){const t=[u(k.screen.width),u(k.screen.height)];return t.sort().reverse(),t},availableScreenResolution:function(){if(O.screen.availWidth&&O.screen.availHeight){const t=[u(O.screen.availWidth),u(O.screen.availHeight)];return t.sort().reverse(),t}},hardwareConcurrency:function(){try{const t=u(navigator.hardwareConcurrency);return isNaN(t)?1:t}catch(t){return 1}},timezoneOffset:function(){return(new Date).getTimezoneOffset()},timezone:function(){var t;if(null===(t=P.Intl)||void 0===t?void 0:t.DateTimeFormat)return(new P.Intl.DateTimeFormat).resolvedOptions().timeZone},sessionStorage:function(){try{return!!window.sessionStorage}catch(t){return!0}},localStorage:function(){try{return!!window.localStorage}catch(t){return!0}},indexedDB:function(){if(!b())try{return!!window.indexedDB}catch(t){return!0}},openDatabase:function(){return!!window.openDatabase},cpuClass:function(){return navigator.cpuClass},platform:function(){return navigator.platform},plugins:function(){if(!navigator.plugins)return;const t=[];for(let e=0;e<navigator.plugins.length;++e){const n=navigator.plugins[e];if(!n)continue;const o=[];for(const t of n)o.push({type:t.type,suffixes:t.suffixes});t.push({name:n.name,description:n.description,mimeTypes:o})}return t},canvas:function(){const[t,e]=function(){const t=document.createElement("canvas");return t.width=240,t.height=140,t.style.display="inline",[t,t.getContext("2d")]}();if(!function(t,e){return!(!e||!t.toDataURL)}(t,e))return{winding:!1,data:""};e.rect(0,0,10,10),e.rect(2,2,6,6);const n=!e.isPointInPath(5,5,"evenodd");e.textBaseline="alphabetic",e.fillStyle="#f60",e.fillRect(125,1,62,20),e.fillStyle="#069",e.font="11pt no-real-font-123";const o="Cwm fjordbank š gly";return e.fillText(o,2,15),e.fillStyle="rgba(102, 204, 0, 0.2)",e.font="18pt Arial",e.fillText(o,4,45),e.globalCompositeOperation="multiply",e.fillStyle="rgb(255,0,255)",e.beginPath(),e.arc(50,50,50,0,2*Math.PI,!0),e.closePath(),e.fill(),e.fillStyle="rgb(0,255,255)",e.beginPath(),e.arc(100,50,50,0,2*Math.PI,!0),e.closePath(),e.fill(),e.fillStyle="rgb(255,255,0)",e.beginPath(),e.arc(75,100,50,0,2*Math.PI,!0),e.closePath(),e.fill(),e.fillStyle="rgb(255,0,255)",e.arc(75,75,75,0,2*Math.PI,!0),e.arc(75,75,25,0,2*Math.PI,!0),e.fill("evenodd"),{winding:n,data:v(t)}},touchSupport:function(){let t,e=0;void 0!==y.maxTouchPoints?e=u(y.maxTouchPoints):void 0!==y.msMaxTouchPoints&&(e=y.msMaxTouchPoints);try{document.createEvent("TouchEvent"),t=!0}catch(n){t=!1}return{maxTouchPoints:e,touchEvent:t,touchStart:"ontouchstart"in C}},fonts:function(){const t=h.body,e=h.createElement("div"),n=h.createElement("div"),o={},r={},i=()=>{const t=h.createElement("span");return Object.assign(t.style,p,{position:"absolute",left:"-9999px",fontSize:"48px"}),t.textContent="mmMwWLliI0O&1",t},a=(t,e)=>{const n=i();return n.style.fontFamily=`'${t}',${e}`,n},c=t=>g.some(((e,n)=>t[n].offsetWidth!==o[e]||t[n].offsetHeight!==r[e])),s=g.map((t=>{const n=i();return n.style.fontFamily=t,e.appendChild(n),n}));t.appendChild(e);for(let d=0,f=g.length;d<f;d++)o[g[d]]=s[d].offsetWidth,r[g[d]]=s[d].offsetHeight;const u=(()=>{const t={};for(const e of m)t[e]=g.map((t=>{const o=a(e,t);return n.appendChild(o),o}));return t})();t.appendChild(n);const l=[];for(let d=0,f=m.length;d<f;d++)c(u[m[d]])&&l.push(m[d]);return t.removeChild(n),t.removeChild(e),l},audio:function(){return s(this,void 0,void 0,(function*(){if(d.userAgent.match(/OS 11.+Version\/11.+Safari/))return-1;const t=f.OfflineAudioContext||f.webkitOfflineAudioContext;if(!t)return-2;const e=new t(1,44100,44100),n=e.createOscillator();n.type="triangle",n.frequency.setValueAtTime(1e4,e.currentTime);const o=e.createDynamicsCompressor();for(const[r,i]of[["threshold",-50],["knee",40],["ratio",12],["reduction",-20],["attack",0],["release",.25]])"function"==typeof o[r].setValueAtTime&&o[r].setValueAtTime(i,e.currentTime);return n.connect(o),o.connect(e.destination),n.start(0),e.startRendering(),new Promise((t=>{const r=setTimeout((()=>{e.oncomplete=()=>{},t(-3)}),1e3);e.oncomplete=e=>{let i;try{clearTimeout(r),i=e.renderedBuffer.getChannelData(0).slice(4500,5e3).reduce(((t,e)=>t+Math.abs(e)),0),n.disconnect(),o.disconnect()}catch(a){return void t(-4)}t(i)}}))}))},pluginsSupport:function(){return void 0!==navigator.plugins},productSub:function(){return navigator.productSub},emptyEvalLength:function(){return eval.toString().length},errorFF:function(){try{throw"a"}catch(t){try{return t.toSource(),!0}catch(e){return!1}}},vendor:function(){return navigator.vendor},chrome:function(){return void 0!==window.chrome},cookiesEnabled:function(){try{x.cookie="cookietest=1";const t=-1!==x.cookie.indexOf("cookietest=");return x.cookie="cookietest=1; expires=Thu, 01-Jan-1970 00:00:01 GMT",t}catch(t){return!1}}};function B(t,e,n){return s(this,void 0,void 0,(function*(){let o=Date.now();const r={};for(const a of Object.keys(t)){if(function(t,e){for(let n=0,o=t.length;n<o;++n)if(t[n]===e)return!0;return!1}(n,a))continue;let c,s;try{c={value:yield t[a](e)}}catch(i){c=i&&"object"==typeof i&&"message"in i?{error:i}:{error:{message:i}}}s=Date.now(),r[a]=Object.assign(Object.assign({},c),{duration:s-o}),o=s}return r}))}function D(t){return JSON.stringify(t,((t,e)=>{var n;return e instanceof Error?Object.assign(Object.assign({},e),{message:e.message,stack:null===(n=e.stack)||void 0===n?void 0:n.split("\n")}):e}),2)}function E(t){return c(function(t){let e="";for(const n of Object.keys(t)){const o=t[n],r=o.error?"error":JSON.stringify(o.value);e+=`${e?"|":""}${n.replace(/([:|\\])/g,"\\$1")}:${r}`}return e}(t))}class R{get(t={}){return s(this,void 0,void 0,(function*(){const e=yield B(I,void 0,[]),n=function(t){let e;return{components:t,get visitorId(){return void 0===e&&(e=E(this.components)),e},set visitorId(t){e=t}}}(e);return t.debug&&console.log(`Copy the text below to get the debug data:\n\n\`\`\`\nversion: 3.0.0\ngetOptions: ${JSON.stringify(t,void 0,2)}\nvisitorId: ${n.visitorId}\ncomponents: ${D(e)}\n\`\`\``),n}))}}function L({delayFallback:t=50}={}){return s(this,void 0,void 0,(function*(){var e;return yield(e=t,new Promise((t=>{window.requestIdleCallback?window.requestIdleCallback((()=>t())):setTimeout(t,e)}))),new R}))}var F={load:L,hashComponents:E,componentsToDebugString:D};const G=c;return t.componentsToDebugString=D,t.default=F,t.getComponents=B,t.hashComponents=E,t.isChromium=M,t.isDesktopSafari=function(){return"safari"in S},t.isGecko=function(){var t;return l(["buildID"in w,(null===(t=A.documentElement)||void 0===t?void 0:t.style)&&"MozAppearance"in A.documentElement.style,"MediaRecorderErrorEvent"in S,"mozInnerScreenX"in S,"CSSMozDocumentRule"in S,"CanvasCaptureMediaStream"in S])>=4},t.isIEOrOldEdge=b,t.load=L,t.murmurX64Hash128=G,t}({}); | ||
var FingerprintJS=function(e){"use strict";function t(e,t){e=[e[0]>>>16,65535&e[0],e[1]>>>16,65535&e[1]],t=[t[0]>>>16,65535&t[0],t[1]>>>16,65535&t[1]];var n=[0,0,0,0];return n[3]+=e[3]+t[3],n[2]+=n[3]>>>16,n[3]&=65535,n[2]+=e[2]+t[2],n[1]+=n[2]>>>16,n[2]&=65535,n[1]+=e[1]+t[1],n[0]+=n[1]>>>16,n[1]&=65535,n[0]+=e[0]+t[0],n[0]&=65535,[n[0]<<16|n[1],n[2]<<16|n[3]]}function n(e,t){e=[e[0]>>>16,65535&e[0],e[1]>>>16,65535&e[1]],t=[t[0]>>>16,65535&t[0],t[1]>>>16,65535&t[1]];var n=[0,0,0,0];return n[3]+=e[3]*t[3],n[2]+=n[3]>>>16,n[3]&=65535,n[2]+=e[2]*t[3],n[1]+=n[2]>>>16,n[2]&=65535,n[2]+=e[3]*t[2],n[1]+=n[2]>>>16,n[2]&=65535,n[1]+=e[1]*t[3],n[0]+=n[1]>>>16,n[1]&=65535,n[1]+=e[2]*t[2],n[0]+=n[1]>>>16,n[1]&=65535,n[1]+=e[3]*t[1],n[0]+=n[1]>>>16,n[1]&=65535,n[0]+=e[0]*t[3]+e[1]*t[2]+e[2]*t[1]+e[3]*t[0],n[0]&=65535,[n[0]<<16|n[1],n[2]<<16|n[3]]}function r(e,t){return 32===(t%=64)?[e[1],e[0]]:t<32?[e[0]<<t|e[1]>>>32-t,e[1]<<t|e[0]>>>32-t]:(t-=32,[e[1]<<t|e[0]>>>32-t,e[0]<<t|e[1]>>>32-t])}function o(e,t){return 0===(t%=64)?e:t<32?[e[0]<<t|e[1]>>>32-t,e[1]<<t]:[e[1]<<t-32,0]}function i(e,t){return[e[0]^t[0],e[1]^t[1]]}function a(e){return e=i(e,[0,e[0]>>>1]),e=i(e=n(e,[4283543511,3981806797]),[0,e[0]>>>1]),e=i(e=n(e,[3301882366,444984403]),[0,e[0]>>>1])}function c(e,c){c=c||0;var u,s=(e=e||"").length%16,l=e.length-s,f=[0,c],d=[0,c],h=[0,0],v=[0,0],g=[2277735313,289559509],p=[1291169091,658871167];for(u=0;u<l;u+=16)h=[255&e.charCodeAt(u+4)|(255&e.charCodeAt(u+5))<<8|(255&e.charCodeAt(u+6))<<16|(255&e.charCodeAt(u+7))<<24,255&e.charCodeAt(u)|(255&e.charCodeAt(u+1))<<8|(255&e.charCodeAt(u+2))<<16|(255&e.charCodeAt(u+3))<<24],v=[255&e.charCodeAt(u+12)|(255&e.charCodeAt(u+13))<<8|(255&e.charCodeAt(u+14))<<16|(255&e.charCodeAt(u+15))<<24,255&e.charCodeAt(u+8)|(255&e.charCodeAt(u+9))<<8|(255&e.charCodeAt(u+10))<<16|(255&e.charCodeAt(u+11))<<24],h=r(h=n(h,g),31),f=t(f=r(f=i(f,h=n(h,p)),27),d),f=t(n(f,[0,5]),[0,1390208809]),v=r(v=n(v,p),33),d=t(d=r(d=i(d,v=n(v,g)),31),f),d=t(n(d,[0,5]),[0,944331445]);switch(h=[0,0],v=[0,0],s){case 15:v=i(v,o([0,e.charCodeAt(u+14)],48));case 14:v=i(v,o([0,e.charCodeAt(u+13)],40));case 13:v=i(v,o([0,e.charCodeAt(u+12)],32));case 12:v=i(v,o([0,e.charCodeAt(u+11)],24));case 11:v=i(v,o([0,e.charCodeAt(u+10)],16));case 10:v=i(v,o([0,e.charCodeAt(u+9)],8));case 9:v=n(v=i(v,[0,e.charCodeAt(u+8)]),p),d=i(d,v=n(v=r(v,33),g));case 8:h=i(h,o([0,e.charCodeAt(u+7)],56));case 7:h=i(h,o([0,e.charCodeAt(u+6)],48));case 6:h=i(h,o([0,e.charCodeAt(u+5)],40));case 5:h=i(h,o([0,e.charCodeAt(u+4)],32));case 4:h=i(h,o([0,e.charCodeAt(u+3)],24));case 3:h=i(h,o([0,e.charCodeAt(u+2)],16));case 2:h=i(h,o([0,e.charCodeAt(u+1)],8));case 1:h=n(h=i(h,[0,e.charCodeAt(u)]),g),f=i(f,h=n(h=r(h,31),p))}return f=t(f=i(f,[0,e.length]),d=i(d,[0,e.length])),d=t(d,f),f=t(f=a(f),d=a(d)),d=t(d,f),("00000000"+(f[0]>>>0).toString(16)).slice(-8)+("00000000"+(f[1]>>>0).toString(16)).slice(-8)+("00000000"+(d[0]>>>0).toString(16)).slice(-8)+("00000000"+(d[1]>>>0).toString(16)).slice(-8)}var u=function(){return(u=Object.assign||function(e){for(var t,n=1,r=arguments.length;n<r;n++)for(var o in t=arguments[n])Object.prototype.hasOwnProperty.call(t,o)&&(e[o]=t[o]);return e}).apply(this,arguments)};function s(e,t,n,r){return new(n||(n=Promise))((function(o,i){function a(e){try{u(r.next(e))}catch(t){i(t)}}function c(e){try{u(r.throw(e))}catch(t){i(t)}}function u(e){var t;e.done?o(e.value):(t=e.value,t instanceof n?t:new n((function(e){e(t)}))).then(a,c)}u((r=r.apply(e,t||[])).next())}))}function l(e,t){var n,r,o,i,a={label:0,sent:function(){if(1&o[0])throw o[1];return o[1]},trys:[],ops:[]};return i={next:c(0),throw:c(1),return:c(2)},"function"==typeof Symbol&&(i[Symbol.iterator]=function(){return this}),i;function c(i){return function(c){return function(i){if(n)throw new TypeError("Generator is already executing.");for(;a;)try{if(n=1,r&&(o=2&i[0]?r.return:i[0]?r.throw||((o=r.return)&&o.call(r),0):r.next)&&!(o=o.call(r,i[1])).done)return o;switch(r=0,o&&(i=[2&i[0],o.value]),i[0]){case 0:case 1:o=i;break;case 4:return a.label++,{value:i[1],done:!1};case 5:a.label++,r=i[1],i=[0];continue;case 7:i=a.ops.pop(),a.trys.pop();continue;default:if(!(o=a.trys,(o=o.length>0&&o[o.length-1])||6!==i[0]&&2!==i[0])){a=0;continue}if(3===i[0]&&(!o||i[1]>o[0]&&i[1]<o[3])){a.label=i[1];break}if(6===i[0]&&a.label<o[1]){a.label=o[1],o=i;break}if(o&&a.label<o[2]){a.label=o[2],a.ops.push(i);break}o[2]&&a.ops.pop(),a.trys.pop();continue}i=t.call(e,a)}catch(c){i=[6,c],r=0}finally{n=o=0}if(5&i[0])throw i[1];return{value:i[0]?i[1]:void 0,done:!0}}([i,c])}}}function f(e){return"number"==typeof e?0|e:parseInt(e)}function d(e){return"number"==typeof e?e:parseFloat(e)}function h(e){return e.reduce((function(e,t){return e+(t?1:0)}),0)}var v=navigator,g=window;function p(e){return e&&"function"==typeof e.setValueAtTime}var m=document,y=["monospace","sans-serif","serif"],w=["sans-serif-thin","ARNO PRO","Agency FB","Arabic Typesetting","Arial Unicode MS","AvantGarde Bk BT","BankGothic Md BT","Batang","Bitstream Vera Sans Mono","Calibri","Century","Century Gothic","Clarendon","EUROSTILE","Franklin Gothic","Futura Bk BT","Futura Md BT","GOTHAM","Gill Sans","HELV","Haettenschweiler","Helvetica Neue","Humanst521 BT","Leelawadee","Letter Gothic","Levenim MT","Lucida Bright","Lucida Sans","Menlo","MS Mincho","MS Outlook","MS Reference Specialty","MS UI Gothic","MT Extra","MYRIAD PRO","Marlett","Meiryo UI","Microsoft Uighur","Minion Pro","Monotype Corsiva","PMingLiU","Pristina","SCRIPTINA","Segoe UI Light","Serifa","SimHei","Small Fonts","Staccato222 BT","TRAJAN PRO","Univers CE 55 Medium","Vrinda","ZWAdobeF"],S={fontStyle:"normal",fontWeight:"normal",letterSpacing:"normal",lineBreak:"auto",lineHeight:"normal",textTransform:"none",textAlign:"left",textDecoration:"none",textShadow:"none",whiteSpace:"normal",wordBreak:"normal",wordSpacing:"normal",position:"absolute",left:"-9999px",fontSize:"48px"};var b=window,C=navigator,A=document;function M(){return h(["MSCSSMatrix"in b,"msSetImmediate"in b,"msIndexedDB"in b,"msMaxTouchPoints"in C,"msPointerEnabled"in C])>=4}function T(){return h(["msWriteProfilerMark"in b,"MSStream"in b,"msLaunchUri"in C,"msSaveBlob"in C])>=3&&!M()}function k(){return h(["webkitPersistentStorage"in C,"webkitTemporaryStorage"in C,0===C.vendor.indexOf("Google"),"webkitResolveLocalFileSystemURL"in b,"BatteryManager"in b,"webkitMediaStream"in b,"webkitSpeechGrammar"in b])>=5}function x(e){return e.toDataURL()}var P=navigator,O=window;var I=navigator;var D=window;var E=window;var R=window;var B=document;var L={osCpu:function(){return navigator.oscpu},languages:function(){var e=[],t=I.language||I.userLanguage||I.browserLanguage||I.systemLanguage;if(void 0!==t&&e.push([t]),Array.isArray(I.languages))k()&&h([!("MediaSettingsRange"in b),"RTCEncodedAudioFrame"in b,""+b.Intl=="[object Intl]",""+b.Reflect=="[object Reflect]"])>=3||e.push(I.languages);else if("string"==typeof I.languages){var n=I.languages;n&&e.push(n.split(","))}return e},colorDepth:function(){return window.screen.colorDepth},deviceMemory:function(){return navigator.deviceMemory},screenResolution:function(){var e=[f(D.screen.width),f(D.screen.height)];return e.sort().reverse(),e},availableScreenResolution:function(){if(E.screen.availWidth&&E.screen.availHeight){var e=[f(E.screen.availWidth),f(E.screen.availHeight)];return e.sort().reverse(),e}},hardwareConcurrency:function(){try{var e=f(navigator.hardwareConcurrency);return isNaN(e)?1:e}catch(t){return 1}},timezoneOffset:function(){var e=(new Date).getFullYear();return Math.max(d(new Date(e,0,1).getTimezoneOffset()),d(new Date(e,6,1).getTimezoneOffset()))},timezone:function(){var e;if(null===(e=R.Intl)||void 0===e?void 0:e.DateTimeFormat)return(new R.Intl.DateTimeFormat).resolvedOptions().timeZone},sessionStorage:function(){try{return!!window.sessionStorage}catch(e){return!0}},localStorage:function(){try{return!!window.localStorage}catch(e){return!0}},indexedDB:function(){if(!M()&&!T())try{return!!window.indexedDB}catch(e){return!0}},openDatabase:function(){return!!window.openDatabase},cpuClass:function(){return navigator.cpuClass},platform:function(){return navigator.platform},plugins:function(){if(M())return[];if(navigator.plugins){for(var e=[],t=0;t<navigator.plugins.length;++t){var n=navigator.plugins[t];if(n){for(var r=[],o=0;o<n.length;++o){var i=n[o];r.push({type:i.type,suffixes:i.suffixes})}e.push({name:n.name,description:n.description,mimeTypes:r})}}return e}},canvas:function(){var e=function(){var e=document.createElement("canvas");return e.width=240,e.height=140,e.style.display="inline",[e,e.getContext("2d")]}(),t=e[0],n=e[1];if(!function(e,t){return!(!t||!e.toDataURL)}(t,n))return{winding:!1,data:""};n.rect(0,0,10,10),n.rect(2,2,6,6);var r=!n.isPointInPath(5,5,"evenodd");n.textBaseline="alphabetic",n.fillStyle="#f60",n.fillRect(125,1,62,20),n.fillStyle="#069",n.font="11pt no-real-font-123";var o="Cwm fjordbank š gly";return n.fillText(o,2,15),n.fillStyle="rgba(102, 204, 0, 0.2)",n.font="18pt Arial",n.fillText(o,4,45),n.globalCompositeOperation="multiply",n.fillStyle="rgb(255,0,255)",n.beginPath(),n.arc(50,50,50,0,2*Math.PI,!0),n.closePath(),n.fill(),n.fillStyle="rgb(0,255,255)",n.beginPath(),n.arc(100,50,50,0,2*Math.PI,!0),n.closePath(),n.fill(),n.fillStyle="rgb(255,255,0)",n.beginPath(),n.arc(75,100,50,0,2*Math.PI,!0),n.closePath(),n.fill(),n.fillStyle="rgb(255,0,255)",n.arc(75,75,75,0,2*Math.PI,!0),n.arc(75,75,25,0,2*Math.PI,!0),n.fill("evenodd"),{winding:r,data:x(t)}},touchSupport:function(){var e,t=0;void 0!==P.maxTouchPoints?t=f(P.maxTouchPoints):void 0!==P.msMaxTouchPoints&&(t=P.msMaxTouchPoints);try{document.createEvent("TouchEvent"),e=!0}catch(n){e=!1}return{maxTouchPoints:t,touchEvent:e,touchStart:"ontouchstart"in O}},fonts:function(){var e=m.body,t=m.createElement("div"),n=m.createElement("div"),r={},o={},i=function(){var e=m.createElement("span");e.textContent="mmMwWLliI0O&1";for(var t=0,n=Object.keys(S);t<n.length;t++){var r=n[t];e.style[r]=S[r]}return e},a=function(e){return y.some((function(t,n){return e[n].offsetWidth!==r[t]||e[n].offsetHeight!==o[t]}))},c=y.map((function(e){var n=i();return n.style.fontFamily=e,t.appendChild(n),n}));e.appendChild(t);for(var u=0,s=y.length;u<s;u++)r[y[u]]=c[u].offsetWidth,o[y[u]]=c[u].offsetHeight;var l=function(){for(var e={},t=function(t){e[t]=y.map((function(e){var r=function(e,t){var n=i();return n.style.fontFamily="'"+e+"',"+t,n}(t,e);return n.appendChild(r),r}))},r=0,o=w;r<o.length;r++){t(o[r])}return e}();e.appendChild(n);for(var f=[],d=0,h=w.length;d<h;d++)a(l[w[d]])&&f.push(w[d]);return e.removeChild(n),e.removeChild(t),f},audio:function(){return s(this,void 0,void 0,(function(){var e,t,n,r,o,i,a,c,u,s;return l(this,(function(l){if(v.userAgent.match(/OS 11.+Version\/11.+Safari/))return[2,-1];if(!(e=g.OfflineAudioContext||g.webkitOfflineAudioContext))return[2,-2];for(t=new e(1,44100,44100),(n=t.createOscillator()).type="triangle",n.frequency.setValueAtTime(1e4,t.currentTime),r=t.createDynamicsCompressor(),o=0,i=[["threshold",-50],["knee",40],["ratio",12],["reduction",-20],["attack",0],["release",.25]];o<i.length;o++)c=(a=i[o])[0],u=a[1],p(s=r[c])&&s.setValueAtTime(u,t.currentTime);return n.connect(r),r.connect(t.destination),n.start(0),t.startRendering(),[2,new Promise((function(e){var o=setTimeout((function(){t.oncomplete=null,e(-3)}),1e3);t.oncomplete=function(t){var i;try{clearTimeout(o),i=t.renderedBuffer.getChannelData(0).slice(4500,5e3).reduce((function(e,t){return e+Math.abs(t)}),0),n.disconnect(),r.disconnect()}catch(a){return void e(-4)}e(i)}}))]}))}))},pluginsSupport:function(){return void 0!==navigator.plugins},productSub:function(){return navigator.productSub},emptyEvalLength:function(){return eval.toString().length},errorFF:function(){try{throw"a"}catch(e){try{return e.toSource(),!0}catch(t){return!1}}},vendor:function(){return navigator.vendor},chrome:function(){return void 0!==window.chrome},cookiesEnabled:function(){try{B.cookie="cookietest=1";var e=-1!==B.cookie.indexOf("cookietest=");return B.cookie="cookietest=1; expires=Thu, 01-Jan-1970 00:00:01 GMT",e}catch(t){return!1}}};function F(e,t,n){return s(this,void 0,void 0,(function(){var r,o,i,a,c,s,f,d,h;return l(this,(function(l){switch(l.label){case 0:r=Date.now(),o={},i=0,a=Object.keys(e),l.label=1;case 1:if(!(i<a.length))return[3,7];if(c=a[i],function(e,t){for(var n=0,r=e.length;n<r;++n)if(e[n]===t)return!0;return!1}(n,c))return[3,6];s=void 0,l.label=2;case 2:return l.trys.push([2,4,,5]),h={},[4,e[c](t)];case 3:return h.value=l.sent(),s=h,[3,5];case 4:return f=l.sent(),s=f&&"object"==typeof f&&"message"in f?{error:f}:{error:{message:f}},[3,5];case 5:d=Date.now(),o[c]=u(u({},s),{duration:d-r}),r=d,l.label=6;case 6:return i++,[3,1];case 7:return[2,o]}}))}))}function G(e){return JSON.stringify(e,(function(e,t){var n;return t instanceof Error?u(u({},t),{message:t.message,stack:null===(n=t.stack)||void 0===n?void 0:n.split("\n")}):t}),2)}function H(e){return c(function(e){for(var t="",n=0,r=Object.keys(e);n<r.length;n++){var o=r[n],i=e[o],a=i.error?"error":JSON.stringify(i.value);t+=(t?"|":"")+o.replace(/([:|\\])/g,"\\$1")+":"+a}return t}(e))}var U=function(){function e(){}return e.prototype.get=function(e){return void 0===e&&(e={}),s(this,void 0,void 0,(function(){var t,n;return l(this,(function(r){switch(r.label){case 0:return[4,F(L,void 0,[])];case 1:return t=r.sent(),n=function(e){var t;return{components:e,get visitorId(){return void 0===t&&(t=H(this.components)),t},set visitorId(e){t=e}}}(t),e.debug&&console.log("Copy the text below to get the debug data:\n\n```\nversion: 3.0.1\ngetOptions: "+JSON.stringify(e,void 0,2)+"\nvisitorId: "+n.visitorId+"\ncomponents: "+G(t)+"\n```"),[2,n]}}))}))},e}();function W(e){var t=(void 0===e?{}:e).delayFallback,n=void 0===t?50:t;return s(this,void 0,void 0,(function(){return l(this,(function(e){switch(e.label){case 0:return[4,(t=n,new Promise((function(e){window.requestIdleCallback?window.requestIdleCallback((function(){return e()})):setTimeout(e,t)})))];case 1:return e.sent(),[2,new U]}var t}))}))}var j={load:W,hashComponents:H,componentsToDebugString:G},N=c;return e.componentsToDebugString=G,e.default=j,e.getComponents=F,e.hashComponents=H,e.isChromium=k,e.isDesktopSafari=function(){return"safari"in b},e.isEdgeHTML=T,e.isGecko=function(){var e;return h(["buildID"in C,(null===(e=A.documentElement)||void 0===e?void 0:e.style)&&"MozAppearance"in A.documentElement.style,"MediaRecorderErrorEvent"in b,"mozInnerScreenX"in b,"CSSMozDocumentRule"in b,"CanvasCaptureMediaStream"in b])>=4},e.isTrident=M,e.isWebKit=function(){return h(["ApplePayError"in b,"CSSPrimitiveValue"in b,"Counter"in b,0===C.vendor.indexOf("Apple"),"getStorageUpdates"in C,"WebKitMediaKeys"in b])>=4},e.load=W,e.murmurX64Hash128=N,e}({}); |
/** | ||
* FingerprintJS v3.0.0 - Copyright (c) FingerprintJS, Inc, 2020 (https://fingerprintjs.com) | ||
* FingerprintJS v3.0.1 - Copyright (c) FingerprintJS, Inc, 2020 (https://fingerprintjs.com) | ||
* Licensed under the MIT (http://www.opensource.org/licenses/mit-license.php) license. | ||
@@ -139,3 +139,4 @@ * | ||
var c2 = [0x4cf5ad43, 0x2745937f]; | ||
for (var i = 0; i < bytes; i = i + 16) { | ||
var i; | ||
for (i = 0; i < bytes; i = i + 16) { | ||
k1 = [ | ||
@@ -262,2 +263,13 @@ (key.charCodeAt(i + 4) & 0xff) | | ||
var __assign = function() { | ||
__assign = Object.assign || function __assign(t) { | ||
for (var s, i = 1, n = arguments.length; i < n; i++) { | ||
s = arguments[i]; | ||
for (var p in s) if (Object.prototype.hasOwnProperty.call(s, p)) t[p] = s[p]; | ||
} | ||
return t; | ||
}; | ||
return __assign.apply(this, arguments); | ||
}; | ||
function __awaiter(thisArg, _arguments, P, generator) { | ||
@@ -273,8 +285,36 @@ function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); } | ||
var version = "3.0.0"; | ||
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 }; | ||
} | ||
} | ||
var version = "3.0.1"; | ||
function requestIdleCallbackIfAvailable(fallbackTimeout) { | ||
return new Promise((resolve) => { | ||
return new Promise(function (resolve) { | ||
if (window.requestIdleCallback) { | ||
window.requestIdleCallback(() => resolve()); | ||
window.requestIdleCallback(function () { return resolve(); }); | ||
} | ||
@@ -294,3 +334,3 @@ else { | ||
function includes(haystack, needle) { | ||
for (let i = 0, l = haystack.length; i < l; ++i) { | ||
for (var i = 0, l = haystack.length; i < l; ++i) { | ||
if (haystack[i] === needle) { | ||
@@ -317,65 +357,82 @@ return true; | ||
} | ||
/** | ||
* Be careful, NaN can return | ||
*/ | ||
function toFloat(value) { | ||
if (typeof value === 'number') { | ||
return value; | ||
} | ||
return parseFloat(value); | ||
} | ||
function countTruthy(values) { | ||
return values.reduce((sum, value) => sum + (value ? 1 : 0), 0); | ||
return values.reduce(function (sum, value) { return sum + (value ? 1 : 0); }, 0); | ||
} | ||
const n = navigator; | ||
const w = window; | ||
var n = navigator; | ||
var w = window; | ||
function isAudioParam(value) { | ||
return value && typeof value.setValueAtTime === 'function'; | ||
} | ||
// Inspired by and based on https://github.com/cozylife/audio-fingerprint | ||
function getAudioFingerprint() { | ||
return __awaiter(this, void 0, void 0, function* () { | ||
// On iOS 11, audio context can only be used in response to user interaction. | ||
// We require users to explicitly enable audio fingerprinting on iOS 11. | ||
// See https://stackoverflow.com/questions/46363048/onaudioprocess-not-called-on-ios11#46534088 | ||
if (n.userAgent.match(/OS 11.+Version\/11.+Safari/)) { | ||
// See comment for excludeUserAgent and https://stackoverflow.com/questions/46363048/onaudioprocess-not-called-on-ios11#46534088 | ||
return -1; | ||
} | ||
const AudioContext = w.OfflineAudioContext || w.webkitOfflineAudioContext; | ||
if (!AudioContext) { | ||
return -2; | ||
} | ||
const context = new AudioContext(1, 44100, 44100); | ||
const oscillator = context.createOscillator(); | ||
oscillator.type = 'triangle'; | ||
oscillator.frequency.setValueAtTime(10000, context.currentTime); | ||
const compressor = context.createDynamicsCompressor(); | ||
for (const [param, value] of [ | ||
['threshold', -50], | ||
['knee', 40], | ||
['ratio', 12], | ||
['reduction', -20], | ||
['attack', 0], | ||
['release', 0.25], | ||
]) { | ||
if (typeof compressor[param].setValueAtTime === 'function') { | ||
compressor[param].setValueAtTime(value, context.currentTime); | ||
return __awaiter(this, void 0, void 0, function () { | ||
var AudioContext, context, oscillator, compressor, _i, _a, _b, name_1, value, param; | ||
return __generator(this, function (_c) { | ||
// On iOS 11, audio context can only be used in response to user interaction. | ||
// We require users to explicitly enable audio fingerprinting on iOS 11. | ||
// See https://stackoverflow.com/questions/46363048/onaudioprocess-not-called-on-ios11#46534088 | ||
if (n.userAgent.match(/OS 11.+Version\/11.+Safari/)) { | ||
// See comment for excludeUserAgent and https://stackoverflow.com/questions/46363048/onaudioprocess-not-called-on-ios11#46534088 | ||
return [2 /*return*/, -1]; | ||
} | ||
} | ||
oscillator.connect(compressor); | ||
compressor.connect(context.destination); | ||
oscillator.start(0); | ||
context.startRendering(); | ||
return new Promise((resolve) => { | ||
const audioTimeoutId = setTimeout(() => { | ||
context.oncomplete = () => { }; | ||
resolve(-3); | ||
}, 1000); | ||
context.oncomplete = (event) => { | ||
let afp; | ||
try { | ||
clearTimeout(audioTimeoutId); | ||
afp = event.renderedBuffer | ||
.getChannelData(0) | ||
.slice(4500, 5000) | ||
.reduce((acc, val) => acc + Math.abs(val), 0); | ||
oscillator.disconnect(); | ||
compressor.disconnect(); | ||
AudioContext = w.OfflineAudioContext || w.webkitOfflineAudioContext; | ||
if (!AudioContext) { | ||
return [2 /*return*/, -2]; | ||
} | ||
context = new AudioContext(1, 44100, 44100); | ||
oscillator = context.createOscillator(); | ||
oscillator.type = 'triangle'; | ||
oscillator.frequency.setValueAtTime(10000, context.currentTime); | ||
compressor = context.createDynamicsCompressor(); | ||
for (_i = 0, _a = [ | ||
['threshold', -50], | ||
['knee', 40], | ||
['ratio', 12], | ||
['reduction', -20], | ||
['attack', 0], | ||
['release', 0.25], | ||
]; _i < _a.length; _i++) { | ||
_b = _a[_i], name_1 = _b[0], value = _b[1]; | ||
param = compressor[name_1]; | ||
if (isAudioParam(param)) { | ||
param.setValueAtTime(value, context.currentTime); | ||
} | ||
catch (error) { | ||
resolve(-4); | ||
return; | ||
} | ||
resolve(afp); | ||
}; | ||
} | ||
oscillator.connect(compressor); | ||
compressor.connect(context.destination); | ||
oscillator.start(0); | ||
context.startRendering(); | ||
return [2 /*return*/, new Promise(function (resolve) { | ||
var audioTimeoutId = setTimeout(function () { | ||
context.oncomplete = null; | ||
resolve(-3); | ||
}, 1000); | ||
context.oncomplete = function (event) { | ||
var afp; | ||
try { | ||
clearTimeout(audioTimeoutId); | ||
afp = event.renderedBuffer | ||
.getChannelData(0) | ||
.slice(4500, 5000) | ||
.reduce(function (acc, val) { return acc + Math.abs(val); }, 0); | ||
oscillator.disconnect(); | ||
compressor.disconnect(); | ||
} | ||
catch (error) { | ||
resolve(-4); | ||
return; | ||
} | ||
resolve(afp); | ||
}; | ||
})]; | ||
}); | ||
@@ -385,8 +442,13 @@ }); | ||
const d = document; | ||
// a font will be compared against all the three default fonts. | ||
// and if it doesn't match all 3 then that font is not available. | ||
const baseFonts = ['monospace', 'sans-serif', 'serif']; | ||
const fontList = [ | ||
// this is android-specific font from "Roboto" family | ||
var d = document; | ||
// We use m or w because these two characters take up the maximum width. | ||
// And we use a LLi so that the same matching fonts can get separated. | ||
var testString = 'mmMwWLliI0O&1'; | ||
// We test using 48px font size, we may use any size. I guess larger the better. | ||
var testSize = '48px'; | ||
// A font will be compared against all the three default fonts. | ||
// And if it doesn't match all 3 then that font is not available. | ||
var baseFonts = ['monospace', 'sans-serif', 'serif']; | ||
var fontList = [ | ||
// This is android-specific font from "Roboto" family | ||
'sans-serif-thin', | ||
@@ -445,3 +507,4 @@ 'ARNO PRO', | ||
]; | ||
const fontResetStyles = { | ||
var fontSpanStyle = { | ||
// CSS font reset to reset external styles | ||
fontStyle: 'normal', | ||
@@ -459,46 +522,37 @@ fontWeight: 'normal', | ||
wordSpacing: 'normal', | ||
// We need this css as in some weird browser this span elements shows up for a microSec which creates | ||
// a bad user experience | ||
position: 'absolute', | ||
left: '-9999px', | ||
fontSize: testSize, | ||
}; | ||
// we use m or w because these two characters take up the maximum width. | ||
// And we use a LLi so that the same matching fonts can get separated | ||
const testString = 'mmMwWLliI0O&1'; | ||
// we test using 48px font size, we may use any size. I guess larger the better. | ||
const testSize = '48px'; | ||
// kudos to http://www.lalit.org/lab/javascript-css-font-detect/ | ||
function getFonts() { | ||
const h = d.body; | ||
var h = d.body; | ||
// div to load spans for the base fonts | ||
const baseFontsDiv = d.createElement('div'); | ||
var baseFontsDiv = d.createElement('div'); | ||
// div to load spans for the fonts to detect | ||
const fontsDiv = d.createElement('div'); | ||
const defaultWidth = {}; | ||
const defaultHeight = {}; | ||
var fontsDiv = d.createElement('div'); | ||
var defaultWidth = {}; | ||
var defaultHeight = {}; | ||
// creates a span where the fonts will be loaded | ||
const createSpan = () => { | ||
const s = d.createElement('span'); | ||
Object.assign(s.style, | ||
// css font reset to reset external styles | ||
fontResetStyles, | ||
/* | ||
* We need this css as in some weird browser this | ||
* span elements shows up for a microSec which creates a | ||
* bad user experience | ||
*/ | ||
{ | ||
position: 'absolute', | ||
left: '-9999px', | ||
fontSize: testSize, | ||
}); | ||
s.textContent = testString; | ||
return s; | ||
var createSpan = function () { | ||
var span = d.createElement('span'); | ||
span.textContent = testString; | ||
for (var _i = 0, _a = Object.keys(fontSpanStyle); _i < _a.length; _i++) { | ||
var prop = _a[_i]; | ||
span.style[prop] = fontSpanStyle[prop]; | ||
} | ||
return span; | ||
}; | ||
// creates a span and load the font to detect and a base font for fallback | ||
const createSpanWithFonts = (fontToDetect, baseFont) => { | ||
const s = createSpan(); | ||
s.style.fontFamily = `'${fontToDetect}',${baseFont}`; | ||
var createSpanWithFonts = function (fontToDetect, baseFont) { | ||
var s = createSpan(); | ||
s.style.fontFamily = "'" + fontToDetect + "'," + baseFont; | ||
return s; | ||
}; | ||
// creates spans for the base fonts and adds them to baseFontsDiv | ||
const initializeBaseFontsSpans = () => { | ||
return baseFonts.map((baseFont) => { | ||
const s = createSpan(); | ||
var initializeBaseFontsSpans = function () { | ||
return baseFonts.map(function (baseFont) { | ||
var s = createSpan(); | ||
s.style.fontFamily = baseFont; | ||
@@ -510,11 +564,15 @@ baseFontsDiv.appendChild(s); | ||
// creates spans for the fonts to detect and adds them to fontsDiv | ||
const initializeFontsSpans = () => { | ||
var initializeFontsSpans = function () { | ||
// Stores {fontName : [spans for that font]} | ||
const spans = {}; | ||
for (const font of fontList) { | ||
spans[font] = baseFonts.map((baseFont) => { | ||
const s = createSpanWithFonts(font, baseFont); | ||
var spans = {}; | ||
var _loop_1 = function (font) { | ||
spans[font] = baseFonts.map(function (baseFont) { | ||
var s = createSpanWithFonts(font, baseFont); | ||
fontsDiv.appendChild(s); | ||
return s; | ||
}); | ||
}; | ||
for (var _i = 0, fontList_1 = fontList; _i < fontList_1.length; _i++) { | ||
var font = fontList_1[_i]; | ||
_loop_1(font); | ||
} | ||
@@ -524,12 +582,14 @@ return spans; | ||
// checks if a font is available | ||
const isFontAvailable = (fontSpans) => { | ||
return baseFonts.some(((baseFont, baseFontIndex) => (fontSpans[baseFontIndex].offsetWidth !== defaultWidth[baseFont] || | ||
fontSpans[baseFontIndex].offsetHeight !== defaultHeight[baseFont]))); | ||
var isFontAvailable = function (fontSpans) { | ||
return baseFonts.some(function (baseFont, baseFontIndex) { | ||
return fontSpans[baseFontIndex].offsetWidth !== defaultWidth[baseFont] || | ||
fontSpans[baseFontIndex].offsetHeight !== defaultHeight[baseFont]; | ||
}); | ||
}; | ||
// create spans for base fonts | ||
const baseFontsSpans = initializeBaseFontsSpans(); | ||
var baseFontsSpans = initializeBaseFontsSpans(); | ||
// add the spans to the DOM | ||
h.appendChild(baseFontsDiv); | ||
// get the default width for the three base fonts | ||
for (let index = 0, length = baseFonts.length; index < length; index++) { | ||
for (var index = 0, length_1 = baseFonts.length; index < length_1; index++) { | ||
defaultWidth[baseFonts[index]] = baseFontsSpans[index].offsetWidth; // width for the default font | ||
@@ -539,8 +599,8 @@ defaultHeight[baseFonts[index]] = baseFontsSpans[index].offsetHeight; // height for the default font | ||
// create spans for fonts to detect | ||
const fontsSpans = initializeFontsSpans(); | ||
var fontsSpans = initializeFontsSpans(); | ||
// add all the spans to the DOM | ||
h.appendChild(fontsDiv); | ||
// check available fonts | ||
const available = []; | ||
for (let i = 0, l = fontList.length; i < l; i++) { | ||
var available = []; | ||
for (var i = 0, l = fontList.length; i < l; i++) { | ||
if (isFontAvailable(fontsSpans[fontList[i]])) { | ||
@@ -556,15 +616,129 @@ available.push(fontList[i]); | ||
/* | ||
* Functions to help with browser features | ||
*/ | ||
var w$1 = window; | ||
var n$1 = navigator; | ||
var d$1 = document; | ||
/** | ||
* Checks whether the browser is based on Trident (the Internet Explorer engine) without using user-agent. | ||
* | ||
* Warning for package users: | ||
* This function is out of Semantic Versioning, i.e. can change unexpectedly. Usage is at your own risk. | ||
*/ | ||
function isTrident() { | ||
// The properties are checked to be in IE 10, IE 11 and not to be in other browsers in October 2020 | ||
return (countTruthy([ | ||
'MSCSSMatrix' in w$1, | ||
'msSetImmediate' in w$1, | ||
'msIndexedDB' in w$1, | ||
'msMaxTouchPoints' in n$1, | ||
'msPointerEnabled' in n$1, | ||
]) >= 4); | ||
} | ||
/** | ||
* Checks whether the browser is based on EdgeHTML (the pre-Chromium Edge engine) without using user-agent. | ||
* | ||
* Warning for package users: | ||
* This function is out of Semantic Versioning, i.e. can change unexpectedly. Usage is at your own risk. | ||
*/ | ||
function isEdgeHTML() { | ||
// Based on research in October 2020 | ||
return (countTruthy(['msWriteProfilerMark' in w$1, 'MSStream' in w$1, 'msLaunchUri' in n$1, 'msSaveBlob' in n$1]) >= 3 && | ||
!isTrident()); | ||
} | ||
/** | ||
* Checks whether the browser is based on Chromium without using user-agent. | ||
* | ||
* Warning for package users: | ||
* This function is out of Semantic Versioning, i.e. can change unexpectedly. Usage is at your own risk. | ||
*/ | ||
function isChromium() { | ||
// Based on research in October 2020. Tested to detect Chromium 42-86. | ||
return (countTruthy([ | ||
'webkitPersistentStorage' in n$1, | ||
'webkitTemporaryStorage' in n$1, | ||
n$1.vendor.indexOf('Google') === 0, | ||
'webkitResolveLocalFileSystemURL' in w$1, | ||
'BatteryManager' in w$1, | ||
'webkitMediaStream' in w$1, | ||
'webkitSpeechGrammar' in w$1, | ||
]) >= 5); | ||
} | ||
/** | ||
* Checks whether the browser is based on mobile or desktop Safari without using user-agent. | ||
* All iOS browsers use WebKit (the Safari engine). | ||
* | ||
* Warning for package users: | ||
* This function is out of Semantic Versioning, i.e. can change unexpectedly. Usage is at your own risk. | ||
*/ | ||
function isWebKit() { | ||
// Based on research in September 2020 | ||
return (countTruthy([ | ||
'ApplePayError' in w$1, | ||
'CSSPrimitiveValue' in w$1, | ||
'Counter' in w$1, | ||
n$1.vendor.indexOf('Apple') === 0, | ||
'getStorageUpdates' in n$1, | ||
'WebKitMediaKeys' in w$1, | ||
]) >= 4); | ||
} | ||
/** | ||
* Checks whether the WebKit browser is a desktop Safari. | ||
* | ||
* Warning for package users: | ||
* This function is out of Semantic Versioning, i.e. can change unexpectedly. Usage is at your own risk. | ||
*/ | ||
function isDesktopSafari() { | ||
return 'safari' in w$1; | ||
} | ||
/** | ||
* Checks whether the browser is based on Gecko (Firefox engine) without using user-agent. | ||
* | ||
* Warning for package users: | ||
* This function is out of Semantic Versioning, i.e. can change unexpectedly. Usage is at your own risk. | ||
*/ | ||
function isGecko() { | ||
var _a; | ||
// Based on research in September 2020 | ||
return (countTruthy([ | ||
'buildID' in n$1, | ||
((_a = d$1.documentElement) === null || _a === void 0 ? void 0 : _a.style) && 'MozAppearance' in d$1.documentElement.style, | ||
'MediaRecorderErrorEvent' in w$1, | ||
'mozInnerScreenX' in w$1, | ||
'CSSMozDocumentRule' in w$1, | ||
'CanvasCaptureMediaStream' in w$1, | ||
]) >= 4); | ||
} | ||
/** | ||
* Checks whether the browser is based on Chromium version ā„86 without using user-agent. | ||
* It doesn't check that the browser is based on Chromium, there is a separate function for this. | ||
*/ | ||
function isChromium86OrNewer() { | ||
// Checked in Chrome 85 vs Chrome 86 both on desktop and Android | ||
return (countTruthy([ | ||
!('MediaSettingsRange' in w$1), | ||
'RTCEncodedAudioFrame' in w$1, | ||
'' + w$1.Intl === '[object Intl]', | ||
'' + w$1.Reflect === '[object Reflect]', | ||
]) >= 3); | ||
} | ||
function getPlugins() { | ||
if (isTrident()) { | ||
return []; | ||
} | ||
if (!navigator.plugins) { | ||
return undefined; | ||
} | ||
const plugins = []; | ||
var plugins = []; | ||
// Safari 10 doesn't support iterating navigator.plugins with for...of | ||
for (let i = 0; i < navigator.plugins.length; ++i) { | ||
const plugin = navigator.plugins[i]; | ||
for (var i = 0; i < navigator.plugins.length; ++i) { | ||
var plugin = navigator.plugins[i]; | ||
if (!plugin) { | ||
continue; | ||
} | ||
const mimeTypes = []; | ||
for (const mimeType of plugin) { | ||
var mimeTypes = []; | ||
for (var j = 0; j < plugin.length; ++j) { | ||
var mimeType = plugin[j]; | ||
mimeTypes.push({ | ||
@@ -578,3 +752,3 @@ type: mimeType.type, | ||
description: plugin.description, | ||
mimeTypes, | ||
mimeTypes: mimeTypes, | ||
}); | ||
@@ -586,3 +760,3 @@ } | ||
function makeCanvasContext() { | ||
const canvas = document.createElement('canvas'); | ||
var canvas = document.createElement('canvas'); | ||
canvas.width = 240; | ||
@@ -603,3 +777,3 @@ canvas.height = 140; | ||
function getCanvasFingerprint() { | ||
const [canvas, context] = makeCanvasContext(); | ||
var _a = makeCanvasContext(), canvas = _a[0], context = _a[1]; | ||
if (!isSupported(canvas, context)) { | ||
@@ -613,3 +787,3 @@ return { winding: false, data: '' }; | ||
context.rect(2, 2, 6, 6); | ||
const winding = !context.isPointInPath(5, 5, 'evenodd'); | ||
var winding = !context.isPointInPath(5, 5, 'evenodd'); | ||
context.textBaseline = 'alphabetic'; | ||
@@ -625,3 +799,3 @@ context.fillStyle = '#f60'; | ||
// context.fillText("CwēØm fjordbank \ud83d\ude03 gly", 2, 15) | ||
const printedText = 'Cwm fjordbank \ud83d\ude03 gly'; | ||
var printedText = 'Cwm fjordbank \ud83d\ude03 gly'; | ||
context.fillText(printedText, 2, 15); | ||
@@ -658,9 +832,9 @@ context.fillStyle = 'rgba(102, 204, 0, 0.2)'; | ||
return { | ||
winding, | ||
data: save(canvas) | ||
winding: winding, | ||
data: save(canvas), | ||
}; | ||
} | ||
const n$1 = navigator; | ||
const w$1 = window; | ||
var n$2 = navigator; | ||
var w$2 = window; | ||
/** | ||
@@ -674,9 +848,9 @@ * This is a crude and primitive touch screen detection. It's not possible to currently reliably detect the availability | ||
function getTouchSupport() { | ||
let maxTouchPoints = 0; | ||
let touchEvent; | ||
if (n$1.maxTouchPoints !== undefined) { | ||
maxTouchPoints = toInt(n$1.maxTouchPoints); | ||
var maxTouchPoints = 0; | ||
var touchEvent; | ||
if (n$2.maxTouchPoints !== undefined) { | ||
maxTouchPoints = toInt(n$2.maxTouchPoints); | ||
} | ||
else if (n$1.msMaxTouchPoints !== undefined) { | ||
maxTouchPoints = n$1.msMaxTouchPoints; | ||
else if (n$2.msMaxTouchPoints !== undefined) { | ||
maxTouchPoints = n$2.msMaxTouchPoints; | ||
} | ||
@@ -690,7 +864,7 @@ try { | ||
} | ||
const touchStart = 'ontouchstart' in w$1; | ||
var touchStart = 'ontouchstart' in w$2; | ||
return { | ||
maxTouchPoints, | ||
touchEvent, | ||
touchStart, | ||
maxTouchPoints: maxTouchPoints, | ||
touchEvent: touchEvent, | ||
touchStart: touchStart, | ||
}; | ||
@@ -703,85 +877,6 @@ } | ||
/* | ||
* Functions to help with browser features | ||
*/ | ||
const w$2 = window; | ||
const n$2 = navigator; | ||
const d$1 = document; | ||
/** | ||
* Checks whether the browser is Internet Explorer or pre-Chromium Edge without using user-agent. | ||
* | ||
* Warning for package users: | ||
* This function is out of Semantic Versioning, i.e. can change unexpectedly. Usage is at your own risk. | ||
*/ | ||
function isIEOrOldEdge() { | ||
// The properties are checked to be in IE 10, IE 11 and Edge 18 and not to be in other browsers | ||
return countTruthy([ | ||
'msWriteProfilerMark' in w$2, | ||
'msLaunchUri' in n$2, | ||
'msSaveBlob' in n$2, | ||
]) >= 2; | ||
} | ||
/** | ||
* Checks whether the browser is based on Chromium without using user-agent. | ||
* | ||
* Warning for package users: | ||
* This function is out of Semantic Versioning, i.e. can change unexpectedly. Usage is at your own risk. | ||
*/ | ||
function isChromium() { | ||
// Based on research in September 2020 | ||
return countTruthy([ | ||
'userActivation' in n$2, | ||
'mediaSession' in n$2, | ||
n$2.vendor.indexOf('Google') === 0, | ||
'BackgroundFetchManager' in w$2, | ||
'BatteryManager' in w$2, | ||
'webkitMediaStream' in w$2, | ||
'webkitSpeechGrammar' in w$2, | ||
]) >= 5; | ||
} | ||
/** | ||
* Checks whether the WebKit browser is a desktop Safari. | ||
* | ||
* Warning for package users: | ||
* This function is out of Semantic Versioning, i.e. can change unexpectedly. Usage is at your own risk. | ||
*/ | ||
function isDesktopSafari() { | ||
return 'safari' in w$2; | ||
} | ||
/** | ||
* Checks whether the browser is based on Gecko (Firefox engine) without using user-agent. | ||
* | ||
* Warning for package users: | ||
* This function is out of Semantic Versioning, i.e. can change unexpectedly. Usage is at your own risk. | ||
*/ | ||
function isGecko() { | ||
var _a; | ||
// Based on research in September 2020 | ||
return countTruthy([ | ||
'buildID' in n$2, | ||
((_a = d$1.documentElement) === null || _a === void 0 ? void 0 : _a.style) && 'MozAppearance' in d$1.documentElement.style, | ||
'MediaRecorderErrorEvent' in w$2, | ||
'mozInnerScreenX' in w$2, | ||
'CSSMozDocumentRule' in w$2, | ||
'CanvasCaptureMediaStream' in w$2, | ||
]) >= 4; | ||
} | ||
/** | ||
* Checks whether the browser is based on Chromium version ā„86 without using user-agent. | ||
* It doesn't check that the browser is based on Chromium, there is a separate function for this. | ||
*/ | ||
function isChromium86OrNewer() { | ||
// Checked in Chrome 85 vs Chrome 86 both on desktop and Android | ||
return countTruthy([ | ||
!('MediaSettingsRange' in w$2), | ||
!('PhotoCapabilities' in w$2), | ||
'RTCEncodedAudioFrame' in w$2, | ||
('' + w$2.Intl) === '[object Intl]', | ||
]) >= 2; | ||
} | ||
const n$3 = navigator; | ||
var n$3 = navigator; | ||
function getLanguages() { | ||
const result = []; | ||
const language = n$3.language || n$3.userLanguage || n$3.browserLanguage || n$3.systemLanguage; | ||
var result = []; | ||
var language = n$3.language || n$3.userLanguage || n$3.browserLanguage || n$3.systemLanguage; | ||
if (language !== undefined) { | ||
@@ -798,3 +893,3 @@ result.push([language]); | ||
else if (typeof n$3.languages === 'string') { | ||
const languages = n$3.languages; | ||
var languages = n$3.languages; | ||
if (languages) { | ||
@@ -815,7 +910,7 @@ result.push(languages.split(',')); | ||
const w$3 = window; | ||
var w$3 = window; | ||
function getScreenResolution() { | ||
// Some browsers return screen resolution as strings, e.g. "1200", instead of a number, e.g. 1200. | ||
// I suspect it's done by certain plugins that randomize browser properties to prevent fingerprinting. | ||
const dimensions = [toInt(w$3.screen.width), toInt(w$3.screen.height)]; | ||
var dimensions = [toInt(w$3.screen.width), toInt(w$3.screen.height)]; | ||
dimensions.sort().reverse(); | ||
@@ -825,3 +920,3 @@ return dimensions; | ||
const w$4 = window; | ||
var w$4 = window; | ||
function getAvailableScreenResolution() { | ||
@@ -831,3 +926,3 @@ if (w$4.screen.availWidth && w$4.screen.availHeight) { | ||
// I suspect it's done by certain plugins that randomize browser properties to prevent fingerprinting. | ||
const dimensions = [toInt(w$4.screen.availWidth), toInt(w$4.screen.availHeight)]; | ||
var dimensions = [toInt(w$4.screen.availWidth), toInt(w$4.screen.availHeight)]; | ||
dimensions.sort().reverse(); | ||
@@ -842,3 +937,3 @@ return dimensions; | ||
// sometimes hardware concurrency is a string | ||
const concurrency = toInt(navigator.hardwareConcurrency); | ||
var concurrency = toInt(navigator.hardwareConcurrency); | ||
return isNaN(concurrency) ? 1 : concurrency; | ||
@@ -852,6 +947,13 @@ } | ||
function getTimezoneOffset() { | ||
return new Date().getTimezoneOffset(); | ||
var currentYear = new Date().getFullYear(); | ||
// The timezone offset may change over time due to daylight saving time (DST) shifts. | ||
// The non-DST timezone offset is used as the result timezone offset. | ||
// Since the DST season differs in the northern and the southern hemispheres, | ||
// both January and July timezones offsets are considered. | ||
return Math.max( | ||
// `getTimezoneOffset` returns a number as a string in some unidentified cases | ||
toFloat(new Date(currentYear, 0, 1).getTimezoneOffset()), toFloat(new Date(currentYear, 6, 1).getTimezoneOffset())); | ||
} | ||
const w$5 = window; | ||
var w$5 = window; | ||
function getTimezone() { | ||
@@ -889,3 +991,3 @@ var _a; | ||
// visitor identifier in normal and private modes. | ||
if (isIEOrOldEdge()) { | ||
if (isTrident() || isEdgeHTML()) { | ||
return undefined; | ||
@@ -949,3 +1051,3 @@ } | ||
const d$2 = document; | ||
var d$2 = document; | ||
/** | ||
@@ -959,2 +1061,6 @@ * navigator.cookieEnabled cannot detect custom or nuanced cookie blocking configurations. For example, when blocking | ||
function areCookiesEnabled() { | ||
// Taken from here: https://github.com/Modernizr/Modernizr/blob/master/feature-detects/cookies.js | ||
// navigator.cookieEnabled cannot detect custom or nuanced cookie blocking configurations. For example, when blocking | ||
// cookies via the Advanced Privacy Settings in IE9, it always returns true. And there have been issues in the past | ||
// with site-specific exceptions. Don't rely on it. | ||
// try..catch because some in situations `document.cookie` is exposed but throws a | ||
@@ -966,3 +1072,3 @@ // SecurityError if you try to access it; e.g. documents created from data URIs | ||
d$2.cookie = 'cookietest=1'; | ||
const result = d$2.cookie.indexOf('cookietest=') !== -1; | ||
var result = d$2.cookie.indexOf('cookietest=') !== -1; | ||
// Delete cookie | ||
@@ -983,3 +1089,3 @@ d$2.cookie = 'cookietest=1; expires=Thu, 01-Jan-1970 00:00:01 GMT'; | ||
*/ | ||
const sources = { | ||
var sources = { | ||
// Expected errors and default values must be handled inside the functions | ||
@@ -1023,22 +1129,42 @@ osCpu: getOsCpu, | ||
function getComponents(sources, sourceOptions, excludeSources) { | ||
return __awaiter(this, void 0, void 0, function* () { | ||
let timestamp = Date.now(); | ||
const components = {}; | ||
for (const sourceKey of Object.keys(sources)) { | ||
if (!excludes(excludeSources, sourceKey)) { | ||
continue; | ||
return __awaiter(this, void 0, void 0, function () { | ||
var timestamp, components, _i, _a, sourceKey, result, error_1, nextTimestamp; | ||
var _b; | ||
return __generator(this, function (_c) { | ||
switch (_c.label) { | ||
case 0: | ||
timestamp = Date.now(); | ||
components = {}; | ||
_i = 0, _a = Object.keys(sources); | ||
_c.label = 1; | ||
case 1: | ||
if (!(_i < _a.length)) return [3 /*break*/, 7]; | ||
sourceKey = _a[_i]; | ||
if (!excludes(excludeSources, sourceKey)) { | ||
return [3 /*break*/, 6]; | ||
} | ||
result = void 0; | ||
_c.label = 2; | ||
case 2: | ||
_c.trys.push([2, 4, , 5]); | ||
_b = {}; | ||
return [4 /*yield*/, sources[sourceKey](sourceOptions)]; | ||
case 3: | ||
result = (_b.value = _c.sent(), _b); | ||
return [3 /*break*/, 5]; | ||
case 4: | ||
error_1 = _c.sent(); | ||
result = error_1 && typeof error_1 === 'object' && 'message' in error_1 ? { error: error_1 } : { error: { message: error_1 } }; | ||
return [3 /*break*/, 5]; | ||
case 5: | ||
nextTimestamp = Date.now(); | ||
components[sourceKey] = __assign(__assign({}, result), { duration: nextTimestamp - timestamp }); // TypeScript has beaten me here | ||
timestamp = nextTimestamp; | ||
_c.label = 6; | ||
case 6: | ||
_i++; | ||
return [3 /*break*/, 1]; | ||
case 7: return [2 /*return*/, components]; | ||
} | ||
let result; | ||
let nextTimestamp; | ||
try { | ||
result = { value: yield sources[sourceKey](sourceOptions) }; | ||
} | ||
catch (error) { | ||
result = error && typeof error === 'object' && 'message' in error ? { error } : { error: { message: error } }; | ||
} | ||
nextTimestamp = Date.now(); | ||
components[sourceKey] = Object.assign(Object.assign({}, result), { duration: nextTimestamp - timestamp }); // TypeScript has beaten me here | ||
timestamp = nextTimestamp; | ||
} | ||
return components; | ||
}); | ||
}); | ||
@@ -1054,7 +1180,8 @@ } | ||
function componentsToCanonicalString(components) { | ||
let result = ''; | ||
for (const componentKey of Object.keys(components)) { | ||
const component = components[componentKey]; | ||
const value = component.error ? 'error' : JSON.stringify(component.value); | ||
result += `${result ? '|' : ''}${componentKey.replace(/([:|\\])/g, '\\$1')}:${value}`; | ||
var result = ''; | ||
for (var _i = 0, _a = Object.keys(components); _i < _a.length; _i++) { | ||
var componentKey = _a[_i]; | ||
var component = components[componentKey]; | ||
var value = component.error ? 'error' : JSON.stringify(component.value); | ||
result += "" + (result ? '|' : '') + componentKey.replace(/([:|\\])/g, '\\$1') + ":" + value; | ||
} | ||
@@ -1064,6 +1191,6 @@ return result; | ||
function componentsToDebugString(components) { | ||
return JSON.stringify(components, (_key, value) => { | ||
return JSON.stringify(components, function (_key, value) { | ||
var _a; | ||
if (value instanceof Error) { | ||
return Object.assign(Object.assign({}, value), { message: value.message, stack: (_a = value.stack) === null || _a === void 0 ? void 0 : _a.split('\n') }); | ||
return __assign(__assign({}, value), { message: value.message, stack: (_a = value.stack) === null || _a === void 0 ? void 0 : _a.split('\n') }); | ||
} | ||
@@ -1081,6 +1208,6 @@ return value; | ||
function makeLazyGetResult(components) { | ||
let visitorIdCache; | ||
var visitorIdCache; | ||
// A plain class isn't used because its getters and setters aren't enumerable. | ||
return { | ||
components, | ||
components: components, | ||
get visitorId() { | ||
@@ -1101,34 +1228,51 @@ if (visitorIdCache === undefined) { | ||
*/ | ||
class OpenAgent { | ||
var OpenAgent = /** @class */ (function () { | ||
function OpenAgent() { | ||
} | ||
/** | ||
* @inheritDoc | ||
*/ | ||
get(options = {}) { | ||
return __awaiter(this, void 0, void 0, function* () { | ||
const components = yield getBuiltinComponents(); | ||
const result = makeLazyGetResult(components); | ||
if (options.debug) { | ||
console.log(`Copy the text below to get the debug data: | ||
\`\`\` | ||
version: ${version} | ||
getOptions: ${JSON.stringify(options, undefined, 2)} | ||
visitorId: ${result.visitorId} | ||
components: ${componentsToDebugString(components)} | ||
\`\`\``); | ||
} | ||
return result; | ||
OpenAgent.prototype.get = function (options) { | ||
if (options === void 0) { options = {}; } | ||
return __awaiter(this, void 0, void 0, function () { | ||
var components, result; | ||
return __generator(this, function (_a) { | ||
switch (_a.label) { | ||
case 0: return [4 /*yield*/, getBuiltinComponents()]; | ||
case 1: | ||
components = _a.sent(); | ||
result = makeLazyGetResult(components); | ||
if (options.debug) { | ||
// console.log is ok here because it's under a debug clause | ||
// eslint-disable-next-line no-console | ||
console.log("Copy the text below to get the debug data:\n\n```\nversion: " + version + "\ngetOptions: " + JSON.stringify(options, undefined, 2) + "\nvisitorId: " + result.visitorId + "\ncomponents: " + componentsToDebugString(components) + "\n```"); | ||
} | ||
return [2 /*return*/, result]; | ||
} | ||
}); | ||
}); | ||
} | ||
} | ||
}; | ||
return OpenAgent; | ||
}()); | ||
/** | ||
* Builds an instance of Agent and waits a delay required for a proper operation. | ||
*/ | ||
function load({ delayFallback = 50 } = {}) { | ||
return __awaiter(this, void 0, void 0, function* () { | ||
// A delay is required to ensure consistent entropy components. | ||
// See https://github.com/fingerprintjs/fingerprintjs/issues/254 | ||
// and https://github.com/fingerprintjs/fingerprintjs/issues/307 | ||
yield requestIdleCallbackIfAvailable(delayFallback); | ||
return new OpenAgent(); | ||
function load(_a) { | ||
var _b = (_a === void 0 ? {} : _a).delayFallback, delayFallback = _b === void 0 ? 50 : _b; | ||
return __awaiter(this, void 0, void 0, function () { | ||
return __generator(this, function (_c) { | ||
switch (_c.label) { | ||
case 0: | ||
// A delay is required to ensure consistent entropy components. | ||
// See https://github.com/fingerprintjs/fingerprintjs/issues/254 | ||
// and https://github.com/fingerprintjs/fingerprintjs/issues/307 | ||
return [4 /*yield*/, requestIdleCallbackIfAvailable(delayFallback)]; | ||
case 1: | ||
// A delay is required to ensure consistent entropy components. | ||
// See https://github.com/fingerprintjs/fingerprintjs/issues/254 | ||
// and https://github.com/fingerprintjs/fingerprintjs/issues/307 | ||
_c.sent(); | ||
return [2 /*return*/, new OpenAgent()]; | ||
} | ||
}); | ||
}); | ||
@@ -1139,6 +1283,6 @@ } | ||
// It should contain all the public exported values. | ||
var index = { load, hashComponents, componentsToDebugString }; | ||
var index = { load: load, hashComponents: hashComponents, componentsToDebugString: componentsToDebugString }; | ||
// The exports below are for private usage. They may change unexpectedly. Use them at your own risk. | ||
/** Not documented, out of Semantic Versioning, usage is at your own risk */ | ||
const murmurX64Hash128 = x64hash128; | ||
var murmurX64Hash128 = x64hash128; | ||
@@ -1151,4 +1295,6 @@ exports.componentsToDebugString = componentsToDebugString; | ||
exports.isDesktopSafari = isDesktopSafari; | ||
exports.isEdgeHTML = isEdgeHTML; | ||
exports.isGecko = isGecko; | ||
exports.isIEOrOldEdge = isIEOrOldEdge; | ||
exports.isTrident = isTrident; | ||
exports.isWebKit = isWebKit; | ||
exports.load = load; | ||
@@ -1155,0 +1301,0 @@ exports.murmurX64Hash128 = murmurX64Hash128; |
/** | ||
* FingerprintJS v3.0.0 - Copyright (c) FingerprintJS, Inc, 2020 (https://fingerprintjs.com) | ||
* FingerprintJS v3.0.1 - Copyright (c) FingerprintJS, Inc, 2020 (https://fingerprintjs.com) | ||
* Licensed under the MIT (http://www.opensource.org/licenses/mit-license.php) license. | ||
@@ -9,2 +9,2 @@ * | ||
!function(e,t){"object"==typeof exports&&"undefined"!=typeof module?t(exports):"function"==typeof define&&define.amd?define(["exports"],t):t((e="undefined"!=typeof globalThis?globalThis:e||self).FingerprintJS={})}(this,(function(e){"use strict";function t(e,t){e=[e[0]>>>16,65535&e[0],e[1]>>>16,65535&e[1]],t=[t[0]>>>16,65535&t[0],t[1]>>>16,65535&t[1]];var n=[0,0,0,0];return n[3]+=e[3]+t[3],n[2]+=n[3]>>>16,n[3]&=65535,n[2]+=e[2]+t[2],n[1]+=n[2]>>>16,n[2]&=65535,n[1]+=e[1]+t[1],n[0]+=n[1]>>>16,n[1]&=65535,n[0]+=e[0]+t[0],n[0]&=65535,[n[0]<<16|n[1],n[2]<<16|n[3]]}function n(e,t){e=[e[0]>>>16,65535&e[0],e[1]>>>16,65535&e[1]],t=[t[0]>>>16,65535&t[0],t[1]>>>16,65535&t[1]];var n=[0,0,0,0];return n[3]+=e[3]*t[3],n[2]+=n[3]>>>16,n[3]&=65535,n[2]+=e[2]*t[3],n[1]+=n[2]>>>16,n[2]&=65535,n[2]+=e[3]*t[2],n[1]+=n[2]>>>16,n[2]&=65535,n[1]+=e[1]*t[3],n[0]+=n[1]>>>16,n[1]&=65535,n[1]+=e[2]*t[2],n[0]+=n[1]>>>16,n[1]&=65535,n[1]+=e[3]*t[1],n[0]+=n[1]>>>16,n[1]&=65535,n[0]+=e[0]*t[3]+e[1]*t[2]+e[2]*t[1]+e[3]*t[0],n[0]&=65535,[n[0]<<16|n[1],n[2]<<16|n[3]]}function o(e,t){return 32===(t%=64)?[e[1],e[0]]:t<32?[e[0]<<t|e[1]>>>32-t,e[1]<<t|e[0]>>>32-t]:(t-=32,[e[1]<<t|e[0]>>>32-t,e[0]<<t|e[1]>>>32-t])}function r(e,t){return 0===(t%=64)?e:t<32?[e[0]<<t|e[1]>>>32-t,e[1]<<t]:[e[1]<<t-32,0]}function i(e,t){return[e[0]^t[0],e[1]^t[1]]}function a(e){return e=i(e,[0,e[0]>>>1]),e=i(e=n(e,[4283543511,3981806797]),[0,e[0]>>>1]),e=i(e=n(e,[3301882366,444984403]),[0,e[0]>>>1])}function c(e,c){c=c||0;for(var s=(e=e||"").length%16,u=e.length-s,l=[0,c],d=[0,c],f=[0,0],h=[0,0],g=[2277735313,289559509],m=[1291169091,658871167],p=0;p<u;p+=16)f=[255&e.charCodeAt(p+4)|(255&e.charCodeAt(p+5))<<8|(255&e.charCodeAt(p+6))<<16|(255&e.charCodeAt(p+7))<<24,255&e.charCodeAt(p)|(255&e.charCodeAt(p+1))<<8|(255&e.charCodeAt(p+2))<<16|(255&e.charCodeAt(p+3))<<24],h=[255&e.charCodeAt(p+12)|(255&e.charCodeAt(p+13))<<8|(255&e.charCodeAt(p+14))<<16|(255&e.charCodeAt(p+15))<<24,255&e.charCodeAt(p+8)|(255&e.charCodeAt(p+9))<<8|(255&e.charCodeAt(p+10))<<16|(255&e.charCodeAt(p+11))<<24],f=o(f=n(f,g),31),l=t(l=o(l=i(l,f=n(f,m)),27),d),l=t(n(l,[0,5]),[0,1390208809]),h=o(h=n(h,m),33),d=t(d=o(d=i(d,h=n(h,g)),31),l),d=t(n(d,[0,5]),[0,944331445]);switch(f=[0,0],h=[0,0],s){case 15:h=i(h,r([0,e.charCodeAt(p+14)],48));case 14:h=i(h,r([0,e.charCodeAt(p+13)],40));case 13:h=i(h,r([0,e.charCodeAt(p+12)],32));case 12:h=i(h,r([0,e.charCodeAt(p+11)],24));case 11:h=i(h,r([0,e.charCodeAt(p+10)],16));case 10:h=i(h,r([0,e.charCodeAt(p+9)],8));case 9:h=n(h=i(h,[0,e.charCodeAt(p+8)]),m),d=i(d,h=n(h=o(h,33),g));case 8:f=i(f,r([0,e.charCodeAt(p+7)],56));case 7:f=i(f,r([0,e.charCodeAt(p+6)],48));case 6:f=i(f,r([0,e.charCodeAt(p+5)],40));case 5:f=i(f,r([0,e.charCodeAt(p+4)],32));case 4:f=i(f,r([0,e.charCodeAt(p+3)],24));case 3:f=i(f,r([0,e.charCodeAt(p+2)],16));case 2:f=i(f,r([0,e.charCodeAt(p+1)],8));case 1:f=n(f=i(f,[0,e.charCodeAt(p)]),g),l=i(l,f=n(f=o(f,31),m))}return l=t(l=i(l,[0,e.length]),d=i(d,[0,e.length])),d=t(d,l),l=t(l=a(l),d=a(d)),d=t(d,l),("00000000"+(l[0]>>>0).toString(16)).slice(-8)+("00000000"+(l[1]>>>0).toString(16)).slice(-8)+("00000000"+(d[0]>>>0).toString(16)).slice(-8)+("00000000"+(d[1]>>>0).toString(16)).slice(-8)}function s(e,t,n,o){return new(n||(n=Promise))((function(r,i){function a(e){try{s(o.next(e))}catch(t){i(t)}}function c(e){try{s(o.throw(e))}catch(t){i(t)}}function s(e){var t;e.done?r(e.value):(t=e.value,t instanceof n?t:new n((function(e){e(t)}))).then(a,c)}s((o=o.apply(e,t||[])).next())}))}function u(e){return"number"==typeof e?0|e:parseInt(e)}function l(e){return e.reduce(((e,t)=>e+(t?1:0)),0)}const d=navigator,f=window;const h=document,g=["monospace","sans-serif","serif"],m=["sans-serif-thin","ARNO PRO","Agency FB","Arabic Typesetting","Arial Unicode MS","AvantGarde Bk BT","BankGothic Md BT","Batang","Bitstream Vera Sans Mono","Calibri","Century","Century Gothic","Clarendon","EUROSTILE","Franklin Gothic","Futura Bk BT","Futura Md BT","GOTHAM","Gill Sans","HELV","Haettenschweiler","Helvetica Neue","Humanst521 BT","Leelawadee","Letter Gothic","Levenim MT","Lucida Bright","Lucida Sans","Menlo","MS Mincho","MS Outlook","MS Reference Specialty","MS UI Gothic","MT Extra","MYRIAD PRO","Marlett","Meiryo UI","Microsoft Uighur","Minion Pro","Monotype Corsiva","PMingLiU","Pristina","SCRIPTINA","Segoe UI Light","Serifa","SimHei","Small Fonts","Staccato222 BT","TRAJAN PRO","Univers CE 55 Medium","Vrinda","ZWAdobeF"],p={fontStyle:"normal",fontWeight:"normal",letterSpacing:"normal",lineBreak:"auto",lineHeight:"normal",textTransform:"none",textAlign:"left",textDecoration:"none",textShadow:"none",whiteSpace:"normal",wordBreak:"normal",wordSpacing:"normal"};function v(e){return e.toDataURL()}const y=navigator,C=window;const S=window,w=navigator,A=document;function b(){return l(["msWriteProfilerMark"in S,"msLaunchUri"in w,"msSaveBlob"in w])>=2}function M(){return l(["userActivation"in w,"mediaSession"in w,0===w.vendor.indexOf("Google"),"BackgroundFetchManager"in S,"BatteryManager"in S,"webkitMediaStream"in S,"webkitSpeechGrammar"in S])>=5}const T=navigator;const x=window;const O=window;const k=window;const P=document;const I={osCpu:function(){return navigator.oscpu},languages:function(){const e=[],t=T.language||T.userLanguage||T.browserLanguage||T.systemLanguage;if(void 0!==t&&e.push([t]),Array.isArray(T.languages))M()&&l([!("MediaSettingsRange"in S),!("PhotoCapabilities"in S),"RTCEncodedAudioFrame"in S,""+S.Intl=="[object Intl]"])>=2||e.push(T.languages);else if("string"==typeof T.languages){const t=T.languages;t&&e.push(t.split(","))}return e},colorDepth:function(){return window.screen.colorDepth},deviceMemory:function(){return navigator.deviceMemory},screenResolution:function(){const e=[u(x.screen.width),u(x.screen.height)];return e.sort().reverse(),e},availableScreenResolution:function(){if(O.screen.availWidth&&O.screen.availHeight){const e=[u(O.screen.availWidth),u(O.screen.availHeight)];return e.sort().reverse(),e}},hardwareConcurrency:function(){try{const e=u(navigator.hardwareConcurrency);return isNaN(e)?1:e}catch(e){return 1}},timezoneOffset:function(){return(new Date).getTimezoneOffset()},timezone:function(){var e;if(null===(e=k.Intl)||void 0===e?void 0:e.DateTimeFormat)return(new k.Intl.DateTimeFormat).resolvedOptions().timeZone},sessionStorage:function(){try{return!!window.sessionStorage}catch(e){return!0}},localStorage:function(){try{return!!window.localStorage}catch(e){return!0}},indexedDB:function(){if(!b())try{return!!window.indexedDB}catch(e){return!0}},openDatabase:function(){return!!window.openDatabase},cpuClass:function(){return navigator.cpuClass},platform:function(){return navigator.platform},plugins:function(){if(!navigator.plugins)return;const e=[];for(let t=0;t<navigator.plugins.length;++t){const n=navigator.plugins[t];if(!n)continue;const o=[];for(const e of n)o.push({type:e.type,suffixes:e.suffixes});e.push({name:n.name,description:n.description,mimeTypes:o})}return e},canvas:function(){const[e,t]=function(){const e=document.createElement("canvas");return e.width=240,e.height=140,e.style.display="inline",[e,e.getContext("2d")]}();if(!function(e,t){return!(!t||!e.toDataURL)}(e,t))return{winding:!1,data:""};t.rect(0,0,10,10),t.rect(2,2,6,6);const n=!t.isPointInPath(5,5,"evenodd");t.textBaseline="alphabetic",t.fillStyle="#f60",t.fillRect(125,1,62,20),t.fillStyle="#069",t.font="11pt no-real-font-123";const o="Cwm fjordbank š gly";return t.fillText(o,2,15),t.fillStyle="rgba(102, 204, 0, 0.2)",t.font="18pt Arial",t.fillText(o,4,45),t.globalCompositeOperation="multiply",t.fillStyle="rgb(255,0,255)",t.beginPath(),t.arc(50,50,50,0,2*Math.PI,!0),t.closePath(),t.fill(),t.fillStyle="rgb(0,255,255)",t.beginPath(),t.arc(100,50,50,0,2*Math.PI,!0),t.closePath(),t.fill(),t.fillStyle="rgb(255,255,0)",t.beginPath(),t.arc(75,100,50,0,2*Math.PI,!0),t.closePath(),t.fill(),t.fillStyle="rgb(255,0,255)",t.arc(75,75,75,0,2*Math.PI,!0),t.arc(75,75,25,0,2*Math.PI,!0),t.fill("evenodd"),{winding:n,data:v(e)}},touchSupport:function(){let e,t=0;void 0!==y.maxTouchPoints?t=u(y.maxTouchPoints):void 0!==y.msMaxTouchPoints&&(t=y.msMaxTouchPoints);try{document.createEvent("TouchEvent"),e=!0}catch(n){e=!1}return{maxTouchPoints:t,touchEvent:e,touchStart:"ontouchstart"in C}},fonts:function(){const e=h.body,t=h.createElement("div"),n=h.createElement("div"),o={},r={},i=()=>{const e=h.createElement("span");return Object.assign(e.style,p,{position:"absolute",left:"-9999px",fontSize:"48px"}),e.textContent="mmMwWLliI0O&1",e},a=(e,t)=>{const n=i();return n.style.fontFamily=`'${e}',${t}`,n},c=e=>g.some(((t,n)=>e[n].offsetWidth!==o[t]||e[n].offsetHeight!==r[t])),s=g.map((e=>{const n=i();return n.style.fontFamily=e,t.appendChild(n),n}));e.appendChild(t);for(let d=0,f=g.length;d<f;d++)o[g[d]]=s[d].offsetWidth,r[g[d]]=s[d].offsetHeight;const u=(()=>{const e={};for(const t of m)e[t]=g.map((e=>{const o=a(t,e);return n.appendChild(o),o}));return e})();e.appendChild(n);const l=[];for(let d=0,f=m.length;d<f;d++)c(u[m[d]])&&l.push(m[d]);return e.removeChild(n),e.removeChild(t),l},audio:function(){return s(this,void 0,void 0,(function*(){if(d.userAgent.match(/OS 11.+Version\/11.+Safari/))return-1;const e=f.OfflineAudioContext||f.webkitOfflineAudioContext;if(!e)return-2;const t=new e(1,44100,44100),n=t.createOscillator();n.type="triangle",n.frequency.setValueAtTime(1e4,t.currentTime);const o=t.createDynamicsCompressor();for(const[r,i]of[["threshold",-50],["knee",40],["ratio",12],["reduction",-20],["attack",0],["release",.25]])"function"==typeof o[r].setValueAtTime&&o[r].setValueAtTime(i,t.currentTime);return n.connect(o),o.connect(t.destination),n.start(0),t.startRendering(),new Promise((e=>{const r=setTimeout((()=>{t.oncomplete=()=>{},e(-3)}),1e3);t.oncomplete=t=>{let i;try{clearTimeout(r),i=t.renderedBuffer.getChannelData(0).slice(4500,5e3).reduce(((e,t)=>e+Math.abs(t)),0),n.disconnect(),o.disconnect()}catch(a){return void e(-4)}e(i)}}))}))},pluginsSupport:function(){return void 0!==navigator.plugins},productSub:function(){return navigator.productSub},emptyEvalLength:function(){return eval.toString().length},errorFF:function(){try{throw"a"}catch(e){try{return e.toSource(),!0}catch(t){return!1}}},vendor:function(){return navigator.vendor},chrome:function(){return void 0!==window.chrome},cookiesEnabled:function(){try{P.cookie="cookietest=1";const e=-1!==P.cookie.indexOf("cookietest=");return P.cookie="cookietest=1; expires=Thu, 01-Jan-1970 00:00:01 GMT",e}catch(e){return!1}}};function B(e,t,n){return s(this,void 0,void 0,(function*(){let o=Date.now();const r={};for(const a of Object.keys(e)){if(function(e,t){for(let n=0,o=e.length;n<o;++n)if(e[n]===t)return!0;return!1}(n,a))continue;let c,s;try{c={value:yield e[a](t)}}catch(i){c=i&&"object"==typeof i&&"message"in i?{error:i}:{error:{message:i}}}s=Date.now(),r[a]=Object.assign(Object.assign({},c),{duration:s-o}),o=s}return r}))}function D(e){return JSON.stringify(e,((e,t)=>{var n;return t instanceof Error?Object.assign(Object.assign({},t),{message:t.message,stack:null===(n=t.stack)||void 0===n?void 0:n.split("\n")}):t}),2)}function E(e){return c(function(e){let t="";for(const n of Object.keys(e)){const o=e[n],r=o.error?"error":JSON.stringify(o.value);t+=`${t?"|":""}${n.replace(/([:|\\])/g,"\\$1")}:${r}`}return t}(e))}class R{get(e={}){return s(this,void 0,void 0,(function*(){const t=yield B(I,void 0,[]),n=function(e){let t;return{components:e,get visitorId(){return void 0===t&&(t=E(this.components)),t},set visitorId(e){t=e}}}(t);return e.debug&&console.log(`Copy the text below to get the debug data:\n\n\`\`\`\nversion: 3.0.0\ngetOptions: ${JSON.stringify(e,void 0,2)}\nvisitorId: ${n.visitorId}\ncomponents: ${D(t)}\n\`\`\``),n}))}}function L({delayFallback:e=50}={}){return s(this,void 0,void 0,(function*(){var t;return yield(t=e,new Promise((e=>{window.requestIdleCallback?window.requestIdleCallback((()=>e())):setTimeout(e,t)}))),new R}))}var F={load:L,hashComponents:E,componentsToDebugString:D};const j=c;e.componentsToDebugString=D,e.default=F,e.getComponents=B,e.hashComponents=E,e.isChromium=M,e.isDesktopSafari=function(){return"safari"in S},e.isGecko=function(){var e;return l(["buildID"in w,(null===(e=A.documentElement)||void 0===e?void 0:e.style)&&"MozAppearance"in A.documentElement.style,"MediaRecorderErrorEvent"in S,"mozInnerScreenX"in S,"CSSMozDocumentRule"in S,"CanvasCaptureMediaStream"in S])>=4},e.isIEOrOldEdge=b,e.load=L,e.murmurX64Hash128=j,Object.defineProperty(e,"__esModule",{value:!0})})); | ||
!function(e,t){"object"==typeof exports&&"undefined"!=typeof module?t(exports):"function"==typeof define&&define.amd?define(["exports"],t):t((e="undefined"!=typeof globalThis?globalThis:e||self).FingerprintJS={})}(this,(function(e){"use strict";function t(e,t){e=[e[0]>>>16,65535&e[0],e[1]>>>16,65535&e[1]],t=[t[0]>>>16,65535&t[0],t[1]>>>16,65535&t[1]];var n=[0,0,0,0];return n[3]+=e[3]+t[3],n[2]+=n[3]>>>16,n[3]&=65535,n[2]+=e[2]+t[2],n[1]+=n[2]>>>16,n[2]&=65535,n[1]+=e[1]+t[1],n[0]+=n[1]>>>16,n[1]&=65535,n[0]+=e[0]+t[0],n[0]&=65535,[n[0]<<16|n[1],n[2]<<16|n[3]]}function n(e,t){e=[e[0]>>>16,65535&e[0],e[1]>>>16,65535&e[1]],t=[t[0]>>>16,65535&t[0],t[1]>>>16,65535&t[1]];var n=[0,0,0,0];return n[3]+=e[3]*t[3],n[2]+=n[3]>>>16,n[3]&=65535,n[2]+=e[2]*t[3],n[1]+=n[2]>>>16,n[2]&=65535,n[2]+=e[3]*t[2],n[1]+=n[2]>>>16,n[2]&=65535,n[1]+=e[1]*t[3],n[0]+=n[1]>>>16,n[1]&=65535,n[1]+=e[2]*t[2],n[0]+=n[1]>>>16,n[1]&=65535,n[1]+=e[3]*t[1],n[0]+=n[1]>>>16,n[1]&=65535,n[0]+=e[0]*t[3]+e[1]*t[2]+e[2]*t[1]+e[3]*t[0],n[0]&=65535,[n[0]<<16|n[1],n[2]<<16|n[3]]}function r(e,t){return 32===(t%=64)?[e[1],e[0]]:t<32?[e[0]<<t|e[1]>>>32-t,e[1]<<t|e[0]>>>32-t]:(t-=32,[e[1]<<t|e[0]>>>32-t,e[0]<<t|e[1]>>>32-t])}function o(e,t){return 0===(t%=64)?e:t<32?[e[0]<<t|e[1]>>>32-t,e[1]<<t]:[e[1]<<t-32,0]}function i(e,t){return[e[0]^t[0],e[1]^t[1]]}function a(e){return e=i(e,[0,e[0]>>>1]),e=i(e=n(e,[4283543511,3981806797]),[0,e[0]>>>1]),e=i(e=n(e,[3301882366,444984403]),[0,e[0]>>>1])}function c(e,c){c=c||0;var u,s=(e=e||"").length%16,l=e.length-s,f=[0,c],d=[0,c],h=[0,0],v=[0,0],g=[2277735313,289559509],p=[1291169091,658871167];for(u=0;u<l;u+=16)h=[255&e.charCodeAt(u+4)|(255&e.charCodeAt(u+5))<<8|(255&e.charCodeAt(u+6))<<16|(255&e.charCodeAt(u+7))<<24,255&e.charCodeAt(u)|(255&e.charCodeAt(u+1))<<8|(255&e.charCodeAt(u+2))<<16|(255&e.charCodeAt(u+3))<<24],v=[255&e.charCodeAt(u+12)|(255&e.charCodeAt(u+13))<<8|(255&e.charCodeAt(u+14))<<16|(255&e.charCodeAt(u+15))<<24,255&e.charCodeAt(u+8)|(255&e.charCodeAt(u+9))<<8|(255&e.charCodeAt(u+10))<<16|(255&e.charCodeAt(u+11))<<24],h=r(h=n(h,g),31),f=t(f=r(f=i(f,h=n(h,p)),27),d),f=t(n(f,[0,5]),[0,1390208809]),v=r(v=n(v,p),33),d=t(d=r(d=i(d,v=n(v,g)),31),f),d=t(n(d,[0,5]),[0,944331445]);switch(h=[0,0],v=[0,0],s){case 15:v=i(v,o([0,e.charCodeAt(u+14)],48));case 14:v=i(v,o([0,e.charCodeAt(u+13)],40));case 13:v=i(v,o([0,e.charCodeAt(u+12)],32));case 12:v=i(v,o([0,e.charCodeAt(u+11)],24));case 11:v=i(v,o([0,e.charCodeAt(u+10)],16));case 10:v=i(v,o([0,e.charCodeAt(u+9)],8));case 9:v=n(v=i(v,[0,e.charCodeAt(u+8)]),p),d=i(d,v=n(v=r(v,33),g));case 8:h=i(h,o([0,e.charCodeAt(u+7)],56));case 7:h=i(h,o([0,e.charCodeAt(u+6)],48));case 6:h=i(h,o([0,e.charCodeAt(u+5)],40));case 5:h=i(h,o([0,e.charCodeAt(u+4)],32));case 4:h=i(h,o([0,e.charCodeAt(u+3)],24));case 3:h=i(h,o([0,e.charCodeAt(u+2)],16));case 2:h=i(h,o([0,e.charCodeAt(u+1)],8));case 1:h=n(h=i(h,[0,e.charCodeAt(u)]),g),f=i(f,h=n(h=r(h,31),p))}return f=t(f=i(f,[0,e.length]),d=i(d,[0,e.length])),d=t(d,f),f=t(f=a(f),d=a(d)),d=t(d,f),("00000000"+(f[0]>>>0).toString(16)).slice(-8)+("00000000"+(f[1]>>>0).toString(16)).slice(-8)+("00000000"+(d[0]>>>0).toString(16)).slice(-8)+("00000000"+(d[1]>>>0).toString(16)).slice(-8)}var u=function(){return(u=Object.assign||function(e){for(var t,n=1,r=arguments.length;n<r;n++)for(var o in t=arguments[n])Object.prototype.hasOwnProperty.call(t,o)&&(e[o]=t[o]);return e}).apply(this,arguments)};function s(e,t,n,r){return new(n||(n=Promise))((function(o,i){function a(e){try{u(r.next(e))}catch(t){i(t)}}function c(e){try{u(r.throw(e))}catch(t){i(t)}}function u(e){var t;e.done?o(e.value):(t=e.value,t instanceof n?t:new n((function(e){e(t)}))).then(a,c)}u((r=r.apply(e,t||[])).next())}))}function l(e,t){var n,r,o,i,a={label:0,sent:function(){if(1&o[0])throw o[1];return o[1]},trys:[],ops:[]};return i={next:c(0),throw:c(1),return:c(2)},"function"==typeof Symbol&&(i[Symbol.iterator]=function(){return this}),i;function c(i){return function(c){return function(i){if(n)throw new TypeError("Generator is already executing.");for(;a;)try{if(n=1,r&&(o=2&i[0]?r.return:i[0]?r.throw||((o=r.return)&&o.call(r),0):r.next)&&!(o=o.call(r,i[1])).done)return o;switch(r=0,o&&(i=[2&i[0],o.value]),i[0]){case 0:case 1:o=i;break;case 4:return a.label++,{value:i[1],done:!1};case 5:a.label++,r=i[1],i=[0];continue;case 7:i=a.ops.pop(),a.trys.pop();continue;default:if(!(o=a.trys,(o=o.length>0&&o[o.length-1])||6!==i[0]&&2!==i[0])){a=0;continue}if(3===i[0]&&(!o||i[1]>o[0]&&i[1]<o[3])){a.label=i[1];break}if(6===i[0]&&a.label<o[1]){a.label=o[1],o=i;break}if(o&&a.label<o[2]){a.label=o[2],a.ops.push(i);break}o[2]&&a.ops.pop(),a.trys.pop();continue}i=t.call(e,a)}catch(c){i=[6,c],r=0}finally{n=o=0}if(5&i[0])throw i[1];return{value:i[0]?i[1]:void 0,done:!0}}([i,c])}}}function f(e){return"number"==typeof e?0|e:parseInt(e)}function d(e){return"number"==typeof e?e:parseFloat(e)}function h(e){return e.reduce((function(e,t){return e+(t?1:0)}),0)}var v=navigator,g=window;function p(e){return e&&"function"==typeof e.setValueAtTime}var m=document,y=["monospace","sans-serif","serif"],b=["sans-serif-thin","ARNO PRO","Agency FB","Arabic Typesetting","Arial Unicode MS","AvantGarde Bk BT","BankGothic Md BT","Batang","Bitstream Vera Sans Mono","Calibri","Century","Century Gothic","Clarendon","EUROSTILE","Franklin Gothic","Futura Bk BT","Futura Md BT","GOTHAM","Gill Sans","HELV","Haettenschweiler","Helvetica Neue","Humanst521 BT","Leelawadee","Letter Gothic","Levenim MT","Lucida Bright","Lucida Sans","Menlo","MS Mincho","MS Outlook","MS Reference Specialty","MS UI Gothic","MT Extra","MYRIAD PRO","Marlett","Meiryo UI","Microsoft Uighur","Minion Pro","Monotype Corsiva","PMingLiU","Pristina","SCRIPTINA","Segoe UI Light","Serifa","SimHei","Small Fonts","Staccato222 BT","TRAJAN PRO","Univers CE 55 Medium","Vrinda","ZWAdobeF"],w={fontStyle:"normal",fontWeight:"normal",letterSpacing:"normal",lineBreak:"auto",lineHeight:"normal",textTransform:"none",textAlign:"left",textDecoration:"none",textShadow:"none",whiteSpace:"normal",wordBreak:"normal",wordSpacing:"normal",position:"absolute",left:"-9999px",fontSize:"48px"};var S=window,C=navigator,A=document;function M(){return h(["MSCSSMatrix"in S,"msSetImmediate"in S,"msIndexedDB"in S,"msMaxTouchPoints"in C,"msPointerEnabled"in C])>=4}function T(){return h(["msWriteProfilerMark"in S,"MSStream"in S,"msLaunchUri"in C,"msSaveBlob"in C])>=3&&!M()}function x(){return h(["webkitPersistentStorage"in C,"webkitTemporaryStorage"in C,0===C.vendor.indexOf("Google"),"webkitResolveLocalFileSystemURL"in S,"BatteryManager"in S,"webkitMediaStream"in S,"webkitSpeechGrammar"in S])>=5}function k(e){return e.toDataURL()}var P=navigator,O=window;var I=navigator;var D=window;var E=window;var R=window;var B=document;var L={osCpu:function(){return navigator.oscpu},languages:function(){var e=[],t=I.language||I.userLanguage||I.browserLanguage||I.systemLanguage;if(void 0!==t&&e.push([t]),Array.isArray(I.languages))x()&&h([!("MediaSettingsRange"in S),"RTCEncodedAudioFrame"in S,""+S.Intl=="[object Intl]",""+S.Reflect=="[object Reflect]"])>=3||e.push(I.languages);else if("string"==typeof I.languages){var n=I.languages;n&&e.push(n.split(","))}return e},colorDepth:function(){return window.screen.colorDepth},deviceMemory:function(){return navigator.deviceMemory},screenResolution:function(){var e=[f(D.screen.width),f(D.screen.height)];return e.sort().reverse(),e},availableScreenResolution:function(){if(E.screen.availWidth&&E.screen.availHeight){var e=[f(E.screen.availWidth),f(E.screen.availHeight)];return e.sort().reverse(),e}},hardwareConcurrency:function(){try{var e=f(navigator.hardwareConcurrency);return isNaN(e)?1:e}catch(t){return 1}},timezoneOffset:function(){var e=(new Date).getFullYear();return Math.max(d(new Date(e,0,1).getTimezoneOffset()),d(new Date(e,6,1).getTimezoneOffset()))},timezone:function(){var e;if(null===(e=R.Intl)||void 0===e?void 0:e.DateTimeFormat)return(new R.Intl.DateTimeFormat).resolvedOptions().timeZone},sessionStorage:function(){try{return!!window.sessionStorage}catch(e){return!0}},localStorage:function(){try{return!!window.localStorage}catch(e){return!0}},indexedDB:function(){if(!M()&&!T())try{return!!window.indexedDB}catch(e){return!0}},openDatabase:function(){return!!window.openDatabase},cpuClass:function(){return navigator.cpuClass},platform:function(){return navigator.platform},plugins:function(){if(M())return[];if(navigator.plugins){for(var e=[],t=0;t<navigator.plugins.length;++t){var n=navigator.plugins[t];if(n){for(var r=[],o=0;o<n.length;++o){var i=n[o];r.push({type:i.type,suffixes:i.suffixes})}e.push({name:n.name,description:n.description,mimeTypes:r})}}return e}},canvas:function(){var e=function(){var e=document.createElement("canvas");return e.width=240,e.height=140,e.style.display="inline",[e,e.getContext("2d")]}(),t=e[0],n=e[1];if(!function(e,t){return!(!t||!e.toDataURL)}(t,n))return{winding:!1,data:""};n.rect(0,0,10,10),n.rect(2,2,6,6);var r=!n.isPointInPath(5,5,"evenodd");n.textBaseline="alphabetic",n.fillStyle="#f60",n.fillRect(125,1,62,20),n.fillStyle="#069",n.font="11pt no-real-font-123";var o="Cwm fjordbank š gly";return n.fillText(o,2,15),n.fillStyle="rgba(102, 204, 0, 0.2)",n.font="18pt Arial",n.fillText(o,4,45),n.globalCompositeOperation="multiply",n.fillStyle="rgb(255,0,255)",n.beginPath(),n.arc(50,50,50,0,2*Math.PI,!0),n.closePath(),n.fill(),n.fillStyle="rgb(0,255,255)",n.beginPath(),n.arc(100,50,50,0,2*Math.PI,!0),n.closePath(),n.fill(),n.fillStyle="rgb(255,255,0)",n.beginPath(),n.arc(75,100,50,0,2*Math.PI,!0),n.closePath(),n.fill(),n.fillStyle="rgb(255,0,255)",n.arc(75,75,75,0,2*Math.PI,!0),n.arc(75,75,25,0,2*Math.PI,!0),n.fill("evenodd"),{winding:r,data:k(t)}},touchSupport:function(){var e,t=0;void 0!==P.maxTouchPoints?t=f(P.maxTouchPoints):void 0!==P.msMaxTouchPoints&&(t=P.msMaxTouchPoints);try{document.createEvent("TouchEvent"),e=!0}catch(n){e=!1}return{maxTouchPoints:t,touchEvent:e,touchStart:"ontouchstart"in O}},fonts:function(){var e=m.body,t=m.createElement("div"),n=m.createElement("div"),r={},o={},i=function(){var e=m.createElement("span");e.textContent="mmMwWLliI0O&1";for(var t=0,n=Object.keys(w);t<n.length;t++){var r=n[t];e.style[r]=w[r]}return e},a=function(e){return y.some((function(t,n){return e[n].offsetWidth!==r[t]||e[n].offsetHeight!==o[t]}))},c=y.map((function(e){var n=i();return n.style.fontFamily=e,t.appendChild(n),n}));e.appendChild(t);for(var u=0,s=y.length;u<s;u++)r[y[u]]=c[u].offsetWidth,o[y[u]]=c[u].offsetHeight;var l=function(){for(var e={},t=function(t){e[t]=y.map((function(e){var r=function(e,t){var n=i();return n.style.fontFamily="'"+e+"',"+t,n}(t,e);return n.appendChild(r),r}))},r=0,o=b;r<o.length;r++){t(o[r])}return e}();e.appendChild(n);for(var f=[],d=0,h=b.length;d<h;d++)a(l[b[d]])&&f.push(b[d]);return e.removeChild(n),e.removeChild(t),f},audio:function(){return s(this,void 0,void 0,(function(){var e,t,n,r,o,i,a,c,u,s;return l(this,(function(l){if(v.userAgent.match(/OS 11.+Version\/11.+Safari/))return[2,-1];if(!(e=g.OfflineAudioContext||g.webkitOfflineAudioContext))return[2,-2];for(t=new e(1,44100,44100),(n=t.createOscillator()).type="triangle",n.frequency.setValueAtTime(1e4,t.currentTime),r=t.createDynamicsCompressor(),o=0,i=[["threshold",-50],["knee",40],["ratio",12],["reduction",-20],["attack",0],["release",.25]];o<i.length;o++)c=(a=i[o])[0],u=a[1],p(s=r[c])&&s.setValueAtTime(u,t.currentTime);return n.connect(r),r.connect(t.destination),n.start(0),t.startRendering(),[2,new Promise((function(e){var o=setTimeout((function(){t.oncomplete=null,e(-3)}),1e3);t.oncomplete=function(t){var i;try{clearTimeout(o),i=t.renderedBuffer.getChannelData(0).slice(4500,5e3).reduce((function(e,t){return e+Math.abs(t)}),0),n.disconnect(),r.disconnect()}catch(a){return void e(-4)}e(i)}}))]}))}))},pluginsSupport:function(){return void 0!==navigator.plugins},productSub:function(){return navigator.productSub},emptyEvalLength:function(){return eval.toString().length},errorFF:function(){try{throw"a"}catch(e){try{return e.toSource(),!0}catch(t){return!1}}},vendor:function(){return navigator.vendor},chrome:function(){return void 0!==window.chrome},cookiesEnabled:function(){try{B.cookie="cookietest=1";var e=-1!==B.cookie.indexOf("cookietest=");return B.cookie="cookietest=1; expires=Thu, 01-Jan-1970 00:00:01 GMT",e}catch(t){return!1}}};function F(e,t,n){return s(this,void 0,void 0,(function(){var r,o,i,a,c,s,f,d,h;return l(this,(function(l){switch(l.label){case 0:r=Date.now(),o={},i=0,a=Object.keys(e),l.label=1;case 1:if(!(i<a.length))return[3,7];if(c=a[i],function(e,t){for(var n=0,r=e.length;n<r;++n)if(e[n]===t)return!0;return!1}(n,c))return[3,6];s=void 0,l.label=2;case 2:return l.trys.push([2,4,,5]),h={},[4,e[c](t)];case 3:return h.value=l.sent(),s=h,[3,5];case 4:return f=l.sent(),s=f&&"object"==typeof f&&"message"in f?{error:f}:{error:{message:f}},[3,5];case 5:d=Date.now(),o[c]=u(u({},s),{duration:d-r}),r=d,l.label=6;case 6:return i++,[3,1];case 7:return[2,o]}}))}))}function G(e){return JSON.stringify(e,(function(e,t){var n;return t instanceof Error?u(u({},t),{message:t.message,stack:null===(n=t.stack)||void 0===n?void 0:n.split("\n")}):t}),2)}function H(e){return c(function(e){for(var t="",n=0,r=Object.keys(e);n<r.length;n++){var o=r[n],i=e[o],a=i.error?"error":JSON.stringify(i.value);t+=(t?"|":"")+o.replace(/([:|\\])/g,"\\$1")+":"+a}return t}(e))}var U=function(){function e(){}return e.prototype.get=function(e){return void 0===e&&(e={}),s(this,void 0,void 0,(function(){var t,n;return l(this,(function(r){switch(r.label){case 0:return[4,F(L,void 0,[])];case 1:return t=r.sent(),n=function(e){var t;return{components:e,get visitorId(){return void 0===t&&(t=H(this.components)),t},set visitorId(e){t=e}}}(t),e.debug&&console.log("Copy the text below to get the debug data:\n\n```\nversion: 3.0.1\ngetOptions: "+JSON.stringify(e,void 0,2)+"\nvisitorId: "+n.visitorId+"\ncomponents: "+G(t)+"\n```"),[2,n]}}))}))},e}();function j(e){var t=(void 0===e?{}:e).delayFallback,n=void 0===t?50:t;return s(this,void 0,void 0,(function(){return l(this,(function(e){switch(e.label){case 0:return[4,(t=n,new Promise((function(e){window.requestIdleCallback?window.requestIdleCallback((function(){return e()})):setTimeout(e,t)})))];case 1:return e.sent(),[2,new U]}var t}))}))}var W={load:j,hashComponents:H,componentsToDebugString:G},N=c;e.componentsToDebugString=G,e.default=W,e.getComponents=F,e.hashComponents=H,e.isChromium=x,e.isDesktopSafari=function(){return"safari"in S},e.isEdgeHTML=T,e.isGecko=function(){var e;return h(["buildID"in C,(null===(e=A.documentElement)||void 0===e?void 0:e.style)&&"MozAppearance"in A.documentElement.style,"MediaRecorderErrorEvent"in S,"mozInnerScreenX"in S,"CSSMozDocumentRule"in S,"CanvasCaptureMediaStream"in S])>=4},e.isTrident=M,e.isWebKit=function(){return h(["ApplePayError"in S,"CSSPrimitiveValue"in S,"Counter"in S,0===C.vendor.indexOf("Apple"),"getStorageUpdates"in C,"WebKitMediaKeys"in S])>=4},e.load=j,e.murmurX64Hash128=N,Object.defineProperty(e,"__esModule",{value:!0})})); |
{ | ||
"name": "@fingerprintjs/fingerprintjs", | ||
"description": "Modern & flexible browser fingerprinting library", | ||
"version": "3.0.0", | ||
"version": "3.0.1", | ||
"keywords": [ | ||
"fraud", | ||
"fraud-detection", | ||
"fraud-prevention", | ||
"fraud detection", | ||
"fraud prevention", | ||
"browser", | ||
@@ -13,2 +13,4 @@ "identification", | ||
"fingerprinting", | ||
"browser fingerprint", | ||
"device fingerprint", | ||
"privacy" | ||
@@ -38,4 +40,7 @@ ], | ||
"playground:build": "cd playground && webpack --mode production", | ||
"lint": "eslint --ext .js,.ts --ignore-path .gitignore --max-warnings 0 .", | ||
"lint:fix": "yarn lint --fix", | ||
"test:local": "karma start tests/karma.local.config.js --single-run", | ||
"test:browserstack": "karma start tests/karma.browserstack.config.js --single-run" | ||
"test:browserstack": "karma start tests/karma.browserstack.config.js --single-run", | ||
"test:dts": "tsc --noEmit dist/fp.d.ts" | ||
}, | ||
@@ -51,3 +56,8 @@ "dependencies": { | ||
"@types/ua-parser-js": "^0.7.33", | ||
"@typescript-eslint/eslint-plugin": "^4.4.1", | ||
"@typescript-eslint/parser": "^4.4.1", | ||
"clean-webpack-plugin": "^3.0.0", | ||
"eslint": "^7.11.0", | ||
"eslint-config-prettier": "^6.13.0", | ||
"eslint-plugin-prettier": "^3.1.4", | ||
"html-webpack-plugin": "^4.5.0", | ||
@@ -62,2 +72,4 @@ "karma": "^5.2.3", | ||
"karma-typescript": "^5.2.0", | ||
"prettier": "^2.1.2", | ||
"promise-polyfill": "^8.2.0", | ||
"rimraf": "^3.0.2", | ||
@@ -64,0 +76,0 @@ "rollup": "^2.28.2", |
@@ -7,4 +7,4 @@ <p align="center"> | ||
<p align="center"> | ||
<a href="https://github.com/fingerprintjs/fingerprintjs/actions?workflow=Test"> | ||
<img src="https://github.com/fingerprintjs/fingerprintjs/workflows/Test/badge.svg" alt="Build status"> | ||
<a href="https://github.com/fingerprintjs/fingerprintjs/actions?workflow=Lint%20and%20test"> | ||
<img src="https://github.com/fingerprintjs/fingerprintjs/workflows/Lint%20and%20test/badge.svg" alt="Build status"> | ||
</a> | ||
@@ -28,8 +28,11 @@ <a href="https://www.npmjs.com/package/@fingerprintjs/fingerprintjs"> | ||
<script> | ||
function onFingerprintJSLoad(fpAgent) { | ||
// The FingerprintJS agent is ready. Get a visitor identifier when you'd like to. | ||
fpAgent.get().then(result => { | ||
// This is the visitor identifier: | ||
const visitorId = result.visitorId; | ||
console.log(visitorId); | ||
function initFingerprintJS() { | ||
FingerprintJS.load().then(fp => { | ||
// The FingerprintJS agent is ready. | ||
// Get a visitor identifier when you'd like to. | ||
fp.get().then(result => { | ||
// This is the visitor identifier: | ||
const visitorId = result.visitorId; | ||
console.log(visitorId); | ||
}); | ||
}); | ||
@@ -39,10 +42,8 @@ } | ||
<script | ||
async src="https://cdn.jsdelivr.net/npm/@fingerprintjs/fingerprintjs@3/dist/fp.min.js" | ||
onload="FingerprintJS.load().then(onFingerprintJSLoad)" | ||
async | ||
src="//cdn.jsdelivr.net/npm/@fingerprintjs/fingerprintjs@3/dist/fp.min.js" | ||
onload="initFingerprintJS()" | ||
></script> | ||
``` | ||
We recommend to upload [the JS script](https://cdn.jsdelivr.net/npm/@fingerprintjs/fingerprintjs@3/dist/fp.min.js) | ||
to your server because AdBlock and other browser extensions can block the public script URL. | ||
### Alternatively you can install from NPM to use with Webpack/Rollup/Browserify | ||
@@ -61,6 +62,7 @@ | ||
// We recommend to call `load` at application startup. | ||
const fpAgent = await FingerprintJS.load(); | ||
const fp = await FingerprintJS.load(); | ||
// The FingerprintJS agent is ready. Get a visitor identifier when you'd like to. | ||
const result = await fpAgent.get(); | ||
// The FingerprintJS agent is ready. | ||
// Get a visitor identifier when you'd like to. | ||
const result = await fp.get(); | ||
@@ -100,2 +102,3 @@ // This is the visitor identifier: | ||
<tr><td>Webhooks</td><td align="center">ā</td><td align="center">ā </td></tr> | ||
<tr><td>Stable identifier between versions</td><td align="center">ā</td><td align="center">ā </td></tr> | ||
</tbody> | ||
@@ -144,3 +147,3 @@ </table> | ||
function initFingerprintJS() { | ||
// ... | ||
// Start loading FingerprintJS here | ||
} | ||
@@ -150,3 +153,3 @@ </script> | ||
async | ||
src="https://cdn.jsdelivr.net/npm/@fingerprintjs/fingerprintjs@3/dist/fp.min.js" | ||
src="//cdn.jsdelivr.net/npm/@fingerprintjs/fingerprintjs@3/dist/fp.min.js" | ||
onload="initFingerprintJS()" | ||
@@ -157,11 +160,34 @@ ></script> | ||
```js | ||
require(['https://cdn.jsdelivr.net/npm/@fingerprintjs/fingerprintjs@3/dist/fp.umd.min.js'], (FingerprintJS) => {/* ... */}); | ||
require( | ||
['//cdn.jsdelivr.net/npm/@fingerprintjs/fingerprintjs@3/dist/fp.umd.min.js'], | ||
(FingerprintJS) => { | ||
// Start loading FingerprintJS here | ||
} | ||
); | ||
``` | ||
- ECMAScript module | ||
```bash | ||
# Install the package first: | ||
npm i @fingerprintjs/fingerprintjs | ||
# or | ||
yarn add @fingerprintjs/fingerprintjs | ||
``` | ||
```js | ||
import FingerprintJS from '@fingerprintjs/fingerprintjs'; | ||
// Start loading FingerprintJS here | ||
``` | ||
- CommonJS | ||
```bash | ||
# Install the package first: | ||
npm i @fingerprintjs/fingerprintjs | ||
# or | ||
yarn add @fingerprintjs/fingerprintjs | ||
``` | ||
```js | ||
const FingerprintJS = require('@fingerprintjs/fingerprintjs'); | ||
// Start loading FingerprintJS here | ||
``` | ||
@@ -197,3 +223,3 @@ | ||
- [Migration guide](migrating-v2-v3.md) | ||
- [Migration guide](docs/migrating_v2_v3.md) | ||
- [V2 documentation](https://github.com/fingerprintjs/fingerprintjs/tree/v2) | ||
@@ -203,4 +229,5 @@ | ||
The library doesn't guarantee the same visitor identifier between versions, | ||
but will try to keep them the same as much as possible. | ||
The OSS version doesn't guarantee the same visitor identifier between versions, | ||
but will try to keep them the same as much as possible. | ||
To get identifiers that remain stable up to 1 year, please consider [upgrading to pro.](https://dashboard.fingerprintjs.com) | ||
@@ -213,7 +240,9 @@ The documented JS API follows [Semantic Versioning](https://semver.org). | ||
```bash | ||
npx browserslist "> 1% in us" | ||
npx browserslist "cover 95% in us, not IE < 10" | ||
``` | ||
See more details and learn how to run the library in old browsers in the [documentation article](docs/browser_support.md). | ||
## Contribution | ||
See the [contribution guidelines](contributing.md) to learn how to start a playground, test and build. |
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
235028
5140
238
34