正在显示
10 个修改的文件
包含
5843 行增加
和
0 行删除
LICENSE
0 → 100644
1 | +Copyright (c) 2020 FingerprintJS, Inc | ||
2 | + | ||
3 | +Permission is hereby granted, free of charge, to any person obtaining a copy | ||
4 | +of this software and associated documentation files (the "Software"), to deal | ||
5 | +in the Software without restriction, including without limitation the rights | ||
6 | +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell | ||
7 | +copies of the Software, and to permit persons to whom the Software is | ||
8 | +furnished to do so, subject to the following conditions: | ||
9 | + | ||
10 | +The above copyright notice and this permission notice shall be included in all | ||
11 | +copies or substantial portions of the Software. | ||
12 | + | ||
13 | +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR | ||
14 | +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, | ||
15 | +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE | ||
16 | +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER | ||
17 | +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, | ||
18 | +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE | ||
19 | +SOFTWARE. |
dist/fp.cjs.js
0 → 100644
1 | +/** | ||
2 | + * FingerprintJS v3.0.3 - Copyright (c) FingerprintJS, Inc, 2020 (https://fingerprintjs.com) | ||
3 | + * Licensed under the MIT (http://www.opensource.org/licenses/mit-license.php) license. | ||
4 | + * | ||
5 | + * This software contains code from open-source projects: | ||
6 | + * MurmurHash3 by Karan Lyons (https://github.com/karanlyons/murmurHash3.js) | ||
7 | + */ | ||
8 | + | ||
9 | +'use strict'; | ||
10 | +(function(window) { | ||
11 | + Object.defineProperty(exports, '__esModule', { value: true }); | ||
12 | + | ||
13 | + var tslib = require('tslib'); | ||
14 | + | ||
15 | + /* | ||
16 | + * Taken from https://github.com/karanlyons/murmurHash3.js/blob/a33d0723127e2e5415056c455f8aed2451ace208/murmurHash3.js | ||
17 | + */ | ||
18 | + // | ||
19 | + // Given two 64bit ints (as an array of two 32bit ints) returns the two | ||
20 | + // added together as a 64bit int (as an array of two 32bit ints). | ||
21 | + // | ||
22 | + function x64Add(m, n) { | ||
23 | + m = [m[0] >>> 16, m[0] & 0xffff, m[1] >>> 16, m[1] & 0xffff]; | ||
24 | + n = [n[0] >>> 16, n[0] & 0xffff, n[1] >>> 16, n[1] & 0xffff]; | ||
25 | + var o = [0, 0, 0, 0]; | ||
26 | + o[3] += m[3] + n[3]; | ||
27 | + o[2] += o[3] >>> 16; | ||
28 | + o[3] &= 0xffff; | ||
29 | + o[2] += m[2] + n[2]; | ||
30 | + o[1] += o[2] >>> 16; | ||
31 | + o[2] &= 0xffff; | ||
32 | + o[1] += m[1] + n[1]; | ||
33 | + o[0] += o[1] >>> 16; | ||
34 | + o[1] &= 0xffff; | ||
35 | + o[0] += m[0] + n[0]; | ||
36 | + o[0] &= 0xffff; | ||
37 | + return [(o[0] << 16) | o[1], (o[2] << 16) | o[3]]; | ||
38 | + } | ||
39 | + // | ||
40 | + // Given two 64bit ints (as an array of two 32bit ints) returns the two | ||
41 | + // multiplied together as a 64bit int (as an array of two 32bit ints). | ||
42 | + // | ||
43 | + function x64Multiply(m, n) { | ||
44 | + m = [m[0] >>> 16, m[0] & 0xffff, m[1] >>> 16, m[1] & 0xffff]; | ||
45 | + n = [n[0] >>> 16, n[0] & 0xffff, n[1] >>> 16, n[1] & 0xffff]; | ||
46 | + var o = [0, 0, 0, 0]; | ||
47 | + o[3] += m[3] * n[3]; | ||
48 | + o[2] += o[3] >>> 16; | ||
49 | + o[3] &= 0xffff; | ||
50 | + o[2] += m[2] * n[3]; | ||
51 | + o[1] += o[2] >>> 16; | ||
52 | + o[2] &= 0xffff; | ||
53 | + o[2] += m[3] * n[2]; | ||
54 | + o[1] += o[2] >>> 16; | ||
55 | + o[2] &= 0xffff; | ||
56 | + o[1] += m[1] * n[3]; | ||
57 | + o[0] += o[1] >>> 16; | ||
58 | + o[1] &= 0xffff; | ||
59 | + o[1] += m[2] * n[2]; | ||
60 | + o[0] += o[1] >>> 16; | ||
61 | + o[1] &= 0xffff; | ||
62 | + o[1] += m[3] * n[1]; | ||
63 | + o[0] += o[1] >>> 16; | ||
64 | + o[1] &= 0xffff; | ||
65 | + o[0] += m[0] * n[3] + m[1] * n[2] + m[2] * n[1] + m[3] * n[0]; | ||
66 | + o[0] &= 0xffff; | ||
67 | + return [(o[0] << 16) | o[1], (o[2] << 16) | o[3]]; | ||
68 | + } | ||
69 | + // | ||
70 | + // Given a 64bit int (as an array of two 32bit ints) and an int | ||
71 | + // representing a number of bit positions, returns the 64bit int (as an | ||
72 | + // array of two 32bit ints) rotated left by that number of positions. | ||
73 | + // | ||
74 | + function x64Rotl(m, n) { | ||
75 | + n %= 64; | ||
76 | + if (n === 32) { | ||
77 | + return [m[1], m[0]]; | ||
78 | + } | ||
79 | + else if (n < 32) { | ||
80 | + return [(m[0] << n) | (m[1] >>> (32 - n)), (m[1] << n) | (m[0] >>> (32 - n))]; | ||
81 | + } | ||
82 | + else { | ||
83 | + n -= 32; | ||
84 | + return [(m[1] << n) | (m[0] >>> (32 - n)), (m[0] << n) | (m[1] >>> (32 - n))]; | ||
85 | + } | ||
86 | + } | ||
87 | + // | ||
88 | + // Given a 64bit int (as an array of two 32bit ints) and an int | ||
89 | + // representing a number of bit positions, returns the 64bit int (as an | ||
90 | + // array of two 32bit ints) shifted left by that number of positions. | ||
91 | + // | ||
92 | + function x64LeftShift(m, n) { | ||
93 | + n %= 64; | ||
94 | + if (n === 0) { | ||
95 | + return m; | ||
96 | + } | ||
97 | + else if (n < 32) { | ||
98 | + return [(m[0] << n) | (m[1] >>> (32 - n)), m[1] << n]; | ||
99 | + } | ||
100 | + else { | ||
101 | + return [m[1] << (n - 32), 0]; | ||
102 | + } | ||
103 | + } | ||
104 | + // | ||
105 | + // Given two 64bit ints (as an array of two 32bit ints) returns the two | ||
106 | + // xored together as a 64bit int (as an array of two 32bit ints). | ||
107 | + // | ||
108 | + function x64Xor(m, n) { | ||
109 | + return [m[0] ^ n[0], m[1] ^ n[1]]; | ||
110 | + } | ||
111 | + // | ||
112 | + // Given a block, returns murmurHash3's final x64 mix of that block. | ||
113 | + // (`[0, h[0] >>> 1]` is a 33 bit unsigned right shift. This is the | ||
114 | + // only place where we need to right shift 64bit ints.) | ||
115 | + // | ||
116 | + function x64Fmix(h) { | ||
117 | + h = x64Xor(h, [0, h[0] >>> 1]); | ||
118 | + h = x64Multiply(h, [0xff51afd7, 0xed558ccd]); | ||
119 | + h = x64Xor(h, [0, h[0] >>> 1]); | ||
120 | + h = x64Multiply(h, [0xc4ceb9fe, 0x1a85ec53]); | ||
121 | + h = x64Xor(h, [0, h[0] >>> 1]); | ||
122 | + return h; | ||
123 | + } | ||
124 | + // | ||
125 | + // Given a string and an optional seed as an int, returns a 128 bit | ||
126 | + // hash using the x64 flavor of MurmurHash3, as an unsigned hex. | ||
127 | + // | ||
128 | + function x64hash128(key, seed) { | ||
129 | + key = key || ''; | ||
130 | + seed = seed || 0; | ||
131 | + var remainder = key.length % 16; | ||
132 | + var bytes = key.length - remainder; | ||
133 | + var h1 = [0, seed]; | ||
134 | + var h2 = [0, seed]; | ||
135 | + var k1 = [0, 0]; | ||
136 | + var k2 = [0, 0]; | ||
137 | + var c1 = [0x87c37b91, 0x114253d5]; | ||
138 | + var c2 = [0x4cf5ad43, 0x2745937f]; | ||
139 | + var i; | ||
140 | + for (i = 0; i < bytes; i = i + 16) { | ||
141 | + k1 = [ | ||
142 | + (key.charCodeAt(i + 4) & 0xff) | | ||
143 | + ((key.charCodeAt(i + 5) & 0xff) << 8) | | ||
144 | + ((key.charCodeAt(i + 6) & 0xff) << 16) | | ||
145 | + ((key.charCodeAt(i + 7) & 0xff) << 24), | ||
146 | + (key.charCodeAt(i) & 0xff) | | ||
147 | + ((key.charCodeAt(i + 1) & 0xff) << 8) | | ||
148 | + ((key.charCodeAt(i + 2) & 0xff) << 16) | | ||
149 | + ((key.charCodeAt(i + 3) & 0xff) << 24), | ||
150 | + ]; | ||
151 | + k2 = [ | ||
152 | + (key.charCodeAt(i + 12) & 0xff) | | ||
153 | + ((key.charCodeAt(i + 13) & 0xff) << 8) | | ||
154 | + ((key.charCodeAt(i + 14) & 0xff) << 16) | | ||
155 | + ((key.charCodeAt(i + 15) & 0xff) << 24), | ||
156 | + (key.charCodeAt(i + 8) & 0xff) | | ||
157 | + ((key.charCodeAt(i + 9) & 0xff) << 8) | | ||
158 | + ((key.charCodeAt(i + 10) & 0xff) << 16) | | ||
159 | + ((key.charCodeAt(i + 11) & 0xff) << 24), | ||
160 | + ]; | ||
161 | + k1 = x64Multiply(k1, c1); | ||
162 | + k1 = x64Rotl(k1, 31); | ||
163 | + k1 = x64Multiply(k1, c2); | ||
164 | + h1 = x64Xor(h1, k1); | ||
165 | + h1 = x64Rotl(h1, 27); | ||
166 | + h1 = x64Add(h1, h2); | ||
167 | + h1 = x64Add(x64Multiply(h1, [0, 5]), [0, 0x52dce729]); | ||
168 | + k2 = x64Multiply(k2, c2); | ||
169 | + k2 = x64Rotl(k2, 33); | ||
170 | + k2 = x64Multiply(k2, c1); | ||
171 | + h2 = x64Xor(h2, k2); | ||
172 | + h2 = x64Rotl(h2, 31); | ||
173 | + h2 = x64Add(h2, h1); | ||
174 | + h2 = x64Add(x64Multiply(h2, [0, 5]), [0, 0x38495ab5]); | ||
175 | + } | ||
176 | + k1 = [0, 0]; | ||
177 | + k2 = [0, 0]; | ||
178 | + switch (remainder) { | ||
179 | + case 15: | ||
180 | + k2 = x64Xor(k2, x64LeftShift([0, key.charCodeAt(i + 14)], 48)); | ||
181 | + // fallthrough | ||
182 | + case 14: | ||
183 | + k2 = x64Xor(k2, x64LeftShift([0, key.charCodeAt(i + 13)], 40)); | ||
184 | + // fallthrough | ||
185 | + case 13: | ||
186 | + k2 = x64Xor(k2, x64LeftShift([0, key.charCodeAt(i + 12)], 32)); | ||
187 | + // fallthrough | ||
188 | + case 12: | ||
189 | + k2 = x64Xor(k2, x64LeftShift([0, key.charCodeAt(i + 11)], 24)); | ||
190 | + // fallthrough | ||
191 | + case 11: | ||
192 | + k2 = x64Xor(k2, x64LeftShift([0, key.charCodeAt(i + 10)], 16)); | ||
193 | + // fallthrough | ||
194 | + case 10: | ||
195 | + k2 = x64Xor(k2, x64LeftShift([0, key.charCodeAt(i + 9)], 8)); | ||
196 | + // fallthrough | ||
197 | + case 9: | ||
198 | + k2 = x64Xor(k2, [0, key.charCodeAt(i + 8)]); | ||
199 | + k2 = x64Multiply(k2, c2); | ||
200 | + k2 = x64Rotl(k2, 33); | ||
201 | + k2 = x64Multiply(k2, c1); | ||
202 | + h2 = x64Xor(h2, k2); | ||
203 | + // fallthrough | ||
204 | + case 8: | ||
205 | + k1 = x64Xor(k1, x64LeftShift([0, key.charCodeAt(i + 7)], 56)); | ||
206 | + // fallthrough | ||
207 | + case 7: | ||
208 | + k1 = x64Xor(k1, x64LeftShift([0, key.charCodeAt(i + 6)], 48)); | ||
209 | + // fallthrough | ||
210 | + case 6: | ||
211 | + k1 = x64Xor(k1, x64LeftShift([0, key.charCodeAt(i + 5)], 40)); | ||
212 | + // fallthrough | ||
213 | + case 5: | ||
214 | + k1 = x64Xor(k1, x64LeftShift([0, key.charCodeAt(i + 4)], 32)); | ||
215 | + // fallthrough | ||
216 | + case 4: | ||
217 | + k1 = x64Xor(k1, x64LeftShift([0, key.charCodeAt(i + 3)], 24)); | ||
218 | + // fallthrough | ||
219 | + case 3: | ||
220 | + k1 = x64Xor(k1, x64LeftShift([0, key.charCodeAt(i + 2)], 16)); | ||
221 | + // fallthrough | ||
222 | + case 2: | ||
223 | + k1 = x64Xor(k1, x64LeftShift([0, key.charCodeAt(i + 1)], 8)); | ||
224 | + // fallthrough | ||
225 | + case 1: | ||
226 | + k1 = x64Xor(k1, [0, key.charCodeAt(i)]); | ||
227 | + k1 = x64Multiply(k1, c1); | ||
228 | + k1 = x64Rotl(k1, 31); | ||
229 | + k1 = x64Multiply(k1, c2); | ||
230 | + h1 = x64Xor(h1, k1); | ||
231 | + // fallthrough | ||
232 | + } | ||
233 | + h1 = x64Xor(h1, [0, key.length]); | ||
234 | + h2 = x64Xor(h2, [0, key.length]); | ||
235 | + h1 = x64Add(h1, h2); | ||
236 | + h2 = x64Add(h2, h1); | ||
237 | + h1 = x64Fmix(h1); | ||
238 | + h2 = x64Fmix(h2); | ||
239 | + h1 = x64Add(h1, h2); | ||
240 | + h2 = x64Add(h2, h1); | ||
241 | + return (('00000000' + (h1[0] >>> 0).toString(16)).slice(-8) + | ||
242 | + ('00000000' + (h1[1] >>> 0).toString(16)).slice(-8) + | ||
243 | + ('00000000' + (h2[0] >>> 0).toString(16)).slice(-8) + | ||
244 | + ('00000000' + (h2[1] >>> 0).toString(16)).slice(-8)); | ||
245 | + } | ||
246 | + | ||
247 | + var version = "3.0.3"; | ||
248 | + | ||
249 | + function requestIdleCallbackIfAvailable(fallbackTimeout) { | ||
250 | + return new Promise(function (resolve) { | ||
251 | + if (window.requestIdleCallback) { | ||
252 | + window.requestIdleCallback(function () { return resolve(); }); | ||
253 | + } | ||
254 | + else { | ||
255 | + setTimeout(resolve, fallbackTimeout); | ||
256 | + } | ||
257 | + }); | ||
258 | + } | ||
259 | + | ||
260 | + /* | ||
261 | + * This file contains functions to work with pure data only (no browser features, DOM, side effects, etc). | ||
262 | + */ | ||
263 | + /** | ||
264 | + * Does the same as Array.prototype.includes but has better typing | ||
265 | + */ | ||
266 | + function includes(haystack, needle) { | ||
267 | + for (var i = 0, l = haystack.length; i < l; ++i) { | ||
268 | + if (haystack[i] === needle) { | ||
269 | + return true; | ||
270 | + } | ||
271 | + } | ||
272 | + return false; | ||
273 | + } | ||
274 | + /** | ||
275 | + * Like `!includes()` but with proper typing | ||
276 | + */ | ||
277 | + function excludes(haystack, needle) { | ||
278 | + return !includes(haystack, needle); | ||
279 | + } | ||
280 | + /** | ||
281 | + * Be careful, NaN can return | ||
282 | + */ | ||
283 | + function toInt(value) { | ||
284 | + if (typeof value === 'number') { | ||
285 | + return value | 0; | ||
286 | + } | ||
287 | + return parseInt(value); | ||
288 | + } | ||
289 | + /** | ||
290 | + * Be careful, NaN can return | ||
291 | + */ | ||
292 | + function toFloat(value) { | ||
293 | + if (typeof value === 'number') { | ||
294 | + return value; | ||
295 | + } | ||
296 | + return parseFloat(value); | ||
297 | + } | ||
298 | + function countTruthy(values) { | ||
299 | + return values.reduce(function (sum, value) { return sum + (value ? 1 : 0); }, 0); | ||
300 | + } | ||
301 | + | ||
302 | + /* | ||
303 | + * Functions to help with browser features | ||
304 | + */ | ||
305 | + var w = window; | ||
306 | + var n = window.navigator; | ||
307 | + var d = window.document; | ||
308 | + /** | ||
309 | + * Checks whether the browser is based on Trident (the Internet Explorer engine) without using user-agent. | ||
310 | + * | ||
311 | + * Warning for package users: | ||
312 | + * This function is out of Semantic Versioning, i.e. can change unexpectedly. Usage is at your own risk. | ||
313 | + */ | ||
314 | + function isTrident() { | ||
315 | + // The properties are checked to be in IE 10, IE 11 and not to be in other browsers in October 2020 | ||
316 | + return (countTruthy([ | ||
317 | + 'MSCSSMatrix' in w, | ||
318 | + 'msSetImmediate' in w, | ||
319 | + 'msIndexedDB' in w, | ||
320 | + 'msMaxTouchPoints' in n, | ||
321 | + 'msPointerEnabled' in n, | ||
322 | + ]) >= 4); | ||
323 | + } | ||
324 | + /** | ||
325 | + * Checks whether the browser is based on EdgeHTML (the pre-Chromium Edge engine) without using user-agent. | ||
326 | + * | ||
327 | + * Warning for package users: | ||
328 | + * This function is out of Semantic Versioning, i.e. can change unexpectedly. Usage is at your own risk. | ||
329 | + */ | ||
330 | + function isEdgeHTML() { | ||
331 | + // Based on research in October 2020 | ||
332 | + return (countTruthy(['msWriteProfilerMark' in w, 'MSStream' in w, 'msLaunchUri' in n, 'msSaveBlob' in n]) >= 3 && | ||
333 | + !isTrident()); | ||
334 | + } | ||
335 | + /** | ||
336 | + * Checks whether the browser is based on Chromium without using user-agent. | ||
337 | + * | ||
338 | + * Warning for package users: | ||
339 | + * This function is out of Semantic Versioning, i.e. can change unexpectedly. Usage is at your own risk. | ||
340 | + */ | ||
341 | + function isChromium() { | ||
342 | + // Based on research in October 2020. Tested to detect Chromium 42-86. | ||
343 | + return (countTruthy([ | ||
344 | + 'webkitPersistentStorage' in n, | ||
345 | + 'webkitTemporaryStorage' in n, | ||
346 | + n.vendor.indexOf('Google') === 0, | ||
347 | + 'webkitResolveLocalFileSystemURL' in w, | ||
348 | + 'BatteryManager' in w, | ||
349 | + 'webkitMediaStream' in w, | ||
350 | + 'webkitSpeechGrammar' in w, | ||
351 | + ]) >= 5); | ||
352 | + } | ||
353 | + /** | ||
354 | + * Checks whether the browser is based on mobile or desktop Safari without using user-agent. | ||
355 | + * All iOS browsers use WebKit (the Safari engine). | ||
356 | + * | ||
357 | + * Warning for package users: | ||
358 | + * This function is out of Semantic Versioning, i.e. can change unexpectedly. Usage is at your own risk. | ||
359 | + */ | ||
360 | + function isWebKit() { | ||
361 | + // Based on research in September 2020 | ||
362 | + return (countTruthy([ | ||
363 | + 'ApplePayError' in w, | ||
364 | + 'CSSPrimitiveValue' in w, | ||
365 | + 'Counter' in w, | ||
366 | + n.vendor.indexOf('Apple') === 0, | ||
367 | + 'getStorageUpdates' in n, | ||
368 | + 'WebKitMediaKeys' in w, | ||
369 | + ]) >= 4); | ||
370 | + } | ||
371 | + /** | ||
372 | + * Checks whether the WebKit browser is a desktop Safari. | ||
373 | + * | ||
374 | + * Warning for package users: | ||
375 | + * This function is out of Semantic Versioning, i.e. can change unexpectedly. Usage is at your own risk. | ||
376 | + */ | ||
377 | + function isDesktopSafari() { | ||
378 | + return (countTruthy([ | ||
379 | + 'safari' in w, | ||
380 | + !('DeviceMotionEvent' in w), | ||
381 | + !('ongestureend' in w), | ||
382 | + !('standalone' in n), | ||
383 | + ]) >= 3); | ||
384 | + } | ||
385 | + /** | ||
386 | + * Checks whether the browser is based on Gecko (Firefox engine) without using user-agent. | ||
387 | + * | ||
388 | + * Warning for package users: | ||
389 | + * This function is out of Semantic Versioning, i.e. can change unexpectedly. Usage is at your own risk. | ||
390 | + */ | ||
391 | + function isGecko() { | ||
392 | + var _a; | ||
393 | + // Based on research in September 2020 | ||
394 | + return (countTruthy([ | ||
395 | + 'buildID' in n, | ||
396 | + ((_a = d.window.documentElement) === null || _a === void 0 ? void 0 : _a.style) && 'MozAppearance' in d.window.documentElement.style, | ||
397 | + 'MediaRecorderErrorEvent' in w, | ||
398 | + 'mozInnerScreenX' in w, | ||
399 | + 'CSSMozDocumentRule' in w, | ||
400 | + 'CanvasCaptureMediaStream' in w, | ||
401 | + ]) >= 4); | ||
402 | + } | ||
403 | + /** | ||
404 | + * Checks whether the browser is based on Chromium version ≥86 without using user-agent. | ||
405 | + * It doesn't check that the browser is based on Chromium, there is a separate function for this. | ||
406 | + */ | ||
407 | + function isChromium86OrNewer() { | ||
408 | + // Checked in Chrome 85 vs Chrome 86 both on desktop and Android | ||
409 | + return (countTruthy([ | ||
410 | + !('MediaSettingsRange' in w), | ||
411 | + 'RTCEncodedAudioFrame' in w, | ||
412 | + '' + w.Intl === '[object Intl]', | ||
413 | + '' + w.Reflect === '[object Reflect]', | ||
414 | + ]) >= 3); | ||
415 | + } | ||
416 | + /** | ||
417 | + * Checks whether the browser is based on WebKit version ≥606 (Safari ≥12) without using user-agent. | ||
418 | + * It doesn't check that the browser is based on WebKit, there is a separate function for this. | ||
419 | + * | ||
420 | + * @link https://en.wikipedia.org/wiki/Safari_version_history#Release_history Safari-WebKit versions map | ||
421 | + */ | ||
422 | + function isWebKit606OrNewer() { | ||
423 | + // Checked in Safari 9–14 | ||
424 | + return (countTruthy([ | ||
425 | + 'DOMRectList' in w, | ||
426 | + 'RTCPeerConnectionIceEvent' in w, | ||
427 | + 'SVGGeometryElement' in w, | ||
428 | + 'ontransitioncancel' in w, | ||
429 | + ]) >= 3); | ||
430 | + } | ||
431 | + | ||
432 | + var w$1 = window; | ||
433 | + var d$1 = window.document; | ||
434 | + // Inspired by and based on https://github.com/cozylife/audio-fingerprint | ||
435 | + function getAudioFingerprint() { | ||
436 | + return tslib.__awaiter(this, void 0, void 0, function () { | ||
437 | + var AudioContext, context, oscillator, compressor, buffer, error_1; | ||
438 | + return tslib.__generator(this, function (_a) { | ||
439 | + switch (_a.label) { | ||
440 | + case 0: | ||
441 | + // In some browsers, audio context always stays suspended unless the context is started in response to a user action | ||
442 | + // (e.g. a click or a tap). It prevents audio fingerprint from being taken at an arbitrary moment of time. | ||
443 | + // Such browsers are old and unpopular, so the audio fingerprinting is just skipped in them. | ||
444 | + // See a similar case explanation at https://stackoverflow.com/questions/46363048/onaudioprocess-not-called-on-ios11#46534088 | ||
445 | + if (doesCurrentBrowserSuspendAudioContext()) { | ||
446 | + return [2 /*return*/, -1]; | ||
447 | + } | ||
448 | + AudioContext = w$1.OfflineAudioContext || w$1.webkitOfflineAudioContext; | ||
449 | + if (!AudioContext) { | ||
450 | + return [2 /*return*/, -2]; | ||
451 | + } | ||
452 | + context = new AudioContext(1, 44100, 44100); | ||
453 | + oscillator = context.createOscillator(); | ||
454 | + oscillator.type = 'triangle'; | ||
455 | + setAudioParam(context, oscillator.frequency, 10000); | ||
456 | + compressor = context.createDynamicsCompressor(); | ||
457 | + setAudioParam(context, compressor.threshold, -50); | ||
458 | + setAudioParam(context, compressor.knee, 40); | ||
459 | + setAudioParam(context, compressor.ratio, 12); | ||
460 | + setAudioParam(context, compressor.reduction, -20); | ||
461 | + setAudioParam(context, compressor.attack, 0); | ||
462 | + setAudioParam(context, compressor.release, 0.25); | ||
463 | + oscillator.connect(compressor); | ||
464 | + compressor.connect(context.destination); | ||
465 | + oscillator.start(0); | ||
466 | + _a.label = 1; | ||
467 | + case 1: | ||
468 | + _a.trys.push([1, 3, 4, 5]); | ||
469 | + return [4 /*yield*/, renderAudio(context)]; | ||
470 | + case 2: | ||
471 | + buffer = _a.sent(); | ||
472 | + return [3 /*break*/, 5]; | ||
473 | + case 3: | ||
474 | + error_1 = _a.sent(); | ||
475 | + if (error_1.name === "timeout" /* Timeout */ || error_1.name === "suspended" /* Suspended */) { | ||
476 | + return [2 /*return*/, -3]; | ||
477 | + } | ||
478 | + throw error_1; | ||
479 | + case 4: | ||
480 | + oscillator.disconnect(); | ||
481 | + compressor.disconnect(); | ||
482 | + return [7 /*endfinally*/]; | ||
483 | + case 5: return [2 /*return*/, getHash(buffer.getChannelData(0))]; | ||
484 | + } | ||
485 | + }); | ||
486 | + }); | ||
487 | + } | ||
488 | + /** | ||
489 | + * Checks if the current browser is known to always suspend audio context | ||
490 | + */ | ||
491 | + function doesCurrentBrowserSuspendAudioContext() { | ||
492 | + return isWebKit() && !isDesktopSafari() && !isWebKit606OrNewer(); | ||
493 | + } | ||
494 | + function setAudioParam(context, param, value) { | ||
495 | + var isAudioParam = function (value) { | ||
496 | + return value && typeof value.setValueAtTime === 'function'; | ||
497 | + }; | ||
498 | + if (isAudioParam(param)) { | ||
499 | + param.setValueAtTime(value, context.currentTime); | ||
500 | + } | ||
501 | + } | ||
502 | + function renderAudio(context) { | ||
503 | + var resumeTriesMaxCount = 3; | ||
504 | + var resumeRetryDelay = 500; | ||
505 | + var runningTimeout = 1000; | ||
506 | + return new Promise(function (resolve, reject) { | ||
507 | + context.oncomplete = function (event) { return resolve(event.renderedBuffer); }; | ||
508 | + var resumeTriesLeft = resumeTriesMaxCount; | ||
509 | + var tryResume = function () { | ||
510 | + context.startRendering(); | ||
511 | + switch (context.state) { | ||
512 | + case 'running': | ||
513 | + setTimeout(function () { return reject(makeInnerError("timeout" /* Timeout */)); }, runningTimeout); | ||
514 | + break; | ||
515 | + // Sometimes the audio context doesn't start after calling `startRendering` (in addition to the cases where | ||
516 | + // audio context doesn't start at all). A known case is starting an audio context when the browser tab is in | ||
517 | + // background on iPhone. Retries usually help in this case. | ||
518 | + case 'suspended': | ||
519 | + // The audio context can reject starting until the tab is in foreground. Long fingerprint duration | ||
520 | + // in background isn't a problem, therefore the retry attempts don't count in background. It can lead to | ||
521 | + // a situation when a fingerprint takes very long time and finishes successfully. FYI, the audio context | ||
522 | + // can be suspended when `window.document.hidden === false` and start running after a retry. | ||
523 | + if (!d$1.hidden) { | ||
524 | + resumeTriesLeft--; | ||
525 | + } | ||
526 | + if (resumeTriesLeft > 0) { | ||
527 | + setTimeout(tryResume, resumeRetryDelay); | ||
528 | + } | ||
529 | + else { | ||
530 | + reject(makeInnerError("suspended" /* Suspended */)); | ||
531 | + } | ||
532 | + break; | ||
533 | + } | ||
534 | + }; | ||
535 | + tryResume(); | ||
536 | + }); | ||
537 | + } | ||
538 | + function getHash(signal) { | ||
539 | + var hash = 0; | ||
540 | + for (var i = 4500; i < 5000; ++i) { | ||
541 | + hash += Math.abs(signal[i]); | ||
542 | + } | ||
543 | + return hash; | ||
544 | + } | ||
545 | + function makeInnerError(name) { | ||
546 | + var error = new Error(name); | ||
547 | + error.name = name; | ||
548 | + return error; | ||
549 | + } | ||
550 | + | ||
551 | + var d$2 = window.document; | ||
552 | + // We use m or w because these two characters take up the maximum width. | ||
553 | + // And we use a LLi so that the same matching fonts can get separated. | ||
554 | + var testString = 'mmMwWLliI0O&1'; | ||
555 | + // We test using 48px font size, we may use any size. I guess larger the better. | ||
556 | + var testSize = '48px'; | ||
557 | + // A font will be compared against all the three default fonts. | ||
558 | + // And if it doesn't match all 3 then that font is not available. | ||
559 | + var baseFonts = ['monospace', 'sans-serif', 'serif']; | ||
560 | + var fontList = [ | ||
561 | + // This is android-specific font from "Roboto" family | ||
562 | + 'sans-serif-thin', | ||
563 | + 'ARNO PRO', | ||
564 | + 'Agency FB', | ||
565 | + 'Arabic Typesetting', | ||
566 | + 'Arial Unicode MS', | ||
567 | + 'AvantGarde Bk BT', | ||
568 | + 'BankGothic Md BT', | ||
569 | + 'Batang', | ||
570 | + 'Bitstream Vera Sans Mono', | ||
571 | + 'Calibri', | ||
572 | + 'Century', | ||
573 | + 'Century Gothic', | ||
574 | + 'Clarendon', | ||
575 | + 'EUROSTILE', | ||
576 | + 'Franklin Gothic', | ||
577 | + 'Futura Bk BT', | ||
578 | + 'Futura Md BT', | ||
579 | + 'GOTHAM', | ||
580 | + 'Gill Sans', | ||
581 | + 'HELV', | ||
582 | + 'Haettenschweiler', | ||
583 | + 'Helvetica Neue', | ||
584 | + 'Humanst521 BT', | ||
585 | + 'Leelawadee', | ||
586 | + 'Letter Gothic', | ||
587 | + 'Levenim MT', | ||
588 | + 'Lucida Bright', | ||
589 | + 'Lucida Sans', | ||
590 | + 'Menlo', | ||
591 | + 'MS Mincho', | ||
592 | + 'MS Outlook', | ||
593 | + 'MS Reference Specialty', | ||
594 | + 'MS UI Gothic', | ||
595 | + 'MT Extra', | ||
596 | + 'MYRIAD PRO', | ||
597 | + 'Marlett', | ||
598 | + 'Meiryo UI', | ||
599 | + 'Microsoft Uighur', | ||
600 | + 'Minion Pro', | ||
601 | + 'Monotype Corsiva', | ||
602 | + 'PMingLiU', | ||
603 | + 'Pristina', | ||
604 | + 'SCRIPTINA', | ||
605 | + 'Segoe UI Light', | ||
606 | + 'Serifa', | ||
607 | + 'SimHei', | ||
608 | + 'Small Fonts', | ||
609 | + 'Staccato222 BT', | ||
610 | + 'TRAJAN PRO', | ||
611 | + 'Univers CE 55 Medium', | ||
612 | + 'Vrinda', | ||
613 | + 'ZWAdobeF', | ||
614 | + ]; | ||
615 | + var fontSpanStyle = { | ||
616 | + // CSS font reset to reset external styles | ||
617 | + fontStyle: 'normal', | ||
618 | + fontWeight: 'normal', | ||
619 | + letterSpacing: 'normal', | ||
620 | + lineBreak: 'auto', | ||
621 | + lineHeight: 'normal', | ||
622 | + textTransform: 'none', | ||
623 | + textAlign: 'left', | ||
624 | + textDecoration: 'none', | ||
625 | + textShadow: 'none', | ||
626 | + whiteSpace: 'normal', | ||
627 | + wordBreak: 'normal', | ||
628 | + wordSpacing: 'normal', | ||
629 | + // We need this css as in some weird browser this span elements shows up for a microSec which creates | ||
630 | + // a bad user experience | ||
631 | + position: 'absolute', | ||
632 | + left: '-9999px', | ||
633 | + fontSize: testSize, | ||
634 | + }; | ||
635 | + // kudos to http://www.lalit.org/lab/javascript-css-font-detect/ | ||
636 | + function getFonts() { | ||
637 | + var h = d$2.body; | ||
638 | + // div to load spans for the base fonts | ||
639 | + var baseFontsDiv = d$2.createElement('div'); | ||
640 | + // div to load spans for the fonts to detect | ||
641 | + var fontsDiv = d$2.createElement('div'); | ||
642 | + var defaultWidth = {}; | ||
643 | + var defaultHeight = {}; | ||
644 | + // creates a span where the fonts will be loaded | ||
645 | + var createSpan = function () { | ||
646 | + var span = d$2.createElement('span'); | ||
647 | + span.textContent = testString; | ||
648 | + for (var _i = 0, _a = Object.keys(fontSpanStyle); _i < _a.length; _i++) { | ||
649 | + var prop = _a[_i]; | ||
650 | + span.style[prop] = fontSpanStyle[prop]; | ||
651 | + } | ||
652 | + return span; | ||
653 | + }; | ||
654 | + // creates a span and load the font to detect and a base font for fallback | ||
655 | + var createSpanWithFonts = function (fontToDetect, baseFont) { | ||
656 | + var s = createSpan(); | ||
657 | + s.style.fontFamily = "'" + fontToDetect + "'," + baseFont; | ||
658 | + return s; | ||
659 | + }; | ||
660 | + // creates spans for the base fonts and adds them to baseFontsDiv | ||
661 | + var initializeBaseFontsSpans = function () { | ||
662 | + return baseFonts.map(function (baseFont) { | ||
663 | + var s = createSpan(); | ||
664 | + s.style.fontFamily = baseFont; | ||
665 | + baseFontsDiv.appendChild(s); | ||
666 | + return s; | ||
667 | + }); | ||
668 | + }; | ||
669 | + // creates spans for the fonts to detect and adds them to fontsDiv | ||
670 | + var initializeFontsSpans = function () { | ||
671 | + // Stores {fontName : [spans for that font]} | ||
672 | + var spans = {}; | ||
673 | + var _loop_1 = function (font) { | ||
674 | + spans[font] = baseFonts.map(function (baseFont) { | ||
675 | + var s = createSpanWithFonts(font, baseFont); | ||
676 | + fontsDiv.appendChild(s); | ||
677 | + return s; | ||
678 | + }); | ||
679 | + }; | ||
680 | + for (var _i = 0, fontList_1 = fontList; _i < fontList_1.length; _i++) { | ||
681 | + var font = fontList_1[_i]; | ||
682 | + _loop_1(font); | ||
683 | + } | ||
684 | + return spans; | ||
685 | + }; | ||
686 | + // checks if a font is available | ||
687 | + var isFontAvailable = function (fontSpans) { | ||
688 | + return baseFonts.some(function (baseFont, baseFontIndex) { | ||
689 | + return fontSpans[baseFontIndex].offsetWidth !== defaultWidth[baseFont] || | ||
690 | + fontSpans[baseFontIndex].offsetHeight !== defaultHeight[baseFont]; | ||
691 | + }); | ||
692 | + }; | ||
693 | + // create spans for base fonts | ||
694 | + var baseFontsSpans = initializeBaseFontsSpans(); | ||
695 | + // add the spans to the DOM | ||
696 | + h.appendChild(baseFontsDiv); | ||
697 | + // get the default width for the three base fonts | ||
698 | + for (var index = 0, length_1 = baseFonts.length; index < length_1; index++) { | ||
699 | + defaultWidth[baseFonts[index]] = baseFontsSpans[index].offsetWidth; // width for the default font | ||
700 | + defaultHeight[baseFonts[index]] = baseFontsSpans[index].offsetHeight; // height for the default font | ||
701 | + } | ||
702 | + // create spans for fonts to detect | ||
703 | + var fontsSpans = initializeFontsSpans(); | ||
704 | + // add all the spans to the DOM | ||
705 | + h.appendChild(fontsDiv); | ||
706 | + // check available fonts | ||
707 | + var available = []; | ||
708 | + for (var i = 0, l = fontList.length; i < l; i++) { | ||
709 | + if (isFontAvailable(fontsSpans[fontList[i]])) { | ||
710 | + available.push(fontList[i]); | ||
711 | + } | ||
712 | + } | ||
713 | + // remove spans from DOM | ||
714 | + h.removeChild(fontsDiv); | ||
715 | + h.removeChild(baseFontsDiv); | ||
716 | + return available; | ||
717 | + } | ||
718 | + | ||
719 | + function getPlugins() { | ||
720 | + if (isTrident()) { | ||
721 | + return []; | ||
722 | + } | ||
723 | + if (!window.navigator.plugins) { | ||
724 | + return undefined; | ||
725 | + } | ||
726 | + var plugins = []; | ||
727 | + // Safari 10 doesn't support iterating window.navigator.plugins with for...of | ||
728 | + for (var i = 0; i < window.navigator.plugins.length; ++i) { | ||
729 | + var plugin = window.navigator.plugins[i]; | ||
730 | + if (!plugin) { | ||
731 | + continue; | ||
732 | + } | ||
733 | + var mimeTypes = []; | ||
734 | + for (var j = 0; j < plugin.length; ++j) { | ||
735 | + var mimeType = plugin[j]; | ||
736 | + mimeTypes.push({ | ||
737 | + type: mimeType.type, | ||
738 | + suffixes: mimeType.suffixes, | ||
739 | + }); | ||
740 | + } | ||
741 | + plugins.push({ | ||
742 | + name: plugin.name, | ||
743 | + description: plugin.description, | ||
744 | + mimeTypes: mimeTypes, | ||
745 | + }); | ||
746 | + } | ||
747 | + return plugins; | ||
748 | + } | ||
749 | + | ||
750 | + function makeCanvasContext() { | ||
751 | + var canvas = window.document.createElement('canvas'); | ||
752 | + canvas.width = 240; | ||
753 | + canvas.height = 140; | ||
754 | + canvas.style.display = 'inline'; | ||
755 | + return [canvas, canvas.getContext('2d')]; | ||
756 | + } | ||
757 | + function isSupported(canvas, context) { | ||
758 | + // TODO: look into: https://developer.mozilla.org/en-US/docs/Web/API/HTMLCanvasElement/toBlob | ||
759 | + return !!(context && canvas.toDataURL); | ||
760 | + } | ||
761 | + function save(canvas) { | ||
762 | + // TODO: look into: https://developer.mozilla.org/en-US/docs/Web/API/HTMLCanvasElement/toBlob | ||
763 | + return canvas.toDataURL(); | ||
764 | + } | ||
765 | + // https://www.browserleaks.com/canvas#how-does-it-work | ||
766 | + function getCanvasFingerprint() { | ||
767 | + var _a = makeCanvasContext(), canvas = _a[0], context = _a[1]; | ||
768 | + if (!isSupported(canvas, context)) { | ||
769 | + return { winding: false, data: '' }; | ||
770 | + } | ||
771 | + // detect browser support of canvas winding | ||
772 | + // http://blogs.adobe.com/webplatform/2013/01/30/winding-rules-in-canvas/ | ||
773 | + // https://github.com/Modernizr/Modernizr/blob/master/feature-detects/canvas/winding.js | ||
774 | + context.rect(0, 0, 10, 10); | ||
775 | + context.rect(2, 2, 6, 6); | ||
776 | + var winding = !context.isPointInPath(5, 5, 'evenodd'); | ||
777 | + context.textBaseline = 'alphabetic'; | ||
778 | + context.fillStyle = '#f60'; | ||
779 | + context.fillRect(125, 1, 62, 20); | ||
780 | + context.fillStyle = '#069'; | ||
781 | + // https://github.com/Valve/fingerprintjs2/issues/66 | ||
782 | + // this can affect FP generation when applying different CSS on different websites | ||
783 | + context.font = '11pt no-real-font-123'; | ||
784 | + // the choice of emojis has a gigantic impact on rendering performance (especially in FF) | ||
785 | + // some newer emojis cause it to slow down 50-200 times | ||
786 | + // context.fillText("Cw爨m fjordbank \ud83d\ude03 gly", 2, 15) | ||
787 | + var printedText = 'Cwm fjordbank \ud83d\ude03 gly'; | ||
788 | + context.fillText(printedText, 2, 15); | ||
789 | + context.fillStyle = 'rgba(102, 204, 0, 0.2)'; | ||
790 | + context.font = '18pt Arial'; | ||
791 | + context.fillText(printedText, 4, 45); | ||
792 | + // canvas blending | ||
793 | + // http://blogs.adobe.com/webplatform/2013/01/28/blending-features-in-canvas/ | ||
794 | + // http://jsfiddle.net/NDYV8/16/ | ||
795 | + context.globalCompositeOperation = 'multiply'; | ||
796 | + context.fillStyle = 'rgb(255,0,255)'; | ||
797 | + context.beginPath(); | ||
798 | + context.arc(50, 50, 50, 0, Math.PI * 2, true); | ||
799 | + context.closePath(); | ||
800 | + context.fill(); | ||
801 | + context.fillStyle = 'rgb(0,255,255)'; | ||
802 | + context.beginPath(); | ||
803 | + context.arc(100, 50, 50, 0, Math.PI * 2, true); | ||
804 | + context.closePath(); | ||
805 | + context.fill(); | ||
806 | + context.fillStyle = 'rgb(255,255,0)'; | ||
807 | + context.beginPath(); | ||
808 | + context.arc(75, 100, 50, 0, Math.PI * 2, true); | ||
809 | + context.closePath(); | ||
810 | + context.fill(); | ||
811 | + context.fillStyle = 'rgb(255,0,255)'; | ||
812 | + // canvas winding | ||
813 | + // http://blogs.adobe.com/webplatform/2013/01/30/winding-rules-in-canvas/ | ||
814 | + // http://jsfiddle.net/NDYV8/19/ | ||
815 | + context.arc(75, 75, 75, 0, Math.PI * 2, true); | ||
816 | + context.arc(75, 75, 25, 0, Math.PI * 2, true); | ||
817 | + context.fill('evenodd'); | ||
818 | + return { | ||
819 | + winding: winding, | ||
820 | + data: save(canvas), | ||
821 | + }; | ||
822 | + } | ||
823 | + | ||
824 | + var n$1 = window.navigator; | ||
825 | + var w$2 = window; | ||
826 | + /** | ||
827 | + * This is a crude and primitive touch screen detection. It's not possible to currently reliably detect the availability | ||
828 | + * of a touch screen with a JS, without actually subscribing to a touch event. | ||
829 | + * | ||
830 | + * @see http://www.stucox.com/blog/you-cant-detect-a-touchscreen/ | ||
831 | + * @see https://github.com/Modernizr/Modernizr/issues/548 | ||
832 | + */ | ||
833 | + function getTouchSupport() { | ||
834 | + var maxTouchPoints = 0; | ||
835 | + var touchEvent; | ||
836 | + if (n$1.maxTouchPoints !== undefined) { | ||
837 | + maxTouchPoints = toInt(n$1.maxTouchPoints); | ||
838 | + } | ||
839 | + else if (n$1.msMaxTouchPoints !== undefined) { | ||
840 | + maxTouchPoints = n$1.msMaxTouchPoints; | ||
841 | + } | ||
842 | + try { | ||
843 | + window.document.createEvent('TouchEvent'); | ||
844 | + touchEvent = true; | ||
845 | + } | ||
846 | + catch (_) { | ||
847 | + touchEvent = false; | ||
848 | + } | ||
849 | + var touchStart = 'ontouchstart' in w$2; | ||
850 | + return { | ||
851 | + maxTouchPoints: maxTouchPoints, | ||
852 | + touchEvent: touchEvent, | ||
853 | + touchStart: touchStart, | ||
854 | + }; | ||
855 | + } | ||
856 | + | ||
857 | + function getOsCpu() { | ||
858 | + return window.navigator.oscpu; | ||
859 | + } | ||
860 | + | ||
861 | + var n$2 = window.navigator; | ||
862 | + function getLanguages() { | ||
863 | + var result = []; | ||
864 | + var language = n$2.language || n$2.userLanguage || n$2.browserLanguage || n$2.systemLanguage; | ||
865 | + if (language !== undefined) { | ||
866 | + result.push([language]); | ||
867 | + } | ||
868 | + if (Array.isArray(n$2.languages)) { | ||
869 | + // Starting from Chromium 86, there is only a single value in `window.navigator.language` in Incognito mode: | ||
870 | + // the value of `window.navigator.language`. Therefore the value is ignored in this browser. | ||
871 | + if (!(isChromium() && isChromium86OrNewer())) { | ||
872 | + result.push(n$2.languages); | ||
873 | + } | ||
874 | + } | ||
875 | + else if (typeof n$2.languages === 'string') { | ||
876 | + var languages = n$2.languages; | ||
877 | + if (languages) { | ||
878 | + result.push(languages.split(',')); | ||
879 | + } | ||
880 | + } | ||
881 | + return result; | ||
882 | + } | ||
883 | + | ||
884 | + function getColorDepth() { | ||
885 | + return window.screen.colorDepth; | ||
886 | + } | ||
887 | + | ||
888 | + function getDeviceMemory() { | ||
889 | + return window.navigator.deviceMemory; | ||
890 | + } | ||
891 | + | ||
892 | + var w$3 = window; | ||
893 | + function getScreenResolution() { | ||
894 | + // Some browsers return screen resolution as strings, e.g. "1200", instead of a number, e.g. 1200. | ||
895 | + // I suspect it's done by certain plugins that randomize browser properties to prevent fingerprinting. | ||
896 | + var dimensions = [toInt(w$3.screen.width), toInt(w$3.screen.height)]; | ||
897 | + dimensions.sort().reverse(); | ||
898 | + return dimensions; | ||
899 | + } | ||
900 | + | ||
901 | + var w$4 = window; | ||
902 | + function getAvailableScreenResolution() { | ||
903 | + if (w$4.screen.availWidth && w$4.screen.availHeight) { | ||
904 | + // Some browsers return screen resolution as strings, e.g. "1200", instead of a number, e.g. 1200. | ||
905 | + // I suspect it's done by certain plugins that randomize browser properties to prevent fingerprinting. | ||
906 | + var dimensions = [toInt(w$4.screen.availWidth), toInt(w$4.screen.availHeight)]; | ||
907 | + dimensions.sort().reverse(); | ||
908 | + return dimensions; | ||
909 | + } | ||
910 | + return undefined; | ||
911 | + } | ||
912 | + | ||
913 | + function getHardwareConcurrency() { | ||
914 | + try { | ||
915 | + // sometimes hardware concurrency is a string | ||
916 | + var concurrency = toInt(window.navigator.hardwareConcurrency); | ||
917 | + return isNaN(concurrency) ? 1 : concurrency; | ||
918 | + } | ||
919 | + catch (e) { | ||
920 | + return 1; | ||
921 | + } | ||
922 | + } | ||
923 | + | ||
924 | + function getTimezoneOffset() { | ||
925 | + var currentYear = new Date().getFullYear(); | ||
926 | + // The timezone offset may change over time due to daylight saving time (DST) shifts. | ||
927 | + // The non-DST timezone offset is used as the result timezone offset. | ||
928 | + // Since the DST season differs in the northern and the southern hemispheres, | ||
929 | + // both January and July timezones offsets are considered. | ||
930 | + return Math.max( | ||
931 | + // `getTimezoneOffset` returns a number as a string in some unidentified cases | ||
932 | + toFloat(new Date(currentYear, 0, 1).getTimezoneOffset()), toFloat(new Date(currentYear, 6, 1).getTimezoneOffset())); | ||
933 | + } | ||
934 | + | ||
935 | + var w$5 = window; | ||
936 | + function getTimezone() { | ||
937 | + var _a; | ||
938 | + if ((_a = w$5.Intl) === null || _a === void 0 ? void 0 : _a.DateTimeFormat) { | ||
939 | + return new w$5.Intl.DateTimeFormat().resolvedOptions().timeZone; | ||
940 | + } | ||
941 | + return undefined; | ||
942 | + } | ||
943 | + | ||
944 | + function getSessionStorage() { | ||
945 | + try { | ||
946 | + return !!window.sessionStorage; | ||
947 | + } | ||
948 | + catch (error) { | ||
949 | + /* SecurityError when referencing it means it exists */ | ||
950 | + return true; | ||
951 | + } | ||
952 | + } | ||
953 | + | ||
954 | + // https://bugzilla.mozilla.org/show_bug.cgi?id=781447 | ||
955 | + function getLocalStorage() { | ||
956 | + try { | ||
957 | + return !!window.localStorage; | ||
958 | + } | ||
959 | + catch (e) { | ||
960 | + /* SecurityError when referencing it means it exists */ | ||
961 | + return true; | ||
962 | + } | ||
963 | + } | ||
964 | + | ||
965 | + function getIndexedDB() { | ||
966 | + // IE and Edge don't allow accessing indexedDB in private mode, therefore IE and Edge will have different | ||
967 | + // visitor identifier in normal and private modes. | ||
968 | + if (isTrident() || isEdgeHTML()) { | ||
969 | + return undefined; | ||
970 | + } | ||
971 | + try { | ||
972 | + return !!window.indexedDB; | ||
973 | + } | ||
974 | + catch (e) { | ||
975 | + /* SecurityError when referencing it means it exists */ | ||
976 | + return true; | ||
977 | + } | ||
978 | + } | ||
979 | + | ||
980 | + function getOpenDatabase() { | ||
981 | + return !!window.openDatabase; | ||
982 | + } | ||
983 | + | ||
984 | + function getCpuClass() { | ||
985 | + return window.navigator.cpuClass; | ||
986 | + } | ||
987 | + | ||
988 | + function getPlatform() { | ||
989 | + return window.navigator.platform; | ||
990 | + } | ||
991 | + | ||
992 | + function getPluginsSupport() { | ||
993 | + return window.navigator.plugins !== undefined; | ||
994 | + } | ||
995 | + | ||
996 | + function getProductSub() { | ||
997 | + return window.navigator.productSub; | ||
998 | + } | ||
999 | + | ||
1000 | + function getEmptyEvalLength() { | ||
1001 | + return eval.toString().length; | ||
1002 | + } | ||
1003 | + | ||
1004 | + function getErrorFF() { | ||
1005 | + try { | ||
1006 | + throw 'a'; | ||
1007 | + } | ||
1008 | + catch (e) { | ||
1009 | + try { | ||
1010 | + e.toSource(); | ||
1011 | + return true; | ||
1012 | + } | ||
1013 | + catch (e2) { | ||
1014 | + return false; | ||
1015 | + } | ||
1016 | + } | ||
1017 | + } | ||
1018 | + | ||
1019 | + function getVendor() { | ||
1020 | + return window.navigator.vendor; | ||
1021 | + } | ||
1022 | + | ||
1023 | + function getChrome() { | ||
1024 | + return window.chrome !== undefined; | ||
1025 | + } | ||
1026 | + | ||
1027 | + var d$3 = window.document; | ||
1028 | + /** | ||
1029 | + * window.navigator.cookieEnabled cannot detect custom or nuanced cookie blocking configurations. For example, when blocking | ||
1030 | + * cookies via the Advanced Privacy Settings in IE9, it always returns true. And there have been issues in the past with | ||
1031 | + * site-specific exceptions. Don't rely on it. | ||
1032 | + * | ||
1033 | + * @see https://github.com/Modernizr/Modernizr/blob/master/feature-detects/cookies.js Taken from here | ||
1034 | + */ | ||
1035 | + function areCookiesEnabled() { | ||
1036 | + // Taken from here: https://github.com/Modernizr/Modernizr/blob/master/feature-detects/cookies.js | ||
1037 | + // window.navigator.cookieEnabled cannot detect custom or nuanced cookie blocking configurations. For example, when blocking | ||
1038 | + // cookies via the Advanced Privacy Settings in IE9, it always returns true. And there have been issues in the past | ||
1039 | + // with site-specific exceptions. Don't rely on it. | ||
1040 | + // try..catch because some in situations `window.document.cookie` is exposed but throws a | ||
1041 | + // SecurityError if you try to access it; e.g. window.documents created from data URIs | ||
1042 | + // or in sandboxed iframes (depending on flags/context) | ||
1043 | + try { | ||
1044 | + // Create cookie | ||
1045 | + d$3.cookie = 'cookietest=1'; | ||
1046 | + var result = d$3.cookie.indexOf('cookietest=') !== -1; | ||
1047 | + // Delete cookie | ||
1048 | + d$3.cookie = 'cookietest=1; expires=Thu, 01-Jan-1970 00:00:01 GMT'; | ||
1049 | + return result; | ||
1050 | + } | ||
1051 | + catch (e) { | ||
1052 | + return false; | ||
1053 | + } | ||
1054 | + } | ||
1055 | + | ||
1056 | + /** | ||
1057 | + * The list of entropy sources used to make visitor identifiers. | ||
1058 | + * | ||
1059 | + * This value isn't restricted by Semantic Versioning, i.e. it may be changed without bumping minor or major version of | ||
1060 | + * this package. | ||
1061 | + */ | ||
1062 | + var sources = { | ||
1063 | + // Expected errors and default values must be handled inside the functions. Unexpected errors must be thrown. | ||
1064 | + osCpu: getOsCpu, | ||
1065 | + languages: getLanguages, | ||
1066 | + colorDepth: getColorDepth, | ||
1067 | + deviceMemory: getDeviceMemory, | ||
1068 | + screenResolution: getScreenResolution, | ||
1069 | + availableScreenResolution: getAvailableScreenResolution, | ||
1070 | + hardwareConcurrency: getHardwareConcurrency, | ||
1071 | + timezoneOffset: getTimezoneOffset, | ||
1072 | + timezone: getTimezone, | ||
1073 | + sessionStorage: getSessionStorage, | ||
1074 | + localStorage: getLocalStorage, | ||
1075 | + indexedDB: getIndexedDB, | ||
1076 | + openDatabase: getOpenDatabase, | ||
1077 | + cpuClass: getCpuClass, | ||
1078 | + // Maybe it should be excluded: https://github.com/fingerprintjs/fingerprintjs/issues/514#issuecomment-688754892 | ||
1079 | + platform: getPlatform, | ||
1080 | + plugins: getPlugins, | ||
1081 | + canvas: getCanvasFingerprint, | ||
1082 | + // adBlock: isAdblockUsed, // https://github.com/fingerprintjs/fingerprintjs/issues/405 | ||
1083 | + touchSupport: getTouchSupport, | ||
1084 | + fonts: getFonts, | ||
1085 | + audio: getAudioFingerprint, | ||
1086 | + pluginsSupport: getPluginsSupport, | ||
1087 | + productSub: getProductSub, | ||
1088 | + emptyEvalLength: getEmptyEvalLength, | ||
1089 | + errorFF: getErrorFF, | ||
1090 | + vendor: getVendor, | ||
1091 | + chrome: getChrome, | ||
1092 | + cookiesEnabled: areCookiesEnabled, | ||
1093 | + }; | ||
1094 | + /** | ||
1095 | + * Gets a components list from the given list of entropy sources. | ||
1096 | + * | ||
1097 | + * Warning for package users: | ||
1098 | + * This function is out of Semantic Versioning, i.e. can change unexpectedly. Usage is at your own risk. | ||
1099 | + */ | ||
1100 | + function getComponents(sources, sourceOptions, excludeSources) { | ||
1101 | + return tslib.__awaiter(this, void 0, void 0, function () { | ||
1102 | + var timestamp, components, _i, _a, sourceKey, result, error_1, nextTimestamp; | ||
1103 | + var _b; | ||
1104 | + return tslib.__generator(this, function (_c) { | ||
1105 | + switch (_c.label) { | ||
1106 | + case 0: | ||
1107 | + timestamp = Date.now(); | ||
1108 | + components = {}; | ||
1109 | + _i = 0, _a = Object.keys(sources); | ||
1110 | + _c.label = 1; | ||
1111 | + case 1: | ||
1112 | + if (!(_i < _a.length)) return [3 /*break*/, 7]; | ||
1113 | + sourceKey = _a[_i]; | ||
1114 | + if (!excludes(excludeSources, sourceKey)) { | ||
1115 | + return [3 /*break*/, 6]; | ||
1116 | + } | ||
1117 | + result = void 0; | ||
1118 | + _c.label = 2; | ||
1119 | + case 2: | ||
1120 | + _c.trys.push([2, 4, , 5]); | ||
1121 | + _b = {}; | ||
1122 | + return [4 /*yield*/, sources[sourceKey](sourceOptions)]; | ||
1123 | + case 3: | ||
1124 | + result = (_b.value = _c.sent(), _b); | ||
1125 | + return [3 /*break*/, 5]; | ||
1126 | + case 4: | ||
1127 | + error_1 = _c.sent(); | ||
1128 | + result = error_1 && typeof error_1 === 'object' && 'message' in error_1 ? { error: error_1 } : { error: { message: error_1 } }; | ||
1129 | + return [3 /*break*/, 5]; | ||
1130 | + case 5: | ||
1131 | + nextTimestamp = Date.now(); | ||
1132 | + components[sourceKey] = tslib.__assign(tslib.__assign({}, result), { duration: nextTimestamp - timestamp }); // TypeScript has beaten me here | ||
1133 | + timestamp = nextTimestamp; | ||
1134 | + _c.label = 6; | ||
1135 | + case 6: | ||
1136 | + _i++; | ||
1137 | + return [3 /*break*/, 1]; | ||
1138 | + case 7: return [2 /*return*/, components]; | ||
1139 | + } | ||
1140 | + }); | ||
1141 | + }); | ||
1142 | + } | ||
1143 | + /** | ||
1144 | + * Collects entropy components from the built-in sources to make the visitor identifier. | ||
1145 | + */ | ||
1146 | + function getBuiltinComponents() { | ||
1147 | + return getComponents(sources, undefined, []); | ||
1148 | + } | ||
1149 | + | ||
1150 | + function componentsToCanonicalString(components) { | ||
1151 | + var result = ''; | ||
1152 | + for (var _i = 0, _a = Object.keys(components); _i < _a.length; _i++) { | ||
1153 | + var componentKey = _a[_i]; | ||
1154 | + var component = components[componentKey]; | ||
1155 | + var value = component.error ? 'error' : JSON.stringify(component.value); | ||
1156 | + result += "" + (result ? '|' : '') + componentKey.replace(/([:|\\])/g, '\\$1') + ":" + value; | ||
1157 | + } | ||
1158 | + return result; | ||
1159 | + } | ||
1160 | + function componentsToDebugString(components) { | ||
1161 | + return JSON.stringify(components, function (_key, value) { | ||
1162 | + var _a; | ||
1163 | + if (value instanceof Error) { | ||
1164 | + return tslib.__assign(tslib.__assign({}, value), { message: value.message, stack: (_a = value.stack) === null || _a === void 0 ? void 0 : _a.split('\n') }); | ||
1165 | + } | ||
1166 | + return value; | ||
1167 | + }, 2); | ||
1168 | + } | ||
1169 | + function hashComponents(components) { | ||
1170 | + return x64hash128(componentsToCanonicalString(components)); | ||
1171 | + } | ||
1172 | + /** | ||
1173 | + * Makes a GetResult implementation that calculates the visitor id hash on demand. | ||
1174 | + * Designed for optimisation. | ||
1175 | + */ | ||
1176 | + function makeLazyGetResult(components) { | ||
1177 | + var visitorIdCache; | ||
1178 | + // A plain class isn't used because its getters and setters aren't enumerable. | ||
1179 | + return { | ||
1180 | + components: components, | ||
1181 | + get visitorId() { | ||
1182 | + if (visitorIdCache === undefined) { | ||
1183 | + visitorIdCache = hashComponents(this.components); | ||
1184 | + } | ||
1185 | + return visitorIdCache; | ||
1186 | + }, | ||
1187 | + set visitorId(visitorId) { | ||
1188 | + visitorIdCache = visitorId; | ||
1189 | + }, | ||
1190 | + }; | ||
1191 | + } | ||
1192 | + /** | ||
1193 | + * The class isn't exported from the index file to not expose the constructor. | ||
1194 | + * The hiding gives more freedom for future non-breaking updates. | ||
1195 | + */ | ||
1196 | + var OpenAgent = /** @class */ (function () { | ||
1197 | + function OpenAgent() { | ||
1198 | + } | ||
1199 | + /** | ||
1200 | + * @inheritDoc | ||
1201 | + */ | ||
1202 | + OpenAgent.prototype.get = function (options) { | ||
1203 | + if (options === void 0) { options = {}; } | ||
1204 | + return tslib.__awaiter(this, void 0, void 0, function () { | ||
1205 | + var components, result; | ||
1206 | + return tslib.__generator(this, function (_a) { | ||
1207 | + switch (_a.label) { | ||
1208 | + case 0: return [4 /*yield*/, getBuiltinComponents()]; | ||
1209 | + case 1: | ||
1210 | + components = _a.sent(); | ||
1211 | + result = makeLazyGetResult(components); | ||
1212 | + if (options.debug) { | ||
1213 | + // console.log is ok here because it's under a debug clause | ||
1214 | + // eslint-disable-next-line no-console | ||
1215 | + console.log("Copy the text below to get the debug data:\n\n```\nversion: " + version + "\nuserAgent: " + window.navigator.userAgent + "\ngetOptions: " + JSON.stringify(options, undefined, 2) + "\nvisitorId: " + result.visitorId + "\ncomponents: " + componentsToDebugString(components) + "\n```"); | ||
1216 | + } | ||
1217 | + return [2 /*return*/, result]; | ||
1218 | + } | ||
1219 | + }); | ||
1220 | + }); | ||
1221 | + }; | ||
1222 | + return OpenAgent; | ||
1223 | + }()); | ||
1224 | + /** | ||
1225 | + * Builds an instance of Agent and waits a delay required for a proper operation. | ||
1226 | + */ | ||
1227 | + function load(_a) { | ||
1228 | + var _b = (_a === void 0 ? {} : _a).delayFallback, delayFallback = _b === void 0 ? 50 : _b; | ||
1229 | + return tslib.__awaiter(this, void 0, void 0, function () { | ||
1230 | + return tslib.__generator(this, function (_c) { | ||
1231 | + switch (_c.label) { | ||
1232 | + case 0: | ||
1233 | + // A delay is required to ensure consistent entropy components. | ||
1234 | + // See https://github.com/fingerprintjs/fingerprintjs/issues/254 | ||
1235 | + // and https://github.com/fingerprintjs/fingerprintjs/issues/307 | ||
1236 | + return [4 /*yield*/, requestIdleCallbackIfAvailable(delayFallback)]; | ||
1237 | + case 1: | ||
1238 | + // A delay is required to ensure consistent entropy components. | ||
1239 | + // See https://github.com/fingerprintjs/fingerprintjs/issues/254 | ||
1240 | + // and https://github.com/fingerprintjs/fingerprintjs/issues/307 | ||
1241 | + _c.sent(); | ||
1242 | + return [2 /*return*/, new OpenAgent()]; | ||
1243 | + } | ||
1244 | + }); | ||
1245 | + }); | ||
1246 | + } | ||
1247 | + | ||
1248 | + // The default export is a syntax sugar (`import * as FP from '...' → import FP from '...'`). | ||
1249 | + // It should contain all the public exported values. | ||
1250 | + var index = { load: load, hashComponents: hashComponents, componentsToDebugString: componentsToDebugString }; | ||
1251 | + // The exports below are for private usage. They may change unexpectedly. Use them at your own risk. | ||
1252 | + /** Not window.documented, out of Semantic Versioning, usage is at your own risk */ | ||
1253 | + var murmurX64Hash128 = x64hash128; | ||
1254 | + | ||
1255 | + exports.componentsToDebugString = componentsToDebugString; | ||
1256 | + exports.default = index; | ||
1257 | + exports.getComponents = getComponents; | ||
1258 | + exports.hashComponents = hashComponents; | ||
1259 | + exports.isChromium = isChromium; | ||
1260 | + exports.isDesktopSafari = isDesktopSafari; | ||
1261 | + exports.isEdgeHTML = isEdgeHTML; | ||
1262 | + exports.isGecko = isGecko; | ||
1263 | + exports.isTrident = isTrident; | ||
1264 | + exports.isWebKit = isWebKit; | ||
1265 | + exports.load = load; | ||
1266 | + exports.murmurX64Hash128 = murmurX64Hash128; | ||
1267 | +})(this) |
dist/fp.d.ts
0 → 100644
1 | +/** | ||
2 | + * FingerprintJS v3.0.3 - Copyright (c) FingerprintJS, Inc, 2020 (https://fingerprintjs.com) | ||
3 | + * Licensed under the MIT (http://www.opensource.org/licenses/mit-license.php) license. | ||
4 | + * | ||
5 | + * This software contains code from open-source projects: | ||
6 | + * MurmurHash3 by Karan Lyons (https://github.com/karanlyons/murmurHash3.js) | ||
7 | + */ | ||
8 | + | ||
9 | +declare function x64hash128(key: string, seed?: number): string; | ||
10 | + | ||
11 | +declare function getAudioFingerprint(): Promise<number>; | ||
12 | + | ||
13 | +declare function getFonts(): string[]; | ||
14 | + | ||
15 | +interface PluginMimeTypeData { | ||
16 | + type: string; | ||
17 | + suffixes: string; | ||
18 | +} | ||
19 | +interface PluginData { | ||
20 | + name: string; | ||
21 | + description: string; | ||
22 | + mimeTypes: PluginMimeTypeData[]; | ||
23 | +} | ||
24 | +declare function getPlugins(): PluginData[] | undefined; | ||
25 | + | ||
26 | +interface CanvasFingerprint { | ||
27 | + winding: boolean; | ||
28 | + data: string; | ||
29 | +} | ||
30 | +declare function getCanvasFingerprint(): CanvasFingerprint; | ||
31 | + | ||
32 | +interface TouchSupport { | ||
33 | + maxTouchPoints: number; | ||
34 | + /** The success or failure of creating a TouchEvent */ | ||
35 | + touchEvent: boolean; | ||
36 | + /** The availability of the "ontouchstart" property */ | ||
37 | + touchStart: boolean; | ||
38 | +} | ||
39 | +/** | ||
40 | + * This is a crude and primitive touch screen detection. It's not possible to currently reliably detect the availability | ||
41 | + * of a touch screen with a JS, without actually subscribing to a touch event. | ||
42 | + * | ||
43 | + * @see http://www.stucox.com/blog/you-cant-detect-a-touchscreen/ | ||
44 | + * @see https://github.com/Modernizr/Modernizr/issues/548 | ||
45 | + */ | ||
46 | +declare function getTouchSupport(): TouchSupport; | ||
47 | + | ||
48 | +declare function getOsCpu(): string | undefined; | ||
49 | + | ||
50 | +declare function getLanguages(): string[][]; | ||
51 | + | ||
52 | +declare function getColorDepth(): number; | ||
53 | + | ||
54 | +declare function getDeviceMemory(): number | undefined; | ||
55 | + | ||
56 | +declare function getScreenResolution(): [number, number]; | ||
57 | + | ||
58 | +declare function getAvailableScreenResolution(): [number, number] | undefined; | ||
59 | + | ||
60 | +declare function getHardwareConcurrency(): number; | ||
61 | + | ||
62 | +declare function getTimezoneOffset(): number; | ||
63 | + | ||
64 | +declare function getTimezone(): string | undefined; | ||
65 | + | ||
66 | +declare function getSessionStorage(): boolean; | ||
67 | + | ||
68 | +declare function getLocalStorage(): boolean; | ||
69 | + | ||
70 | +declare function getIndexedDB(): boolean | undefined; | ||
71 | + | ||
72 | +declare function getOpenDatabase(): boolean; | ||
73 | + | ||
74 | +declare function getCpuClass(): string | undefined; | ||
75 | + | ||
76 | +declare function getPlatform(): string; | ||
77 | + | ||
78 | +declare function getPluginsSupport(): boolean; | ||
79 | + | ||
80 | +declare function getProductSub(): string; | ||
81 | + | ||
82 | +declare function getEmptyEvalLength(): number; | ||
83 | + | ||
84 | +declare function getErrorFF(): boolean; | ||
85 | + | ||
86 | +declare function getVendor(): string; | ||
87 | + | ||
88 | +declare function getChrome(): boolean; | ||
89 | + | ||
90 | +/** | ||
91 | + * navigator.cookieEnabled cannot detect custom or nuanced cookie blocking configurations. For example, when blocking | ||
92 | + * cookies via the Advanced Privacy Settings in IE9, it always returns true. And there have been issues in the past with | ||
93 | + * site-specific exceptions. Don't rely on it. | ||
94 | + * | ||
95 | + * @see https://github.com/Modernizr/Modernizr/blob/master/feature-detects/cookies.js Taken from here | ||
96 | + */ | ||
97 | +declare function areCookiesEnabled(): boolean; | ||
98 | + | ||
99 | +/** | ||
100 | + * The list of entropy sources used to make visitor identifiers. | ||
101 | + * | ||
102 | + * This value isn't restricted by Semantic Versioning, i.e. it may be changed without bumping minor or major version of | ||
103 | + * this package. | ||
104 | + */ | ||
105 | +declare const sources: { | ||
106 | + osCpu: typeof getOsCpu; | ||
107 | + languages: typeof getLanguages; | ||
108 | + colorDepth: typeof getColorDepth; | ||
109 | + deviceMemory: typeof getDeviceMemory; | ||
110 | + screenResolution: typeof getScreenResolution; | ||
111 | + availableScreenResolution: typeof getAvailableScreenResolution; | ||
112 | + hardwareConcurrency: typeof getHardwareConcurrency; | ||
113 | + timezoneOffset: typeof getTimezoneOffset; | ||
114 | + timezone: typeof getTimezone; | ||
115 | + sessionStorage: typeof getSessionStorage; | ||
116 | + localStorage: typeof getLocalStorage; | ||
117 | + indexedDB: typeof getIndexedDB; | ||
118 | + openDatabase: typeof getOpenDatabase; | ||
119 | + cpuClass: typeof getCpuClass; | ||
120 | + platform: typeof getPlatform; | ||
121 | + plugins: typeof getPlugins; | ||
122 | + canvas: typeof getCanvasFingerprint; | ||
123 | + touchSupport: typeof getTouchSupport; | ||
124 | + fonts: typeof getFonts; | ||
125 | + audio: typeof getAudioFingerprint; | ||
126 | + pluginsSupport: typeof getPluginsSupport; | ||
127 | + productSub: typeof getProductSub; | ||
128 | + emptyEvalLength: typeof getEmptyEvalLength; | ||
129 | + errorFF: typeof getErrorFF; | ||
130 | + vendor: typeof getVendor; | ||
131 | + chrome: typeof getChrome; | ||
132 | + cookiesEnabled: typeof areCookiesEnabled; | ||
133 | +}; | ||
134 | +/** | ||
135 | + * A functions that returns data with entropy to identify visitor. | ||
136 | + * Source must handle expected errors by itself and turn them into entropy. | ||
137 | + * The return value must be compatible with `JSON.stringify` or be undefined. | ||
138 | + */ | ||
139 | +declare type Source<TOptions, TValue> = (options: TOptions) => Promise<TValue> | TValue; | ||
140 | +/** | ||
141 | + * Generic dictionary of unknown sources | ||
142 | + */ | ||
143 | +declare type UnknownSources<TOptions> = Record<string, Source<TOptions, unknown>>; | ||
144 | +/** | ||
145 | + * Converts an entropy source type to the source return value type | ||
146 | + */ | ||
147 | +declare type SourceValue<TSource extends Source<any, any>> = TSource extends Source<any, infer T> ? T : never; | ||
148 | +/** | ||
149 | + * Result of getting entropy data from a source | ||
150 | + */ | ||
151 | +declare type Component<T> = ({ | ||
152 | + value: T; | ||
153 | + error?: undefined; | ||
154 | +} | { | ||
155 | + value?: undefined; | ||
156 | + error: Error | { | ||
157 | + message: unknown; | ||
158 | + }; | ||
159 | +}) & { | ||
160 | + duration: number; | ||
161 | +}; | ||
162 | +/** | ||
163 | + * Generic dictionary of unknown components | ||
164 | + */ | ||
165 | +declare type UnknownComponents = Record<string, Component<unknown>>; | ||
166 | +/** | ||
167 | + * Converts an entropy source list type to a corresponding component list type. | ||
168 | + * | ||
169 | + * Warning for package users: | ||
170 | + * This type is out of Semantic Versioning, i.e. can change unexpectedly. Usage is at your own risk. | ||
171 | + */ | ||
172 | +declare type SourcesToComponents<TSources extends UnknownSources<any>> = { | ||
173 | + [K in keyof TSources]: Component<SourceValue<TSources[K]>>; | ||
174 | +}; | ||
175 | +/** | ||
176 | + * List of components from the built-in entropy sources. | ||
177 | + * | ||
178 | + * Warning! This type is out of Semantic Versioning, i.e. may have incompatible changes within a major version. If you | ||
179 | + * want to avoid breaking changes, use `UnknownComponents` instead that is more generic but guarantees backward | ||
180 | + * compatibility within a major version. This is because browsers change constantly and therefore entropy sources have | ||
181 | + * to change too. | ||
182 | + */ | ||
183 | +declare type BuiltinComponents = SourcesToComponents<typeof sources>; | ||
184 | +/** | ||
185 | + * Gets a components list from the given list of entropy sources. | ||
186 | + * | ||
187 | + * Warning for package users: | ||
188 | + * This function is out of Semantic Versioning, i.e. can change unexpectedly. Usage is at your own risk. | ||
189 | + */ | ||
190 | +declare function getComponents<TSourceOptions, TSources extends UnknownSources<TSourceOptions>, TExclude extends string>(sources: TSources, sourceOptions: TSourceOptions, excludeSources: readonly TExclude[]): Promise<Omit<SourcesToComponents<TSources>, TExclude>>; | ||
191 | + | ||
192 | +/** | ||
193 | + * Options for Fingerprint class loading | ||
194 | + */ | ||
195 | +interface LoadOptions { | ||
196 | + /** | ||
197 | + * When browser doesn't support `requestIdleCallback` a `setTimeout` will be used. This number is only for Safari and | ||
198 | + * old Edge, because Chrome/Blink based browsers support `requestIdleCallback`. The value is in milliseconds. | ||
199 | + * @default 50 | ||
200 | + */ | ||
201 | + delayFallback?: number; | ||
202 | +} | ||
203 | +/** | ||
204 | + * Options for getting visitor identifier | ||
205 | + */ | ||
206 | +interface GetOptions { | ||
207 | + /** | ||
208 | + * Whether to print debug messages to the console. | ||
209 | + * Required to ease investigations of problems. | ||
210 | + */ | ||
211 | + debug?: boolean; | ||
212 | +} | ||
213 | +/** | ||
214 | + * Result of getting a visitor identifier | ||
215 | + */ | ||
216 | +interface GetResult { | ||
217 | + /** | ||
218 | + * The visitor identifier | ||
219 | + */ | ||
220 | + visitorId: string; | ||
221 | + /** | ||
222 | + * List of components that has formed the visitor identifier. | ||
223 | + * | ||
224 | + * Warning! The type of this property is specific but out of Semantic Versioning, i.e. may have incompatible changes | ||
225 | + * within a major version. If you want to avoid breaking changes, treat the property as having type | ||
226 | + * `UnknownComponents` that is more generic but guarantees backward compatibility within a major version. | ||
227 | + */ | ||
228 | + components: BuiltinComponents; | ||
229 | +} | ||
230 | +/** | ||
231 | + * Agent object that can get visitor identifier | ||
232 | + */ | ||
233 | +interface Agent { | ||
234 | + /** | ||
235 | + * Gets the visitor identifier | ||
236 | + */ | ||
237 | + get(options?: Readonly<GetOptions>): Promise<GetResult>; | ||
238 | +} | ||
239 | +declare function componentsToDebugString(components: UnknownComponents): string; | ||
240 | +declare function hashComponents(components: UnknownComponents): string; | ||
241 | +/** | ||
242 | + * Builds an instance of Agent and waits a delay required for a proper operation. | ||
243 | + */ | ||
244 | +declare function load({ delayFallback }?: Readonly<LoadOptions>): Promise<Agent>; | ||
245 | + | ||
246 | +/** | ||
247 | + * Checks whether the browser is based on Trident (the Internet Explorer engine) without using user-agent. | ||
248 | + * | ||
249 | + * Warning for package users: | ||
250 | + * This function is out of Semantic Versioning, i.e. can change unexpectedly. Usage is at your own risk. | ||
251 | + */ | ||
252 | +declare function isTrident(): boolean; | ||
253 | +/** | ||
254 | + * Checks whether the browser is based on EdgeHTML (the pre-Chromium Edge engine) without using user-agent. | ||
255 | + * | ||
256 | + * Warning for package users: | ||
257 | + * This function is out of Semantic Versioning, i.e. can change unexpectedly. Usage is at your own risk. | ||
258 | + */ | ||
259 | +declare function isEdgeHTML(): boolean; | ||
260 | +/** | ||
261 | + * Checks whether the browser is based on Chromium without using user-agent. | ||
262 | + * | ||
263 | + * Warning for package users: | ||
264 | + * This function is out of Semantic Versioning, i.e. can change unexpectedly. Usage is at your own risk. | ||
265 | + */ | ||
266 | +declare function isChromium(): boolean; | ||
267 | +/** | ||
268 | + * Checks whether the browser is based on mobile or desktop Safari without using user-agent. | ||
269 | + * All iOS browsers use WebKit (the Safari engine). | ||
270 | + * | ||
271 | + * Warning for package users: | ||
272 | + * This function is out of Semantic Versioning, i.e. can change unexpectedly. Usage is at your own risk. | ||
273 | + */ | ||
274 | +declare function isWebKit(): boolean; | ||
275 | +/** | ||
276 | + * Checks whether the WebKit browser is a desktop Safari. | ||
277 | + * | ||
278 | + * Warning for package users: | ||
279 | + * This function is out of Semantic Versioning, i.e. can change unexpectedly. Usage is at your own risk. | ||
280 | + */ | ||
281 | +declare function isDesktopSafari(): boolean; | ||
282 | +/** | ||
283 | + * Checks whether the browser is based on Gecko (Firefox engine) without using user-agent. | ||
284 | + * | ||
285 | + * Warning for package users: | ||
286 | + * This function is out of Semantic Versioning, i.e. can change unexpectedly. Usage is at your own risk. | ||
287 | + */ | ||
288 | +declare function isGecko(): boolean; | ||
289 | + | ||
290 | +declare const _default: { | ||
291 | + load: typeof load; | ||
292 | + hashComponents: typeof hashComponents; | ||
293 | + componentsToDebugString: typeof componentsToDebugString; | ||
294 | +}; | ||
295 | +/** Not documented, out of Semantic Versioning, usage is at your own risk */ | ||
296 | +declare const murmurX64Hash128: typeof x64hash128; | ||
297 | + | ||
298 | +export default _default; | ||
299 | +export { Agent, BuiltinComponents, Component, GetOptions, GetResult, LoadOptions, SourcesToComponents, UnknownComponents, componentsToDebugString, getComponents, hashComponents, isChromium, isDesktopSafari, isEdgeHTML, isGecko, isTrident, isWebKit, load, murmurX64Hash128 }; |
dist/fp.esm.js
0 → 100644
1 | +/** | ||
2 | + * FingerprintJS v3.0.3 - Copyright (c) FingerprintJS, Inc, 2020 (https://fingerprintjs.com) | ||
3 | + * Licensed under the MIT (http://www.opensource.org/licenses/mit-license.php) license. | ||
4 | + * | ||
5 | + * This software contains code from open-source projects: | ||
6 | + * MurmurHash3 by Karan Lyons (https://github.com/karanlyons/murmurHash3.js) | ||
7 | + */ | ||
8 | + | ||
9 | +import { __awaiter, __generator, __assign } from 'tslib'; | ||
10 | + | ||
11 | +/* | ||
12 | + * Taken from https://github.com/karanlyons/murmurHash3.js/blob/a33d0723127e2e5415056c455f8aed2451ace208/murmurHash3.js | ||
13 | + */ | ||
14 | +// | ||
15 | +// Given two 64bit ints (as an array of two 32bit ints) returns the two | ||
16 | +// added together as a 64bit int (as an array of two 32bit ints). | ||
17 | +// | ||
18 | +function x64Add(m, n) { | ||
19 | + m = [m[0] >>> 16, m[0] & 0xffff, m[1] >>> 16, m[1] & 0xffff]; | ||
20 | + n = [n[0] >>> 16, n[0] & 0xffff, n[1] >>> 16, n[1] & 0xffff]; | ||
21 | + var o = [0, 0, 0, 0]; | ||
22 | + o[3] += m[3] + n[3]; | ||
23 | + o[2] += o[3] >>> 16; | ||
24 | + o[3] &= 0xffff; | ||
25 | + o[2] += m[2] + n[2]; | ||
26 | + o[1] += o[2] >>> 16; | ||
27 | + o[2] &= 0xffff; | ||
28 | + o[1] += m[1] + n[1]; | ||
29 | + o[0] += o[1] >>> 16; | ||
30 | + o[1] &= 0xffff; | ||
31 | + o[0] += m[0] + n[0]; | ||
32 | + o[0] &= 0xffff; | ||
33 | + return [(o[0] << 16) | o[1], (o[2] << 16) | o[3]]; | ||
34 | +} | ||
35 | +// | ||
36 | +// Given two 64bit ints (as an array of two 32bit ints) returns the two | ||
37 | +// multiplied together as a 64bit int (as an array of two 32bit ints). | ||
38 | +// | ||
39 | +function x64Multiply(m, n) { | ||
40 | + m = [m[0] >>> 16, m[0] & 0xffff, m[1] >>> 16, m[1] & 0xffff]; | ||
41 | + n = [n[0] >>> 16, n[0] & 0xffff, n[1] >>> 16, n[1] & 0xffff]; | ||
42 | + var o = [0, 0, 0, 0]; | ||
43 | + o[3] += m[3] * n[3]; | ||
44 | + o[2] += o[3] >>> 16; | ||
45 | + o[3] &= 0xffff; | ||
46 | + o[2] += m[2] * n[3]; | ||
47 | + o[1] += o[2] >>> 16; | ||
48 | + o[2] &= 0xffff; | ||
49 | + o[2] += m[3] * n[2]; | ||
50 | + o[1] += o[2] >>> 16; | ||
51 | + o[2] &= 0xffff; | ||
52 | + o[1] += m[1] * n[3]; | ||
53 | + o[0] += o[1] >>> 16; | ||
54 | + o[1] &= 0xffff; | ||
55 | + o[1] += m[2] * n[2]; | ||
56 | + o[0] += o[1] >>> 16; | ||
57 | + o[1] &= 0xffff; | ||
58 | + o[1] += m[3] * n[1]; | ||
59 | + o[0] += o[1] >>> 16; | ||
60 | + o[1] &= 0xffff; | ||
61 | + o[0] += m[0] * n[3] + m[1] * n[2] + m[2] * n[1] + m[3] * n[0]; | ||
62 | + o[0] &= 0xffff; | ||
63 | + return [(o[0] << 16) | o[1], (o[2] << 16) | o[3]]; | ||
64 | +} | ||
65 | +// | ||
66 | +// Given a 64bit int (as an array of two 32bit ints) and an int | ||
67 | +// representing a number of bit positions, returns the 64bit int (as an | ||
68 | +// array of two 32bit ints) rotated left by that number of positions. | ||
69 | +// | ||
70 | +function x64Rotl(m, n) { | ||
71 | + n %= 64; | ||
72 | + if (n === 32) { | ||
73 | + return [m[1], m[0]]; | ||
74 | + } | ||
75 | + else if (n < 32) { | ||
76 | + return [(m[0] << n) | (m[1] >>> (32 - n)), (m[1] << n) | (m[0] >>> (32 - n))]; | ||
77 | + } | ||
78 | + else { | ||
79 | + n -= 32; | ||
80 | + return [(m[1] << n) | (m[0] >>> (32 - n)), (m[0] << n) | (m[1] >>> (32 - n))]; | ||
81 | + } | ||
82 | +} | ||
83 | +// | ||
84 | +// Given a 64bit int (as an array of two 32bit ints) and an int | ||
85 | +// representing a number of bit positions, returns the 64bit int (as an | ||
86 | +// array of two 32bit ints) shifted left by that number of positions. | ||
87 | +// | ||
88 | +function x64LeftShift(m, n) { | ||
89 | + n %= 64; | ||
90 | + if (n === 0) { | ||
91 | + return m; | ||
92 | + } | ||
93 | + else if (n < 32) { | ||
94 | + return [(m[0] << n) | (m[1] >>> (32 - n)), m[1] << n]; | ||
95 | + } | ||
96 | + else { | ||
97 | + return [m[1] << (n - 32), 0]; | ||
98 | + } | ||
99 | +} | ||
100 | +// | ||
101 | +// Given two 64bit ints (as an array of two 32bit ints) returns the two | ||
102 | +// xored together as a 64bit int (as an array of two 32bit ints). | ||
103 | +// | ||
104 | +function x64Xor(m, n) { | ||
105 | + return [m[0] ^ n[0], m[1] ^ n[1]]; | ||
106 | +} | ||
107 | +// | ||
108 | +// Given a block, returns murmurHash3's final x64 mix of that block. | ||
109 | +// (`[0, h[0] >>> 1]` is a 33 bit unsigned right shift. This is the | ||
110 | +// only place where we need to right shift 64bit ints.) | ||
111 | +// | ||
112 | +function x64Fmix(h) { | ||
113 | + h = x64Xor(h, [0, h[0] >>> 1]); | ||
114 | + h = x64Multiply(h, [0xff51afd7, 0xed558ccd]); | ||
115 | + h = x64Xor(h, [0, h[0] >>> 1]); | ||
116 | + h = x64Multiply(h, [0xc4ceb9fe, 0x1a85ec53]); | ||
117 | + h = x64Xor(h, [0, h[0] >>> 1]); | ||
118 | + return h; | ||
119 | +} | ||
120 | +// | ||
121 | +// Given a string and an optional seed as an int, returns a 128 bit | ||
122 | +// hash using the x64 flavor of MurmurHash3, as an unsigned hex. | ||
123 | +// | ||
124 | +function x64hash128(key, seed) { | ||
125 | + key = key || ''; | ||
126 | + seed = seed || 0; | ||
127 | + var remainder = key.length % 16; | ||
128 | + var bytes = key.length - remainder; | ||
129 | + var h1 = [0, seed]; | ||
130 | + var h2 = [0, seed]; | ||
131 | + var k1 = [0, 0]; | ||
132 | + var k2 = [0, 0]; | ||
133 | + var c1 = [0x87c37b91, 0x114253d5]; | ||
134 | + var c2 = [0x4cf5ad43, 0x2745937f]; | ||
135 | + var i; | ||
136 | + for (i = 0; i < bytes; i = i + 16) { | ||
137 | + k1 = [ | ||
138 | + (key.charCodeAt(i + 4) & 0xff) | | ||
139 | + ((key.charCodeAt(i + 5) & 0xff) << 8) | | ||
140 | + ((key.charCodeAt(i + 6) & 0xff) << 16) | | ||
141 | + ((key.charCodeAt(i + 7) & 0xff) << 24), | ||
142 | + (key.charCodeAt(i) & 0xff) | | ||
143 | + ((key.charCodeAt(i + 1) & 0xff) << 8) | | ||
144 | + ((key.charCodeAt(i + 2) & 0xff) << 16) | | ||
145 | + ((key.charCodeAt(i + 3) & 0xff) << 24), | ||
146 | + ]; | ||
147 | + k2 = [ | ||
148 | + (key.charCodeAt(i + 12) & 0xff) | | ||
149 | + ((key.charCodeAt(i + 13) & 0xff) << 8) | | ||
150 | + ((key.charCodeAt(i + 14) & 0xff) << 16) | | ||
151 | + ((key.charCodeAt(i + 15) & 0xff) << 24), | ||
152 | + (key.charCodeAt(i + 8) & 0xff) | | ||
153 | + ((key.charCodeAt(i + 9) & 0xff) << 8) | | ||
154 | + ((key.charCodeAt(i + 10) & 0xff) << 16) | | ||
155 | + ((key.charCodeAt(i + 11) & 0xff) << 24), | ||
156 | + ]; | ||
157 | + k1 = x64Multiply(k1, c1); | ||
158 | + k1 = x64Rotl(k1, 31); | ||
159 | + k1 = x64Multiply(k1, c2); | ||
160 | + h1 = x64Xor(h1, k1); | ||
161 | + h1 = x64Rotl(h1, 27); | ||
162 | + h1 = x64Add(h1, h2); | ||
163 | + h1 = x64Add(x64Multiply(h1, [0, 5]), [0, 0x52dce729]); | ||
164 | + k2 = x64Multiply(k2, c2); | ||
165 | + k2 = x64Rotl(k2, 33); | ||
166 | + k2 = x64Multiply(k2, c1); | ||
167 | + h2 = x64Xor(h2, k2); | ||
168 | + h2 = x64Rotl(h2, 31); | ||
169 | + h2 = x64Add(h2, h1); | ||
170 | + h2 = x64Add(x64Multiply(h2, [0, 5]), [0, 0x38495ab5]); | ||
171 | + } | ||
172 | + k1 = [0, 0]; | ||
173 | + k2 = [0, 0]; | ||
174 | + switch (remainder) { | ||
175 | + case 15: | ||
176 | + k2 = x64Xor(k2, x64LeftShift([0, key.charCodeAt(i + 14)], 48)); | ||
177 | + // fallthrough | ||
178 | + case 14: | ||
179 | + k2 = x64Xor(k2, x64LeftShift([0, key.charCodeAt(i + 13)], 40)); | ||
180 | + // fallthrough | ||
181 | + case 13: | ||
182 | + k2 = x64Xor(k2, x64LeftShift([0, key.charCodeAt(i + 12)], 32)); | ||
183 | + // fallthrough | ||
184 | + case 12: | ||
185 | + k2 = x64Xor(k2, x64LeftShift([0, key.charCodeAt(i + 11)], 24)); | ||
186 | + // fallthrough | ||
187 | + case 11: | ||
188 | + k2 = x64Xor(k2, x64LeftShift([0, key.charCodeAt(i + 10)], 16)); | ||
189 | + // fallthrough | ||
190 | + case 10: | ||
191 | + k2 = x64Xor(k2, x64LeftShift([0, key.charCodeAt(i + 9)], 8)); | ||
192 | + // fallthrough | ||
193 | + case 9: | ||
194 | + k2 = x64Xor(k2, [0, key.charCodeAt(i + 8)]); | ||
195 | + k2 = x64Multiply(k2, c2); | ||
196 | + k2 = x64Rotl(k2, 33); | ||
197 | + k2 = x64Multiply(k2, c1); | ||
198 | + h2 = x64Xor(h2, k2); | ||
199 | + // fallthrough | ||
200 | + case 8: | ||
201 | + k1 = x64Xor(k1, x64LeftShift([0, key.charCodeAt(i + 7)], 56)); | ||
202 | + // fallthrough | ||
203 | + case 7: | ||
204 | + k1 = x64Xor(k1, x64LeftShift([0, key.charCodeAt(i + 6)], 48)); | ||
205 | + // fallthrough | ||
206 | + case 6: | ||
207 | + k1 = x64Xor(k1, x64LeftShift([0, key.charCodeAt(i + 5)], 40)); | ||
208 | + // fallthrough | ||
209 | + case 5: | ||
210 | + k1 = x64Xor(k1, x64LeftShift([0, key.charCodeAt(i + 4)], 32)); | ||
211 | + // fallthrough | ||
212 | + case 4: | ||
213 | + k1 = x64Xor(k1, x64LeftShift([0, key.charCodeAt(i + 3)], 24)); | ||
214 | + // fallthrough | ||
215 | + case 3: | ||
216 | + k1 = x64Xor(k1, x64LeftShift([0, key.charCodeAt(i + 2)], 16)); | ||
217 | + // fallthrough | ||
218 | + case 2: | ||
219 | + k1 = x64Xor(k1, x64LeftShift([0, key.charCodeAt(i + 1)], 8)); | ||
220 | + // fallthrough | ||
221 | + case 1: | ||
222 | + k1 = x64Xor(k1, [0, key.charCodeAt(i)]); | ||
223 | + k1 = x64Multiply(k1, c1); | ||
224 | + k1 = x64Rotl(k1, 31); | ||
225 | + k1 = x64Multiply(k1, c2); | ||
226 | + h1 = x64Xor(h1, k1); | ||
227 | + // fallthrough | ||
228 | + } | ||
229 | + h1 = x64Xor(h1, [0, key.length]); | ||
230 | + h2 = x64Xor(h2, [0, key.length]); | ||
231 | + h1 = x64Add(h1, h2); | ||
232 | + h2 = x64Add(h2, h1); | ||
233 | + h1 = x64Fmix(h1); | ||
234 | + h2 = x64Fmix(h2); | ||
235 | + h1 = x64Add(h1, h2); | ||
236 | + h2 = x64Add(h2, h1); | ||
237 | + return (('00000000' + (h1[0] >>> 0).toString(16)).slice(-8) + | ||
238 | + ('00000000' + (h1[1] >>> 0).toString(16)).slice(-8) + | ||
239 | + ('00000000' + (h2[0] >>> 0).toString(16)).slice(-8) + | ||
240 | + ('00000000' + (h2[1] >>> 0).toString(16)).slice(-8)); | ||
241 | +} | ||
242 | + | ||
243 | +var version = "3.0.3"; | ||
244 | + | ||
245 | +function requestIdleCallbackIfAvailable(fallbackTimeout) { | ||
246 | + return new Promise(function (resolve) { | ||
247 | + if (window.requestIdleCallback) { | ||
248 | + window.requestIdleCallback(function () { return resolve(); }); | ||
249 | + } | ||
250 | + else { | ||
251 | + setTimeout(resolve, fallbackTimeout); | ||
252 | + } | ||
253 | + }); | ||
254 | +} | ||
255 | + | ||
256 | +/* | ||
257 | + * This file contains functions to work with pure data only (no browser features, DOM, side effects, etc). | ||
258 | + */ | ||
259 | +/** | ||
260 | + * Does the same as Array.prototype.includes but has better typing | ||
261 | + */ | ||
262 | +function includes(haystack, needle) { | ||
263 | + for (var i = 0, l = haystack.length; i < l; ++i) { | ||
264 | + if (haystack[i] === needle) { | ||
265 | + return true; | ||
266 | + } | ||
267 | + } | ||
268 | + return false; | ||
269 | +} | ||
270 | +/** | ||
271 | + * Like `!includes()` but with proper typing | ||
272 | + */ | ||
273 | +function excludes(haystack, needle) { | ||
274 | + return !includes(haystack, needle); | ||
275 | +} | ||
276 | +/** | ||
277 | + * Be careful, NaN can return | ||
278 | + */ | ||
279 | +function toInt(value) { | ||
280 | + if (typeof value === 'number') { | ||
281 | + return value | 0; | ||
282 | + } | ||
283 | + return parseInt(value); | ||
284 | +} | ||
285 | +/** | ||
286 | + * Be careful, NaN can return | ||
287 | + */ | ||
288 | +function toFloat(value) { | ||
289 | + if (typeof value === 'number') { | ||
290 | + return value; | ||
291 | + } | ||
292 | + return parseFloat(value); | ||
293 | +} | ||
294 | +function countTruthy(values) { | ||
295 | + return values.reduce(function (sum, value) { return sum + (value ? 1 : 0); }, 0); | ||
296 | +} | ||
297 | + | ||
298 | +/* | ||
299 | + * Functions to help with browser features | ||
300 | + */ | ||
301 | +var w = window; | ||
302 | +var n = navigator; | ||
303 | +var d = document; | ||
304 | +/** | ||
305 | + * Checks whether the browser is based on Trident (the Internet Explorer engine) without using user-agent. | ||
306 | + * | ||
307 | + * Warning for package users: | ||
308 | + * This function is out of Semantic Versioning, i.e. can change unexpectedly. Usage is at your own risk. | ||
309 | + */ | ||
310 | +function isTrident() { | ||
311 | + // The properties are checked to be in IE 10, IE 11 and not to be in other browsers in October 2020 | ||
312 | + return (countTruthy([ | ||
313 | + 'MSCSSMatrix' in w, | ||
314 | + 'msSetImmediate' in w, | ||
315 | + 'msIndexedDB' in w, | ||
316 | + 'msMaxTouchPoints' in n, | ||
317 | + 'msPointerEnabled' in n, | ||
318 | + ]) >= 4); | ||
319 | +} | ||
320 | +/** | ||
321 | + * Checks whether the browser is based on EdgeHTML (the pre-Chromium Edge engine) without using user-agent. | ||
322 | + * | ||
323 | + * Warning for package users: | ||
324 | + * This function is out of Semantic Versioning, i.e. can change unexpectedly. Usage is at your own risk. | ||
325 | + */ | ||
326 | +function isEdgeHTML() { | ||
327 | + // Based on research in October 2020 | ||
328 | + return (countTruthy(['msWriteProfilerMark' in w, 'MSStream' in w, 'msLaunchUri' in n, 'msSaveBlob' in n]) >= 3 && | ||
329 | + !isTrident()); | ||
330 | +} | ||
331 | +/** | ||
332 | + * Checks whether the browser is based on Chromium without using user-agent. | ||
333 | + * | ||
334 | + * Warning for package users: | ||
335 | + * This function is out of Semantic Versioning, i.e. can change unexpectedly. Usage is at your own risk. | ||
336 | + */ | ||
337 | +function isChromium() { | ||
338 | + // Based on research in October 2020. Tested to detect Chromium 42-86. | ||
339 | + return (countTruthy([ | ||
340 | + 'webkitPersistentStorage' in n, | ||
341 | + 'webkitTemporaryStorage' in n, | ||
342 | + n.vendor.indexOf('Google') === 0, | ||
343 | + 'webkitResolveLocalFileSystemURL' in w, | ||
344 | + 'BatteryManager' in w, | ||
345 | + 'webkitMediaStream' in w, | ||
346 | + 'webkitSpeechGrammar' in w, | ||
347 | + ]) >= 5); | ||
348 | +} | ||
349 | +/** | ||
350 | + * Checks whether the browser is based on mobile or desktop Safari without using user-agent. | ||
351 | + * All iOS browsers use WebKit (the Safari engine). | ||
352 | + * | ||
353 | + * Warning for package users: | ||
354 | + * This function is out of Semantic Versioning, i.e. can change unexpectedly. Usage is at your own risk. | ||
355 | + */ | ||
356 | +function isWebKit() { | ||
357 | + // Based on research in September 2020 | ||
358 | + return (countTruthy([ | ||
359 | + 'ApplePayError' in w, | ||
360 | + 'CSSPrimitiveValue' in w, | ||
361 | + 'Counter' in w, | ||
362 | + n.vendor.indexOf('Apple') === 0, | ||
363 | + 'getStorageUpdates' in n, | ||
364 | + 'WebKitMediaKeys' in w, | ||
365 | + ]) >= 4); | ||
366 | +} | ||
367 | +/** | ||
368 | + * Checks whether the WebKit browser is a desktop Safari. | ||
369 | + * | ||
370 | + * Warning for package users: | ||
371 | + * This function is out of Semantic Versioning, i.e. can change unexpectedly. Usage is at your own risk. | ||
372 | + */ | ||
373 | +function isDesktopSafari() { | ||
374 | + return (countTruthy([ | ||
375 | + 'safari' in w, | ||
376 | + !('DeviceMotionEvent' in w), | ||
377 | + !('ongestureend' in w), | ||
378 | + !('standalone' in n), | ||
379 | + ]) >= 3); | ||
380 | +} | ||
381 | +/** | ||
382 | + * Checks whether the browser is based on Gecko (Firefox engine) without using user-agent. | ||
383 | + * | ||
384 | + * Warning for package users: | ||
385 | + * This function is out of Semantic Versioning, i.e. can change unexpectedly. Usage is at your own risk. | ||
386 | + */ | ||
387 | +function isGecko() { | ||
388 | + var _a; | ||
389 | + // Based on research in September 2020 | ||
390 | + return (countTruthy([ | ||
391 | + 'buildID' in n, | ||
392 | + ((_a = d.documentElement) === null || _a === void 0 ? void 0 : _a.style) && 'MozAppearance' in d.documentElement.style, | ||
393 | + 'MediaRecorderErrorEvent' in w, | ||
394 | + 'mozInnerScreenX' in w, | ||
395 | + 'CSSMozDocumentRule' in w, | ||
396 | + 'CanvasCaptureMediaStream' in w, | ||
397 | + ]) >= 4); | ||
398 | +} | ||
399 | +/** | ||
400 | + * Checks whether the browser is based on Chromium version ≥86 without using user-agent. | ||
401 | + * It doesn't check that the browser is based on Chromium, there is a separate function for this. | ||
402 | + */ | ||
403 | +function isChromium86OrNewer() { | ||
404 | + // Checked in Chrome 85 vs Chrome 86 both on desktop and Android | ||
405 | + return (countTruthy([ | ||
406 | + !('MediaSettingsRange' in w), | ||
407 | + 'RTCEncodedAudioFrame' in w, | ||
408 | + '' + w.Intl === '[object Intl]', | ||
409 | + '' + w.Reflect === '[object Reflect]', | ||
410 | + ]) >= 3); | ||
411 | +} | ||
412 | +/** | ||
413 | + * Checks whether the browser is based on WebKit version ≥606 (Safari ≥12) without using user-agent. | ||
414 | + * It doesn't check that the browser is based on WebKit, there is a separate function for this. | ||
415 | + * | ||
416 | + * @link https://en.wikipedia.org/wiki/Safari_version_history#Release_history Safari-WebKit versions map | ||
417 | + */ | ||
418 | +function isWebKit606OrNewer() { | ||
419 | + // Checked in Safari 9–14 | ||
420 | + return (countTruthy([ | ||
421 | + 'DOMRectList' in w, | ||
422 | + 'RTCPeerConnectionIceEvent' in w, | ||
423 | + 'SVGGeometryElement' in w, | ||
424 | + 'ontransitioncancel' in w, | ||
425 | + ]) >= 3); | ||
426 | +} | ||
427 | + | ||
428 | +var w$1 = window; | ||
429 | +var d$1 = document; | ||
430 | +// Inspired by and based on https://github.com/cozylife/audio-fingerprint | ||
431 | +function getAudioFingerprint() { | ||
432 | + return __awaiter(this, void 0, void 0, function () { | ||
433 | + var AudioContext, context, oscillator, compressor, buffer, error_1; | ||
434 | + return __generator(this, function (_a) { | ||
435 | + switch (_a.label) { | ||
436 | + case 0: | ||
437 | + // In some browsers, audio context always stays suspended unless the context is started in response to a user action | ||
438 | + // (e.g. a click or a tap). It prevents audio fingerprint from being taken at an arbitrary moment of time. | ||
439 | + // Such browsers are old and unpopular, so the audio fingerprinting is just skipped in them. | ||
440 | + // See a similar case explanation at https://stackoverflow.com/questions/46363048/onaudioprocess-not-called-on-ios11#46534088 | ||
441 | + if (doesCurrentBrowserSuspendAudioContext()) { | ||
442 | + return [2 /*return*/, -1]; | ||
443 | + } | ||
444 | + AudioContext = w$1.OfflineAudioContext || w$1.webkitOfflineAudioContext; | ||
445 | + if (!AudioContext) { | ||
446 | + return [2 /*return*/, -2]; | ||
447 | + } | ||
448 | + context = new AudioContext(1, 44100, 44100); | ||
449 | + oscillator = context.createOscillator(); | ||
450 | + oscillator.type = 'triangle'; | ||
451 | + setAudioParam(context, oscillator.frequency, 10000); | ||
452 | + compressor = context.createDynamicsCompressor(); | ||
453 | + setAudioParam(context, compressor.threshold, -50); | ||
454 | + setAudioParam(context, compressor.knee, 40); | ||
455 | + setAudioParam(context, compressor.ratio, 12); | ||
456 | + setAudioParam(context, compressor.reduction, -20); | ||
457 | + setAudioParam(context, compressor.attack, 0); | ||
458 | + setAudioParam(context, compressor.release, 0.25); | ||
459 | + oscillator.connect(compressor); | ||
460 | + compressor.connect(context.destination); | ||
461 | + oscillator.start(0); | ||
462 | + _a.label = 1; | ||
463 | + case 1: | ||
464 | + _a.trys.push([1, 3, 4, 5]); | ||
465 | + return [4 /*yield*/, renderAudio(context)]; | ||
466 | + case 2: | ||
467 | + buffer = _a.sent(); | ||
468 | + return [3 /*break*/, 5]; | ||
469 | + case 3: | ||
470 | + error_1 = _a.sent(); | ||
471 | + if (error_1.name === "timeout" /* Timeout */ || error_1.name === "suspended" /* Suspended */) { | ||
472 | + return [2 /*return*/, -3]; | ||
473 | + } | ||
474 | + throw error_1; | ||
475 | + case 4: | ||
476 | + oscillator.disconnect(); | ||
477 | + compressor.disconnect(); | ||
478 | + return [7 /*endfinally*/]; | ||
479 | + case 5: return [2 /*return*/, getHash(buffer.getChannelData(0))]; | ||
480 | + } | ||
481 | + }); | ||
482 | + }); | ||
483 | +} | ||
484 | +/** | ||
485 | + * Checks if the current browser is known to always suspend audio context | ||
486 | + */ | ||
487 | +function doesCurrentBrowserSuspendAudioContext() { | ||
488 | + return isWebKit() && !isDesktopSafari() && !isWebKit606OrNewer(); | ||
489 | +} | ||
490 | +function setAudioParam(context, param, value) { | ||
491 | + var isAudioParam = function (value) { | ||
492 | + return value && typeof value.setValueAtTime === 'function'; | ||
493 | + }; | ||
494 | + if (isAudioParam(param)) { | ||
495 | + param.setValueAtTime(value, context.currentTime); | ||
496 | + } | ||
497 | +} | ||
498 | +function renderAudio(context) { | ||
499 | + var resumeTriesMaxCount = 3; | ||
500 | + var resumeRetryDelay = 500; | ||
501 | + var runningTimeout = 1000; | ||
502 | + return new Promise(function (resolve, reject) { | ||
503 | + context.oncomplete = function (event) { return resolve(event.renderedBuffer); }; | ||
504 | + var resumeTriesLeft = resumeTriesMaxCount; | ||
505 | + var tryResume = function () { | ||
506 | + context.startRendering(); | ||
507 | + switch (context.state) { | ||
508 | + case 'running': | ||
509 | + setTimeout(function () { return reject(makeInnerError("timeout" /* Timeout */)); }, runningTimeout); | ||
510 | + break; | ||
511 | + // Sometimes the audio context doesn't start after calling `startRendering` (in addition to the cases where | ||
512 | + // audio context doesn't start at all). A known case is starting an audio context when the browser tab is in | ||
513 | + // background on iPhone. Retries usually help in this case. | ||
514 | + case 'suspended': | ||
515 | + // The audio context can reject starting until the tab is in foreground. Long fingerprint duration | ||
516 | + // in background isn't a problem, therefore the retry attempts don't count in background. It can lead to | ||
517 | + // a situation when a fingerprint takes very long time and finishes successfully. FYI, the audio context | ||
518 | + // can be suspended when `document.hidden === false` and start running after a retry. | ||
519 | + if (!d$1.hidden) { | ||
520 | + resumeTriesLeft--; | ||
521 | + } | ||
522 | + if (resumeTriesLeft > 0) { | ||
523 | + setTimeout(tryResume, resumeRetryDelay); | ||
524 | + } | ||
525 | + else { | ||
526 | + reject(makeInnerError("suspended" /* Suspended */)); | ||
527 | + } | ||
528 | + break; | ||
529 | + } | ||
530 | + }; | ||
531 | + tryResume(); | ||
532 | + }); | ||
533 | +} | ||
534 | +function getHash(signal) { | ||
535 | + var hash = 0; | ||
536 | + for (var i = 4500; i < 5000; ++i) { | ||
537 | + hash += Math.abs(signal[i]); | ||
538 | + } | ||
539 | + return hash; | ||
540 | +} | ||
541 | +function makeInnerError(name) { | ||
542 | + var error = new Error(name); | ||
543 | + error.name = name; | ||
544 | + return error; | ||
545 | +} | ||
546 | + | ||
547 | +var d$2 = document; | ||
548 | +// We use m or w because these two characters take up the maximum width. | ||
549 | +// And we use a LLi so that the same matching fonts can get separated. | ||
550 | +var testString = 'mmMwWLliI0O&1'; | ||
551 | +// We test using 48px font size, we may use any size. I guess larger the better. | ||
552 | +var testSize = '48px'; | ||
553 | +// A font will be compared against all the three default fonts. | ||
554 | +// And if it doesn't match all 3 then that font is not available. | ||
555 | +var baseFonts = ['monospace', 'sans-serif', 'serif']; | ||
556 | +var fontList = [ | ||
557 | + // This is android-specific font from "Roboto" family | ||
558 | + 'sans-serif-thin', | ||
559 | + 'ARNO PRO', | ||
560 | + 'Agency FB', | ||
561 | + 'Arabic Typesetting', | ||
562 | + 'Arial Unicode MS', | ||
563 | + 'AvantGarde Bk BT', | ||
564 | + 'BankGothic Md BT', | ||
565 | + 'Batang', | ||
566 | + 'Bitstream Vera Sans Mono', | ||
567 | + 'Calibri', | ||
568 | + 'Century', | ||
569 | + 'Century Gothic', | ||
570 | + 'Clarendon', | ||
571 | + 'EUROSTILE', | ||
572 | + 'Franklin Gothic', | ||
573 | + 'Futura Bk BT', | ||
574 | + 'Futura Md BT', | ||
575 | + 'GOTHAM', | ||
576 | + 'Gill Sans', | ||
577 | + 'HELV', | ||
578 | + 'Haettenschweiler', | ||
579 | + 'Helvetica Neue', | ||
580 | + 'Humanst521 BT', | ||
581 | + 'Leelawadee', | ||
582 | + 'Letter Gothic', | ||
583 | + 'Levenim MT', | ||
584 | + 'Lucida Bright', | ||
585 | + 'Lucida Sans', | ||
586 | + 'Menlo', | ||
587 | + 'MS Mincho', | ||
588 | + 'MS Outlook', | ||
589 | + 'MS Reference Specialty', | ||
590 | + 'MS UI Gothic', | ||
591 | + 'MT Extra', | ||
592 | + 'MYRIAD PRO', | ||
593 | + 'Marlett', | ||
594 | + 'Meiryo UI', | ||
595 | + 'Microsoft Uighur', | ||
596 | + 'Minion Pro', | ||
597 | + 'Monotype Corsiva', | ||
598 | + 'PMingLiU', | ||
599 | + 'Pristina', | ||
600 | + 'SCRIPTINA', | ||
601 | + 'Segoe UI Light', | ||
602 | + 'Serifa', | ||
603 | + 'SimHei', | ||
604 | + 'Small Fonts', | ||
605 | + 'Staccato222 BT', | ||
606 | + 'TRAJAN PRO', | ||
607 | + 'Univers CE 55 Medium', | ||
608 | + 'Vrinda', | ||
609 | + 'ZWAdobeF', | ||
610 | +]; | ||
611 | +var fontSpanStyle = { | ||
612 | + // CSS font reset to reset external styles | ||
613 | + fontStyle: 'normal', | ||
614 | + fontWeight: 'normal', | ||
615 | + letterSpacing: 'normal', | ||
616 | + lineBreak: 'auto', | ||
617 | + lineHeight: 'normal', | ||
618 | + textTransform: 'none', | ||
619 | + textAlign: 'left', | ||
620 | + textDecoration: 'none', | ||
621 | + textShadow: 'none', | ||
622 | + whiteSpace: 'normal', | ||
623 | + wordBreak: 'normal', | ||
624 | + wordSpacing: 'normal', | ||
625 | + // We need this css as in some weird browser this span elements shows up for a microSec which creates | ||
626 | + // a bad user experience | ||
627 | + position: 'absolute', | ||
628 | + left: '-9999px', | ||
629 | + fontSize: testSize, | ||
630 | +}; | ||
631 | +// kudos to http://www.lalit.org/lab/javascript-css-font-detect/ | ||
632 | +function getFonts() { | ||
633 | + var h = d$2.body; | ||
634 | + // div to load spans for the base fonts | ||
635 | + var baseFontsDiv = d$2.createElement('div'); | ||
636 | + // div to load spans for the fonts to detect | ||
637 | + var fontsDiv = d$2.createElement('div'); | ||
638 | + var defaultWidth = {}; | ||
639 | + var defaultHeight = {}; | ||
640 | + // creates a span where the fonts will be loaded | ||
641 | + var createSpan = function () { | ||
642 | + var span = d$2.createElement('span'); | ||
643 | + span.textContent = testString; | ||
644 | + for (var _i = 0, _a = Object.keys(fontSpanStyle); _i < _a.length; _i++) { | ||
645 | + var prop = _a[_i]; | ||
646 | + span.style[prop] = fontSpanStyle[prop]; | ||
647 | + } | ||
648 | + return span; | ||
649 | + }; | ||
650 | + // creates a span and load the font to detect and a base font for fallback | ||
651 | + var createSpanWithFonts = function (fontToDetect, baseFont) { | ||
652 | + var s = createSpan(); | ||
653 | + s.style.fontFamily = "'" + fontToDetect + "'," + baseFont; | ||
654 | + return s; | ||
655 | + }; | ||
656 | + // creates spans for the base fonts and adds them to baseFontsDiv | ||
657 | + var initializeBaseFontsSpans = function () { | ||
658 | + return baseFonts.map(function (baseFont) { | ||
659 | + var s = createSpan(); | ||
660 | + s.style.fontFamily = baseFont; | ||
661 | + baseFontsDiv.appendChild(s); | ||
662 | + return s; | ||
663 | + }); | ||
664 | + }; | ||
665 | + // creates spans for the fonts to detect and adds them to fontsDiv | ||
666 | + var initializeFontsSpans = function () { | ||
667 | + // Stores {fontName : [spans for that font]} | ||
668 | + var spans = {}; | ||
669 | + var _loop_1 = function (font) { | ||
670 | + spans[font] = baseFonts.map(function (baseFont) { | ||
671 | + var s = createSpanWithFonts(font, baseFont); | ||
672 | + fontsDiv.appendChild(s); | ||
673 | + return s; | ||
674 | + }); | ||
675 | + }; | ||
676 | + for (var _i = 0, fontList_1 = fontList; _i < fontList_1.length; _i++) { | ||
677 | + var font = fontList_1[_i]; | ||
678 | + _loop_1(font); | ||
679 | + } | ||
680 | + return spans; | ||
681 | + }; | ||
682 | + // checks if a font is available | ||
683 | + var isFontAvailable = function (fontSpans) { | ||
684 | + return baseFonts.some(function (baseFont, baseFontIndex) { | ||
685 | + return fontSpans[baseFontIndex].offsetWidth !== defaultWidth[baseFont] || | ||
686 | + fontSpans[baseFontIndex].offsetHeight !== defaultHeight[baseFont]; | ||
687 | + }); | ||
688 | + }; | ||
689 | + // create spans for base fonts | ||
690 | + var baseFontsSpans = initializeBaseFontsSpans(); | ||
691 | + // add the spans to the DOM | ||
692 | + h.appendChild(baseFontsDiv); | ||
693 | + // get the default width for the three base fonts | ||
694 | + for (var index = 0, length_1 = baseFonts.length; index < length_1; index++) { | ||
695 | + defaultWidth[baseFonts[index]] = baseFontsSpans[index].offsetWidth; // width for the default font | ||
696 | + defaultHeight[baseFonts[index]] = baseFontsSpans[index].offsetHeight; // height for the default font | ||
697 | + } | ||
698 | + // create spans for fonts to detect | ||
699 | + var fontsSpans = initializeFontsSpans(); | ||
700 | + // add all the spans to the DOM | ||
701 | + h.appendChild(fontsDiv); | ||
702 | + // check available fonts | ||
703 | + var available = []; | ||
704 | + for (var i = 0, l = fontList.length; i < l; i++) { | ||
705 | + if (isFontAvailable(fontsSpans[fontList[i]])) { | ||
706 | + available.push(fontList[i]); | ||
707 | + } | ||
708 | + } | ||
709 | + // remove spans from DOM | ||
710 | + h.removeChild(fontsDiv); | ||
711 | + h.removeChild(baseFontsDiv); | ||
712 | + return available; | ||
713 | +} | ||
714 | + | ||
715 | +function getPlugins() { | ||
716 | + if (isTrident()) { | ||
717 | + return []; | ||
718 | + } | ||
719 | + if (!navigator.plugins) { | ||
720 | + return undefined; | ||
721 | + } | ||
722 | + var plugins = []; | ||
723 | + // Safari 10 doesn't support iterating navigator.plugins with for...of | ||
724 | + for (var i = 0; i < navigator.plugins.length; ++i) { | ||
725 | + var plugin = navigator.plugins[i]; | ||
726 | + if (!plugin) { | ||
727 | + continue; | ||
728 | + } | ||
729 | + var mimeTypes = []; | ||
730 | + for (var j = 0; j < plugin.length; ++j) { | ||
731 | + var mimeType = plugin[j]; | ||
732 | + mimeTypes.push({ | ||
733 | + type: mimeType.type, | ||
734 | + suffixes: mimeType.suffixes, | ||
735 | + }); | ||
736 | + } | ||
737 | + plugins.push({ | ||
738 | + name: plugin.name, | ||
739 | + description: plugin.description, | ||
740 | + mimeTypes: mimeTypes, | ||
741 | + }); | ||
742 | + } | ||
743 | + return plugins; | ||
744 | +} | ||
745 | + | ||
746 | +function makeCanvasContext() { | ||
747 | + var canvas = document.createElement('canvas'); | ||
748 | + canvas.width = 240; | ||
749 | + canvas.height = 140; | ||
750 | + canvas.style.display = 'inline'; | ||
751 | + return [canvas, canvas.getContext('2d')]; | ||
752 | +} | ||
753 | +function isSupported(canvas, context) { | ||
754 | + // TODO: look into: https://developer.mozilla.org/en-US/docs/Web/API/HTMLCanvasElement/toBlob | ||
755 | + return !!(context && canvas.toDataURL); | ||
756 | +} | ||
757 | +function save(canvas) { | ||
758 | + // TODO: look into: https://developer.mozilla.org/en-US/docs/Web/API/HTMLCanvasElement/toBlob | ||
759 | + return canvas.toDataURL(); | ||
760 | +} | ||
761 | +// https://www.browserleaks.com/canvas#how-does-it-work | ||
762 | +function getCanvasFingerprint() { | ||
763 | + var _a = makeCanvasContext(), canvas = _a[0], context = _a[1]; | ||
764 | + if (!isSupported(canvas, context)) { | ||
765 | + return { winding: false, data: '' }; | ||
766 | + } | ||
767 | + // detect browser support of canvas winding | ||
768 | + // http://blogs.adobe.com/webplatform/2013/01/30/winding-rules-in-canvas/ | ||
769 | + // https://github.com/Modernizr/Modernizr/blob/master/feature-detects/canvas/winding.js | ||
770 | + context.rect(0, 0, 10, 10); | ||
771 | + context.rect(2, 2, 6, 6); | ||
772 | + var winding = !context.isPointInPath(5, 5, 'evenodd'); | ||
773 | + context.textBaseline = 'alphabetic'; | ||
774 | + context.fillStyle = '#f60'; | ||
775 | + context.fillRect(125, 1, 62, 20); | ||
776 | + context.fillStyle = '#069'; | ||
777 | + // https://github.com/Valve/fingerprintjs2/issues/66 | ||
778 | + // this can affect FP generation when applying different CSS on different websites | ||
779 | + context.font = '11pt no-real-font-123'; | ||
780 | + // the choice of emojis has a gigantic impact on rendering performance (especially in FF) | ||
781 | + // some newer emojis cause it to slow down 50-200 times | ||
782 | + // context.fillText("Cw爨m fjordbank \ud83d\ude03 gly", 2, 15) | ||
783 | + var printedText = 'Cwm fjordbank \ud83d\ude03 gly'; | ||
784 | + context.fillText(printedText, 2, 15); | ||
785 | + context.fillStyle = 'rgba(102, 204, 0, 0.2)'; | ||
786 | + context.font = '18pt Arial'; | ||
787 | + context.fillText(printedText, 4, 45); | ||
788 | + // canvas blending | ||
789 | + // http://blogs.adobe.com/webplatform/2013/01/28/blending-features-in-canvas/ | ||
790 | + // http://jsfiddle.net/NDYV8/16/ | ||
791 | + context.globalCompositeOperation = 'multiply'; | ||
792 | + context.fillStyle = 'rgb(255,0,255)'; | ||
793 | + context.beginPath(); | ||
794 | + context.arc(50, 50, 50, 0, Math.PI * 2, true); | ||
795 | + context.closePath(); | ||
796 | + context.fill(); | ||
797 | + context.fillStyle = 'rgb(0,255,255)'; | ||
798 | + context.beginPath(); | ||
799 | + context.arc(100, 50, 50, 0, Math.PI * 2, true); | ||
800 | + context.closePath(); | ||
801 | + context.fill(); | ||
802 | + context.fillStyle = 'rgb(255,255,0)'; | ||
803 | + context.beginPath(); | ||
804 | + context.arc(75, 100, 50, 0, Math.PI * 2, true); | ||
805 | + context.closePath(); | ||
806 | + context.fill(); | ||
807 | + context.fillStyle = 'rgb(255,0,255)'; | ||
808 | + // canvas winding | ||
809 | + // http://blogs.adobe.com/webplatform/2013/01/30/winding-rules-in-canvas/ | ||
810 | + // http://jsfiddle.net/NDYV8/19/ | ||
811 | + context.arc(75, 75, 75, 0, Math.PI * 2, true); | ||
812 | + context.arc(75, 75, 25, 0, Math.PI * 2, true); | ||
813 | + context.fill('evenodd'); | ||
814 | + return { | ||
815 | + winding: winding, | ||
816 | + data: save(canvas), | ||
817 | + }; | ||
818 | +} | ||
819 | + | ||
820 | +var n$1 = navigator; | ||
821 | +var w$2 = window; | ||
822 | +/** | ||
823 | + * This is a crude and primitive touch screen detection. It's not possible to currently reliably detect the availability | ||
824 | + * of a touch screen with a JS, without actually subscribing to a touch event. | ||
825 | + * | ||
826 | + * @see http://www.stucox.com/blog/you-cant-detect-a-touchscreen/ | ||
827 | + * @see https://github.com/Modernizr/Modernizr/issues/548 | ||
828 | + */ | ||
829 | +function getTouchSupport() { | ||
830 | + var maxTouchPoints = 0; | ||
831 | + var touchEvent; | ||
832 | + if (n$1.maxTouchPoints !== undefined) { | ||
833 | + maxTouchPoints = toInt(n$1.maxTouchPoints); | ||
834 | + } | ||
835 | + else if (n$1.msMaxTouchPoints !== undefined) { | ||
836 | + maxTouchPoints = n$1.msMaxTouchPoints; | ||
837 | + } | ||
838 | + try { | ||
839 | + document.createEvent('TouchEvent'); | ||
840 | + touchEvent = true; | ||
841 | + } | ||
842 | + catch (_) { | ||
843 | + touchEvent = false; | ||
844 | + } | ||
845 | + var touchStart = 'ontouchstart' in w$2; | ||
846 | + return { | ||
847 | + maxTouchPoints: maxTouchPoints, | ||
848 | + touchEvent: touchEvent, | ||
849 | + touchStart: touchStart, | ||
850 | + }; | ||
851 | +} | ||
852 | + | ||
853 | +function getOsCpu() { | ||
854 | + return navigator.oscpu; | ||
855 | +} | ||
856 | + | ||
857 | +var n$2 = navigator; | ||
858 | +function getLanguages() { | ||
859 | + var result = []; | ||
860 | + var language = n$2.language || n$2.userLanguage || n$2.browserLanguage || n$2.systemLanguage; | ||
861 | + if (language !== undefined) { | ||
862 | + result.push([language]); | ||
863 | + } | ||
864 | + if (Array.isArray(n$2.languages)) { | ||
865 | + // Starting from Chromium 86, there is only a single value in `navigator.language` in Incognito mode: | ||
866 | + // the value of `navigator.language`. Therefore the value is ignored in this browser. | ||
867 | + if (!(isChromium() && isChromium86OrNewer())) { | ||
868 | + result.push(n$2.languages); | ||
869 | + } | ||
870 | + } | ||
871 | + else if (typeof n$2.languages === 'string') { | ||
872 | + var languages = n$2.languages; | ||
873 | + if (languages) { | ||
874 | + result.push(languages.split(',')); | ||
875 | + } | ||
876 | + } | ||
877 | + return result; | ||
878 | +} | ||
879 | + | ||
880 | +function getColorDepth() { | ||
881 | + return window.screen.colorDepth; | ||
882 | +} | ||
883 | + | ||
884 | +function getDeviceMemory() { | ||
885 | + return navigator.deviceMemory; | ||
886 | +} | ||
887 | + | ||
888 | +var w$3 = window; | ||
889 | +function getScreenResolution() { | ||
890 | + // Some browsers return screen resolution as strings, e.g. "1200", instead of a number, e.g. 1200. | ||
891 | + // I suspect it's done by certain plugins that randomize browser properties to prevent fingerprinting. | ||
892 | + var dimensions = [toInt(w$3.screen.width), toInt(w$3.screen.height)]; | ||
893 | + dimensions.sort().reverse(); | ||
894 | + return dimensions; | ||
895 | +} | ||
896 | + | ||
897 | +var w$4 = window; | ||
898 | +function getAvailableScreenResolution() { | ||
899 | + if (w$4.screen.availWidth && w$4.screen.availHeight) { | ||
900 | + // Some browsers return screen resolution as strings, e.g. "1200", instead of a number, e.g. 1200. | ||
901 | + // I suspect it's done by certain plugins that randomize browser properties to prevent fingerprinting. | ||
902 | + var dimensions = [toInt(w$4.screen.availWidth), toInt(w$4.screen.availHeight)]; | ||
903 | + dimensions.sort().reverse(); | ||
904 | + return dimensions; | ||
905 | + } | ||
906 | + return undefined; | ||
907 | +} | ||
908 | + | ||
909 | +function getHardwareConcurrency() { | ||
910 | + try { | ||
911 | + // sometimes hardware concurrency is a string | ||
912 | + var concurrency = toInt(navigator.hardwareConcurrency); | ||
913 | + return isNaN(concurrency) ? 1 : concurrency; | ||
914 | + } | ||
915 | + catch (e) { | ||
916 | + return 1; | ||
917 | + } | ||
918 | +} | ||
919 | + | ||
920 | +function getTimezoneOffset() { | ||
921 | + var currentYear = new Date().getFullYear(); | ||
922 | + // The timezone offset may change over time due to daylight saving time (DST) shifts. | ||
923 | + // The non-DST timezone offset is used as the result timezone offset. | ||
924 | + // Since the DST season differs in the northern and the southern hemispheres, | ||
925 | + // both January and July timezones offsets are considered. | ||
926 | + return Math.max( | ||
927 | + // `getTimezoneOffset` returns a number as a string in some unidentified cases | ||
928 | + toFloat(new Date(currentYear, 0, 1).getTimezoneOffset()), toFloat(new Date(currentYear, 6, 1).getTimezoneOffset())); | ||
929 | +} | ||
930 | + | ||
931 | +var w$5 = window; | ||
932 | +function getTimezone() { | ||
933 | + var _a; | ||
934 | + if ((_a = w$5.Intl) === null || _a === void 0 ? void 0 : _a.DateTimeFormat) { | ||
935 | + return new w$5.Intl.DateTimeFormat().resolvedOptions().timeZone; | ||
936 | + } | ||
937 | + return undefined; | ||
938 | +} | ||
939 | + | ||
940 | +function getSessionStorage() { | ||
941 | + try { | ||
942 | + return !!window.sessionStorage; | ||
943 | + } | ||
944 | + catch (error) { | ||
945 | + /* SecurityError when referencing it means it exists */ | ||
946 | + return true; | ||
947 | + } | ||
948 | +} | ||
949 | + | ||
950 | +// https://bugzilla.mozilla.org/show_bug.cgi?id=781447 | ||
951 | +function getLocalStorage() { | ||
952 | + try { | ||
953 | + return !!window.localStorage; | ||
954 | + } | ||
955 | + catch (e) { | ||
956 | + /* SecurityError when referencing it means it exists */ | ||
957 | + return true; | ||
958 | + } | ||
959 | +} | ||
960 | + | ||
961 | +function getIndexedDB() { | ||
962 | + // IE and Edge don't allow accessing indexedDB in private mode, therefore IE and Edge will have different | ||
963 | + // visitor identifier in normal and private modes. | ||
964 | + if (isTrident() || isEdgeHTML()) { | ||
965 | + return undefined; | ||
966 | + } | ||
967 | + try { | ||
968 | + return !!window.indexedDB; | ||
969 | + } | ||
970 | + catch (e) { | ||
971 | + /* SecurityError when referencing it means it exists */ | ||
972 | + return true; | ||
973 | + } | ||
974 | +} | ||
975 | + | ||
976 | +function getOpenDatabase() { | ||
977 | + return !!window.openDatabase; | ||
978 | +} | ||
979 | + | ||
980 | +function getCpuClass() { | ||
981 | + return navigator.cpuClass; | ||
982 | +} | ||
983 | + | ||
984 | +function getPlatform() { | ||
985 | + return navigator.platform; | ||
986 | +} | ||
987 | + | ||
988 | +function getPluginsSupport() { | ||
989 | + return navigator.plugins !== undefined; | ||
990 | +} | ||
991 | + | ||
992 | +function getProductSub() { | ||
993 | + return navigator.productSub; | ||
994 | +} | ||
995 | + | ||
996 | +function getEmptyEvalLength() { | ||
997 | + return eval.toString().length; | ||
998 | +} | ||
999 | + | ||
1000 | +function getErrorFF() { | ||
1001 | + try { | ||
1002 | + throw 'a'; | ||
1003 | + } | ||
1004 | + catch (e) { | ||
1005 | + try { | ||
1006 | + e.toSource(); | ||
1007 | + return true; | ||
1008 | + } | ||
1009 | + catch (e2) { | ||
1010 | + return false; | ||
1011 | + } | ||
1012 | + } | ||
1013 | +} | ||
1014 | + | ||
1015 | +function getVendor() { | ||
1016 | + return navigator.vendor; | ||
1017 | +} | ||
1018 | + | ||
1019 | +function getChrome() { | ||
1020 | + return window.chrome !== undefined; | ||
1021 | +} | ||
1022 | + | ||
1023 | +var d$3 = document; | ||
1024 | +/** | ||
1025 | + * navigator.cookieEnabled cannot detect custom or nuanced cookie blocking configurations. For example, when blocking | ||
1026 | + * cookies via the Advanced Privacy Settings in IE9, it always returns true. And there have been issues in the past with | ||
1027 | + * site-specific exceptions. Don't rely on it. | ||
1028 | + * | ||
1029 | + * @see https://github.com/Modernizr/Modernizr/blob/master/feature-detects/cookies.js Taken from here | ||
1030 | + */ | ||
1031 | +function areCookiesEnabled() { | ||
1032 | + // Taken from here: https://github.com/Modernizr/Modernizr/blob/master/feature-detects/cookies.js | ||
1033 | + // navigator.cookieEnabled cannot detect custom or nuanced cookie blocking configurations. For example, when blocking | ||
1034 | + // cookies via the Advanced Privacy Settings in IE9, it always returns true. And there have been issues in the past | ||
1035 | + // with site-specific exceptions. Don't rely on it. | ||
1036 | + // try..catch because some in situations `document.cookie` is exposed but throws a | ||
1037 | + // SecurityError if you try to access it; e.g. documents created from data URIs | ||
1038 | + // or in sandboxed iframes (depending on flags/context) | ||
1039 | + try { | ||
1040 | + // Create cookie | ||
1041 | + d$3.cookie = 'cookietest=1'; | ||
1042 | + var result = d$3.cookie.indexOf('cookietest=') !== -1; | ||
1043 | + // Delete cookie | ||
1044 | + d$3.cookie = 'cookietest=1; expires=Thu, 01-Jan-1970 00:00:01 GMT'; | ||
1045 | + return result; | ||
1046 | + } | ||
1047 | + catch (e) { | ||
1048 | + return false; | ||
1049 | + } | ||
1050 | +} | ||
1051 | + | ||
1052 | +/** | ||
1053 | + * The list of entropy sources used to make visitor identifiers. | ||
1054 | + * | ||
1055 | + * This value isn't restricted by Semantic Versioning, i.e. it may be changed without bumping minor or major version of | ||
1056 | + * this package. | ||
1057 | + */ | ||
1058 | +var sources = { | ||
1059 | + // Expected errors and default values must be handled inside the functions. Unexpected errors must be thrown. | ||
1060 | + osCpu: getOsCpu, | ||
1061 | + languages: getLanguages, | ||
1062 | + colorDepth: getColorDepth, | ||
1063 | + deviceMemory: getDeviceMemory, | ||
1064 | + screenResolution: getScreenResolution, | ||
1065 | + availableScreenResolution: getAvailableScreenResolution, | ||
1066 | + hardwareConcurrency: getHardwareConcurrency, | ||
1067 | + timezoneOffset: getTimezoneOffset, | ||
1068 | + timezone: getTimezone, | ||
1069 | + sessionStorage: getSessionStorage, | ||
1070 | + localStorage: getLocalStorage, | ||
1071 | + indexedDB: getIndexedDB, | ||
1072 | + openDatabase: getOpenDatabase, | ||
1073 | + cpuClass: getCpuClass, | ||
1074 | + // Maybe it should be excluded: https://github.com/fingerprintjs/fingerprintjs/issues/514#issuecomment-688754892 | ||
1075 | + platform: getPlatform, | ||
1076 | + plugins: getPlugins, | ||
1077 | + canvas: getCanvasFingerprint, | ||
1078 | + // adBlock: isAdblockUsed, // https://github.com/fingerprintjs/fingerprintjs/issues/405 | ||
1079 | + touchSupport: getTouchSupport, | ||
1080 | + fonts: getFonts, | ||
1081 | + audio: getAudioFingerprint, | ||
1082 | + pluginsSupport: getPluginsSupport, | ||
1083 | + productSub: getProductSub, | ||
1084 | + emptyEvalLength: getEmptyEvalLength, | ||
1085 | + errorFF: getErrorFF, | ||
1086 | + vendor: getVendor, | ||
1087 | + chrome: getChrome, | ||
1088 | + cookiesEnabled: areCookiesEnabled, | ||
1089 | +}; | ||
1090 | +/** | ||
1091 | + * Gets a components list from the given list of entropy sources. | ||
1092 | + * | ||
1093 | + * Warning for package users: | ||
1094 | + * This function is out of Semantic Versioning, i.e. can change unexpectedly. Usage is at your own risk. | ||
1095 | + */ | ||
1096 | +function getComponents(sources, sourceOptions, excludeSources) { | ||
1097 | + return __awaiter(this, void 0, void 0, function () { | ||
1098 | + var timestamp, components, _i, _a, sourceKey, result, error_1, nextTimestamp; | ||
1099 | + var _b; | ||
1100 | + return __generator(this, function (_c) { | ||
1101 | + switch (_c.label) { | ||
1102 | + case 0: | ||
1103 | + timestamp = Date.now(); | ||
1104 | + components = {}; | ||
1105 | + _i = 0, _a = Object.keys(sources); | ||
1106 | + _c.label = 1; | ||
1107 | + case 1: | ||
1108 | + if (!(_i < _a.length)) return [3 /*break*/, 7]; | ||
1109 | + sourceKey = _a[_i]; | ||
1110 | + if (!excludes(excludeSources, sourceKey)) { | ||
1111 | + return [3 /*break*/, 6]; | ||
1112 | + } | ||
1113 | + result = void 0; | ||
1114 | + _c.label = 2; | ||
1115 | + case 2: | ||
1116 | + _c.trys.push([2, 4, , 5]); | ||
1117 | + _b = {}; | ||
1118 | + return [4 /*yield*/, sources[sourceKey](sourceOptions)]; | ||
1119 | + case 3: | ||
1120 | + result = (_b.value = _c.sent(), _b); | ||
1121 | + return [3 /*break*/, 5]; | ||
1122 | + case 4: | ||
1123 | + error_1 = _c.sent(); | ||
1124 | + result = error_1 && typeof error_1 === 'object' && 'message' in error_1 ? { error: error_1 } : { error: { message: error_1 } }; | ||
1125 | + return [3 /*break*/, 5]; | ||
1126 | + case 5: | ||
1127 | + nextTimestamp = Date.now(); | ||
1128 | + components[sourceKey] = __assign(__assign({}, result), { duration: nextTimestamp - timestamp }); // TypeScript has beaten me here | ||
1129 | + timestamp = nextTimestamp; | ||
1130 | + _c.label = 6; | ||
1131 | + case 6: | ||
1132 | + _i++; | ||
1133 | + return [3 /*break*/, 1]; | ||
1134 | + case 7: return [2 /*return*/, components]; | ||
1135 | + } | ||
1136 | + }); | ||
1137 | + }); | ||
1138 | +} | ||
1139 | +/** | ||
1140 | + * Collects entropy components from the built-in sources to make the visitor identifier. | ||
1141 | + */ | ||
1142 | +function getBuiltinComponents() { | ||
1143 | + return getComponents(sources, undefined, []); | ||
1144 | +} | ||
1145 | + | ||
1146 | +function componentsToCanonicalString(components) { | ||
1147 | + var result = ''; | ||
1148 | + for (var _i = 0, _a = Object.keys(components); _i < _a.length; _i++) { | ||
1149 | + var componentKey = _a[_i]; | ||
1150 | + var component = components[componentKey]; | ||
1151 | + var value = component.error ? 'error' : JSON.stringify(component.value); | ||
1152 | + result += "" + (result ? '|' : '') + componentKey.replace(/([:|\\])/g, '\\$1') + ":" + value; | ||
1153 | + } | ||
1154 | + return result; | ||
1155 | +} | ||
1156 | +function componentsToDebugString(components) { | ||
1157 | + return JSON.stringify(components, function (_key, value) { | ||
1158 | + var _a; | ||
1159 | + if (value instanceof Error) { | ||
1160 | + return __assign(__assign({}, value), { message: value.message, stack: (_a = value.stack) === null || _a === void 0 ? void 0 : _a.split('\n') }); | ||
1161 | + } | ||
1162 | + return value; | ||
1163 | + }, 2); | ||
1164 | +} | ||
1165 | +function hashComponents(components) { | ||
1166 | + return x64hash128(componentsToCanonicalString(components)); | ||
1167 | +} | ||
1168 | +/** | ||
1169 | + * Makes a GetResult implementation that calculates the visitor id hash on demand. | ||
1170 | + * Designed for optimisation. | ||
1171 | + */ | ||
1172 | +function makeLazyGetResult(components) { | ||
1173 | + var visitorIdCache; | ||
1174 | + // A plain class isn't used because its getters and setters aren't enumerable. | ||
1175 | + return { | ||
1176 | + components: components, | ||
1177 | + get visitorId() { | ||
1178 | + if (visitorIdCache === undefined) { | ||
1179 | + visitorIdCache = hashComponents(this.components); | ||
1180 | + } | ||
1181 | + return visitorIdCache; | ||
1182 | + }, | ||
1183 | + set visitorId(visitorId) { | ||
1184 | + visitorIdCache = visitorId; | ||
1185 | + }, | ||
1186 | + }; | ||
1187 | +} | ||
1188 | +/** | ||
1189 | + * The class isn't exported from the index file to not expose the constructor. | ||
1190 | + * The hiding gives more freedom for future non-breaking updates. | ||
1191 | + */ | ||
1192 | +var OpenAgent = /** @class */ (function () { | ||
1193 | + function OpenAgent() { | ||
1194 | + } | ||
1195 | + /** | ||
1196 | + * @inheritDoc | ||
1197 | + */ | ||
1198 | + OpenAgent.prototype.get = function (options) { | ||
1199 | + if (options === void 0) { options = {}; } | ||
1200 | + return __awaiter(this, void 0, void 0, function () { | ||
1201 | + var components, result; | ||
1202 | + return __generator(this, function (_a) { | ||
1203 | + switch (_a.label) { | ||
1204 | + case 0: return [4 /*yield*/, getBuiltinComponents()]; | ||
1205 | + case 1: | ||
1206 | + components = _a.sent(); | ||
1207 | + result = makeLazyGetResult(components); | ||
1208 | + if (options.debug) { | ||
1209 | + // console.log is ok here because it's under a debug clause | ||
1210 | + // eslint-disable-next-line no-console | ||
1211 | + console.log("Copy the text below to get the debug data:\n\n```\nversion: " + version + "\nuserAgent: " + navigator.userAgent + "\ngetOptions: " + JSON.stringify(options, undefined, 2) + "\nvisitorId: " + result.visitorId + "\ncomponents: " + componentsToDebugString(components) + "\n```"); | ||
1212 | + } | ||
1213 | + return [2 /*return*/, result]; | ||
1214 | + } | ||
1215 | + }); | ||
1216 | + }); | ||
1217 | + }; | ||
1218 | + return OpenAgent; | ||
1219 | +}()); | ||
1220 | +/** | ||
1221 | + * Builds an instance of Agent and waits a delay required for a proper operation. | ||
1222 | + */ | ||
1223 | +function load(_a) { | ||
1224 | + var _b = (_a === void 0 ? {} : _a).delayFallback, delayFallback = _b === void 0 ? 50 : _b; | ||
1225 | + return __awaiter(this, void 0, void 0, function () { | ||
1226 | + return __generator(this, function (_c) { | ||
1227 | + switch (_c.label) { | ||
1228 | + case 0: | ||
1229 | + // A delay is required to ensure consistent entropy components. | ||
1230 | + // See https://github.com/fingerprintjs/fingerprintjs/issues/254 | ||
1231 | + // and https://github.com/fingerprintjs/fingerprintjs/issues/307 | ||
1232 | + return [4 /*yield*/, requestIdleCallbackIfAvailable(delayFallback)]; | ||
1233 | + case 1: | ||
1234 | + // A delay is required to ensure consistent entropy components. | ||
1235 | + // See https://github.com/fingerprintjs/fingerprintjs/issues/254 | ||
1236 | + // and https://github.com/fingerprintjs/fingerprintjs/issues/307 | ||
1237 | + _c.sent(); | ||
1238 | + return [2 /*return*/, new OpenAgent()]; | ||
1239 | + } | ||
1240 | + }); | ||
1241 | + }); | ||
1242 | +} | ||
1243 | + | ||
1244 | +// The default export is a syntax sugar (`import * as FP from '...' → import FP from '...'`). | ||
1245 | +// It should contain all the public exported values. | ||
1246 | +var index = { load: load, hashComponents: hashComponents, componentsToDebugString: componentsToDebugString }; | ||
1247 | +// The exports below are for private usage. They may change unexpectedly. Use them at your own risk. | ||
1248 | +/** Not documented, out of Semantic Versioning, usage is at your own risk */ | ||
1249 | +var murmurX64Hash128 = x64hash128; | ||
1250 | + | ||
1251 | +export default index; | ||
1252 | +export { componentsToDebugString, getComponents, hashComponents, isChromium, isDesktopSafari, isEdgeHTML, isGecko, isTrident, isWebKit, load, murmurX64Hash128 }; |
dist/fp.js
0 → 100644
1 | +/** | ||
2 | + * FingerprintJS v3.0.3 - Copyright (c) FingerprintJS, Inc, 2020 (https://fingerprintjs.com) | ||
3 | + * Licensed under the MIT (http://www.opensource.org/licenses/mit-license.php) license. | ||
4 | + * | ||
5 | + * This software contains code from open-source projects: | ||
6 | + * MurmurHash3 by Karan Lyons (https://github.com/karanlyons/murmurHash3.js) | ||
7 | + */ | ||
8 | + | ||
9 | +var FingerprintJS = (function (exports) { | ||
10 | + 'use strict'; | ||
11 | + | ||
12 | + /* | ||
13 | + * Taken from https://github.com/karanlyons/murmurHash3.js/blob/a33d0723127e2e5415056c455f8aed2451ace208/murmurHash3.js | ||
14 | + */ | ||
15 | + // | ||
16 | + // Given two 64bit ints (as an array of two 32bit ints) returns the two | ||
17 | + // added together as a 64bit int (as an array of two 32bit ints). | ||
18 | + // | ||
19 | + function x64Add(m, n) { | ||
20 | + m = [m[0] >>> 16, m[0] & 0xffff, m[1] >>> 16, m[1] & 0xffff]; | ||
21 | + n = [n[0] >>> 16, n[0] & 0xffff, n[1] >>> 16, n[1] & 0xffff]; | ||
22 | + var o = [0, 0, 0, 0]; | ||
23 | + o[3] += m[3] + n[3]; | ||
24 | + o[2] += o[3] >>> 16; | ||
25 | + o[3] &= 0xffff; | ||
26 | + o[2] += m[2] + n[2]; | ||
27 | + o[1] += o[2] >>> 16; | ||
28 | + o[2] &= 0xffff; | ||
29 | + o[1] += m[1] + n[1]; | ||
30 | + o[0] += o[1] >>> 16; | ||
31 | + o[1] &= 0xffff; | ||
32 | + o[0] += m[0] + n[0]; | ||
33 | + o[0] &= 0xffff; | ||
34 | + return [(o[0] << 16) | o[1], (o[2] << 16) | o[3]]; | ||
35 | + } | ||
36 | + // | ||
37 | + // Given two 64bit ints (as an array of two 32bit ints) returns the two | ||
38 | + // multiplied together as a 64bit int (as an array of two 32bit ints). | ||
39 | + // | ||
40 | + function x64Multiply(m, n) { | ||
41 | + m = [m[0] >>> 16, m[0] & 0xffff, m[1] >>> 16, m[1] & 0xffff]; | ||
42 | + n = [n[0] >>> 16, n[0] & 0xffff, n[1] >>> 16, n[1] & 0xffff]; | ||
43 | + var o = [0, 0, 0, 0]; | ||
44 | + o[3] += m[3] * n[3]; | ||
45 | + o[2] += o[3] >>> 16; | ||
46 | + o[3] &= 0xffff; | ||
47 | + o[2] += m[2] * n[3]; | ||
48 | + o[1] += o[2] >>> 16; | ||
49 | + o[2] &= 0xffff; | ||
50 | + o[2] += m[3] * n[2]; | ||
51 | + o[1] += o[2] >>> 16; | ||
52 | + o[2] &= 0xffff; | ||
53 | + o[1] += m[1] * n[3]; | ||
54 | + o[0] += o[1] >>> 16; | ||
55 | + o[1] &= 0xffff; | ||
56 | + o[1] += m[2] * n[2]; | ||
57 | + o[0] += o[1] >>> 16; | ||
58 | + o[1] &= 0xffff; | ||
59 | + o[1] += m[3] * n[1]; | ||
60 | + o[0] += o[1] >>> 16; | ||
61 | + o[1] &= 0xffff; | ||
62 | + o[0] += m[0] * n[3] + m[1] * n[2] + m[2] * n[1] + m[3] * n[0]; | ||
63 | + o[0] &= 0xffff; | ||
64 | + return [(o[0] << 16) | o[1], (o[2] << 16) | o[3]]; | ||
65 | + } | ||
66 | + // | ||
67 | + // Given a 64bit int (as an array of two 32bit ints) and an int | ||
68 | + // representing a number of bit positions, returns the 64bit int (as an | ||
69 | + // array of two 32bit ints) rotated left by that number of positions. | ||
70 | + // | ||
71 | + function x64Rotl(m, n) { | ||
72 | + n %= 64; | ||
73 | + if (n === 32) { | ||
74 | + return [m[1], m[0]]; | ||
75 | + } | ||
76 | + else if (n < 32) { | ||
77 | + return [(m[0] << n) | (m[1] >>> (32 - n)), (m[1] << n) | (m[0] >>> (32 - n))]; | ||
78 | + } | ||
79 | + else { | ||
80 | + n -= 32; | ||
81 | + return [(m[1] << n) | (m[0] >>> (32 - n)), (m[0] << n) | (m[1] >>> (32 - n))]; | ||
82 | + } | ||
83 | + } | ||
84 | + // | ||
85 | + // Given a 64bit int (as an array of two 32bit ints) and an int | ||
86 | + // representing a number of bit positions, returns the 64bit int (as an | ||
87 | + // array of two 32bit ints) shifted left by that number of positions. | ||
88 | + // | ||
89 | + function x64LeftShift(m, n) { | ||
90 | + n %= 64; | ||
91 | + if (n === 0) { | ||
92 | + return m; | ||
93 | + } | ||
94 | + else if (n < 32) { | ||
95 | + return [(m[0] << n) | (m[1] >>> (32 - n)), m[1] << n]; | ||
96 | + } | ||
97 | + else { | ||
98 | + return [m[1] << (n - 32), 0]; | ||
99 | + } | ||
100 | + } | ||
101 | + // | ||
102 | + // Given two 64bit ints (as an array of two 32bit ints) returns the two | ||
103 | + // xored together as a 64bit int (as an array of two 32bit ints). | ||
104 | + // | ||
105 | + function x64Xor(m, n) { | ||
106 | + return [m[0] ^ n[0], m[1] ^ n[1]]; | ||
107 | + } | ||
108 | + // | ||
109 | + // Given a block, returns murmurHash3's final x64 mix of that block. | ||
110 | + // (`[0, h[0] >>> 1]` is a 33 bit unsigned right shift. This is the | ||
111 | + // only place where we need to right shift 64bit ints.) | ||
112 | + // | ||
113 | + function x64Fmix(h) { | ||
114 | + h = x64Xor(h, [0, h[0] >>> 1]); | ||
115 | + h = x64Multiply(h, [0xff51afd7, 0xed558ccd]); | ||
116 | + h = x64Xor(h, [0, h[0] >>> 1]); | ||
117 | + h = x64Multiply(h, [0xc4ceb9fe, 0x1a85ec53]); | ||
118 | + h = x64Xor(h, [0, h[0] >>> 1]); | ||
119 | + return h; | ||
120 | + } | ||
121 | + // | ||
122 | + // Given a string and an optional seed as an int, returns a 128 bit | ||
123 | + // hash using the x64 flavor of MurmurHash3, as an unsigned hex. | ||
124 | + // | ||
125 | + function x64hash128(key, seed) { | ||
126 | + key = key || ''; | ||
127 | + seed = seed || 0; | ||
128 | + var remainder = key.length % 16; | ||
129 | + var bytes = key.length - remainder; | ||
130 | + var h1 = [0, seed]; | ||
131 | + var h2 = [0, seed]; | ||
132 | + var k1 = [0, 0]; | ||
133 | + var k2 = [0, 0]; | ||
134 | + var c1 = [0x87c37b91, 0x114253d5]; | ||
135 | + var c2 = [0x4cf5ad43, 0x2745937f]; | ||
136 | + var i; | ||
137 | + for (i = 0; i < bytes; i = i + 16) { | ||
138 | + k1 = [ | ||
139 | + (key.charCodeAt(i + 4) & 0xff) | | ||
140 | + ((key.charCodeAt(i + 5) & 0xff) << 8) | | ||
141 | + ((key.charCodeAt(i + 6) & 0xff) << 16) | | ||
142 | + ((key.charCodeAt(i + 7) & 0xff) << 24), | ||
143 | + (key.charCodeAt(i) & 0xff) | | ||
144 | + ((key.charCodeAt(i + 1) & 0xff) << 8) | | ||
145 | + ((key.charCodeAt(i + 2) & 0xff) << 16) | | ||
146 | + ((key.charCodeAt(i + 3) & 0xff) << 24), | ||
147 | + ]; | ||
148 | + k2 = [ | ||
149 | + (key.charCodeAt(i + 12) & 0xff) | | ||
150 | + ((key.charCodeAt(i + 13) & 0xff) << 8) | | ||
151 | + ((key.charCodeAt(i + 14) & 0xff) << 16) | | ||
152 | + ((key.charCodeAt(i + 15) & 0xff) << 24), | ||
153 | + (key.charCodeAt(i + 8) & 0xff) | | ||
154 | + ((key.charCodeAt(i + 9) & 0xff) << 8) | | ||
155 | + ((key.charCodeAt(i + 10) & 0xff) << 16) | | ||
156 | + ((key.charCodeAt(i + 11) & 0xff) << 24), | ||
157 | + ]; | ||
158 | + k1 = x64Multiply(k1, c1); | ||
159 | + k1 = x64Rotl(k1, 31); | ||
160 | + k1 = x64Multiply(k1, c2); | ||
161 | + h1 = x64Xor(h1, k1); | ||
162 | + h1 = x64Rotl(h1, 27); | ||
163 | + h1 = x64Add(h1, h2); | ||
164 | + h1 = x64Add(x64Multiply(h1, [0, 5]), [0, 0x52dce729]); | ||
165 | + k2 = x64Multiply(k2, c2); | ||
166 | + k2 = x64Rotl(k2, 33); | ||
167 | + k2 = x64Multiply(k2, c1); | ||
168 | + h2 = x64Xor(h2, k2); | ||
169 | + h2 = x64Rotl(h2, 31); | ||
170 | + h2 = x64Add(h2, h1); | ||
171 | + h2 = x64Add(x64Multiply(h2, [0, 5]), [0, 0x38495ab5]); | ||
172 | + } | ||
173 | + k1 = [0, 0]; | ||
174 | + k2 = [0, 0]; | ||
175 | + switch (remainder) { | ||
176 | + case 15: | ||
177 | + k2 = x64Xor(k2, x64LeftShift([0, key.charCodeAt(i + 14)], 48)); | ||
178 | + // fallthrough | ||
179 | + case 14: | ||
180 | + k2 = x64Xor(k2, x64LeftShift([0, key.charCodeAt(i + 13)], 40)); | ||
181 | + // fallthrough | ||
182 | + case 13: | ||
183 | + k2 = x64Xor(k2, x64LeftShift([0, key.charCodeAt(i + 12)], 32)); | ||
184 | + // fallthrough | ||
185 | + case 12: | ||
186 | + k2 = x64Xor(k2, x64LeftShift([0, key.charCodeAt(i + 11)], 24)); | ||
187 | + // fallthrough | ||
188 | + case 11: | ||
189 | + k2 = x64Xor(k2, x64LeftShift([0, key.charCodeAt(i + 10)], 16)); | ||
190 | + // fallthrough | ||
191 | + case 10: | ||
192 | + k2 = x64Xor(k2, x64LeftShift([0, key.charCodeAt(i + 9)], 8)); | ||
193 | + // fallthrough | ||
194 | + case 9: | ||
195 | + k2 = x64Xor(k2, [0, key.charCodeAt(i + 8)]); | ||
196 | + k2 = x64Multiply(k2, c2); | ||
197 | + k2 = x64Rotl(k2, 33); | ||
198 | + k2 = x64Multiply(k2, c1); | ||
199 | + h2 = x64Xor(h2, k2); | ||
200 | + // fallthrough | ||
201 | + case 8: | ||
202 | + k1 = x64Xor(k1, x64LeftShift([0, key.charCodeAt(i + 7)], 56)); | ||
203 | + // fallthrough | ||
204 | + case 7: | ||
205 | + k1 = x64Xor(k1, x64LeftShift([0, key.charCodeAt(i + 6)], 48)); | ||
206 | + // fallthrough | ||
207 | + case 6: | ||
208 | + k1 = x64Xor(k1, x64LeftShift([0, key.charCodeAt(i + 5)], 40)); | ||
209 | + // fallthrough | ||
210 | + case 5: | ||
211 | + k1 = x64Xor(k1, x64LeftShift([0, key.charCodeAt(i + 4)], 32)); | ||
212 | + // fallthrough | ||
213 | + case 4: | ||
214 | + k1 = x64Xor(k1, x64LeftShift([0, key.charCodeAt(i + 3)], 24)); | ||
215 | + // fallthrough | ||
216 | + case 3: | ||
217 | + k1 = x64Xor(k1, x64LeftShift([0, key.charCodeAt(i + 2)], 16)); | ||
218 | + // fallthrough | ||
219 | + case 2: | ||
220 | + k1 = x64Xor(k1, x64LeftShift([0, key.charCodeAt(i + 1)], 8)); | ||
221 | + // fallthrough | ||
222 | + case 1: | ||
223 | + k1 = x64Xor(k1, [0, key.charCodeAt(i)]); | ||
224 | + k1 = x64Multiply(k1, c1); | ||
225 | + k1 = x64Rotl(k1, 31); | ||
226 | + k1 = x64Multiply(k1, c2); | ||
227 | + h1 = x64Xor(h1, k1); | ||
228 | + // fallthrough | ||
229 | + } | ||
230 | + h1 = x64Xor(h1, [0, key.length]); | ||
231 | + h2 = x64Xor(h2, [0, key.length]); | ||
232 | + h1 = x64Add(h1, h2); | ||
233 | + h2 = x64Add(h2, h1); | ||
234 | + h1 = x64Fmix(h1); | ||
235 | + h2 = x64Fmix(h2); | ||
236 | + h1 = x64Add(h1, h2); | ||
237 | + h2 = x64Add(h2, h1); | ||
238 | + return (('00000000' + (h1[0] >>> 0).toString(16)).slice(-8) + | ||
239 | + ('00000000' + (h1[1] >>> 0).toString(16)).slice(-8) + | ||
240 | + ('00000000' + (h2[0] >>> 0).toString(16)).slice(-8) + | ||
241 | + ('00000000' + (h2[1] >>> 0).toString(16)).slice(-8)); | ||
242 | + } | ||
243 | + | ||
244 | + /*! ***************************************************************************** | ||
245 | + Copyright (c) Microsoft Corporation. | ||
246 | + | ||
247 | + Permission to use, copy, modify, and/or distribute this software for any | ||
248 | + purpose with or without fee is hereby granted. | ||
249 | + | ||
250 | + THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH | ||
251 | + REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY | ||
252 | + AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, | ||
253 | + INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM | ||
254 | + LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR | ||
255 | + OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR | ||
256 | + PERFORMANCE OF THIS SOFTWARE. | ||
257 | + ***************************************************************************** */ | ||
258 | + | ||
259 | + var __assign = function() { | ||
260 | + __assign = Object.assign || function __assign(t) { | ||
261 | + for (var s, i = 1, n = arguments.length; i < n; i++) { | ||
262 | + s = arguments[i]; | ||
263 | + for (var p in s) if (Object.prototype.hasOwnProperty.call(s, p)) t[p] = s[p]; | ||
264 | + } | ||
265 | + return t; | ||
266 | + }; | ||
267 | + return __assign.apply(this, arguments); | ||
268 | + }; | ||
269 | + | ||
270 | + function __awaiter(thisArg, _arguments, P, generator) { | ||
271 | + function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); } | ||
272 | + return new (P || (P = Promise))(function (resolve, reject) { | ||
273 | + function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } } | ||
274 | + function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } } | ||
275 | + function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); } | ||
276 | + step((generator = generator.apply(thisArg, _arguments || [])).next()); | ||
277 | + }); | ||
278 | + } | ||
279 | + | ||
280 | + function __generator(thisArg, body) { | ||
281 | + var _ = { label: 0, sent: function() { if (t[0] & 1) throw t[1]; return t[1]; }, trys: [], ops: [] }, f, y, t, g; | ||
282 | + return g = { next: verb(0), "throw": verb(1), "return": verb(2) }, typeof Symbol === "function" && (g[Symbol.iterator] = function() { return this; }), g; | ||
283 | + function verb(n) { return function (v) { return step([n, v]); }; } | ||
284 | + function step(op) { | ||
285 | + if (f) throw new TypeError("Generator is already executing."); | ||
286 | + while (_) try { | ||
287 | + 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; | ||
288 | + if (y = 0, t) op = [op[0] & 2, t.value]; | ||
289 | + switch (op[0]) { | ||
290 | + case 0: case 1: t = op; break; | ||
291 | + case 4: _.label++; return { value: op[1], done: false }; | ||
292 | + case 5: _.label++; y = op[1]; op = [0]; continue; | ||
293 | + case 7: op = _.ops.pop(); _.trys.pop(); continue; | ||
294 | + default: | ||
295 | + if (!(t = _.trys, t = t.length > 0 && t[t.length - 1]) && (op[0] === 6 || op[0] === 2)) { _ = 0; continue; } | ||
296 | + if (op[0] === 3 && (!t || (op[1] > t[0] && op[1] < t[3]))) { _.label = op[1]; break; } | ||
297 | + if (op[0] === 6 && _.label < t[1]) { _.label = t[1]; t = op; break; } | ||
298 | + if (t && _.label < t[2]) { _.label = t[2]; _.ops.push(op); break; } | ||
299 | + if (t[2]) _.ops.pop(); | ||
300 | + _.trys.pop(); continue; | ||
301 | + } | ||
302 | + op = body.call(thisArg, _); | ||
303 | + } catch (e) { op = [6, e]; y = 0; } finally { f = t = 0; } | ||
304 | + if (op[0] & 5) throw op[1]; return { value: op[0] ? op[1] : void 0, done: true }; | ||
305 | + } | ||
306 | + } | ||
307 | + | ||
308 | + var version = "3.0.3"; | ||
309 | + | ||
310 | + function requestIdleCallbackIfAvailable(fallbackTimeout) { | ||
311 | + return new Promise(function (resolve) { | ||
312 | + if (window.requestIdleCallback) { | ||
313 | + window.requestIdleCallback(function () { return resolve(); }); | ||
314 | + } | ||
315 | + else { | ||
316 | + setTimeout(resolve, fallbackTimeout); | ||
317 | + } | ||
318 | + }); | ||
319 | + } | ||
320 | + | ||
321 | + /* | ||
322 | + * This file contains functions to work with pure data only (no browser features, DOM, side effects, etc). | ||
323 | + */ | ||
324 | + /** | ||
325 | + * Does the same as Array.prototype.includes but has better typing | ||
326 | + */ | ||
327 | + function includes(haystack, needle) { | ||
328 | + for (var i = 0, l = haystack.length; i < l; ++i) { | ||
329 | + if (haystack[i] === needle) { | ||
330 | + return true; | ||
331 | + } | ||
332 | + } | ||
333 | + return false; | ||
334 | + } | ||
335 | + /** | ||
336 | + * Like `!includes()` but with proper typing | ||
337 | + */ | ||
338 | + function excludes(haystack, needle) { | ||
339 | + return !includes(haystack, needle); | ||
340 | + } | ||
341 | + /** | ||
342 | + * Be careful, NaN can return | ||
343 | + */ | ||
344 | + function toInt(value) { | ||
345 | + if (typeof value === 'number') { | ||
346 | + return value | 0; | ||
347 | + } | ||
348 | + return parseInt(value); | ||
349 | + } | ||
350 | + /** | ||
351 | + * Be careful, NaN can return | ||
352 | + */ | ||
353 | + function toFloat(value) { | ||
354 | + if (typeof value === 'number') { | ||
355 | + return value; | ||
356 | + } | ||
357 | + return parseFloat(value); | ||
358 | + } | ||
359 | + function countTruthy(values) { | ||
360 | + return values.reduce(function (sum, value) { return sum + (value ? 1 : 0); }, 0); | ||
361 | + } | ||
362 | + | ||
363 | + /* | ||
364 | + * Functions to help with browser features | ||
365 | + */ | ||
366 | + var w = window; | ||
367 | + var n = navigator; | ||
368 | + var d = document; | ||
369 | + /** | ||
370 | + * Checks whether the browser is based on Trident (the Internet Explorer engine) without using user-agent. | ||
371 | + * | ||
372 | + * Warning for package users: | ||
373 | + * This function is out of Semantic Versioning, i.e. can change unexpectedly. Usage is at your own risk. | ||
374 | + */ | ||
375 | + function isTrident() { | ||
376 | + // The properties are checked to be in IE 10, IE 11 and not to be in other browsers in October 2020 | ||
377 | + return (countTruthy([ | ||
378 | + 'MSCSSMatrix' in w, | ||
379 | + 'msSetImmediate' in w, | ||
380 | + 'msIndexedDB' in w, | ||
381 | + 'msMaxTouchPoints' in n, | ||
382 | + 'msPointerEnabled' in n, | ||
383 | + ]) >= 4); | ||
384 | + } | ||
385 | + /** | ||
386 | + * Checks whether the browser is based on EdgeHTML (the pre-Chromium Edge engine) without using user-agent. | ||
387 | + * | ||
388 | + * Warning for package users: | ||
389 | + * This function is out of Semantic Versioning, i.e. can change unexpectedly. Usage is at your own risk. | ||
390 | + */ | ||
391 | + function isEdgeHTML() { | ||
392 | + // Based on research in October 2020 | ||
393 | + return (countTruthy(['msWriteProfilerMark' in w, 'MSStream' in w, 'msLaunchUri' in n, 'msSaveBlob' in n]) >= 3 && | ||
394 | + !isTrident()); | ||
395 | + } | ||
396 | + /** | ||
397 | + * Checks whether the browser is based on Chromium without using user-agent. | ||
398 | + * | ||
399 | + * Warning for package users: | ||
400 | + * This function is out of Semantic Versioning, i.e. can change unexpectedly. Usage is at your own risk. | ||
401 | + */ | ||
402 | + function isChromium() { | ||
403 | + // Based on research in October 2020. Tested to detect Chromium 42-86. | ||
404 | + return (countTruthy([ | ||
405 | + 'webkitPersistentStorage' in n, | ||
406 | + 'webkitTemporaryStorage' in n, | ||
407 | + n.vendor.indexOf('Google') === 0, | ||
408 | + 'webkitResolveLocalFileSystemURL' in w, | ||
409 | + 'BatteryManager' in w, | ||
410 | + 'webkitMediaStream' in w, | ||
411 | + 'webkitSpeechGrammar' in w, | ||
412 | + ]) >= 5); | ||
413 | + } | ||
414 | + /** | ||
415 | + * Checks whether the browser is based on mobile or desktop Safari without using user-agent. | ||
416 | + * All iOS browsers use WebKit (the Safari engine). | ||
417 | + * | ||
418 | + * Warning for package users: | ||
419 | + * This function is out of Semantic Versioning, i.e. can change unexpectedly. Usage is at your own risk. | ||
420 | + */ | ||
421 | + function isWebKit() { | ||
422 | + // Based on research in September 2020 | ||
423 | + return (countTruthy([ | ||
424 | + 'ApplePayError' in w, | ||
425 | + 'CSSPrimitiveValue' in w, | ||
426 | + 'Counter' in w, | ||
427 | + n.vendor.indexOf('Apple') === 0, | ||
428 | + 'getStorageUpdates' in n, | ||
429 | + 'WebKitMediaKeys' in w, | ||
430 | + ]) >= 4); | ||
431 | + } | ||
432 | + /** | ||
433 | + * Checks whether the WebKit browser is a desktop Safari. | ||
434 | + * | ||
435 | + * Warning for package users: | ||
436 | + * This function is out of Semantic Versioning, i.e. can change unexpectedly. Usage is at your own risk. | ||
437 | + */ | ||
438 | + function isDesktopSafari() { | ||
439 | + return (countTruthy([ | ||
440 | + 'safari' in w, | ||
441 | + !('DeviceMotionEvent' in w), | ||
442 | + !('ongestureend' in w), | ||
443 | + !('standalone' in n), | ||
444 | + ]) >= 3); | ||
445 | + } | ||
446 | + /** | ||
447 | + * Checks whether the browser is based on Gecko (Firefox engine) without using user-agent. | ||
448 | + * | ||
449 | + * Warning for package users: | ||
450 | + * This function is out of Semantic Versioning, i.e. can change unexpectedly. Usage is at your own risk. | ||
451 | + */ | ||
452 | + function isGecko() { | ||
453 | + var _a; | ||
454 | + // Based on research in September 2020 | ||
455 | + return (countTruthy([ | ||
456 | + 'buildID' in n, | ||
457 | + ((_a = d.documentElement) === null || _a === void 0 ? void 0 : _a.style) && 'MozAppearance' in d.documentElement.style, | ||
458 | + 'MediaRecorderErrorEvent' in w, | ||
459 | + 'mozInnerScreenX' in w, | ||
460 | + 'CSSMozDocumentRule' in w, | ||
461 | + 'CanvasCaptureMediaStream' in w, | ||
462 | + ]) >= 4); | ||
463 | + } | ||
464 | + /** | ||
465 | + * Checks whether the browser is based on Chromium version ≥86 without using user-agent. | ||
466 | + * It doesn't check that the browser is based on Chromium, there is a separate function for this. | ||
467 | + */ | ||
468 | + function isChromium86OrNewer() { | ||
469 | + // Checked in Chrome 85 vs Chrome 86 both on desktop and Android | ||
470 | + return (countTruthy([ | ||
471 | + !('MediaSettingsRange' in w), | ||
472 | + 'RTCEncodedAudioFrame' in w, | ||
473 | + '' + w.Intl === '[object Intl]', | ||
474 | + '' + w.Reflect === '[object Reflect]', | ||
475 | + ]) >= 3); | ||
476 | + } | ||
477 | + /** | ||
478 | + * Checks whether the browser is based on WebKit version ≥606 (Safari ≥12) without using user-agent. | ||
479 | + * It doesn't check that the browser is based on WebKit, there is a separate function for this. | ||
480 | + * | ||
481 | + * @link https://en.wikipedia.org/wiki/Safari_version_history#Release_history Safari-WebKit versions map | ||
482 | + */ | ||
483 | + function isWebKit606OrNewer() { | ||
484 | + // Checked in Safari 9–14 | ||
485 | + return (countTruthy([ | ||
486 | + 'DOMRectList' in w, | ||
487 | + 'RTCPeerConnectionIceEvent' in w, | ||
488 | + 'SVGGeometryElement' in w, | ||
489 | + 'ontransitioncancel' in w, | ||
490 | + ]) >= 3); | ||
491 | + } | ||
492 | + | ||
493 | + var w$1 = window; | ||
494 | + var d$1 = document; | ||
495 | + // Inspired by and based on https://github.com/cozylife/audio-fingerprint | ||
496 | + function getAudioFingerprint() { | ||
497 | + return __awaiter(this, void 0, void 0, function () { | ||
498 | + var AudioContext, context, oscillator, compressor, buffer, error_1; | ||
499 | + return __generator(this, function (_a) { | ||
500 | + switch (_a.label) { | ||
501 | + case 0: | ||
502 | + // In some browsers, audio context always stays suspended unless the context is started in response to a user action | ||
503 | + // (e.g. a click or a tap). It prevents audio fingerprint from being taken at an arbitrary moment of time. | ||
504 | + // Such browsers are old and unpopular, so the audio fingerprinting is just skipped in them. | ||
505 | + // See a similar case explanation at https://stackoverflow.com/questions/46363048/onaudioprocess-not-called-on-ios11#46534088 | ||
506 | + if (doesCurrentBrowserSuspendAudioContext()) { | ||
507 | + return [2 /*return*/, -1]; | ||
508 | + } | ||
509 | + AudioContext = w$1.OfflineAudioContext || w$1.webkitOfflineAudioContext; | ||
510 | + if (!AudioContext) { | ||
511 | + return [2 /*return*/, -2]; | ||
512 | + } | ||
513 | + context = new AudioContext(1, 44100, 44100); | ||
514 | + oscillator = context.createOscillator(); | ||
515 | + oscillator.type = 'triangle'; | ||
516 | + setAudioParam(context, oscillator.frequency, 10000); | ||
517 | + compressor = context.createDynamicsCompressor(); | ||
518 | + setAudioParam(context, compressor.threshold, -50); | ||
519 | + setAudioParam(context, compressor.knee, 40); | ||
520 | + setAudioParam(context, compressor.ratio, 12); | ||
521 | + setAudioParam(context, compressor.reduction, -20); | ||
522 | + setAudioParam(context, compressor.attack, 0); | ||
523 | + setAudioParam(context, compressor.release, 0.25); | ||
524 | + oscillator.connect(compressor); | ||
525 | + compressor.connect(context.destination); | ||
526 | + oscillator.start(0); | ||
527 | + _a.label = 1; | ||
528 | + case 1: | ||
529 | + _a.trys.push([1, 3, 4, 5]); | ||
530 | + return [4 /*yield*/, renderAudio(context)]; | ||
531 | + case 2: | ||
532 | + buffer = _a.sent(); | ||
533 | + return [3 /*break*/, 5]; | ||
534 | + case 3: | ||
535 | + error_1 = _a.sent(); | ||
536 | + if (error_1.name === "timeout" /* Timeout */ || error_1.name === "suspended" /* Suspended */) { | ||
537 | + return [2 /*return*/, -3]; | ||
538 | + } | ||
539 | + throw error_1; | ||
540 | + case 4: | ||
541 | + oscillator.disconnect(); | ||
542 | + compressor.disconnect(); | ||
543 | + return [7 /*endfinally*/]; | ||
544 | + case 5: return [2 /*return*/, getHash(buffer.getChannelData(0))]; | ||
545 | + } | ||
546 | + }); | ||
547 | + }); | ||
548 | + } | ||
549 | + /** | ||
550 | + * Checks if the current browser is known to always suspend audio context | ||
551 | + */ | ||
552 | + function doesCurrentBrowserSuspendAudioContext() { | ||
553 | + return isWebKit() && !isDesktopSafari() && !isWebKit606OrNewer(); | ||
554 | + } | ||
555 | + function setAudioParam(context, param, value) { | ||
556 | + var isAudioParam = function (value) { | ||
557 | + return value && typeof value.setValueAtTime === 'function'; | ||
558 | + }; | ||
559 | + if (isAudioParam(param)) { | ||
560 | + param.setValueAtTime(value, context.currentTime); | ||
561 | + } | ||
562 | + } | ||
563 | + function renderAudio(context) { | ||
564 | + var resumeTriesMaxCount = 3; | ||
565 | + var resumeRetryDelay = 500; | ||
566 | + var runningTimeout = 1000; | ||
567 | + return new Promise(function (resolve, reject) { | ||
568 | + context.oncomplete = function (event) { return resolve(event.renderedBuffer); }; | ||
569 | + var resumeTriesLeft = resumeTriesMaxCount; | ||
570 | + var tryResume = function () { | ||
571 | + context.startRendering(); | ||
572 | + switch (context.state) { | ||
573 | + case 'running': | ||
574 | + setTimeout(function () { return reject(makeInnerError("timeout" /* Timeout */)); }, runningTimeout); | ||
575 | + break; | ||
576 | + // Sometimes the audio context doesn't start after calling `startRendering` (in addition to the cases where | ||
577 | + // audio context doesn't start at all). A known case is starting an audio context when the browser tab is in | ||
578 | + // background on iPhone. Retries usually help in this case. | ||
579 | + case 'suspended': | ||
580 | + // The audio context can reject starting until the tab is in foreground. Long fingerprint duration | ||
581 | + // in background isn't a problem, therefore the retry attempts don't count in background. It can lead to | ||
582 | + // a situation when a fingerprint takes very long time and finishes successfully. FYI, the audio context | ||
583 | + // can be suspended when `document.hidden === false` and start running after a retry. | ||
584 | + if (!d$1.hidden) { | ||
585 | + resumeTriesLeft--; | ||
586 | + } | ||
587 | + if (resumeTriesLeft > 0) { | ||
588 | + setTimeout(tryResume, resumeRetryDelay); | ||
589 | + } | ||
590 | + else { | ||
591 | + reject(makeInnerError("suspended" /* Suspended */)); | ||
592 | + } | ||
593 | + break; | ||
594 | + } | ||
595 | + }; | ||
596 | + tryResume(); | ||
597 | + }); | ||
598 | + } | ||
599 | + function getHash(signal) { | ||
600 | + var hash = 0; | ||
601 | + for (var i = 4500; i < 5000; ++i) { | ||
602 | + hash += Math.abs(signal[i]); | ||
603 | + } | ||
604 | + return hash; | ||
605 | + } | ||
606 | + function makeInnerError(name) { | ||
607 | + var error = new Error(name); | ||
608 | + error.name = name; | ||
609 | + return error; | ||
610 | + } | ||
611 | + | ||
612 | + var d$2 = document; | ||
613 | + // We use m or w because these two characters take up the maximum width. | ||
614 | + // And we use a LLi so that the same matching fonts can get separated. | ||
615 | + var testString = 'mmMwWLliI0O&1'; | ||
616 | + // We test using 48px font size, we may use any size. I guess larger the better. | ||
617 | + var testSize = '48px'; | ||
618 | + // A font will be compared against all the three default fonts. | ||
619 | + // And if it doesn't match all 3 then that font is not available. | ||
620 | + var baseFonts = ['monospace', 'sans-serif', 'serif']; | ||
621 | + var fontList = [ | ||
622 | + // This is android-specific font from "Roboto" family | ||
623 | + 'sans-serif-thin', | ||
624 | + 'ARNO PRO', | ||
625 | + 'Agency FB', | ||
626 | + 'Arabic Typesetting', | ||
627 | + 'Arial Unicode MS', | ||
628 | + 'AvantGarde Bk BT', | ||
629 | + 'BankGothic Md BT', | ||
630 | + 'Batang', | ||
631 | + 'Bitstream Vera Sans Mono', | ||
632 | + 'Calibri', | ||
633 | + 'Century', | ||
634 | + 'Century Gothic', | ||
635 | + 'Clarendon', | ||
636 | + 'EUROSTILE', | ||
637 | + 'Franklin Gothic', | ||
638 | + 'Futura Bk BT', | ||
639 | + 'Futura Md BT', | ||
640 | + 'GOTHAM', | ||
641 | + 'Gill Sans', | ||
642 | + 'HELV', | ||
643 | + 'Haettenschweiler', | ||
644 | + 'Helvetica Neue', | ||
645 | + 'Humanst521 BT', | ||
646 | + 'Leelawadee', | ||
647 | + 'Letter Gothic', | ||
648 | + 'Levenim MT', | ||
649 | + 'Lucida Bright', | ||
650 | + 'Lucida Sans', | ||
651 | + 'Menlo', | ||
652 | + 'MS Mincho', | ||
653 | + 'MS Outlook', | ||
654 | + 'MS Reference Specialty', | ||
655 | + 'MS UI Gothic', | ||
656 | + 'MT Extra', | ||
657 | + 'MYRIAD PRO', | ||
658 | + 'Marlett', | ||
659 | + 'Meiryo UI', | ||
660 | + 'Microsoft Uighur', | ||
661 | + 'Minion Pro', | ||
662 | + 'Monotype Corsiva', | ||
663 | + 'PMingLiU', | ||
664 | + 'Pristina', | ||
665 | + 'SCRIPTINA', | ||
666 | + 'Segoe UI Light', | ||
667 | + 'Serifa', | ||
668 | + 'SimHei', | ||
669 | + 'Small Fonts', | ||
670 | + 'Staccato222 BT', | ||
671 | + 'TRAJAN PRO', | ||
672 | + 'Univers CE 55 Medium', | ||
673 | + 'Vrinda', | ||
674 | + 'ZWAdobeF', | ||
675 | + ]; | ||
676 | + var fontSpanStyle = { | ||
677 | + // CSS font reset to reset external styles | ||
678 | + fontStyle: 'normal', | ||
679 | + fontWeight: 'normal', | ||
680 | + letterSpacing: 'normal', | ||
681 | + lineBreak: 'auto', | ||
682 | + lineHeight: 'normal', | ||
683 | + textTransform: 'none', | ||
684 | + textAlign: 'left', | ||
685 | + textDecoration: 'none', | ||
686 | + textShadow: 'none', | ||
687 | + whiteSpace: 'normal', | ||
688 | + wordBreak: 'normal', | ||
689 | + wordSpacing: 'normal', | ||
690 | + // We need this css as in some weird browser this span elements shows up for a microSec which creates | ||
691 | + // a bad user experience | ||
692 | + position: 'absolute', | ||
693 | + left: '-9999px', | ||
694 | + fontSize: testSize, | ||
695 | + }; | ||
696 | + // kudos to http://www.lalit.org/lab/javascript-css-font-detect/ | ||
697 | + function getFonts() { | ||
698 | + var h = d$2.body; | ||
699 | + // div to load spans for the base fonts | ||
700 | + var baseFontsDiv = d$2.createElement('div'); | ||
701 | + // div to load spans for the fonts to detect | ||
702 | + var fontsDiv = d$2.createElement('div'); | ||
703 | + var defaultWidth = {}; | ||
704 | + var defaultHeight = {}; | ||
705 | + // creates a span where the fonts will be loaded | ||
706 | + var createSpan = function () { | ||
707 | + var span = d$2.createElement('span'); | ||
708 | + span.textContent = testString; | ||
709 | + for (var _i = 0, _a = Object.keys(fontSpanStyle); _i < _a.length; _i++) { | ||
710 | + var prop = _a[_i]; | ||
711 | + span.style[prop] = fontSpanStyle[prop]; | ||
712 | + } | ||
713 | + return span; | ||
714 | + }; | ||
715 | + // creates a span and load the font to detect and a base font for fallback | ||
716 | + var createSpanWithFonts = function (fontToDetect, baseFont) { | ||
717 | + var s = createSpan(); | ||
718 | + s.style.fontFamily = "'" + fontToDetect + "'," + baseFont; | ||
719 | + return s; | ||
720 | + }; | ||
721 | + // creates spans for the base fonts and adds them to baseFontsDiv | ||
722 | + var initializeBaseFontsSpans = function () { | ||
723 | + return baseFonts.map(function (baseFont) { | ||
724 | + var s = createSpan(); | ||
725 | + s.style.fontFamily = baseFont; | ||
726 | + baseFontsDiv.appendChild(s); | ||
727 | + return s; | ||
728 | + }); | ||
729 | + }; | ||
730 | + // creates spans for the fonts to detect and adds them to fontsDiv | ||
731 | + var initializeFontsSpans = function () { | ||
732 | + // Stores {fontName : [spans for that font]} | ||
733 | + var spans = {}; | ||
734 | + var _loop_1 = function (font) { | ||
735 | + spans[font] = baseFonts.map(function (baseFont) { | ||
736 | + var s = createSpanWithFonts(font, baseFont); | ||
737 | + fontsDiv.appendChild(s); | ||
738 | + return s; | ||
739 | + }); | ||
740 | + }; | ||
741 | + for (var _i = 0, fontList_1 = fontList; _i < fontList_1.length; _i++) { | ||
742 | + var font = fontList_1[_i]; | ||
743 | + _loop_1(font); | ||
744 | + } | ||
745 | + return spans; | ||
746 | + }; | ||
747 | + // checks if a font is available | ||
748 | + var isFontAvailable = function (fontSpans) { | ||
749 | + return baseFonts.some(function (baseFont, baseFontIndex) { | ||
750 | + return fontSpans[baseFontIndex].offsetWidth !== defaultWidth[baseFont] || | ||
751 | + fontSpans[baseFontIndex].offsetHeight !== defaultHeight[baseFont]; | ||
752 | + }); | ||
753 | + }; | ||
754 | + // create spans for base fonts | ||
755 | + var baseFontsSpans = initializeBaseFontsSpans(); | ||
756 | + // add the spans to the DOM | ||
757 | + h.appendChild(baseFontsDiv); | ||
758 | + // get the default width for the three base fonts | ||
759 | + for (var index = 0, length_1 = baseFonts.length; index < length_1; index++) { | ||
760 | + defaultWidth[baseFonts[index]] = baseFontsSpans[index].offsetWidth; // width for the default font | ||
761 | + defaultHeight[baseFonts[index]] = baseFontsSpans[index].offsetHeight; // height for the default font | ||
762 | + } | ||
763 | + // create spans for fonts to detect | ||
764 | + var fontsSpans = initializeFontsSpans(); | ||
765 | + // add all the spans to the DOM | ||
766 | + h.appendChild(fontsDiv); | ||
767 | + // check available fonts | ||
768 | + var available = []; | ||
769 | + for (var i = 0, l = fontList.length; i < l; i++) { | ||
770 | + if (isFontAvailable(fontsSpans[fontList[i]])) { | ||
771 | + available.push(fontList[i]); | ||
772 | + } | ||
773 | + } | ||
774 | + // remove spans from DOM | ||
775 | + h.removeChild(fontsDiv); | ||
776 | + h.removeChild(baseFontsDiv); | ||
777 | + return available; | ||
778 | + } | ||
779 | + | ||
780 | + function getPlugins() { | ||
781 | + if (isTrident()) { | ||
782 | + return []; | ||
783 | + } | ||
784 | + if (!navigator.plugins) { | ||
785 | + return undefined; | ||
786 | + } | ||
787 | + var plugins = []; | ||
788 | + // Safari 10 doesn't support iterating navigator.plugins with for...of | ||
789 | + for (var i = 0; i < navigator.plugins.length; ++i) { | ||
790 | + var plugin = navigator.plugins[i]; | ||
791 | + if (!plugin) { | ||
792 | + continue; | ||
793 | + } | ||
794 | + var mimeTypes = []; | ||
795 | + for (var j = 0; j < plugin.length; ++j) { | ||
796 | + var mimeType = plugin[j]; | ||
797 | + mimeTypes.push({ | ||
798 | + type: mimeType.type, | ||
799 | + suffixes: mimeType.suffixes, | ||
800 | + }); | ||
801 | + } | ||
802 | + plugins.push({ | ||
803 | + name: plugin.name, | ||
804 | + description: plugin.description, | ||
805 | + mimeTypes: mimeTypes, | ||
806 | + }); | ||
807 | + } | ||
808 | + return plugins; | ||
809 | + } | ||
810 | + | ||
811 | + function makeCanvasContext() { | ||
812 | + var canvas = document.createElement('canvas'); | ||
813 | + canvas.width = 240; | ||
814 | + canvas.height = 140; | ||
815 | + canvas.style.display = 'inline'; | ||
816 | + return [canvas, canvas.getContext('2d')]; | ||
817 | + } | ||
818 | + function isSupported(canvas, context) { | ||
819 | + // TODO: look into: https://developer.mozilla.org/en-US/docs/Web/API/HTMLCanvasElement/toBlob | ||
820 | + return !!(context && canvas.toDataURL); | ||
821 | + } | ||
822 | + function save(canvas) { | ||
823 | + // TODO: look into: https://developer.mozilla.org/en-US/docs/Web/API/HTMLCanvasElement/toBlob | ||
824 | + return canvas.toDataURL(); | ||
825 | + } | ||
826 | + // https://www.browserleaks.com/canvas#how-does-it-work | ||
827 | + function getCanvasFingerprint() { | ||
828 | + var _a = makeCanvasContext(), canvas = _a[0], context = _a[1]; | ||
829 | + if (!isSupported(canvas, context)) { | ||
830 | + return { winding: false, data: '' }; | ||
831 | + } | ||
832 | + // detect browser support of canvas winding | ||
833 | + // http://blogs.adobe.com/webplatform/2013/01/30/winding-rules-in-canvas/ | ||
834 | + // https://github.com/Modernizr/Modernizr/blob/master/feature-detects/canvas/winding.js | ||
835 | + context.rect(0, 0, 10, 10); | ||
836 | + context.rect(2, 2, 6, 6); | ||
837 | + var winding = !context.isPointInPath(5, 5, 'evenodd'); | ||
838 | + context.textBaseline = 'alphabetic'; | ||
839 | + context.fillStyle = '#f60'; | ||
840 | + context.fillRect(125, 1, 62, 20); | ||
841 | + context.fillStyle = '#069'; | ||
842 | + // https://github.com/Valve/fingerprintjs2/issues/66 | ||
843 | + // this can affect FP generation when applying different CSS on different websites | ||
844 | + context.font = '11pt no-real-font-123'; | ||
845 | + // the choice of emojis has a gigantic impact on rendering performance (especially in FF) | ||
846 | + // some newer emojis cause it to slow down 50-200 times | ||
847 | + // context.fillText("Cw爨m fjordbank \ud83d\ude03 gly", 2, 15) | ||
848 | + var printedText = 'Cwm fjordbank \ud83d\ude03 gly'; | ||
849 | + context.fillText(printedText, 2, 15); | ||
850 | + context.fillStyle = 'rgba(102, 204, 0, 0.2)'; | ||
851 | + context.font = '18pt Arial'; | ||
852 | + context.fillText(printedText, 4, 45); | ||
853 | + // canvas blending | ||
854 | + // http://blogs.adobe.com/webplatform/2013/01/28/blending-features-in-canvas/ | ||
855 | + // http://jsfiddle.net/NDYV8/16/ | ||
856 | + context.globalCompositeOperation = 'multiply'; | ||
857 | + context.fillStyle = 'rgb(255,0,255)'; | ||
858 | + context.beginPath(); | ||
859 | + context.arc(50, 50, 50, 0, Math.PI * 2, true); | ||
860 | + context.closePath(); | ||
861 | + context.fill(); | ||
862 | + context.fillStyle = 'rgb(0,255,255)'; | ||
863 | + context.beginPath(); | ||
864 | + context.arc(100, 50, 50, 0, Math.PI * 2, true); | ||
865 | + context.closePath(); | ||
866 | + context.fill(); | ||
867 | + context.fillStyle = 'rgb(255,255,0)'; | ||
868 | + context.beginPath(); | ||
869 | + context.arc(75, 100, 50, 0, Math.PI * 2, true); | ||
870 | + context.closePath(); | ||
871 | + context.fill(); | ||
872 | + context.fillStyle = 'rgb(255,0,255)'; | ||
873 | + // canvas winding | ||
874 | + // http://blogs.adobe.com/webplatform/2013/01/30/winding-rules-in-canvas/ | ||
875 | + // http://jsfiddle.net/NDYV8/19/ | ||
876 | + context.arc(75, 75, 75, 0, Math.PI * 2, true); | ||
877 | + context.arc(75, 75, 25, 0, Math.PI * 2, true); | ||
878 | + context.fill('evenodd'); | ||
879 | + return { | ||
880 | + winding: winding, | ||
881 | + data: save(canvas), | ||
882 | + }; | ||
883 | + } | ||
884 | + | ||
885 | + var n$1 = navigator; | ||
886 | + var w$2 = window; | ||
887 | + /** | ||
888 | + * This is a crude and primitive touch screen detection. It's not possible to currently reliably detect the availability | ||
889 | + * of a touch screen with a JS, without actually subscribing to a touch event. | ||
890 | + * | ||
891 | + * @see http://www.stucox.com/blog/you-cant-detect-a-touchscreen/ | ||
892 | + * @see https://github.com/Modernizr/Modernizr/issues/548 | ||
893 | + */ | ||
894 | + function getTouchSupport() { | ||
895 | + var maxTouchPoints = 0; | ||
896 | + var touchEvent; | ||
897 | + if (n$1.maxTouchPoints !== undefined) { | ||
898 | + maxTouchPoints = toInt(n$1.maxTouchPoints); | ||
899 | + } | ||
900 | + else if (n$1.msMaxTouchPoints !== undefined) { | ||
901 | + maxTouchPoints = n$1.msMaxTouchPoints; | ||
902 | + } | ||
903 | + try { | ||
904 | + document.createEvent('TouchEvent'); | ||
905 | + touchEvent = true; | ||
906 | + } | ||
907 | + catch (_) { | ||
908 | + touchEvent = false; | ||
909 | + } | ||
910 | + var touchStart = 'ontouchstart' in w$2; | ||
911 | + return { | ||
912 | + maxTouchPoints: maxTouchPoints, | ||
913 | + touchEvent: touchEvent, | ||
914 | + touchStart: touchStart, | ||
915 | + }; | ||
916 | + } | ||
917 | + | ||
918 | + function getOsCpu() { | ||
919 | + return navigator.oscpu; | ||
920 | + } | ||
921 | + | ||
922 | + var n$2 = navigator; | ||
923 | + function getLanguages() { | ||
924 | + var result = []; | ||
925 | + var language = n$2.language || n$2.userLanguage || n$2.browserLanguage || n$2.systemLanguage; | ||
926 | + if (language !== undefined) { | ||
927 | + result.push([language]); | ||
928 | + } | ||
929 | + if (Array.isArray(n$2.languages)) { | ||
930 | + // Starting from Chromium 86, there is only a single value in `navigator.language` in Incognito mode: | ||
931 | + // the value of `navigator.language`. Therefore the value is ignored in this browser. | ||
932 | + if (!(isChromium() && isChromium86OrNewer())) { | ||
933 | + result.push(n$2.languages); | ||
934 | + } | ||
935 | + } | ||
936 | + else if (typeof n$2.languages === 'string') { | ||
937 | + var languages = n$2.languages; | ||
938 | + if (languages) { | ||
939 | + result.push(languages.split(',')); | ||
940 | + } | ||
941 | + } | ||
942 | + return result; | ||
943 | + } | ||
944 | + | ||
945 | + function getColorDepth() { | ||
946 | + return window.screen.colorDepth; | ||
947 | + } | ||
948 | + | ||
949 | + function getDeviceMemory() { | ||
950 | + return navigator.deviceMemory; | ||
951 | + } | ||
952 | + | ||
953 | + var w$3 = window; | ||
954 | + function getScreenResolution() { | ||
955 | + // Some browsers return screen resolution as strings, e.g. "1200", instead of a number, e.g. 1200. | ||
956 | + // I suspect it's done by certain plugins that randomize browser properties to prevent fingerprinting. | ||
957 | + var dimensions = [toInt(w$3.screen.width), toInt(w$3.screen.height)]; | ||
958 | + dimensions.sort().reverse(); | ||
959 | + return dimensions; | ||
960 | + } | ||
961 | + | ||
962 | + var w$4 = window; | ||
963 | + function getAvailableScreenResolution() { | ||
964 | + if (w$4.screen.availWidth && w$4.screen.availHeight) { | ||
965 | + // Some browsers return screen resolution as strings, e.g. "1200", instead of a number, e.g. 1200. | ||
966 | + // I suspect it's done by certain plugins that randomize browser properties to prevent fingerprinting. | ||
967 | + var dimensions = [toInt(w$4.screen.availWidth), toInt(w$4.screen.availHeight)]; | ||
968 | + dimensions.sort().reverse(); | ||
969 | + return dimensions; | ||
970 | + } | ||
971 | + return undefined; | ||
972 | + } | ||
973 | + | ||
974 | + function getHardwareConcurrency() { | ||
975 | + try { | ||
976 | + // sometimes hardware concurrency is a string | ||
977 | + var concurrency = toInt(navigator.hardwareConcurrency); | ||
978 | + return isNaN(concurrency) ? 1 : concurrency; | ||
979 | + } | ||
980 | + catch (e) { | ||
981 | + return 1; | ||
982 | + } | ||
983 | + } | ||
984 | + | ||
985 | + function getTimezoneOffset() { | ||
986 | + var currentYear = new Date().getFullYear(); | ||
987 | + // The timezone offset may change over time due to daylight saving time (DST) shifts. | ||
988 | + // The non-DST timezone offset is used as the result timezone offset. | ||
989 | + // Since the DST season differs in the northern and the southern hemispheres, | ||
990 | + // both January and July timezones offsets are considered. | ||
991 | + return Math.max( | ||
992 | + // `getTimezoneOffset` returns a number as a string in some unidentified cases | ||
993 | + toFloat(new Date(currentYear, 0, 1).getTimezoneOffset()), toFloat(new Date(currentYear, 6, 1).getTimezoneOffset())); | ||
994 | + } | ||
995 | + | ||
996 | + var w$5 = window; | ||
997 | + function getTimezone() { | ||
998 | + var _a; | ||
999 | + if ((_a = w$5.Intl) === null || _a === void 0 ? void 0 : _a.DateTimeFormat) { | ||
1000 | + return new w$5.Intl.DateTimeFormat().resolvedOptions().timeZone; | ||
1001 | + } | ||
1002 | + return undefined; | ||
1003 | + } | ||
1004 | + | ||
1005 | + function getSessionStorage() { | ||
1006 | + try { | ||
1007 | + return !!window.sessionStorage; | ||
1008 | + } | ||
1009 | + catch (error) { | ||
1010 | + /* SecurityError when referencing it means it exists */ | ||
1011 | + return true; | ||
1012 | + } | ||
1013 | + } | ||
1014 | + | ||
1015 | + // https://bugzilla.mozilla.org/show_bug.cgi?id=781447 | ||
1016 | + function getLocalStorage() { | ||
1017 | + try { | ||
1018 | + return !!window.localStorage; | ||
1019 | + } | ||
1020 | + catch (e) { | ||
1021 | + /* SecurityError when referencing it means it exists */ | ||
1022 | + return true; | ||
1023 | + } | ||
1024 | + } | ||
1025 | + | ||
1026 | + function getIndexedDB() { | ||
1027 | + // IE and Edge don't allow accessing indexedDB in private mode, therefore IE and Edge will have different | ||
1028 | + // visitor identifier in normal and private modes. | ||
1029 | + if (isTrident() || isEdgeHTML()) { | ||
1030 | + return undefined; | ||
1031 | + } | ||
1032 | + try { | ||
1033 | + return !!window.indexedDB; | ||
1034 | + } | ||
1035 | + catch (e) { | ||
1036 | + /* SecurityError when referencing it means it exists */ | ||
1037 | + return true; | ||
1038 | + } | ||
1039 | + } | ||
1040 | + | ||
1041 | + function getOpenDatabase() { | ||
1042 | + return !!window.openDatabase; | ||
1043 | + } | ||
1044 | + | ||
1045 | + function getCpuClass() { | ||
1046 | + return navigator.cpuClass; | ||
1047 | + } | ||
1048 | + | ||
1049 | + function getPlatform() { | ||
1050 | + return navigator.platform; | ||
1051 | + } | ||
1052 | + | ||
1053 | + function getPluginsSupport() { | ||
1054 | + return navigator.plugins !== undefined; | ||
1055 | + } | ||
1056 | + | ||
1057 | + function getProductSub() { | ||
1058 | + return navigator.productSub; | ||
1059 | + } | ||
1060 | + | ||
1061 | + function getEmptyEvalLength() { | ||
1062 | + return eval.toString().length; | ||
1063 | + } | ||
1064 | + | ||
1065 | + function getErrorFF() { | ||
1066 | + try { | ||
1067 | + throw 'a'; | ||
1068 | + } | ||
1069 | + catch (e) { | ||
1070 | + try { | ||
1071 | + e.toSource(); | ||
1072 | + return true; | ||
1073 | + } | ||
1074 | + catch (e2) { | ||
1075 | + return false; | ||
1076 | + } | ||
1077 | + } | ||
1078 | + } | ||
1079 | + | ||
1080 | + function getVendor() { | ||
1081 | + return navigator.vendor; | ||
1082 | + } | ||
1083 | + | ||
1084 | + function getChrome() { | ||
1085 | + return window.chrome !== undefined; | ||
1086 | + } | ||
1087 | + | ||
1088 | + var d$3 = document; | ||
1089 | + /** | ||
1090 | + * navigator.cookieEnabled cannot detect custom or nuanced cookie blocking configurations. For example, when blocking | ||
1091 | + * cookies via the Advanced Privacy Settings in IE9, it always returns true. And there have been issues in the past with | ||
1092 | + * site-specific exceptions. Don't rely on it. | ||
1093 | + * | ||
1094 | + * @see https://github.com/Modernizr/Modernizr/blob/master/feature-detects/cookies.js Taken from here | ||
1095 | + */ | ||
1096 | + function areCookiesEnabled() { | ||
1097 | + // Taken from here: https://github.com/Modernizr/Modernizr/blob/master/feature-detects/cookies.js | ||
1098 | + // navigator.cookieEnabled cannot detect custom or nuanced cookie blocking configurations. For example, when blocking | ||
1099 | + // cookies via the Advanced Privacy Settings in IE9, it always returns true. And there have been issues in the past | ||
1100 | + // with site-specific exceptions. Don't rely on it. | ||
1101 | + // try..catch because some in situations `document.cookie` is exposed but throws a | ||
1102 | + // SecurityError if you try to access it; e.g. documents created from data URIs | ||
1103 | + // or in sandboxed iframes (depending on flags/context) | ||
1104 | + try { | ||
1105 | + // Create cookie | ||
1106 | + d$3.cookie = 'cookietest=1'; | ||
1107 | + var result = d$3.cookie.indexOf('cookietest=') !== -1; | ||
1108 | + // Delete cookie | ||
1109 | + d$3.cookie = 'cookietest=1; expires=Thu, 01-Jan-1970 00:00:01 GMT'; | ||
1110 | + return result; | ||
1111 | + } | ||
1112 | + catch (e) { | ||
1113 | + return false; | ||
1114 | + } | ||
1115 | + } | ||
1116 | + | ||
1117 | + /** | ||
1118 | + * The list of entropy sources used to make visitor identifiers. | ||
1119 | + * | ||
1120 | + * This value isn't restricted by Semantic Versioning, i.e. it may be changed without bumping minor or major version of | ||
1121 | + * this package. | ||
1122 | + */ | ||
1123 | + var sources = { | ||
1124 | + // Expected errors and default values must be handled inside the functions. Unexpected errors must be thrown. | ||
1125 | + osCpu: getOsCpu, | ||
1126 | + languages: getLanguages, | ||
1127 | + colorDepth: getColorDepth, | ||
1128 | + deviceMemory: getDeviceMemory, | ||
1129 | + screenResolution: getScreenResolution, | ||
1130 | + availableScreenResolution: getAvailableScreenResolution, | ||
1131 | + hardwareConcurrency: getHardwareConcurrency, | ||
1132 | + timezoneOffset: getTimezoneOffset, | ||
1133 | + timezone: getTimezone, | ||
1134 | + sessionStorage: getSessionStorage, | ||
1135 | + localStorage: getLocalStorage, | ||
1136 | + indexedDB: getIndexedDB, | ||
1137 | + openDatabase: getOpenDatabase, | ||
1138 | + cpuClass: getCpuClass, | ||
1139 | + // Maybe it should be excluded: https://github.com/fingerprintjs/fingerprintjs/issues/514#issuecomment-688754892 | ||
1140 | + platform: getPlatform, | ||
1141 | + plugins: getPlugins, | ||
1142 | + canvas: getCanvasFingerprint, | ||
1143 | + // adBlock: isAdblockUsed, // https://github.com/fingerprintjs/fingerprintjs/issues/405 | ||
1144 | + touchSupport: getTouchSupport, | ||
1145 | + fonts: getFonts, | ||
1146 | + audio: getAudioFingerprint, | ||
1147 | + pluginsSupport: getPluginsSupport, | ||
1148 | + productSub: getProductSub, | ||
1149 | + emptyEvalLength: getEmptyEvalLength, | ||
1150 | + errorFF: getErrorFF, | ||
1151 | + vendor: getVendor, | ||
1152 | + chrome: getChrome, | ||
1153 | + cookiesEnabled: areCookiesEnabled, | ||
1154 | + }; | ||
1155 | + /** | ||
1156 | + * Gets a components list from the given list of entropy sources. | ||
1157 | + * | ||
1158 | + * Warning for package users: | ||
1159 | + * This function is out of Semantic Versioning, i.e. can change unexpectedly. Usage is at your own risk. | ||
1160 | + */ | ||
1161 | + function getComponents(sources, sourceOptions, excludeSources) { | ||
1162 | + return __awaiter(this, void 0, void 0, function () { | ||
1163 | + var timestamp, components, _i, _a, sourceKey, result, error_1, nextTimestamp; | ||
1164 | + var _b; | ||
1165 | + return __generator(this, function (_c) { | ||
1166 | + switch (_c.label) { | ||
1167 | + case 0: | ||
1168 | + timestamp = Date.now(); | ||
1169 | + components = {}; | ||
1170 | + _i = 0, _a = Object.keys(sources); | ||
1171 | + _c.label = 1; | ||
1172 | + case 1: | ||
1173 | + if (!(_i < _a.length)) return [3 /*break*/, 7]; | ||
1174 | + sourceKey = _a[_i]; | ||
1175 | + if (!excludes(excludeSources, sourceKey)) { | ||
1176 | + return [3 /*break*/, 6]; | ||
1177 | + } | ||
1178 | + result = void 0; | ||
1179 | + _c.label = 2; | ||
1180 | + case 2: | ||
1181 | + _c.trys.push([2, 4, , 5]); | ||
1182 | + _b = {}; | ||
1183 | + return [4 /*yield*/, sources[sourceKey](sourceOptions)]; | ||
1184 | + case 3: | ||
1185 | + result = (_b.value = _c.sent(), _b); | ||
1186 | + return [3 /*break*/, 5]; | ||
1187 | + case 4: | ||
1188 | + error_1 = _c.sent(); | ||
1189 | + result = error_1 && typeof error_1 === 'object' && 'message' in error_1 ? { error: error_1 } : { error: { message: error_1 } }; | ||
1190 | + return [3 /*break*/, 5]; | ||
1191 | + case 5: | ||
1192 | + nextTimestamp = Date.now(); | ||
1193 | + components[sourceKey] = __assign(__assign({}, result), { duration: nextTimestamp - timestamp }); // TypeScript has beaten me here | ||
1194 | + timestamp = nextTimestamp; | ||
1195 | + _c.label = 6; | ||
1196 | + case 6: | ||
1197 | + _i++; | ||
1198 | + return [3 /*break*/, 1]; | ||
1199 | + case 7: return [2 /*return*/, components]; | ||
1200 | + } | ||
1201 | + }); | ||
1202 | + }); | ||
1203 | + } | ||
1204 | + /** | ||
1205 | + * Collects entropy components from the built-in sources to make the visitor identifier. | ||
1206 | + */ | ||
1207 | + function getBuiltinComponents() { | ||
1208 | + return getComponents(sources, undefined, []); | ||
1209 | + } | ||
1210 | + | ||
1211 | + function componentsToCanonicalString(components) { | ||
1212 | + var result = ''; | ||
1213 | + for (var _i = 0, _a = Object.keys(components); _i < _a.length; _i++) { | ||
1214 | + var componentKey = _a[_i]; | ||
1215 | + var component = components[componentKey]; | ||
1216 | + var value = component.error ? 'error' : JSON.stringify(component.value); | ||
1217 | + result += "" + (result ? '|' : '') + componentKey.replace(/([:|\\])/g, '\\$1') + ":" + value; | ||
1218 | + } | ||
1219 | + return result; | ||
1220 | + } | ||
1221 | + function componentsToDebugString(components) { | ||
1222 | + return JSON.stringify(components, function (_key, value) { | ||
1223 | + var _a; | ||
1224 | + if (value instanceof Error) { | ||
1225 | + return __assign(__assign({}, value), { message: value.message, stack: (_a = value.stack) === null || _a === void 0 ? void 0 : _a.split('\n') }); | ||
1226 | + } | ||
1227 | + return value; | ||
1228 | + }, 2); | ||
1229 | + } | ||
1230 | + function hashComponents(components) { | ||
1231 | + return x64hash128(componentsToCanonicalString(components)); | ||
1232 | + } | ||
1233 | + /** | ||
1234 | + * Makes a GetResult implementation that calculates the visitor id hash on demand. | ||
1235 | + * Designed for optimisation. | ||
1236 | + */ | ||
1237 | + function makeLazyGetResult(components) { | ||
1238 | + var visitorIdCache; | ||
1239 | + // A plain class isn't used because its getters and setters aren't enumerable. | ||
1240 | + return { | ||
1241 | + components: components, | ||
1242 | + get visitorId() { | ||
1243 | + if (visitorIdCache === undefined) { | ||
1244 | + visitorIdCache = hashComponents(this.components); | ||
1245 | + } | ||
1246 | + return visitorIdCache; | ||
1247 | + }, | ||
1248 | + set visitorId(visitorId) { | ||
1249 | + visitorIdCache = visitorId; | ||
1250 | + }, | ||
1251 | + }; | ||
1252 | + } | ||
1253 | + /** | ||
1254 | + * The class isn't exported from the index file to not expose the constructor. | ||
1255 | + * The hiding gives more freedom for future non-breaking updates. | ||
1256 | + */ | ||
1257 | + var OpenAgent = /** @class */ (function () { | ||
1258 | + function OpenAgent() { | ||
1259 | + } | ||
1260 | + /** | ||
1261 | + * @inheritDoc | ||
1262 | + */ | ||
1263 | + OpenAgent.prototype.get = function (options) { | ||
1264 | + if (options === void 0) { options = {}; } | ||
1265 | + return __awaiter(this, void 0, void 0, function () { | ||
1266 | + var components, result; | ||
1267 | + return __generator(this, function (_a) { | ||
1268 | + switch (_a.label) { | ||
1269 | + case 0: return [4 /*yield*/, getBuiltinComponents()]; | ||
1270 | + case 1: | ||
1271 | + components = _a.sent(); | ||
1272 | + result = makeLazyGetResult(components); | ||
1273 | + if (options.debug) { | ||
1274 | + // console.log is ok here because it's under a debug clause | ||
1275 | + // eslint-disable-next-line no-console | ||
1276 | + console.log("Copy the text below to get the debug data:\n\n```\nversion: " + version + "\nuserAgent: " + navigator.userAgent + "\ngetOptions: " + JSON.stringify(options, undefined, 2) + "\nvisitorId: " + result.visitorId + "\ncomponents: " + componentsToDebugString(components) + "\n```"); | ||
1277 | + } | ||
1278 | + return [2 /*return*/, result]; | ||
1279 | + } | ||
1280 | + }); | ||
1281 | + }); | ||
1282 | + }; | ||
1283 | + return OpenAgent; | ||
1284 | + }()); | ||
1285 | + /** | ||
1286 | + * Builds an instance of Agent and waits a delay required for a proper operation. | ||
1287 | + */ | ||
1288 | + function load(_a) { | ||
1289 | + var _b = (_a === void 0 ? {} : _a).delayFallback, delayFallback = _b === void 0 ? 50 : _b; | ||
1290 | + return __awaiter(this, void 0, void 0, function () { | ||
1291 | + return __generator(this, function (_c) { | ||
1292 | + switch (_c.label) { | ||
1293 | + case 0: | ||
1294 | + // A delay is required to ensure consistent entropy components. | ||
1295 | + // See https://github.com/fingerprintjs/fingerprintjs/issues/254 | ||
1296 | + // and https://github.com/fingerprintjs/fingerprintjs/issues/307 | ||
1297 | + return [4 /*yield*/, requestIdleCallbackIfAvailable(delayFallback)]; | ||
1298 | + case 1: | ||
1299 | + // A delay is required to ensure consistent entropy components. | ||
1300 | + // See https://github.com/fingerprintjs/fingerprintjs/issues/254 | ||
1301 | + // and https://github.com/fingerprintjs/fingerprintjs/issues/307 | ||
1302 | + _c.sent(); | ||
1303 | + return [2 /*return*/, new OpenAgent()]; | ||
1304 | + } | ||
1305 | + }); | ||
1306 | + }); | ||
1307 | + } | ||
1308 | + | ||
1309 | + // The default export is a syntax sugar (`import * as FP from '...' → import FP from '...'`). | ||
1310 | + // It should contain all the public exported values. | ||
1311 | + var index = { load: load, hashComponents: hashComponents, componentsToDebugString: componentsToDebugString }; | ||
1312 | + // The exports below are for private usage. They may change unexpectedly. Use them at your own risk. | ||
1313 | + /** Not documented, out of Semantic Versioning, usage is at your own risk */ | ||
1314 | + var murmurX64Hash128 = x64hash128; | ||
1315 | + | ||
1316 | + exports.componentsToDebugString = componentsToDebugString; | ||
1317 | + exports.default = index; | ||
1318 | + exports.getComponents = getComponents; | ||
1319 | + exports.hashComponents = hashComponents; | ||
1320 | + exports.isChromium = isChromium; | ||
1321 | + exports.isDesktopSafari = isDesktopSafari; | ||
1322 | + exports.isEdgeHTML = isEdgeHTML; | ||
1323 | + exports.isGecko = isGecko; | ||
1324 | + exports.isTrident = isTrident; | ||
1325 | + exports.isWebKit = isWebKit; | ||
1326 | + exports.load = load; | ||
1327 | + exports.murmurX64Hash128 = murmurX64Hash128; | ||
1328 | + | ||
1329 | + return exports; | ||
1330 | + | ||
1331 | +}({})); |
dist/fp.min.js
0 → 100644
1 | +/** | ||
2 | + * FingerprintJS v3.0.3 - Copyright (c) FingerprintJS, Inc, 2020 (https://fingerprintjs.com) | ||
3 | + * Licensed under the MIT (http://www.opensource.org/licenses/mit-license.php) license. | ||
4 | + * | ||
5 | + * This software contains code from open-source projects: | ||
6 | + * MurmurHash3 by Karan Lyons (https://github.com/karanlyons/murmurHash3.js) | ||
7 | + */ | ||
8 | + | ||
9 | +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=window,g=navigator,p=document;function m(){return h(["MSCSSMatrix"in v,"msSetImmediate"in v,"msIndexedDB"in v,"msMaxTouchPoints"in g,"msPointerEnabled"in g])>=4}function y(){return h(["msWriteProfilerMark"in v,"MSStream"in v,"msLaunchUri"in g,"msSaveBlob"in g])>=3&&!m()}function w(){return h(["webkitPersistentStorage"in g,"webkitTemporaryStorage"in g,0===g.vendor.indexOf("Google"),"webkitResolveLocalFileSystemURL"in v,"BatteryManager"in v,"webkitMediaStream"in v,"webkitSpeechGrammar"in v])>=5}function b(){return h(["ApplePayError"in v,"CSSPrimitiveValue"in v,"Counter"in v,0===g.vendor.indexOf("Apple"),"getStorageUpdates"in g,"WebKitMediaKeys"in v])>=4}function S(){return h(["safari"in v,!("DeviceMotionEvent"in v),!("ongestureend"in v),!("standalone"in g)])>=3}var C=window,A=document;function M(e,t,n){(function(e){return e&&"function"==typeof e.setValueAtTime})(t)&&t.setValueAtTime(n,e.currentTime)}function T(e){return new Promise((function(t,n){e.oncomplete=function(e){return t(e.renderedBuffer)};var r=3,o=function(){switch(e.startRendering(),e.state){case"running":setTimeout((function(){return n(x("timeout"))}),1e3);break;case"suspended":A.hidden||r--,r>0?setTimeout(o,500):n(x("suspended"))}};o()}))}function k(e){for(var t=0,n=4500;n<5e3;++n)t+=Math.abs(e[n]);return t}function x(e){var t=new Error(e);return t.name=e,t}var P=document,I=["monospace","sans-serif","serif"],O=["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"],E={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"};function D(e){return e.toDataURL()}var R=navigator,B=window;var L=navigator;var F=window;var G=window;var H=window;var U=document;var W={osCpu:function(){return navigator.oscpu},languages:function(){var e=[],t=L.language||L.userLanguage||L.browserLanguage||L.systemLanguage;if(void 0!==t&&e.push([t]),Array.isArray(L.languages))w()&&h([!("MediaSettingsRange"in v),"RTCEncodedAudioFrame"in v,""+v.Intl=="[object Intl]",""+v.Reflect=="[object Reflect]"])>=3||e.push(L.languages);else if("string"==typeof L.languages){var n=L.languages;n&&e.push(n.split(","))}return e},colorDepth:function(){return window.screen.colorDepth},deviceMemory:function(){return navigator.deviceMemory},screenResolution:function(){var e=[f(F.screen.width),f(F.screen.height)];return e.sort().reverse(),e},availableScreenResolution:function(){if(G.screen.availWidth&&G.screen.availHeight){var e=[f(G.screen.availWidth),f(G.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=H.Intl)||void 0===e?void 0:e.DateTimeFormat)return(new H.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()&&!y())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:D(t)}},touchSupport:function(){var e,t=0;void 0!==R.maxTouchPoints?t=f(R.maxTouchPoints):void 0!==R.msMaxTouchPoints&&(t=R.msMaxTouchPoints);try{document.createEvent("TouchEvent"),e=!0}catch(n){e=!1}return{maxTouchPoints:t,touchEvent:e,touchStart:"ontouchstart"in B}},fonts:function(){var e=P.body,t=P.createElement("div"),n=P.createElement("div"),r={},o={},i=function(){var e=P.createElement("span");e.textContent="mmMwWLliI0O&1";for(var t=0,n=Object.keys(E);t<n.length;t++){var r=n[t];e.style[r]=E[r]}return e},a=function(e){return I.some((function(t,n){return e[n].offsetWidth!==r[t]||e[n].offsetHeight!==o[t]}))},c=I.map((function(e){var n=i();return n.style.fontFamily=e,t.appendChild(n),n}));e.appendChild(t);for(var u=0,s=I.length;u<s;u++)r[I[u]]=c[u].offsetWidth,o[I[u]]=c[u].offsetHeight;var l=function(){for(var e={},t=function(t){e[t]=I.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=O;r<o.length;r++){t(o[r])}return e}();e.appendChild(n);for(var f=[],d=0,h=O.length;d<h;d++)a(l[O[d]])&&f.push(O[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;return l(this,(function(a){switch(a.label){case 0:if(b()&&!S()&&!(h(["DOMRectList"in v,"RTCPeerConnectionIceEvent"in v,"SVGGeometryElement"in v,"ontransitioncancel"in v])>=3))return[2,-1];if(!(e=C.OfflineAudioContext||C.webkitOfflineAudioContext))return[2,-2];t=new e(1,44100,44100),(n=t.createOscillator()).type="triangle",M(t,n.frequency,1e4),r=t.createDynamicsCompressor(),M(t,r.threshold,-50),M(t,r.knee,40),M(t,r.ratio,12),M(t,r.reduction,-20),M(t,r.attack,0),M(t,r.release,.25),n.connect(r),r.connect(t.destination),n.start(0),a.label=1;case 1:return a.trys.push([1,3,4,5]),[4,T(t)];case 2:return o=a.sent(),[3,5];case 3:if("timeout"===(i=a.sent()).name||"suspended"===i.name)return[2,-3];throw i;case 4:return n.disconnect(),r.disconnect(),[7];case 5:return[2,k(o.getChannelData(0))]}}))}))},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{U.cookie="cookietest=1";var e=-1!==U.cookie.indexOf("cookietest=");return U.cookie="cookietest=1; expires=Thu, 01-Jan-1970 00:00:01 GMT",e}catch(t){return!1}}};function j(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 N(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 z(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 V=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,j(W,void 0,[])];case 1:return t=r.sent(),n=function(e){var t;return{components:e,get visitorId(){return void 0===t&&(t=z(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.3\nuserAgent: "+navigator.userAgent+"\ngetOptions: "+JSON.stringify(e,void 0,2)+"\nvisitorId: "+n.visitorId+"\ncomponents: "+N(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 V]}var t}))}))}var q={load:J,hashComponents:z,componentsToDebugString:N},K=c;return e.componentsToDebugString=N,e.default=q,e.getComponents=j,e.hashComponents=z,e.isChromium=w,e.isDesktopSafari=S,e.isEdgeHTML=y,e.isGecko=function(){var e;return h(["buildID"in g,(null===(e=p.documentElement)||void 0===e?void 0:e.style)&&"MozAppearance"in p.documentElement.style,"MediaRecorderErrorEvent"in v,"mozInnerScreenX"in v,"CSSMozDocumentRule"in v,"CanvasCaptureMediaStream"in v])>=4},e.isTrident=m,e.isWebKit=b,e.load=J,e.murmurX64Hash128=K,e}({}); |
dist/fp.umd.js
0 → 100644
1 | +/** | ||
2 | + * FingerprintJS v3.0.3 - Copyright (c) FingerprintJS, Inc, 2020 (https://fingerprintjs.com) | ||
3 | + * Licensed under the MIT (http://www.opensource.org/licenses/mit-license.php) license. | ||
4 | + * | ||
5 | + * This software contains code from open-source projects: | ||
6 | + * MurmurHash3 by Karan Lyons (https://github.com/karanlyons/murmurHash3.js) | ||
7 | + */ | ||
8 | + | ||
9 | +(function (global, factory) { | ||
10 | + typeof exports === 'object' && typeof module !== 'undefined' ? factory(exports) : | ||
11 | + typeof define === 'function' && define.amd ? define(['exports'], factory) : | ||
12 | + (global = typeof globalThis !== 'undefined' ? globalThis : global || self, factory(global.FingerprintJS = {})); | ||
13 | +}(this, (function (exports) { 'use strict'; | ||
14 | + | ||
15 | + /* | ||
16 | + * Taken from https://github.com/karanlyons/murmurHash3.js/blob/a33d0723127e2e5415056c455f8aed2451ace208/murmurHash3.js | ||
17 | + */ | ||
18 | + // | ||
19 | + // Given two 64bit ints (as an array of two 32bit ints) returns the two | ||
20 | + // added together as a 64bit int (as an array of two 32bit ints). | ||
21 | + // | ||
22 | + function x64Add(m, n) { | ||
23 | + m = [m[0] >>> 16, m[0] & 0xffff, m[1] >>> 16, m[1] & 0xffff]; | ||
24 | + n = [n[0] >>> 16, n[0] & 0xffff, n[1] >>> 16, n[1] & 0xffff]; | ||
25 | + var o = [0, 0, 0, 0]; | ||
26 | + o[3] += m[3] + n[3]; | ||
27 | + o[2] += o[3] >>> 16; | ||
28 | + o[3] &= 0xffff; | ||
29 | + o[2] += m[2] + n[2]; | ||
30 | + o[1] += o[2] >>> 16; | ||
31 | + o[2] &= 0xffff; | ||
32 | + o[1] += m[1] + n[1]; | ||
33 | + o[0] += o[1] >>> 16; | ||
34 | + o[1] &= 0xffff; | ||
35 | + o[0] += m[0] + n[0]; | ||
36 | + o[0] &= 0xffff; | ||
37 | + return [(o[0] << 16) | o[1], (o[2] << 16) | o[3]]; | ||
38 | + } | ||
39 | + // | ||
40 | + // Given two 64bit ints (as an array of two 32bit ints) returns the two | ||
41 | + // multiplied together as a 64bit int (as an array of two 32bit ints). | ||
42 | + // | ||
43 | + function x64Multiply(m, n) { | ||
44 | + m = [m[0] >>> 16, m[0] & 0xffff, m[1] >>> 16, m[1] & 0xffff]; | ||
45 | + n = [n[0] >>> 16, n[0] & 0xffff, n[1] >>> 16, n[1] & 0xffff]; | ||
46 | + var o = [0, 0, 0, 0]; | ||
47 | + o[3] += m[3] * n[3]; | ||
48 | + o[2] += o[3] >>> 16; | ||
49 | + o[3] &= 0xffff; | ||
50 | + o[2] += m[2] * n[3]; | ||
51 | + o[1] += o[2] >>> 16; | ||
52 | + o[2] &= 0xffff; | ||
53 | + o[2] += m[3] * n[2]; | ||
54 | + o[1] += o[2] >>> 16; | ||
55 | + o[2] &= 0xffff; | ||
56 | + o[1] += m[1] * n[3]; | ||
57 | + o[0] += o[1] >>> 16; | ||
58 | + o[1] &= 0xffff; | ||
59 | + o[1] += m[2] * n[2]; | ||
60 | + o[0] += o[1] >>> 16; | ||
61 | + o[1] &= 0xffff; | ||
62 | + o[1] += m[3] * n[1]; | ||
63 | + o[0] += o[1] >>> 16; | ||
64 | + o[1] &= 0xffff; | ||
65 | + o[0] += m[0] * n[3] + m[1] * n[2] + m[2] * n[1] + m[3] * n[0]; | ||
66 | + o[0] &= 0xffff; | ||
67 | + return [(o[0] << 16) | o[1], (o[2] << 16) | o[3]]; | ||
68 | + } | ||
69 | + // | ||
70 | + // Given a 64bit int (as an array of two 32bit ints) and an int | ||
71 | + // representing a number of bit positions, returns the 64bit int (as an | ||
72 | + // array of two 32bit ints) rotated left by that number of positions. | ||
73 | + // | ||
74 | + function x64Rotl(m, n) { | ||
75 | + n %= 64; | ||
76 | + if (n === 32) { | ||
77 | + return [m[1], m[0]]; | ||
78 | + } | ||
79 | + else if (n < 32) { | ||
80 | + return [(m[0] << n) | (m[1] >>> (32 - n)), (m[1] << n) | (m[0] >>> (32 - n))]; | ||
81 | + } | ||
82 | + else { | ||
83 | + n -= 32; | ||
84 | + return [(m[1] << n) | (m[0] >>> (32 - n)), (m[0] << n) | (m[1] >>> (32 - n))]; | ||
85 | + } | ||
86 | + } | ||
87 | + // | ||
88 | + // Given a 64bit int (as an array of two 32bit ints) and an int | ||
89 | + // representing a number of bit positions, returns the 64bit int (as an | ||
90 | + // array of two 32bit ints) shifted left by that number of positions. | ||
91 | + // | ||
92 | + function x64LeftShift(m, n) { | ||
93 | + n %= 64; | ||
94 | + if (n === 0) { | ||
95 | + return m; | ||
96 | + } | ||
97 | + else if (n < 32) { | ||
98 | + return [(m[0] << n) | (m[1] >>> (32 - n)), m[1] << n]; | ||
99 | + } | ||
100 | + else { | ||
101 | + return [m[1] << (n - 32), 0]; | ||
102 | + } | ||
103 | + } | ||
104 | + // | ||
105 | + // Given two 64bit ints (as an array of two 32bit ints) returns the two | ||
106 | + // xored together as a 64bit int (as an array of two 32bit ints). | ||
107 | + // | ||
108 | + function x64Xor(m, n) { | ||
109 | + return [m[0] ^ n[0], m[1] ^ n[1]]; | ||
110 | + } | ||
111 | + // | ||
112 | + // Given a block, returns murmurHash3's final x64 mix of that block. | ||
113 | + // (`[0, h[0] >>> 1]` is a 33 bit unsigned right shift. This is the | ||
114 | + // only place where we need to right shift 64bit ints.) | ||
115 | + // | ||
116 | + function x64Fmix(h) { | ||
117 | + h = x64Xor(h, [0, h[0] >>> 1]); | ||
118 | + h = x64Multiply(h, [0xff51afd7, 0xed558ccd]); | ||
119 | + h = x64Xor(h, [0, h[0] >>> 1]); | ||
120 | + h = x64Multiply(h, [0xc4ceb9fe, 0x1a85ec53]); | ||
121 | + h = x64Xor(h, [0, h[0] >>> 1]); | ||
122 | + return h; | ||
123 | + } | ||
124 | + // | ||
125 | + // Given a string and an optional seed as an int, returns a 128 bit | ||
126 | + // hash using the x64 flavor of MurmurHash3, as an unsigned hex. | ||
127 | + // | ||
128 | + function x64hash128(key, seed) { | ||
129 | + key = key || ''; | ||
130 | + seed = seed || 0; | ||
131 | + var remainder = key.length % 16; | ||
132 | + var bytes = key.length - remainder; | ||
133 | + var h1 = [0, seed]; | ||
134 | + var h2 = [0, seed]; | ||
135 | + var k1 = [0, 0]; | ||
136 | + var k2 = [0, 0]; | ||
137 | + var c1 = [0x87c37b91, 0x114253d5]; | ||
138 | + var c2 = [0x4cf5ad43, 0x2745937f]; | ||
139 | + var i; | ||
140 | + for (i = 0; i < bytes; i = i + 16) { | ||
141 | + k1 = [ | ||
142 | + (key.charCodeAt(i + 4) & 0xff) | | ||
143 | + ((key.charCodeAt(i + 5) & 0xff) << 8) | | ||
144 | + ((key.charCodeAt(i + 6) & 0xff) << 16) | | ||
145 | + ((key.charCodeAt(i + 7) & 0xff) << 24), | ||
146 | + (key.charCodeAt(i) & 0xff) | | ||
147 | + ((key.charCodeAt(i + 1) & 0xff) << 8) | | ||
148 | + ((key.charCodeAt(i + 2) & 0xff) << 16) | | ||
149 | + ((key.charCodeAt(i + 3) & 0xff) << 24), | ||
150 | + ]; | ||
151 | + k2 = [ | ||
152 | + (key.charCodeAt(i + 12) & 0xff) | | ||
153 | + ((key.charCodeAt(i + 13) & 0xff) << 8) | | ||
154 | + ((key.charCodeAt(i + 14) & 0xff) << 16) | | ||
155 | + ((key.charCodeAt(i + 15) & 0xff) << 24), | ||
156 | + (key.charCodeAt(i + 8) & 0xff) | | ||
157 | + ((key.charCodeAt(i + 9) & 0xff) << 8) | | ||
158 | + ((key.charCodeAt(i + 10) & 0xff) << 16) | | ||
159 | + ((key.charCodeAt(i + 11) & 0xff) << 24), | ||
160 | + ]; | ||
161 | + k1 = x64Multiply(k1, c1); | ||
162 | + k1 = x64Rotl(k1, 31); | ||
163 | + k1 = x64Multiply(k1, c2); | ||
164 | + h1 = x64Xor(h1, k1); | ||
165 | + h1 = x64Rotl(h1, 27); | ||
166 | + h1 = x64Add(h1, h2); | ||
167 | + h1 = x64Add(x64Multiply(h1, [0, 5]), [0, 0x52dce729]); | ||
168 | + k2 = x64Multiply(k2, c2); | ||
169 | + k2 = x64Rotl(k2, 33); | ||
170 | + k2 = x64Multiply(k2, c1); | ||
171 | + h2 = x64Xor(h2, k2); | ||
172 | + h2 = x64Rotl(h2, 31); | ||
173 | + h2 = x64Add(h2, h1); | ||
174 | + h2 = x64Add(x64Multiply(h2, [0, 5]), [0, 0x38495ab5]); | ||
175 | + } | ||
176 | + k1 = [0, 0]; | ||
177 | + k2 = [0, 0]; | ||
178 | + switch (remainder) { | ||
179 | + case 15: | ||
180 | + k2 = x64Xor(k2, x64LeftShift([0, key.charCodeAt(i + 14)], 48)); | ||
181 | + // fallthrough | ||
182 | + case 14: | ||
183 | + k2 = x64Xor(k2, x64LeftShift([0, key.charCodeAt(i + 13)], 40)); | ||
184 | + // fallthrough | ||
185 | + case 13: | ||
186 | + k2 = x64Xor(k2, x64LeftShift([0, key.charCodeAt(i + 12)], 32)); | ||
187 | + // fallthrough | ||
188 | + case 12: | ||
189 | + k2 = x64Xor(k2, x64LeftShift([0, key.charCodeAt(i + 11)], 24)); | ||
190 | + // fallthrough | ||
191 | + case 11: | ||
192 | + k2 = x64Xor(k2, x64LeftShift([0, key.charCodeAt(i + 10)], 16)); | ||
193 | + // fallthrough | ||
194 | + case 10: | ||
195 | + k2 = x64Xor(k2, x64LeftShift([0, key.charCodeAt(i + 9)], 8)); | ||
196 | + // fallthrough | ||
197 | + case 9: | ||
198 | + k2 = x64Xor(k2, [0, key.charCodeAt(i + 8)]); | ||
199 | + k2 = x64Multiply(k2, c2); | ||
200 | + k2 = x64Rotl(k2, 33); | ||
201 | + k2 = x64Multiply(k2, c1); | ||
202 | + h2 = x64Xor(h2, k2); | ||
203 | + // fallthrough | ||
204 | + case 8: | ||
205 | + k1 = x64Xor(k1, x64LeftShift([0, key.charCodeAt(i + 7)], 56)); | ||
206 | + // fallthrough | ||
207 | + case 7: | ||
208 | + k1 = x64Xor(k1, x64LeftShift([0, key.charCodeAt(i + 6)], 48)); | ||
209 | + // fallthrough | ||
210 | + case 6: | ||
211 | + k1 = x64Xor(k1, x64LeftShift([0, key.charCodeAt(i + 5)], 40)); | ||
212 | + // fallthrough | ||
213 | + case 5: | ||
214 | + k1 = x64Xor(k1, x64LeftShift([0, key.charCodeAt(i + 4)], 32)); | ||
215 | + // fallthrough | ||
216 | + case 4: | ||
217 | + k1 = x64Xor(k1, x64LeftShift([0, key.charCodeAt(i + 3)], 24)); | ||
218 | + // fallthrough | ||
219 | + case 3: | ||
220 | + k1 = x64Xor(k1, x64LeftShift([0, key.charCodeAt(i + 2)], 16)); | ||
221 | + // fallthrough | ||
222 | + case 2: | ||
223 | + k1 = x64Xor(k1, x64LeftShift([0, key.charCodeAt(i + 1)], 8)); | ||
224 | + // fallthrough | ||
225 | + case 1: | ||
226 | + k1 = x64Xor(k1, [0, key.charCodeAt(i)]); | ||
227 | + k1 = x64Multiply(k1, c1); | ||
228 | + k1 = x64Rotl(k1, 31); | ||
229 | + k1 = x64Multiply(k1, c2); | ||
230 | + h1 = x64Xor(h1, k1); | ||
231 | + // fallthrough | ||
232 | + } | ||
233 | + h1 = x64Xor(h1, [0, key.length]); | ||
234 | + h2 = x64Xor(h2, [0, key.length]); | ||
235 | + h1 = x64Add(h1, h2); | ||
236 | + h2 = x64Add(h2, h1); | ||
237 | + h1 = x64Fmix(h1); | ||
238 | + h2 = x64Fmix(h2); | ||
239 | + h1 = x64Add(h1, h2); | ||
240 | + h2 = x64Add(h2, h1); | ||
241 | + return (('00000000' + (h1[0] >>> 0).toString(16)).slice(-8) + | ||
242 | + ('00000000' + (h1[1] >>> 0).toString(16)).slice(-8) + | ||
243 | + ('00000000' + (h2[0] >>> 0).toString(16)).slice(-8) + | ||
244 | + ('00000000' + (h2[1] >>> 0).toString(16)).slice(-8)); | ||
245 | + } | ||
246 | + | ||
247 | + /*! ***************************************************************************** | ||
248 | + Copyright (c) Microsoft Corporation. | ||
249 | + | ||
250 | + Permission to use, copy, modify, and/or distribute this software for any | ||
251 | + purpose with or without fee is hereby granted. | ||
252 | + | ||
253 | + THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH | ||
254 | + REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY | ||
255 | + AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, | ||
256 | + INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM | ||
257 | + LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR | ||
258 | + OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR | ||
259 | + PERFORMANCE OF THIS SOFTWARE. | ||
260 | + ***************************************************************************** */ | ||
261 | + | ||
262 | + var __assign = function() { | ||
263 | + __assign = Object.assign || function __assign(t) { | ||
264 | + for (var s, i = 1, n = arguments.length; i < n; i++) { | ||
265 | + s = arguments[i]; | ||
266 | + for (var p in s) if (Object.prototype.hasOwnProperty.call(s, p)) t[p] = s[p]; | ||
267 | + } | ||
268 | + return t; | ||
269 | + }; | ||
270 | + return __assign.apply(this, arguments); | ||
271 | + }; | ||
272 | + | ||
273 | + function __awaiter(thisArg, _arguments, P, generator) { | ||
274 | + function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); } | ||
275 | + return new (P || (P = Promise))(function (resolve, reject) { | ||
276 | + function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } } | ||
277 | + function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } } | ||
278 | + function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); } | ||
279 | + step((generator = generator.apply(thisArg, _arguments || [])).next()); | ||
280 | + }); | ||
281 | + } | ||
282 | + | ||
283 | + function __generator(thisArg, body) { | ||
284 | + var _ = { label: 0, sent: function() { if (t[0] & 1) throw t[1]; return t[1]; }, trys: [], ops: [] }, f, y, t, g; | ||
285 | + return g = { next: verb(0), "throw": verb(1), "return": verb(2) }, typeof Symbol === "function" && (g[Symbol.iterator] = function() { return this; }), g; | ||
286 | + function verb(n) { return function (v) { return step([n, v]); }; } | ||
287 | + function step(op) { | ||
288 | + if (f) throw new TypeError("Generator is already executing."); | ||
289 | + while (_) try { | ||
290 | + 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; | ||
291 | + if (y = 0, t) op = [op[0] & 2, t.value]; | ||
292 | + switch (op[0]) { | ||
293 | + case 0: case 1: t = op; break; | ||
294 | + case 4: _.label++; return { value: op[1], done: false }; | ||
295 | + case 5: _.label++; y = op[1]; op = [0]; continue; | ||
296 | + case 7: op = _.ops.pop(); _.trys.pop(); continue; | ||
297 | + default: | ||
298 | + if (!(t = _.trys, t = t.length > 0 && t[t.length - 1]) && (op[0] === 6 || op[0] === 2)) { _ = 0; continue; } | ||
299 | + if (op[0] === 3 && (!t || (op[1] > t[0] && op[1] < t[3]))) { _.label = op[1]; break; } | ||
300 | + if (op[0] === 6 && _.label < t[1]) { _.label = t[1]; t = op; break; } | ||
301 | + if (t && _.label < t[2]) { _.label = t[2]; _.ops.push(op); break; } | ||
302 | + if (t[2]) _.ops.pop(); | ||
303 | + _.trys.pop(); continue; | ||
304 | + } | ||
305 | + op = body.call(thisArg, _); | ||
306 | + } catch (e) { op = [6, e]; y = 0; } finally { f = t = 0; } | ||
307 | + if (op[0] & 5) throw op[1]; return { value: op[0] ? op[1] : void 0, done: true }; | ||
308 | + } | ||
309 | + } | ||
310 | + | ||
311 | + var version = "3.0.3"; | ||
312 | + | ||
313 | + function requestIdleCallbackIfAvailable(fallbackTimeout) { | ||
314 | + return new Promise(function (resolve) { | ||
315 | + if (window.requestIdleCallback) { | ||
316 | + window.requestIdleCallback(function () { return resolve(); }); | ||
317 | + } | ||
318 | + else { | ||
319 | + setTimeout(resolve, fallbackTimeout); | ||
320 | + } | ||
321 | + }); | ||
322 | + } | ||
323 | + | ||
324 | + /* | ||
325 | + * This file contains functions to work with pure data only (no browser features, DOM, side effects, etc). | ||
326 | + */ | ||
327 | + /** | ||
328 | + * Does the same as Array.prototype.includes but has better typing | ||
329 | + */ | ||
330 | + function includes(haystack, needle) { | ||
331 | + for (var i = 0, l = haystack.length; i < l; ++i) { | ||
332 | + if (haystack[i] === needle) { | ||
333 | + return true; | ||
334 | + } | ||
335 | + } | ||
336 | + return false; | ||
337 | + } | ||
338 | + /** | ||
339 | + * Like `!includes()` but with proper typing | ||
340 | + */ | ||
341 | + function excludes(haystack, needle) { | ||
342 | + return !includes(haystack, needle); | ||
343 | + } | ||
344 | + /** | ||
345 | + * Be careful, NaN can return | ||
346 | + */ | ||
347 | + function toInt(value) { | ||
348 | + if (typeof value === 'number') { | ||
349 | + return value | 0; | ||
350 | + } | ||
351 | + return parseInt(value); | ||
352 | + } | ||
353 | + /** | ||
354 | + * Be careful, NaN can return | ||
355 | + */ | ||
356 | + function toFloat(value) { | ||
357 | + if (typeof value === 'number') { | ||
358 | + return value; | ||
359 | + } | ||
360 | + return parseFloat(value); | ||
361 | + } | ||
362 | + function countTruthy(values) { | ||
363 | + return values.reduce(function (sum, value) { return sum + (value ? 1 : 0); }, 0); | ||
364 | + } | ||
365 | + | ||
366 | + /* | ||
367 | + * Functions to help with browser features | ||
368 | + */ | ||
369 | + var w = window; | ||
370 | + var n = navigator; | ||
371 | + var d = document; | ||
372 | + /** | ||
373 | + * Checks whether the browser is based on Trident (the Internet Explorer engine) without using user-agent. | ||
374 | + * | ||
375 | + * Warning for package users: | ||
376 | + * This function is out of Semantic Versioning, i.e. can change unexpectedly. Usage is at your own risk. | ||
377 | + */ | ||
378 | + function isTrident() { | ||
379 | + // The properties are checked to be in IE 10, IE 11 and not to be in other browsers in October 2020 | ||
380 | + return (countTruthy([ | ||
381 | + 'MSCSSMatrix' in w, | ||
382 | + 'msSetImmediate' in w, | ||
383 | + 'msIndexedDB' in w, | ||
384 | + 'msMaxTouchPoints' in n, | ||
385 | + 'msPointerEnabled' in n, | ||
386 | + ]) >= 4); | ||
387 | + } | ||
388 | + /** | ||
389 | + * Checks whether the browser is based on EdgeHTML (the pre-Chromium Edge engine) without using user-agent. | ||
390 | + * | ||
391 | + * Warning for package users: | ||
392 | + * This function is out of Semantic Versioning, i.e. can change unexpectedly. Usage is at your own risk. | ||
393 | + */ | ||
394 | + function isEdgeHTML() { | ||
395 | + // Based on research in October 2020 | ||
396 | + return (countTruthy(['msWriteProfilerMark' in w, 'MSStream' in w, 'msLaunchUri' in n, 'msSaveBlob' in n]) >= 3 && | ||
397 | + !isTrident()); | ||
398 | + } | ||
399 | + /** | ||
400 | + * Checks whether the browser is based on Chromium without using user-agent. | ||
401 | + * | ||
402 | + * Warning for package users: | ||
403 | + * This function is out of Semantic Versioning, i.e. can change unexpectedly. Usage is at your own risk. | ||
404 | + */ | ||
405 | + function isChromium() { | ||
406 | + // Based on research in October 2020. Tested to detect Chromium 42-86. | ||
407 | + return (countTruthy([ | ||
408 | + 'webkitPersistentStorage' in n, | ||
409 | + 'webkitTemporaryStorage' in n, | ||
410 | + n.vendor.indexOf('Google') === 0, | ||
411 | + 'webkitResolveLocalFileSystemURL' in w, | ||
412 | + 'BatteryManager' in w, | ||
413 | + 'webkitMediaStream' in w, | ||
414 | + 'webkitSpeechGrammar' in w, | ||
415 | + ]) >= 5); | ||
416 | + } | ||
417 | + /** | ||
418 | + * Checks whether the browser is based on mobile or desktop Safari without using user-agent. | ||
419 | + * All iOS browsers use WebKit (the Safari engine). | ||
420 | + * | ||
421 | + * Warning for package users: | ||
422 | + * This function is out of Semantic Versioning, i.e. can change unexpectedly. Usage is at your own risk. | ||
423 | + */ | ||
424 | + function isWebKit() { | ||
425 | + // Based on research in September 2020 | ||
426 | + return (countTruthy([ | ||
427 | + 'ApplePayError' in w, | ||
428 | + 'CSSPrimitiveValue' in w, | ||
429 | + 'Counter' in w, | ||
430 | + n.vendor.indexOf('Apple') === 0, | ||
431 | + 'getStorageUpdates' in n, | ||
432 | + 'WebKitMediaKeys' in w, | ||
433 | + ]) >= 4); | ||
434 | + } | ||
435 | + /** | ||
436 | + * Checks whether the WebKit browser is a desktop Safari. | ||
437 | + * | ||
438 | + * Warning for package users: | ||
439 | + * This function is out of Semantic Versioning, i.e. can change unexpectedly. Usage is at your own risk. | ||
440 | + */ | ||
441 | + function isDesktopSafari() { | ||
442 | + return (countTruthy([ | ||
443 | + 'safari' in w, | ||
444 | + !('DeviceMotionEvent' in w), | ||
445 | + !('ongestureend' in w), | ||
446 | + !('standalone' in n), | ||
447 | + ]) >= 3); | ||
448 | + } | ||
449 | + /** | ||
450 | + * Checks whether the browser is based on Gecko (Firefox engine) without using user-agent. | ||
451 | + * | ||
452 | + * Warning for package users: | ||
453 | + * This function is out of Semantic Versioning, i.e. can change unexpectedly. Usage is at your own risk. | ||
454 | + */ | ||
455 | + function isGecko() { | ||
456 | + var _a; | ||
457 | + // Based on research in September 2020 | ||
458 | + return (countTruthy([ | ||
459 | + 'buildID' in n, | ||
460 | + ((_a = d.documentElement) === null || _a === void 0 ? void 0 : _a.style) && 'MozAppearance' in d.documentElement.style, | ||
461 | + 'MediaRecorderErrorEvent' in w, | ||
462 | + 'mozInnerScreenX' in w, | ||
463 | + 'CSSMozDocumentRule' in w, | ||
464 | + 'CanvasCaptureMediaStream' in w, | ||
465 | + ]) >= 4); | ||
466 | + } | ||
467 | + /** | ||
468 | + * Checks whether the browser is based on Chromium version ≥86 without using user-agent. | ||
469 | + * It doesn't check that the browser is based on Chromium, there is a separate function for this. | ||
470 | + */ | ||
471 | + function isChromium86OrNewer() { | ||
472 | + // Checked in Chrome 85 vs Chrome 86 both on desktop and Android | ||
473 | + return (countTruthy([ | ||
474 | + !('MediaSettingsRange' in w), | ||
475 | + 'RTCEncodedAudioFrame' in w, | ||
476 | + '' + w.Intl === '[object Intl]', | ||
477 | + '' + w.Reflect === '[object Reflect]', | ||
478 | + ]) >= 3); | ||
479 | + } | ||
480 | + /** | ||
481 | + * Checks whether the browser is based on WebKit version ≥606 (Safari ≥12) without using user-agent. | ||
482 | + * It doesn't check that the browser is based on WebKit, there is a separate function for this. | ||
483 | + * | ||
484 | + * @link https://en.wikipedia.org/wiki/Safari_version_history#Release_history Safari-WebKit versions map | ||
485 | + */ | ||
486 | + function isWebKit606OrNewer() { | ||
487 | + // Checked in Safari 9–14 | ||
488 | + return (countTruthy([ | ||
489 | + 'DOMRectList' in w, | ||
490 | + 'RTCPeerConnectionIceEvent' in w, | ||
491 | + 'SVGGeometryElement' in w, | ||
492 | + 'ontransitioncancel' in w, | ||
493 | + ]) >= 3); | ||
494 | + } | ||
495 | + | ||
496 | + var w$1 = window; | ||
497 | + var d$1 = document; | ||
498 | + // Inspired by and based on https://github.com/cozylife/audio-fingerprint | ||
499 | + function getAudioFingerprint() { | ||
500 | + return __awaiter(this, void 0, void 0, function () { | ||
501 | + var AudioContext, context, oscillator, compressor, buffer, error_1; | ||
502 | + return __generator(this, function (_a) { | ||
503 | + switch (_a.label) { | ||
504 | + case 0: | ||
505 | + // In some browsers, audio context always stays suspended unless the context is started in response to a user action | ||
506 | + // (e.g. a click or a tap). It prevents audio fingerprint from being taken at an arbitrary moment of time. | ||
507 | + // Such browsers are old and unpopular, so the audio fingerprinting is just skipped in them. | ||
508 | + // See a similar case explanation at https://stackoverflow.com/questions/46363048/onaudioprocess-not-called-on-ios11#46534088 | ||
509 | + if (doesCurrentBrowserSuspendAudioContext()) { | ||
510 | + return [2 /*return*/, -1]; | ||
511 | + } | ||
512 | + AudioContext = w$1.OfflineAudioContext || w$1.webkitOfflineAudioContext; | ||
513 | + if (!AudioContext) { | ||
514 | + return [2 /*return*/, -2]; | ||
515 | + } | ||
516 | + context = new AudioContext(1, 44100, 44100); | ||
517 | + oscillator = context.createOscillator(); | ||
518 | + oscillator.type = 'triangle'; | ||
519 | + setAudioParam(context, oscillator.frequency, 10000); | ||
520 | + compressor = context.createDynamicsCompressor(); | ||
521 | + setAudioParam(context, compressor.threshold, -50); | ||
522 | + setAudioParam(context, compressor.knee, 40); | ||
523 | + setAudioParam(context, compressor.ratio, 12); | ||
524 | + setAudioParam(context, compressor.reduction, -20); | ||
525 | + setAudioParam(context, compressor.attack, 0); | ||
526 | + setAudioParam(context, compressor.release, 0.25); | ||
527 | + oscillator.connect(compressor); | ||
528 | + compressor.connect(context.destination); | ||
529 | + oscillator.start(0); | ||
530 | + _a.label = 1; | ||
531 | + case 1: | ||
532 | + _a.trys.push([1, 3, 4, 5]); | ||
533 | + return [4 /*yield*/, renderAudio(context)]; | ||
534 | + case 2: | ||
535 | + buffer = _a.sent(); | ||
536 | + return [3 /*break*/, 5]; | ||
537 | + case 3: | ||
538 | + error_1 = _a.sent(); | ||
539 | + if (error_1.name === "timeout" /* Timeout */ || error_1.name === "suspended" /* Suspended */) { | ||
540 | + return [2 /*return*/, -3]; | ||
541 | + } | ||
542 | + throw error_1; | ||
543 | + case 4: | ||
544 | + oscillator.disconnect(); | ||
545 | + compressor.disconnect(); | ||
546 | + return [7 /*endfinally*/]; | ||
547 | + case 5: return [2 /*return*/, getHash(buffer.getChannelData(0))]; | ||
548 | + } | ||
549 | + }); | ||
550 | + }); | ||
551 | + } | ||
552 | + /** | ||
553 | + * Checks if the current browser is known to always suspend audio context | ||
554 | + */ | ||
555 | + function doesCurrentBrowserSuspendAudioContext() { | ||
556 | + return isWebKit() && !isDesktopSafari() && !isWebKit606OrNewer(); | ||
557 | + } | ||
558 | + function setAudioParam(context, param, value) { | ||
559 | + var isAudioParam = function (value) { | ||
560 | + return value && typeof value.setValueAtTime === 'function'; | ||
561 | + }; | ||
562 | + if (isAudioParam(param)) { | ||
563 | + param.setValueAtTime(value, context.currentTime); | ||
564 | + } | ||
565 | + } | ||
566 | + function renderAudio(context) { | ||
567 | + var resumeTriesMaxCount = 3; | ||
568 | + var resumeRetryDelay = 500; | ||
569 | + var runningTimeout = 1000; | ||
570 | + return new Promise(function (resolve, reject) { | ||
571 | + context.oncomplete = function (event) { return resolve(event.renderedBuffer); }; | ||
572 | + var resumeTriesLeft = resumeTriesMaxCount; | ||
573 | + var tryResume = function () { | ||
574 | + context.startRendering(); | ||
575 | + switch (context.state) { | ||
576 | + case 'running': | ||
577 | + setTimeout(function () { return reject(makeInnerError("timeout" /* Timeout */)); }, runningTimeout); | ||
578 | + break; | ||
579 | + // Sometimes the audio context doesn't start after calling `startRendering` (in addition to the cases where | ||
580 | + // audio context doesn't start at all). A known case is starting an audio context when the browser tab is in | ||
581 | + // background on iPhone. Retries usually help in this case. | ||
582 | + case 'suspended': | ||
583 | + // The audio context can reject starting until the tab is in foreground. Long fingerprint duration | ||
584 | + // in background isn't a problem, therefore the retry attempts don't count in background. It can lead to | ||
585 | + // a situation when a fingerprint takes very long time and finishes successfully. FYI, the audio context | ||
586 | + // can be suspended when `document.hidden === false` and start running after a retry. | ||
587 | + if (!d$1.hidden) { | ||
588 | + resumeTriesLeft--; | ||
589 | + } | ||
590 | + if (resumeTriesLeft > 0) { | ||
591 | + setTimeout(tryResume, resumeRetryDelay); | ||
592 | + } | ||
593 | + else { | ||
594 | + reject(makeInnerError("suspended" /* Suspended */)); | ||
595 | + } | ||
596 | + break; | ||
597 | + } | ||
598 | + }; | ||
599 | + tryResume(); | ||
600 | + }); | ||
601 | + } | ||
602 | + function getHash(signal) { | ||
603 | + var hash = 0; | ||
604 | + for (var i = 4500; i < 5000; ++i) { | ||
605 | + hash += Math.abs(signal[i]); | ||
606 | + } | ||
607 | + return hash; | ||
608 | + } | ||
609 | + function makeInnerError(name) { | ||
610 | + var error = new Error(name); | ||
611 | + error.name = name; | ||
612 | + return error; | ||
613 | + } | ||
614 | + | ||
615 | + var d$2 = document; | ||
616 | + // We use m or w because these two characters take up the maximum width. | ||
617 | + // And we use a LLi so that the same matching fonts can get separated. | ||
618 | + var testString = 'mmMwWLliI0O&1'; | ||
619 | + // We test using 48px font size, we may use any size. I guess larger the better. | ||
620 | + var testSize = '48px'; | ||
621 | + // A font will be compared against all the three default fonts. | ||
622 | + // And if it doesn't match all 3 then that font is not available. | ||
623 | + var baseFonts = ['monospace', 'sans-serif', 'serif']; | ||
624 | + var fontList = [ | ||
625 | + // This is android-specific font from "Roboto" family | ||
626 | + 'sans-serif-thin', | ||
627 | + 'ARNO PRO', | ||
628 | + 'Agency FB', | ||
629 | + 'Arabic Typesetting', | ||
630 | + 'Arial Unicode MS', | ||
631 | + 'AvantGarde Bk BT', | ||
632 | + 'BankGothic Md BT', | ||
633 | + 'Batang', | ||
634 | + 'Bitstream Vera Sans Mono', | ||
635 | + 'Calibri', | ||
636 | + 'Century', | ||
637 | + 'Century Gothic', | ||
638 | + 'Clarendon', | ||
639 | + 'EUROSTILE', | ||
640 | + 'Franklin Gothic', | ||
641 | + 'Futura Bk BT', | ||
642 | + 'Futura Md BT', | ||
643 | + 'GOTHAM', | ||
644 | + 'Gill Sans', | ||
645 | + 'HELV', | ||
646 | + 'Haettenschweiler', | ||
647 | + 'Helvetica Neue', | ||
648 | + 'Humanst521 BT', | ||
649 | + 'Leelawadee', | ||
650 | + 'Letter Gothic', | ||
651 | + 'Levenim MT', | ||
652 | + 'Lucida Bright', | ||
653 | + 'Lucida Sans', | ||
654 | + 'Menlo', | ||
655 | + 'MS Mincho', | ||
656 | + 'MS Outlook', | ||
657 | + 'MS Reference Specialty', | ||
658 | + 'MS UI Gothic', | ||
659 | + 'MT Extra', | ||
660 | + 'MYRIAD PRO', | ||
661 | + 'Marlett', | ||
662 | + 'Meiryo UI', | ||
663 | + 'Microsoft Uighur', | ||
664 | + 'Minion Pro', | ||
665 | + 'Monotype Corsiva', | ||
666 | + 'PMingLiU', | ||
667 | + 'Pristina', | ||
668 | + 'SCRIPTINA', | ||
669 | + 'Segoe UI Light', | ||
670 | + 'Serifa', | ||
671 | + 'SimHei', | ||
672 | + 'Small Fonts', | ||
673 | + 'Staccato222 BT', | ||
674 | + 'TRAJAN PRO', | ||
675 | + 'Univers CE 55 Medium', | ||
676 | + 'Vrinda', | ||
677 | + 'ZWAdobeF', | ||
678 | + ]; | ||
679 | + var fontSpanStyle = { | ||
680 | + // CSS font reset to reset external styles | ||
681 | + fontStyle: 'normal', | ||
682 | + fontWeight: 'normal', | ||
683 | + letterSpacing: 'normal', | ||
684 | + lineBreak: 'auto', | ||
685 | + lineHeight: 'normal', | ||
686 | + textTransform: 'none', | ||
687 | + textAlign: 'left', | ||
688 | + textDecoration: 'none', | ||
689 | + textShadow: 'none', | ||
690 | + whiteSpace: 'normal', | ||
691 | + wordBreak: 'normal', | ||
692 | + wordSpacing: 'normal', | ||
693 | + // We need this css as in some weird browser this span elements shows up for a microSec which creates | ||
694 | + // a bad user experience | ||
695 | + position: 'absolute', | ||
696 | + left: '-9999px', | ||
697 | + fontSize: testSize, | ||
698 | + }; | ||
699 | + // kudos to http://www.lalit.org/lab/javascript-css-font-detect/ | ||
700 | + function getFonts() { | ||
701 | + var h = d$2.body; | ||
702 | + // div to load spans for the base fonts | ||
703 | + var baseFontsDiv = d$2.createElement('div'); | ||
704 | + // div to load spans for the fonts to detect | ||
705 | + var fontsDiv = d$2.createElement('div'); | ||
706 | + var defaultWidth = {}; | ||
707 | + var defaultHeight = {}; | ||
708 | + // creates a span where the fonts will be loaded | ||
709 | + var createSpan = function () { | ||
710 | + var span = d$2.createElement('span'); | ||
711 | + span.textContent = testString; | ||
712 | + for (var _i = 0, _a = Object.keys(fontSpanStyle); _i < _a.length; _i++) { | ||
713 | + var prop = _a[_i]; | ||
714 | + span.style[prop] = fontSpanStyle[prop]; | ||
715 | + } | ||
716 | + return span; | ||
717 | + }; | ||
718 | + // creates a span and load the font to detect and a base font for fallback | ||
719 | + var createSpanWithFonts = function (fontToDetect, baseFont) { | ||
720 | + var s = createSpan(); | ||
721 | + s.style.fontFamily = "'" + fontToDetect + "'," + baseFont; | ||
722 | + return s; | ||
723 | + }; | ||
724 | + // creates spans for the base fonts and adds them to baseFontsDiv | ||
725 | + var initializeBaseFontsSpans = function () { | ||
726 | + return baseFonts.map(function (baseFont) { | ||
727 | + var s = createSpan(); | ||
728 | + s.style.fontFamily = baseFont; | ||
729 | + baseFontsDiv.appendChild(s); | ||
730 | + return s; | ||
731 | + }); | ||
732 | + }; | ||
733 | + // creates spans for the fonts to detect and adds them to fontsDiv | ||
734 | + var initializeFontsSpans = function () { | ||
735 | + // Stores {fontName : [spans for that font]} | ||
736 | + var spans = {}; | ||
737 | + var _loop_1 = function (font) { | ||
738 | + spans[font] = baseFonts.map(function (baseFont) { | ||
739 | + var s = createSpanWithFonts(font, baseFont); | ||
740 | + fontsDiv.appendChild(s); | ||
741 | + return s; | ||
742 | + }); | ||
743 | + }; | ||
744 | + for (var _i = 0, fontList_1 = fontList; _i < fontList_1.length; _i++) { | ||
745 | + var font = fontList_1[_i]; | ||
746 | + _loop_1(font); | ||
747 | + } | ||
748 | + return spans; | ||
749 | + }; | ||
750 | + // checks if a font is available | ||
751 | + var isFontAvailable = function (fontSpans) { | ||
752 | + return baseFonts.some(function (baseFont, baseFontIndex) { | ||
753 | + return fontSpans[baseFontIndex].offsetWidth !== defaultWidth[baseFont] || | ||
754 | + fontSpans[baseFontIndex].offsetHeight !== defaultHeight[baseFont]; | ||
755 | + }); | ||
756 | + }; | ||
757 | + // create spans for base fonts | ||
758 | + var baseFontsSpans = initializeBaseFontsSpans(); | ||
759 | + // add the spans to the DOM | ||
760 | + h.appendChild(baseFontsDiv); | ||
761 | + // get the default width for the three base fonts | ||
762 | + for (var index = 0, length_1 = baseFonts.length; index < length_1; index++) { | ||
763 | + defaultWidth[baseFonts[index]] = baseFontsSpans[index].offsetWidth; // width for the default font | ||
764 | + defaultHeight[baseFonts[index]] = baseFontsSpans[index].offsetHeight; // height for the default font | ||
765 | + } | ||
766 | + // create spans for fonts to detect | ||
767 | + var fontsSpans = initializeFontsSpans(); | ||
768 | + // add all the spans to the DOM | ||
769 | + h.appendChild(fontsDiv); | ||
770 | + // check available fonts | ||
771 | + var available = []; | ||
772 | + for (var i = 0, l = fontList.length; i < l; i++) { | ||
773 | + if (isFontAvailable(fontsSpans[fontList[i]])) { | ||
774 | + available.push(fontList[i]); | ||
775 | + } | ||
776 | + } | ||
777 | + // remove spans from DOM | ||
778 | + h.removeChild(fontsDiv); | ||
779 | + h.removeChild(baseFontsDiv); | ||
780 | + return available; | ||
781 | + } | ||
782 | + | ||
783 | + function getPlugins() { | ||
784 | + if (isTrident()) { | ||
785 | + return []; | ||
786 | + } | ||
787 | + if (!navigator.plugins) { | ||
788 | + return undefined; | ||
789 | + } | ||
790 | + var plugins = []; | ||
791 | + // Safari 10 doesn't support iterating navigator.plugins with for...of | ||
792 | + for (var i = 0; i < navigator.plugins.length; ++i) { | ||
793 | + var plugin = navigator.plugins[i]; | ||
794 | + if (!plugin) { | ||
795 | + continue; | ||
796 | + } | ||
797 | + var mimeTypes = []; | ||
798 | + for (var j = 0; j < plugin.length; ++j) { | ||
799 | + var mimeType = plugin[j]; | ||
800 | + mimeTypes.push({ | ||
801 | + type: mimeType.type, | ||
802 | + suffixes: mimeType.suffixes, | ||
803 | + }); | ||
804 | + } | ||
805 | + plugins.push({ | ||
806 | + name: plugin.name, | ||
807 | + description: plugin.description, | ||
808 | + mimeTypes: mimeTypes, | ||
809 | + }); | ||
810 | + } | ||
811 | + return plugins; | ||
812 | + } | ||
813 | + | ||
814 | + function makeCanvasContext() { | ||
815 | + var canvas = document.createElement('canvas'); | ||
816 | + canvas.width = 240; | ||
817 | + canvas.height = 140; | ||
818 | + canvas.style.display = 'inline'; | ||
819 | + return [canvas, canvas.getContext('2d')]; | ||
820 | + } | ||
821 | + function isSupported(canvas, context) { | ||
822 | + // TODO: look into: https://developer.mozilla.org/en-US/docs/Web/API/HTMLCanvasElement/toBlob | ||
823 | + return !!(context && canvas.toDataURL); | ||
824 | + } | ||
825 | + function save(canvas) { | ||
826 | + // TODO: look into: https://developer.mozilla.org/en-US/docs/Web/API/HTMLCanvasElement/toBlob | ||
827 | + return canvas.toDataURL(); | ||
828 | + } | ||
829 | + // https://www.browserleaks.com/canvas#how-does-it-work | ||
830 | + function getCanvasFingerprint() { | ||
831 | + var _a = makeCanvasContext(), canvas = _a[0], context = _a[1]; | ||
832 | + if (!isSupported(canvas, context)) { | ||
833 | + return { winding: false, data: '' }; | ||
834 | + } | ||
835 | + // detect browser support of canvas winding | ||
836 | + // http://blogs.adobe.com/webplatform/2013/01/30/winding-rules-in-canvas/ | ||
837 | + // https://github.com/Modernizr/Modernizr/blob/master/feature-detects/canvas/winding.js | ||
838 | + context.rect(0, 0, 10, 10); | ||
839 | + context.rect(2, 2, 6, 6); | ||
840 | + var winding = !context.isPointInPath(5, 5, 'evenodd'); | ||
841 | + context.textBaseline = 'alphabetic'; | ||
842 | + context.fillStyle = '#f60'; | ||
843 | + context.fillRect(125, 1, 62, 20); | ||
844 | + context.fillStyle = '#069'; | ||
845 | + // https://github.com/Valve/fingerprintjs2/issues/66 | ||
846 | + // this can affect FP generation when applying different CSS on different websites | ||
847 | + context.font = '11pt no-real-font-123'; | ||
848 | + // the choice of emojis has a gigantic impact on rendering performance (especially in FF) | ||
849 | + // some newer emojis cause it to slow down 50-200 times | ||
850 | + // context.fillText("Cw爨m fjordbank \ud83d\ude03 gly", 2, 15) | ||
851 | + var printedText = 'Cwm fjordbank \ud83d\ude03 gly'; | ||
852 | + context.fillText(printedText, 2, 15); | ||
853 | + context.fillStyle = 'rgba(102, 204, 0, 0.2)'; | ||
854 | + context.font = '18pt Arial'; | ||
855 | + context.fillText(printedText, 4, 45); | ||
856 | + // canvas blending | ||
857 | + // http://blogs.adobe.com/webplatform/2013/01/28/blending-features-in-canvas/ | ||
858 | + // http://jsfiddle.net/NDYV8/16/ | ||
859 | + context.globalCompositeOperation = 'multiply'; | ||
860 | + context.fillStyle = 'rgb(255,0,255)'; | ||
861 | + context.beginPath(); | ||
862 | + context.arc(50, 50, 50, 0, Math.PI * 2, true); | ||
863 | + context.closePath(); | ||
864 | + context.fill(); | ||
865 | + context.fillStyle = 'rgb(0,255,255)'; | ||
866 | + context.beginPath(); | ||
867 | + context.arc(100, 50, 50, 0, Math.PI * 2, true); | ||
868 | + context.closePath(); | ||
869 | + context.fill(); | ||
870 | + context.fillStyle = 'rgb(255,255,0)'; | ||
871 | + context.beginPath(); | ||
872 | + context.arc(75, 100, 50, 0, Math.PI * 2, true); | ||
873 | + context.closePath(); | ||
874 | + context.fill(); | ||
875 | + context.fillStyle = 'rgb(255,0,255)'; | ||
876 | + // canvas winding | ||
877 | + // http://blogs.adobe.com/webplatform/2013/01/30/winding-rules-in-canvas/ | ||
878 | + // http://jsfiddle.net/NDYV8/19/ | ||
879 | + context.arc(75, 75, 75, 0, Math.PI * 2, true); | ||
880 | + context.arc(75, 75, 25, 0, Math.PI * 2, true); | ||
881 | + context.fill('evenodd'); | ||
882 | + return { | ||
883 | + winding: winding, | ||
884 | + data: save(canvas), | ||
885 | + }; | ||
886 | + } | ||
887 | + | ||
888 | + var n$1 = navigator; | ||
889 | + var w$2 = window; | ||
890 | + /** | ||
891 | + * This is a crude and primitive touch screen detection. It's not possible to currently reliably detect the availability | ||
892 | + * of a touch screen with a JS, without actually subscribing to a touch event. | ||
893 | + * | ||
894 | + * @see http://www.stucox.com/blog/you-cant-detect-a-touchscreen/ | ||
895 | + * @see https://github.com/Modernizr/Modernizr/issues/548 | ||
896 | + */ | ||
897 | + function getTouchSupport() { | ||
898 | + var maxTouchPoints = 0; | ||
899 | + var touchEvent; | ||
900 | + if (n$1.maxTouchPoints !== undefined) { | ||
901 | + maxTouchPoints = toInt(n$1.maxTouchPoints); | ||
902 | + } | ||
903 | + else if (n$1.msMaxTouchPoints !== undefined) { | ||
904 | + maxTouchPoints = n$1.msMaxTouchPoints; | ||
905 | + } | ||
906 | + try { | ||
907 | + document.createEvent('TouchEvent'); | ||
908 | + touchEvent = true; | ||
909 | + } | ||
910 | + catch (_) { | ||
911 | + touchEvent = false; | ||
912 | + } | ||
913 | + var touchStart = 'ontouchstart' in w$2; | ||
914 | + return { | ||
915 | + maxTouchPoints: maxTouchPoints, | ||
916 | + touchEvent: touchEvent, | ||
917 | + touchStart: touchStart, | ||
918 | + }; | ||
919 | + } | ||
920 | + | ||
921 | + function getOsCpu() { | ||
922 | + return navigator.oscpu; | ||
923 | + } | ||
924 | + | ||
925 | + var n$2 = navigator; | ||
926 | + function getLanguages() { | ||
927 | + var result = []; | ||
928 | + var language = n$2.language || n$2.userLanguage || n$2.browserLanguage || n$2.systemLanguage; | ||
929 | + if (language !== undefined) { | ||
930 | + result.push([language]); | ||
931 | + } | ||
932 | + if (Array.isArray(n$2.languages)) { | ||
933 | + // Starting from Chromium 86, there is only a single value in `navigator.language` in Incognito mode: | ||
934 | + // the value of `navigator.language`. Therefore the value is ignored in this browser. | ||
935 | + if (!(isChromium() && isChromium86OrNewer())) { | ||
936 | + result.push(n$2.languages); | ||
937 | + } | ||
938 | + } | ||
939 | + else if (typeof n$2.languages === 'string') { | ||
940 | + var languages = n$2.languages; | ||
941 | + if (languages) { | ||
942 | + result.push(languages.split(',')); | ||
943 | + } | ||
944 | + } | ||
945 | + return result; | ||
946 | + } | ||
947 | + | ||
948 | + function getColorDepth() { | ||
949 | + return window.screen.colorDepth; | ||
950 | + } | ||
951 | + | ||
952 | + function getDeviceMemory() { | ||
953 | + return navigator.deviceMemory; | ||
954 | + } | ||
955 | + | ||
956 | + var w$3 = window; | ||
957 | + function getScreenResolution() { | ||
958 | + // Some browsers return screen resolution as strings, e.g. "1200", instead of a number, e.g. 1200. | ||
959 | + // I suspect it's done by certain plugins that randomize browser properties to prevent fingerprinting. | ||
960 | + var dimensions = [toInt(w$3.screen.width), toInt(w$3.screen.height)]; | ||
961 | + dimensions.sort().reverse(); | ||
962 | + return dimensions; | ||
963 | + } | ||
964 | + | ||
965 | + var w$4 = window; | ||
966 | + function getAvailableScreenResolution() { | ||
967 | + if (w$4.screen.availWidth && w$4.screen.availHeight) { | ||
968 | + // Some browsers return screen resolution as strings, e.g. "1200", instead of a number, e.g. 1200. | ||
969 | + // I suspect it's done by certain plugins that randomize browser properties to prevent fingerprinting. | ||
970 | + var dimensions = [toInt(w$4.screen.availWidth), toInt(w$4.screen.availHeight)]; | ||
971 | + dimensions.sort().reverse(); | ||
972 | + return dimensions; | ||
973 | + } | ||
974 | + return undefined; | ||
975 | + } | ||
976 | + | ||
977 | + function getHardwareConcurrency() { | ||
978 | + try { | ||
979 | + // sometimes hardware concurrency is a string | ||
980 | + var concurrency = toInt(navigator.hardwareConcurrency); | ||
981 | + return isNaN(concurrency) ? 1 : concurrency; | ||
982 | + } | ||
983 | + catch (e) { | ||
984 | + return 1; | ||
985 | + } | ||
986 | + } | ||
987 | + | ||
988 | + function getTimezoneOffset() { | ||
989 | + var currentYear = new Date().getFullYear(); | ||
990 | + // The timezone offset may change over time due to daylight saving time (DST) shifts. | ||
991 | + // The non-DST timezone offset is used as the result timezone offset. | ||
992 | + // Since the DST season differs in the northern and the southern hemispheres, | ||
993 | + // both January and July timezones offsets are considered. | ||
994 | + return Math.max( | ||
995 | + // `getTimezoneOffset` returns a number as a string in some unidentified cases | ||
996 | + toFloat(new Date(currentYear, 0, 1).getTimezoneOffset()), toFloat(new Date(currentYear, 6, 1).getTimezoneOffset())); | ||
997 | + } | ||
998 | + | ||
999 | + var w$5 = window; | ||
1000 | + function getTimezone() { | ||
1001 | + var _a; | ||
1002 | + if ((_a = w$5.Intl) === null || _a === void 0 ? void 0 : _a.DateTimeFormat) { | ||
1003 | + return new w$5.Intl.DateTimeFormat().resolvedOptions().timeZone; | ||
1004 | + } | ||
1005 | + return undefined; | ||
1006 | + } | ||
1007 | + | ||
1008 | + function getSessionStorage() { | ||
1009 | + try { | ||
1010 | + return !!window.sessionStorage; | ||
1011 | + } | ||
1012 | + catch (error) { | ||
1013 | + /* SecurityError when referencing it means it exists */ | ||
1014 | + return true; | ||
1015 | + } | ||
1016 | + } | ||
1017 | + | ||
1018 | + // https://bugzilla.mozilla.org/show_bug.cgi?id=781447 | ||
1019 | + function getLocalStorage() { | ||
1020 | + try { | ||
1021 | + return !!window.localStorage; | ||
1022 | + } | ||
1023 | + catch (e) { | ||
1024 | + /* SecurityError when referencing it means it exists */ | ||
1025 | + return true; | ||
1026 | + } | ||
1027 | + } | ||
1028 | + | ||
1029 | + function getIndexedDB() { | ||
1030 | + // IE and Edge don't allow accessing indexedDB in private mode, therefore IE and Edge will have different | ||
1031 | + // visitor identifier in normal and private modes. | ||
1032 | + if (isTrident() || isEdgeHTML()) { | ||
1033 | + return undefined; | ||
1034 | + } | ||
1035 | + try { | ||
1036 | + return !!window.indexedDB; | ||
1037 | + } | ||
1038 | + catch (e) { | ||
1039 | + /* SecurityError when referencing it means it exists */ | ||
1040 | + return true; | ||
1041 | + } | ||
1042 | + } | ||
1043 | + | ||
1044 | + function getOpenDatabase() { | ||
1045 | + return !!window.openDatabase; | ||
1046 | + } | ||
1047 | + | ||
1048 | + function getCpuClass() { | ||
1049 | + return navigator.cpuClass; | ||
1050 | + } | ||
1051 | + | ||
1052 | + function getPlatform() { | ||
1053 | + return navigator.platform; | ||
1054 | + } | ||
1055 | + | ||
1056 | + function getPluginsSupport() { | ||
1057 | + return navigator.plugins !== undefined; | ||
1058 | + } | ||
1059 | + | ||
1060 | + function getProductSub() { | ||
1061 | + return navigator.productSub; | ||
1062 | + } | ||
1063 | + | ||
1064 | + function getEmptyEvalLength() { | ||
1065 | + return eval.toString().length; | ||
1066 | + } | ||
1067 | + | ||
1068 | + function getErrorFF() { | ||
1069 | + try { | ||
1070 | + throw 'a'; | ||
1071 | + } | ||
1072 | + catch (e) { | ||
1073 | + try { | ||
1074 | + e.toSource(); | ||
1075 | + return true; | ||
1076 | + } | ||
1077 | + catch (e2) { | ||
1078 | + return false; | ||
1079 | + } | ||
1080 | + } | ||
1081 | + } | ||
1082 | + | ||
1083 | + function getVendor() { | ||
1084 | + return navigator.vendor; | ||
1085 | + } | ||
1086 | + | ||
1087 | + function getChrome() { | ||
1088 | + return window.chrome !== undefined; | ||
1089 | + } | ||
1090 | + | ||
1091 | + var d$3 = document; | ||
1092 | + /** | ||
1093 | + * navigator.cookieEnabled cannot detect custom or nuanced cookie blocking configurations. For example, when blocking | ||
1094 | + * cookies via the Advanced Privacy Settings in IE9, it always returns true. And there have been issues in the past with | ||
1095 | + * site-specific exceptions. Don't rely on it. | ||
1096 | + * | ||
1097 | + * @see https://github.com/Modernizr/Modernizr/blob/master/feature-detects/cookies.js Taken from here | ||
1098 | + */ | ||
1099 | + function areCookiesEnabled() { | ||
1100 | + // Taken from here: https://github.com/Modernizr/Modernizr/blob/master/feature-detects/cookies.js | ||
1101 | + // navigator.cookieEnabled cannot detect custom or nuanced cookie blocking configurations. For example, when blocking | ||
1102 | + // cookies via the Advanced Privacy Settings in IE9, it always returns true. And there have been issues in the past | ||
1103 | + // with site-specific exceptions. Don't rely on it. | ||
1104 | + // try..catch because some in situations `document.cookie` is exposed but throws a | ||
1105 | + // SecurityError if you try to access it; e.g. documents created from data URIs | ||
1106 | + // or in sandboxed iframes (depending on flags/context) | ||
1107 | + try { | ||
1108 | + // Create cookie | ||
1109 | + d$3.cookie = 'cookietest=1'; | ||
1110 | + var result = d$3.cookie.indexOf('cookietest=') !== -1; | ||
1111 | + // Delete cookie | ||
1112 | + d$3.cookie = 'cookietest=1; expires=Thu, 01-Jan-1970 00:00:01 GMT'; | ||
1113 | + return result; | ||
1114 | + } | ||
1115 | + catch (e) { | ||
1116 | + return false; | ||
1117 | + } | ||
1118 | + } | ||
1119 | + | ||
1120 | + /** | ||
1121 | + * The list of entropy sources used to make visitor identifiers. | ||
1122 | + * | ||
1123 | + * This value isn't restricted by Semantic Versioning, i.e. it may be changed without bumping minor or major version of | ||
1124 | + * this package. | ||
1125 | + */ | ||
1126 | + var sources = { | ||
1127 | + // Expected errors and default values must be handled inside the functions. Unexpected errors must be thrown. | ||
1128 | + osCpu: getOsCpu, | ||
1129 | + languages: getLanguages, | ||
1130 | + colorDepth: getColorDepth, | ||
1131 | + deviceMemory: getDeviceMemory, | ||
1132 | + screenResolution: getScreenResolution, | ||
1133 | + availableScreenResolution: getAvailableScreenResolution, | ||
1134 | + hardwareConcurrency: getHardwareConcurrency, | ||
1135 | + timezoneOffset: getTimezoneOffset, | ||
1136 | + timezone: getTimezone, | ||
1137 | + sessionStorage: getSessionStorage, | ||
1138 | + localStorage: getLocalStorage, | ||
1139 | + indexedDB: getIndexedDB, | ||
1140 | + openDatabase: getOpenDatabase, | ||
1141 | + cpuClass: getCpuClass, | ||
1142 | + // Maybe it should be excluded: https://github.com/fingerprintjs/fingerprintjs/issues/514#issuecomment-688754892 | ||
1143 | + platform: getPlatform, | ||
1144 | + plugins: getPlugins, | ||
1145 | + canvas: getCanvasFingerprint, | ||
1146 | + // adBlock: isAdblockUsed, // https://github.com/fingerprintjs/fingerprintjs/issues/405 | ||
1147 | + touchSupport: getTouchSupport, | ||
1148 | + fonts: getFonts, | ||
1149 | + audio: getAudioFingerprint, | ||
1150 | + pluginsSupport: getPluginsSupport, | ||
1151 | + productSub: getProductSub, | ||
1152 | + emptyEvalLength: getEmptyEvalLength, | ||
1153 | + errorFF: getErrorFF, | ||
1154 | + vendor: getVendor, | ||
1155 | + chrome: getChrome, | ||
1156 | + cookiesEnabled: areCookiesEnabled, | ||
1157 | + }; | ||
1158 | + /** | ||
1159 | + * Gets a components list from the given list of entropy sources. | ||
1160 | + * | ||
1161 | + * Warning for package users: | ||
1162 | + * This function is out of Semantic Versioning, i.e. can change unexpectedly. Usage is at your own risk. | ||
1163 | + */ | ||
1164 | + function getComponents(sources, sourceOptions, excludeSources) { | ||
1165 | + return __awaiter(this, void 0, void 0, function () { | ||
1166 | + var timestamp, components, _i, _a, sourceKey, result, error_1, nextTimestamp; | ||
1167 | + var _b; | ||
1168 | + return __generator(this, function (_c) { | ||
1169 | + switch (_c.label) { | ||
1170 | + case 0: | ||
1171 | + timestamp = Date.now(); | ||
1172 | + components = {}; | ||
1173 | + _i = 0, _a = Object.keys(sources); | ||
1174 | + _c.label = 1; | ||
1175 | + case 1: | ||
1176 | + if (!(_i < _a.length)) return [3 /*break*/, 7]; | ||
1177 | + sourceKey = _a[_i]; | ||
1178 | + if (!excludes(excludeSources, sourceKey)) { | ||
1179 | + return [3 /*break*/, 6]; | ||
1180 | + } | ||
1181 | + result = void 0; | ||
1182 | + _c.label = 2; | ||
1183 | + case 2: | ||
1184 | + _c.trys.push([2, 4, , 5]); | ||
1185 | + _b = {}; | ||
1186 | + return [4 /*yield*/, sources[sourceKey](sourceOptions)]; | ||
1187 | + case 3: | ||
1188 | + result = (_b.value = _c.sent(), _b); | ||
1189 | + return [3 /*break*/, 5]; | ||
1190 | + case 4: | ||
1191 | + error_1 = _c.sent(); | ||
1192 | + result = error_1 && typeof error_1 === 'object' && 'message' in error_1 ? { error: error_1 } : { error: { message: error_1 } }; | ||
1193 | + return [3 /*break*/, 5]; | ||
1194 | + case 5: | ||
1195 | + nextTimestamp = Date.now(); | ||
1196 | + components[sourceKey] = __assign(__assign({}, result), { duration: nextTimestamp - timestamp }); // TypeScript has beaten me here | ||
1197 | + timestamp = nextTimestamp; | ||
1198 | + _c.label = 6; | ||
1199 | + case 6: | ||
1200 | + _i++; | ||
1201 | + return [3 /*break*/, 1]; | ||
1202 | + case 7: return [2 /*return*/, components]; | ||
1203 | + } | ||
1204 | + }); | ||
1205 | + }); | ||
1206 | + } | ||
1207 | + /** | ||
1208 | + * Collects entropy components from the built-in sources to make the visitor identifier. | ||
1209 | + */ | ||
1210 | + function getBuiltinComponents() { | ||
1211 | + return getComponents(sources, undefined, []); | ||
1212 | + } | ||
1213 | + | ||
1214 | + function componentsToCanonicalString(components) { | ||
1215 | + var result = ''; | ||
1216 | + for (var _i = 0, _a = Object.keys(components); _i < _a.length; _i++) { | ||
1217 | + var componentKey = _a[_i]; | ||
1218 | + var component = components[componentKey]; | ||
1219 | + var value = component.error ? 'error' : JSON.stringify(component.value); | ||
1220 | + result += "" + (result ? '|' : '') + componentKey.replace(/([:|\\])/g, '\\$1') + ":" + value; | ||
1221 | + } | ||
1222 | + return result; | ||
1223 | + } | ||
1224 | + function componentsToDebugString(components) { | ||
1225 | + return JSON.stringify(components, function (_key, value) { | ||
1226 | + var _a; | ||
1227 | + if (value instanceof Error) { | ||
1228 | + return __assign(__assign({}, value), { message: value.message, stack: (_a = value.stack) === null || _a === void 0 ? void 0 : _a.split('\n') }); | ||
1229 | + } | ||
1230 | + return value; | ||
1231 | + }, 2); | ||
1232 | + } | ||
1233 | + function hashComponents(components) { | ||
1234 | + return x64hash128(componentsToCanonicalString(components)); | ||
1235 | + } | ||
1236 | + /** | ||
1237 | + * Makes a GetResult implementation that calculates the visitor id hash on demand. | ||
1238 | + * Designed for optimisation. | ||
1239 | + */ | ||
1240 | + function makeLazyGetResult(components) { | ||
1241 | + var visitorIdCache; | ||
1242 | + // A plain class isn't used because its getters and setters aren't enumerable. | ||
1243 | + return { | ||
1244 | + components: components, | ||
1245 | + get visitorId() { | ||
1246 | + if (visitorIdCache === undefined) { | ||
1247 | + visitorIdCache = hashComponents(this.components); | ||
1248 | + } | ||
1249 | + return visitorIdCache; | ||
1250 | + }, | ||
1251 | + set visitorId(visitorId) { | ||
1252 | + visitorIdCache = visitorId; | ||
1253 | + }, | ||
1254 | + }; | ||
1255 | + } | ||
1256 | + /** | ||
1257 | + * The class isn't exported from the index file to not expose the constructor. | ||
1258 | + * The hiding gives more freedom for future non-breaking updates. | ||
1259 | + */ | ||
1260 | + var OpenAgent = /** @class */ (function () { | ||
1261 | + function OpenAgent() { | ||
1262 | + } | ||
1263 | + /** | ||
1264 | + * @inheritDoc | ||
1265 | + */ | ||
1266 | + OpenAgent.prototype.get = function (options) { | ||
1267 | + if (options === void 0) { options = {}; } | ||
1268 | + return __awaiter(this, void 0, void 0, function () { | ||
1269 | + var components, result; | ||
1270 | + return __generator(this, function (_a) { | ||
1271 | + switch (_a.label) { | ||
1272 | + case 0: return [4 /*yield*/, getBuiltinComponents()]; | ||
1273 | + case 1: | ||
1274 | + components = _a.sent(); | ||
1275 | + result = makeLazyGetResult(components); | ||
1276 | + if (options.debug) { | ||
1277 | + // console.log is ok here because it's under a debug clause | ||
1278 | + // eslint-disable-next-line no-console | ||
1279 | + console.log("Copy the text below to get the debug data:\n\n```\nversion: " + version + "\nuserAgent: " + navigator.userAgent + "\ngetOptions: " + JSON.stringify(options, undefined, 2) + "\nvisitorId: " + result.visitorId + "\ncomponents: " + componentsToDebugString(components) + "\n```"); | ||
1280 | + } | ||
1281 | + return [2 /*return*/, result]; | ||
1282 | + } | ||
1283 | + }); | ||
1284 | + }); | ||
1285 | + }; | ||
1286 | + return OpenAgent; | ||
1287 | + }()); | ||
1288 | + /** | ||
1289 | + * Builds an instance of Agent and waits a delay required for a proper operation. | ||
1290 | + */ | ||
1291 | + function load(_a) { | ||
1292 | + var _b = (_a === void 0 ? {} : _a).delayFallback, delayFallback = _b === void 0 ? 50 : _b; | ||
1293 | + return __awaiter(this, void 0, void 0, function () { | ||
1294 | + return __generator(this, function (_c) { | ||
1295 | + switch (_c.label) { | ||
1296 | + case 0: | ||
1297 | + // A delay is required to ensure consistent entropy components. | ||
1298 | + // See https://github.com/fingerprintjs/fingerprintjs/issues/254 | ||
1299 | + // and https://github.com/fingerprintjs/fingerprintjs/issues/307 | ||
1300 | + return [4 /*yield*/, requestIdleCallbackIfAvailable(delayFallback)]; | ||
1301 | + case 1: | ||
1302 | + // A delay is required to ensure consistent entropy components. | ||
1303 | + // See https://github.com/fingerprintjs/fingerprintjs/issues/254 | ||
1304 | + // and https://github.com/fingerprintjs/fingerprintjs/issues/307 | ||
1305 | + _c.sent(); | ||
1306 | + return [2 /*return*/, new OpenAgent()]; | ||
1307 | + } | ||
1308 | + }); | ||
1309 | + }); | ||
1310 | + } | ||
1311 | + | ||
1312 | + // The default export is a syntax sugar (`import * as FP from '...' → import FP from '...'`). | ||
1313 | + // It should contain all the public exported values. | ||
1314 | + var index = { load: load, hashComponents: hashComponents, componentsToDebugString: componentsToDebugString }; | ||
1315 | + // The exports below are for private usage. They may change unexpectedly. Use them at your own risk. | ||
1316 | + /** Not documented, out of Semantic Versioning, usage is at your own risk */ | ||
1317 | + var murmurX64Hash128 = x64hash128; | ||
1318 | + | ||
1319 | + exports.componentsToDebugString = componentsToDebugString; | ||
1320 | + exports.default = index; | ||
1321 | + exports.getComponents = getComponents; | ||
1322 | + exports.hashComponents = hashComponents; | ||
1323 | + exports.isChromium = isChromium; | ||
1324 | + exports.isDesktopSafari = isDesktopSafari; | ||
1325 | + exports.isEdgeHTML = isEdgeHTML; | ||
1326 | + exports.isGecko = isGecko; | ||
1327 | + exports.isTrident = isTrident; | ||
1328 | + exports.isWebKit = isWebKit; | ||
1329 | + exports.load = load; | ||
1330 | + exports.murmurX64Hash128 = murmurX64Hash128; | ||
1331 | + | ||
1332 | + Object.defineProperty(exports, '__esModule', { value: true }); | ||
1333 | + | ||
1334 | +}))); |
dist/fp.umd.min.js
0 → 100644
1 | +/** | ||
2 | + * FingerprintJS v3.0.3 - Copyright (c) FingerprintJS, Inc, 2020 (https://fingerprintjs.com) | ||
3 | + * Licensed under the MIT (http://www.opensource.org/licenses/mit-license.php) license. | ||
4 | + * | ||
5 | + * This software contains code from open-source projects: | ||
6 | + * MurmurHash3 by Karan Lyons (https://github.com/karanlyons/murmurHash3.js) | ||
7 | + */ | ||
8 | + | ||
9 | +!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=window,g=navigator,p=document;function m(){return h(["MSCSSMatrix"in v,"msSetImmediate"in v,"msIndexedDB"in v,"msMaxTouchPoints"in g,"msPointerEnabled"in g])>=4}function y(){return h(["msWriteProfilerMark"in v,"MSStream"in v,"msLaunchUri"in g,"msSaveBlob"in g])>=3&&!m()}function b(){return h(["webkitPersistentStorage"in g,"webkitTemporaryStorage"in g,0===g.vendor.indexOf("Google"),"webkitResolveLocalFileSystemURL"in v,"BatteryManager"in v,"webkitMediaStream"in v,"webkitSpeechGrammar"in v])>=5}function w(){return h(["ApplePayError"in v,"CSSPrimitiveValue"in v,"Counter"in v,0===g.vendor.indexOf("Apple"),"getStorageUpdates"in g,"WebKitMediaKeys"in v])>=4}function S(){return h(["safari"in v,!("DeviceMotionEvent"in v),!("ongestureend"in v),!("standalone"in g)])>=3}var C=window,A=document;function M(e,t,n){(function(e){return e&&"function"==typeof e.setValueAtTime})(t)&&t.setValueAtTime(n,e.currentTime)}function T(e){return new Promise((function(t,n){e.oncomplete=function(e){return t(e.renderedBuffer)};var r=3,o=function(){switch(e.startRendering(),e.state){case"running":setTimeout((function(){return n(k("timeout"))}),1e3);break;case"suspended":A.hidden||r--,r>0?setTimeout(o,500):n(k("suspended"))}};o()}))}function x(e){for(var t=0,n=4500;n<5e3;++n)t+=Math.abs(e[n]);return t}function k(e){var t=new Error(e);return t.name=e,t}var P=document,O=["monospace","sans-serif","serif"],I=["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"],E={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"};function D(e){return e.toDataURL()}var R=navigator,B=window;var L=navigator;var F=window;var G=window;var H=window;var U=document;var j={osCpu:function(){return navigator.oscpu},languages:function(){var e=[],t=L.language||L.userLanguage||L.browserLanguage||L.systemLanguage;if(void 0!==t&&e.push([t]),Array.isArray(L.languages))b()&&h([!("MediaSettingsRange"in v),"RTCEncodedAudioFrame"in v,""+v.Intl=="[object Intl]",""+v.Reflect=="[object Reflect]"])>=3||e.push(L.languages);else if("string"==typeof L.languages){var n=L.languages;n&&e.push(n.split(","))}return e},colorDepth:function(){return window.screen.colorDepth},deviceMemory:function(){return navigator.deviceMemory},screenResolution:function(){var e=[f(F.screen.width),f(F.screen.height)];return e.sort().reverse(),e},availableScreenResolution:function(){if(G.screen.availWidth&&G.screen.availHeight){var e=[f(G.screen.availWidth),f(G.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=H.Intl)||void 0===e?void 0:e.DateTimeFormat)return(new H.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()&&!y())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:D(t)}},touchSupport:function(){var e,t=0;void 0!==R.maxTouchPoints?t=f(R.maxTouchPoints):void 0!==R.msMaxTouchPoints&&(t=R.msMaxTouchPoints);try{document.createEvent("TouchEvent"),e=!0}catch(n){e=!1}return{maxTouchPoints:t,touchEvent:e,touchStart:"ontouchstart"in B}},fonts:function(){var e=P.body,t=P.createElement("div"),n=P.createElement("div"),r={},o={},i=function(){var e=P.createElement("span");e.textContent="mmMwWLliI0O&1";for(var t=0,n=Object.keys(E);t<n.length;t++){var r=n[t];e.style[r]=E[r]}return e},a=function(e){return O.some((function(t,n){return e[n].offsetWidth!==r[t]||e[n].offsetHeight!==o[t]}))},c=O.map((function(e){var n=i();return n.style.fontFamily=e,t.appendChild(n),n}));e.appendChild(t);for(var u=0,s=O.length;u<s;u++)r[O[u]]=c[u].offsetWidth,o[O[u]]=c[u].offsetHeight;var l=function(){for(var e={},t=function(t){e[t]=O.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=I;r<o.length;r++){t(o[r])}return e}();e.appendChild(n);for(var f=[],d=0,h=I.length;d<h;d++)a(l[I[d]])&&f.push(I[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;return l(this,(function(a){switch(a.label){case 0:if(w()&&!S()&&!(h(["DOMRectList"in v,"RTCPeerConnectionIceEvent"in v,"SVGGeometryElement"in v,"ontransitioncancel"in v])>=3))return[2,-1];if(!(e=C.OfflineAudioContext||C.webkitOfflineAudioContext))return[2,-2];t=new e(1,44100,44100),(n=t.createOscillator()).type="triangle",M(t,n.frequency,1e4),r=t.createDynamicsCompressor(),M(t,r.threshold,-50),M(t,r.knee,40),M(t,r.ratio,12),M(t,r.reduction,-20),M(t,r.attack,0),M(t,r.release,.25),n.connect(r),r.connect(t.destination),n.start(0),a.label=1;case 1:return a.trys.push([1,3,4,5]),[4,T(t)];case 2:return o=a.sent(),[3,5];case 3:if("timeout"===(i=a.sent()).name||"suspended"===i.name)return[2,-3];throw i;case 4:return n.disconnect(),r.disconnect(),[7];case 5:return[2,x(o.getChannelData(0))]}}))}))},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{U.cookie="cookietest=1";var e=-1!==U.cookie.indexOf("cookietest=");return U.cookie="cookietest=1; expires=Thu, 01-Jan-1970 00:00:01 GMT",e}catch(t){return!1}}};function W(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 N(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 z(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 V=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,W(j,void 0,[])];case 1:return t=r.sent(),n=function(e){var t;return{components:e,get visitorId(){return void 0===t&&(t=z(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.3\nuserAgent: "+navigator.userAgent+"\ngetOptions: "+JSON.stringify(e,void 0,2)+"\nvisitorId: "+n.visitorId+"\ncomponents: "+N(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 V]}var t}))}))}var q={load:J,hashComponents:z,componentsToDebugString:N},K=c;e.componentsToDebugString=N,e.default=q,e.getComponents=W,e.hashComponents=z,e.isChromium=b,e.isDesktopSafari=S,e.isEdgeHTML=y,e.isGecko=function(){var e;return h(["buildID"in g,(null===(e=p.documentElement)||void 0===e?void 0:e.style)&&"MozAppearance"in p.documentElement.style,"MediaRecorderErrorEvent"in v,"mozInnerScreenX"in v,"CSSMozDocumentRule"in v,"CanvasCaptureMediaStream"in v])>=4},e.isTrident=m,e.isWebKit=w,e.load=J,e.murmurX64Hash128=K,Object.defineProperty(e,"__esModule",{value:!0})})); |
package.json
0 → 100644
1 | +{ | ||
2 | + "name": "@fingerprintjs/fingerprintjs", | ||
3 | + "description": "Modern & flexible browser fingerprinting library", | ||
4 | + "version": "3.0.3", | ||
5 | + "keywords": [ | ||
6 | + "fraud", | ||
7 | + "fraud detection", | ||
8 | + "fraud prevention", | ||
9 | + "browser", | ||
10 | + "identification", | ||
11 | + "fingerprint", | ||
12 | + "fingerprinting", | ||
13 | + "browser fingerprint", | ||
14 | + "device fingerprint", | ||
15 | + "privacy" | ||
16 | + ], | ||
17 | + "author": "FingerprintJS, Inc (https://fingerprintjs.com)", | ||
18 | + "license": "MIT", | ||
19 | + "repository": { | ||
20 | + "type": "git", | ||
21 | + "url": "https://github.com/fingerprintjs/fingerprintjs.git" | ||
22 | + }, | ||
23 | + "bugs": { | ||
24 | + "url": "https://github.com/fingerprintjs/fingerprintjs/issues" | ||
25 | + }, | ||
26 | + "homepage": "https://github.com/fingerprintjs/fingerprintjs", | ||
27 | + "main": "dist/fp.cjs.js", | ||
28 | + "module": "dist/fp.esm.js", | ||
29 | + "types": "dist/fp.d.ts", | ||
30 | + "sideEffects": false, | ||
31 | + "files": [ | ||
32 | + "dist" | ||
33 | + ], | ||
34 | + "scripts": { | ||
35 | + "build": "rimraf dist && rollup -c", | ||
36 | + "build:watch": "yarn build --watch", | ||
37 | + "playground:start": "cd playground && webpack-dev-server --mode development", | ||
38 | + "playground:build": "cd playground && webpack --mode production", | ||
39 | + "lint": "eslint --ext .js,.ts --ignore-path .gitignore --max-warnings 0 .", | ||
40 | + "lint:fix": "yarn lint --fix", | ||
41 | + "test:local": "karma start tests/karma.local.config.js --single-run", | ||
42 | + "test:browserstack": "karma start tests/karma.browserstack.config.js --single-run", | ||
43 | + "test:dts": "tsc --noEmit dist/fp.d.ts" | ||
44 | + }, | ||
45 | + "dependencies": { | ||
46 | + "tslib": "^2.0.1" | ||
47 | + }, | ||
48 | + "devDependencies": { | ||
49 | + "@rollup/plugin-json": "^4.1.0", | ||
50 | + "@rollup/plugin-node-resolve": "^9.0.0", | ||
51 | + "@rollup/plugin-typescript": "^6.0.0", | ||
52 | + "@types/jasmine": "^3.5.14", | ||
53 | + "@types/ua-parser-js": "^0.7.33", | ||
54 | + "@typescript-eslint/eslint-plugin": "^4.4.1", | ||
55 | + "@typescript-eslint/parser": "^4.4.1", | ||
56 | + "clean-webpack-plugin": "^3.0.0", | ||
57 | + "eslint": "^7.11.0", | ||
58 | + "eslint-config-prettier": "^6.13.0", | ||
59 | + "eslint-plugin-prettier": "^3.1.4", | ||
60 | + "file-loader": "^6.2.0", | ||
61 | + "html-webpack-plugin": "^4.5.0", | ||
62 | + "karma": "^5.2.3", | ||
63 | + "karma-browserstack-launcher": "^1.6.0", | ||
64 | + "karma-chrome-launcher": "^3.1.0", | ||
65 | + "karma-firefox-launcher": "^1.3.0", | ||
66 | + "karma-jasmine": "^4.0.1", | ||
67 | + "karma-spec-reporter": "^0.0.32", | ||
68 | + "karma-summary-reporter": "^1.9.0", | ||
69 | + "karma-typescript": "^5.2.0", | ||
70 | + "prettier": "^2.1.2", | ||
71 | + "promise-polyfill": "^8.2.0", | ||
72 | + "rimraf": "^3.0.2", | ||
73 | + "rollup": "^2.28.2", | ||
74 | + "rollup-plugin-dts": "^1.4.13", | ||
75 | + "rollup-plugin-license": "^2.2.0", | ||
76 | + "rollup-plugin-terser": "^7.0.2", | ||
77 | + "terser-webpack-plugin": "^4.2.3", | ||
78 | + "ts-loader": "^8.0.7", | ||
79 | + "typescript": "^4.0.3", | ||
80 | + "ua-parser-js": "^0.7.22", | ||
81 | + "webpack": "^4.44.2", | ||
82 | + "webpack-cli": "^3.3.12", | ||
83 | + "webpack-dev-server": "^3.11.0" | ||
84 | + } | ||
85 | +} |
readme.md
0 → 100644
1 | +<p align="center"> | ||
2 | + <a href="https://fingerprintjs.com"> | ||
3 | + <img src="resources/logo.svg" alt="FingerprintJS" width="300px" /> | ||
4 | + </a> | ||
5 | +</p> | ||
6 | +<p align="center"> | ||
7 | + <a href="https://github.com/fingerprintjs/fingerprintjs/actions?workflow=Lint%20and%20test"> | ||
8 | + <img src="https://github.com/fingerprintjs/fingerprintjs/workflows/Lint%20and%20test/badge.svg" alt="Build status"> | ||
9 | + </a> | ||
10 | + <a href="https://www.npmjs.com/package/@fingerprintjs/fingerprintjs"> | ||
11 | + <img src="https://img.shields.io/npm/dt/fingerprintjs2.svg" alt="Total downloads from NPM"> | ||
12 | + </a> | ||
13 | + <a href="https://www.npmjs.com/package/@fingerprintjs/fingerprintjs"> | ||
14 | + <img src="https://img.shields.io/npm/v/@fingerprintjs/fingerprintjs.svg" alt="Current NPM version"> | ||
15 | + </a> | ||
16 | +</p> | ||
17 | + | ||
18 | +Makes a website visitor identifier from a browser fingerprint. | ||
19 | +Unlike cookies and local storage, fingerprint stays the same in incognito/private mode and even when browser data is purged. | ||
20 | +[Demo](https://fingerprintjs.github.io/fingerprintjs/). | ||
21 | + | ||
22 | +## Quick start | ||
23 | + | ||
24 | +### Install from CDN | ||
25 | + | ||
26 | +```html | ||
27 | +<script> | ||
28 | + function initFingerprintJS() { | ||
29 | + FingerprintJS.load().then(fp => { | ||
30 | + // The FingerprintJS agent is ready. | ||
31 | + // Get a visitor identifier when you'd like to. | ||
32 | + fp.get().then(result => { | ||
33 | + // This is the visitor identifier: | ||
34 | + const visitorId = result.visitorId; | ||
35 | + console.log(visitorId); | ||
36 | + }); | ||
37 | + }); | ||
38 | + } | ||
39 | +</script> | ||
40 | +<script | ||
41 | + async | ||
42 | + src="//cdn.jsdelivr.net/npm/@fingerprintjs/fingerprintjs@3/dist/fp.min.js" | ||
43 | + onload="initFingerprintJS()" | ||
44 | +></script> | ||
45 | +``` | ||
46 | + | ||
47 | +### Alternatively you can install from NPM to use with Webpack/Rollup/Browserify | ||
48 | + | ||
49 | +```bash | ||
50 | +npm i @fingerprintjs/fingerprintjs | ||
51 | +# or | ||
52 | +yarn add @fingerprintjs/fingerprintjs | ||
53 | +``` | ||
54 | + | ||
55 | +```js | ||
56 | +import FingerprintJS from '@fingerprintjs/fingerprintjs'; | ||
57 | + | ||
58 | +(async () => { | ||
59 | + // We recommend to call `load` at application startup. | ||
60 | + const fp = await FingerprintJS.load(); | ||
61 | + | ||
62 | + // The FingerprintJS agent is ready. | ||
63 | + // Get a visitor identifier when you'd like to. | ||
64 | + const result = await fp.get(); | ||
65 | + | ||
66 | + // This is the visitor identifier: | ||
67 | + const visitorId = result.visitorId; | ||
68 | + console.log(visitorId); | ||
69 | +})(); | ||
70 | +``` | ||
71 | + | ||
72 | +📕 [Full documentation](#open-source-version-reference) | ||
73 | + | ||
74 | +## Upgrade to [Pro version](https://fingerprintjs.com) to get 99.5% identification accuracy | ||
75 | + | ||
76 | +<p align="center"> | ||
77 | + <a href="https://fingerprintjs.com"> | ||
78 | + <img src="resources/pro_screenshot.png" alt="Pro screenshot" width="697px" /> | ||
79 | + </a> | ||
80 | +</p> | ||
81 | + | ||
82 | +<table> | ||
83 | + <thead> | ||
84 | + <tr> | ||
85 | + <th></th> | ||
86 | + <!-- The <img>s are to make the table take the full width --> | ||
87 | + <th align="center"><img width="350" height="0"> <p>Open Source version</p></th> | ||
88 | + <th align="center"><img width="350" height="0"> <p>Pro version</p></th> | ||
89 | + </tr> | ||
90 | + </thead> | ||
91 | + <tbody> | ||
92 | + <tr><td>Identification accuracy</td><td align="center">60%</td><td align="center">99.5%</td></tr> | ||
93 | + <tr><td>Bot detection</td><td align="center">❌</td><td align="center">✅</td></tr> | ||
94 | + <tr><td>Incognito / Private mode detection</td><td align="center">❌</td><td align="center">✅</td></tr> | ||
95 | + <tr><td>Geolocation</td><td align="center">❌</td><td align="center">✅</td></tr> | ||
96 | + <tr><td>Security</td><td align="center">❌</td><td align="center">✅</td></tr> | ||
97 | + <tr><td>Server API</td><td align="center">❌</td><td align="center">✅</td></tr> | ||
98 | + <tr><td>Webhooks</td><td align="center">❌</td><td align="center">✅</td></tr> | ||
99 | + <tr><td>Stable identifier between versions</td><td align="center">❌</td><td align="center">✅</td></tr> | ||
100 | + </tbody> | ||
101 | +</table> | ||
102 | + | ||
103 | +Pro result example: | ||
104 | + | ||
105 | +```js | ||
106 | +{ | ||
107 | + "requestId": "HFMlljrzKEiZmhUNDx7Z", | ||
108 | + "visitorId": "kHqPGWS1Mj18sZFsP8Wl", | ||
109 | + "visitorFound": true, | ||
110 | + "incognito": false, | ||
111 | + "bot": { "probability": 0.96 }, | ||
112 | + "browserName": "Chrome", | ||
113 | + "browserVersion": "85.0.4183", | ||
114 | + "os": "Mac OS X", | ||
115 | + "osVersion": "10.15.6", | ||
116 | + "device": "Other", | ||
117 | + "ip": "192.65.67.131", | ||
118 | + "ipLocation": { | ||
119 | + "accuracyRadius": 100, | ||
120 | + "latitude": 37.409657, | ||
121 | + "longitude": -121.965467 | ||
122 | + // ... | ||
123 | + } | ||
124 | +} | ||
125 | +``` | ||
126 | + | ||
127 | +🍿 [Live demo](https://fingerprintjs.com/demo) | ||
128 | + | ||
129 | +⏱ [How to upgrade from Open Source to Pro in 30 seconds](https://dev.fingerprintjs.com/v3/docs/migrating-from-previous-versions#from-fingerprintjs-open-source-version-3) | ||
130 | + | ||
131 | +📕 [FingerprintJS Pro documentation](https://dev.fingerprintjs.com) | ||
132 | + | ||
133 | +## Open-source version reference | ||
134 | + | ||
135 | +### Installation | ||
136 | + | ||
137 | +The library is shipped in various formats: | ||
138 | + | ||
139 | +- Global variable | ||
140 | + ```html | ||
141 | + <script> | ||
142 | + function initFingerprintJS() { | ||
143 | + // Start loading FingerprintJS here | ||
144 | + } | ||
145 | + </script> | ||
146 | + <script | ||
147 | + async | ||
148 | + src="//cdn.jsdelivr.net/npm/@fingerprintjs/fingerprintjs@3/dist/fp.min.js" | ||
149 | + onload="initFingerprintJS()" | ||
150 | + ></script> | ||
151 | + ``` | ||
152 | +- UMD | ||
153 | + ```js | ||
154 | + require( | ||
155 | + ['//cdn.jsdelivr.net/npm/@fingerprintjs/fingerprintjs@3/dist/fp.umd.min.js'], | ||
156 | + (FingerprintJS) => { | ||
157 | + // Start loading FingerprintJS here | ||
158 | + } | ||
159 | + ); | ||
160 | + ``` | ||
161 | +- ECMAScript module | ||
162 | + ```bash | ||
163 | + # Install the package first: | ||
164 | + npm i @fingerprintjs/fingerprintjs | ||
165 | + # or | ||
166 | + yarn add @fingerprintjs/fingerprintjs | ||
167 | + ``` | ||
168 | + | ||
169 | + ```js | ||
170 | + import FingerprintJS from '@fingerprintjs/fingerprintjs'; | ||
171 | + | ||
172 | + // Start loading FingerprintJS here | ||
173 | + ``` | ||
174 | +- CommonJS | ||
175 | + ```bash | ||
176 | + # Install the package first: | ||
177 | + npm i @fingerprintjs/fingerprintjs | ||
178 | + # or | ||
179 | + yarn add @fingerprintjs/fingerprintjs | ||
180 | + ``` | ||
181 | + | ||
182 | + ```js | ||
183 | + const FingerprintJS = require('@fingerprintjs/fingerprintjs'); | ||
184 | + | ||
185 | + // Start loading FingerprintJS here | ||
186 | + ``` | ||
187 | + | ||
188 | +### API | ||
189 | + | ||
190 | +- `FingerprintJS.load({ delayFallback?: number }): Promise<Agent>` | ||
191 | + | ||
192 | + Builds an instance of Agent and waits a delay required for a proper operation. | ||
193 | + `delayFallback` is an optional parameter that sets duration (milliseconds) of the fallback for browsers that don't support [requestIdleCallback](https://developer.mozilla.org/en-US/docs/Web/API/Window/requestIdleCallback); | ||
194 | + it has a good default value which we don't recommend to change. | ||
195 | + | ||
196 | +- `agent.get({ debug?: boolean }): Promise<{ visitorId: string, components: {/* ... */} }>` | ||
197 | + | ||
198 | + Gets the visitor identifier. | ||
199 | + `debug: true` prints debug messages to the console. | ||
200 | + `visitorId` is the visitor identifier. | ||
201 | + `components` is a dictionary of components that have formed the identifier; | ||
202 | + each value is an object like `{ value: any, duration: number }` in case of success | ||
203 | + and `{ error: object, duration: number }` in case of an unexpected error during getting the component. | ||
204 | + | ||
205 | +- `FingerprintJS.hashComponents(components: object): string` | ||
206 | + | ||
207 | + Converts a dictionary of components (described above) into a short hash string a.k.a. a visitor identifier. | ||
208 | + Designed for extending the library with your own components. | ||
209 | + | ||
210 | +- `FingerprintJS.componentsToDebugString(components: object): string` | ||
211 | + | ||
212 | + Converts a dictionary of components (described above) into human-friendly format. | ||
213 | + | ||
214 | +## Migrating from v2 | ||
215 | + | ||
216 | +- [Migration guide](docs/migrating_v2_v3.md) | ||
217 | +- [V2 documentation](https://github.com/fingerprintjs/fingerprintjs/tree/v2) | ||
218 | + | ||
219 | +## Version policy | ||
220 | + | ||
221 | +The OSS version doesn't guarantee the same visitor identifier between versions, | ||
222 | +but will try to keep them the same as much as possible. | ||
223 | +To get identifiers that remain stable up to 1 year, please consider [upgrading to pro.](https://dashboard.fingerprintjs.com) | ||
224 | + | ||
225 | +The documented JS API follows [Semantic Versioning](https://semver.org). | ||
226 | +Use undocumented features at your own risk. | ||
227 | + | ||
228 | +## Browser support | ||
229 | + | ||
230 | +```bash | ||
231 | +npx browserslist "cover 95% in us, not IE < 10" | ||
232 | +``` | ||
233 | + | ||
234 | +See more details and learn how to run the library in old browsers in the [documentation article](docs/browser_support.md). | ||
235 | + | ||
236 | +## Contributing | ||
237 | + | ||
238 | +See the [contributing guidelines](contributing.md) to learn how to start a playground, test and build. |
请
注册
或
登录
后发表评论