vendor: 5,407 bytes, lines 1-164
1/* 2 * Fingerprintjs2 2.0.5 - Modern & flexible browser fingerprint library v2 3 * https://github.com/Valve/fingerprintjs2 4 * Copyright (c) 2015 Valentin Vasilyev ([email protected]) 5 * Licensed under the MIT (http://www.opensource.org/licenses/mit-license.php) license. 6 * 7 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" 8 * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 9 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 10 * ARE DISCLAIMED. IN NO EVENT SHALL VALENTIN VASILYEV BE LIABLE FOR ANY 11 * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES 12 * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; 13 * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND 14 * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 15 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF 16 * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 17 */ 18/* global define */ 19(function(name, context, definition) { 20 "use strict"; 21 if ( 22 typeof window !== "undefined" && 23 typeof define === "function" && 24 define.amd 25 ) { 26 define(definition); 27 } else if (typeof module !== "undefined" && module.exports) { 28 module.exports = definition(); 29 } else if (context.exports) { 30 context.exports = definition(); 31 } else { 32 context[name] = definition(); 33 } 34})("Fingerprint2", this, function() { 35 "use strict"; 36 37 /// MurmurHash3 related functions 38 39 // 40 // Given two 64bit ints (as an array of two 32bit ints) returns the two 41 // added together as a 64bit int (as an array of two 32bit ints). 42 // 43 var x64Add = function(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[2]; 51 o[1] += o[2] >>> 16; 52 o[2] &= 0xffff; 53 o[1] += m[1] + n[1]; 54 o[0] += o[1] >>> 16; 55 o[1] &= 0xffff; 56 o[0] += m[0] + n[0]; 57 o[0] &= 0xffff; 58 return [(o[0] << 16) | o[1], (o[2] << 16) | o[3]]; 59 }; 60 61 // 62 // Given two 64bit ints (as an array of two 32bit ints) returns the two 63 // multiplied together as a 64bit int (as an array of two 32bit ints). 64 // 65 var x64Multiply = function(m, n) { 66 m = [m[0] >>> 16, m[0] & 0xffff, m[1] >>> 16, m[1] & 0xffff]; 67 n = [n[0] >>> 16, n[0] & 0xffff, n[1] >>> 16, n[1] & 0xffff]; 68 var o = [0, 0, 0, 0]; 69 o[3] += m[3] * n[3]; 70 o[2] += o[3] >>> 16; 71 o[3] &= 0xffff; 72 o[2] += m[2] * n[3]; 73 o[1] += o[2] >>> 16; 74 o[2] &= 0xffff; 75 o[2] += m[3] * n[2]; 76 o[1] += o[2] >>> 16; 77 o[2] &= 0xffff; 78 o[1] += m[1] * n[3]; 79 o[0] += o[1] >>> 16; 80 o[1] &= 0xffff; 81 o[1] += m[2] * n[2]; 82 o[0] += o[1] >>> 16; 83 o[1] &= 0xffff; 84 o[1] += m[3] * n[1]; 85 o[0] += o[1] >>> 16; 86 o[1] &= 0xffff; 87 o[0] += m[0] * n[3] + m[1] * n[2] + m[2] * n[1] + m[3] * n[0]; 88 o[0] &= 0xffff; 89 return [(o[0] << 16) | o[1], (o[2] << 16) | o[3]]; 90 }; 91 // 92 // Given a 64bit int (as an array of two 32bit ints) and an int 93 // representing a number of bit positions, returns the 64bit int (as an 94 // array of two 32bit ints) rotated left by that number of positions. 95 // 96 var x64Rotl = function(m, n) { 97 n %= 64; 98 if (n === 32) { 99 return [m[1], m[0]]; 100 } else if (n < 32) { 101 return [ 102 (m[0] << n) | (m[1] >>> (32 - n)), 103 (m[1] << n) | (m[0] >>> (32 - n)) 104 ]; 105 } else { 106 n -= 32; 107 return [ 108 (m[1] << n) | (m[0] >>> (32 - n)), 109 (m[0] << n) | (m[1] >>> (32 - n)) 110 ]; 111 } 112 }; 113 // 114 // Given a 64bit int (as an array of two 32bit ints) and an int 115 // representing a number of bit positions, returns the 64bit int (as an 116 // array of two 32bit ints) shifted left by that number of positions. 117 // 118 var x64LeftShift = function(m, n) { 119 n %= 64; 120 if (n === 0) { 121 return m; 122 } else if (n < 32) { 123 return [(m[0] << n) | (m[1] >>> (32 - n)), m[1] << n]; 124 } else { 125 return [m[1] << (n - 32), 0]; 126 } 127 }; 128 // 129 // Given two 64bit ints (as an array of two 32bit ints) returns the two 130 // xored together as a 64bit int (as an array of two 32bit ints). 131 // 132 var x64Xor = function(m, n) { 133 return [m[0] ^ n[0], m[1] ^ n[1]]; 134 }; 135 // 136 // Given a block, returns murmurHash3's final x64 mix of that block. 137 // (`[0, h[0] >>> 1]` is a 33 bit unsigned right shift. This is the 138 // only place where we need to right shift 64bit ints.) 139 // 140 var x64Fmix = function(h) { 141 h = x64Xor(h, [0, h[0] >>> 1]); 142 h = x64Multiply(h, [0xff51afd7, 0xed558ccd]); 143 h = x64Xor(h, [0, h[0] >>> 1]); 144 h = x64Multiply(h, [0xc4ceb9fe, 0x1a85ec53]); 145 h = x64Xor(h, [0, h[0] >>> 1]); 146 return h; 147 }; 148 149 // 150 // Given a string and an optional seed as an int, returns a 128 bit 151 // hash using the x64 flavor of MurmurHash3, as an unsigned hex. 152 // 153 var x64hash128 = function(key, seed) { 154 key = key || ""; 155 seed = seed || 0; 156 var remainder = key.length % 16; 157 var bytes = key.length - remainder; 158 var h1 = [0, seed]; 159 var h2 = [0, seed]; 160 var k1 = [0, 0]; 161 var k2 = [0, 0]; 162 var c1 = [0x87c37b91, 0x114253d5]; 163 var c2 = [0x4cf5ad43, 0x2745937f]; 164 for (var i = 0; i < bytes; i = i + 16) {
165 k1 = [ 166 (key.charCodeAt(i + 4) & 0xff) | 167 ((key.charCodeAt(i + 5) & 0xff) << 8) | 168 ((key.charCodeAt(i + 6) & 0xff) << 16) | 169 ((key.charCodeAt(i + 7) & 0xff) << 24), 170 (key.charCodeAt(i) & 0xff) | 171 ((key.charCodeAt(i + 1) & 0xff) << 8) | 172 ((key.charCodeAt(i + 2) & 0xff) << 16) | 173 ((key.charCodeAt(i + 3) & 0xff) << 24) 174 ]; 175 k2 = [ 176 (key.charCodeAt(i + 12) & 0xff) | 177 ((key.charCodeAt(i + 13) & 0xff) << 8) | 178 ((key.charCodeAt(i + 14) & 0xff) << 16) | 179 ((key.charCodeAt(i + 15) & 0xff) << 24), 180 (key.charCodeAt(i + 8) & 0xff) | 181 ((key.charCodeAt(i + 9) & 0xff) << 8) | 182 ((key.charCodeAt(i + 10) & 0xff) << 16) | 183 ((key.charCodeAt(i + 11) & 0xff) << 24) 184 ]; 185 k1 = x64Multiply(k1, c1); 186 k1 = x64Rotl(k1, 31); 187 k1 = x64Multiply(k1, c2); 188 h1 = x64Xor(h1, k1); 189 h1 = x64Rotl(h1, 27); 190 h1 = x64Add(h1, h2); 191 h1 = x64Add(x64Multiply(h1, [0, 5]), [0, 0x52dce729]); 192 k2 = x64Multiply(k2, c2); 193 k2 = x64Rotl(k2, 33); 194 k2 = x64Multiply(k2, c1); 195 h2 = x64Xor(h2, k2); 196 h2 = x64Rotl(h2, 31); 197 h2 = x64Add(h2, h1); 198 h2 = x64Add(x64Multiply(h2, [0, 5]), [0, 0x38495ab5]); 199 } 200 k1 = [0, 0]; 201 k2 = [0, 0]; 202 switch (remainder) { 203 case 15:
204 k2 = x64Xor(k2, x64LeftShift([0, key.charCodeAt(i + 14)], 48)); 205 // fallthrough 206 case 14: 207 k2 = x64Xor(k2, x64LeftShift([0, key.charCodeAt(i + 13)], 40)); 208 // fallthrough 209 case 13: 210 k2 = x64Xor(k2, x64LeftShift([0, key.charCodeAt(i + 12)], 32)); 211 // fallthrough 212 case 12: 213 k2 = x64Xor(k2, x64LeftShift([0, key.charCodeAt(i + 11)], 24)); 214 // fallthrough 215 case 11: 216 k2 = x64Xor(k2, x64LeftShift([0, key.charCodeAt(i + 10)], 16)); 217 // fallthrough 218 case 10: 219 k2 = x64Xor(k2, x64LeftShift([0, key.charCodeAt(i + 9)], 8)); 220 // fallthrough 221 case 9: 222 k2 = x64Xor(k2, [0, key.charCodeAt(i + 8)]); 223 k2 = x64Multiply(k2, c2); 224 k2 = x64Rotl(k2, 33); 225 k2 = x64Multiply(k2, c1); 226 h2 = x64Xor(h2, k2); 227 // fallthrough 228 case 8: 229 k1 = x64Xor(k1, x64LeftShift([0, key.charCodeAt(i + 7)], 56)); 230 // fallthrough 231 case 7: 232 k1 = x64Xor(k1, x64LeftShift([0, key.charCodeAt(i + 6)], 48)); 233 // fallthrough 234 case 6:
235 k1 = x64Xor(k1, x64LeftShift([0, key.charCodeAt(i + 5)], 40)); 236 // fallthrough 237 case 5: 238 k1 = x64Xor(k1, x64LeftShift([0, key.charCodeAt(i + 4)], 32)); 239 // fallthrough 240 case 4: 241 k1 = x64Xor(k1, x64LeftShift([0, key.charCodeAt(i + 3)], 24)); 242 // fallthrough 243 case 3: 244 k1 = x64Xor(k1, x64LeftShift([0, key.charCodeAt(i + 2)], 16)); 245 // fallthrough 246 case 2: 247 k1 = x64Xor(k1, x64LeftShift([0, key.charCodeAt(i + 1)], 8)); 248 // fallthrough 249 case 1: 250 k1 = x64Xor(k1, [0, key.charCodeAt(i)]); 251 k1 = x64Multiply(k1, c1); 252 k1 = x64Rotl(k1, 31); 253 k1 = x64Multiply(k1, c2); 254 h1 = x64Xor(h1, k1); 255 // fallthrough 256 } 257 h1 = x64Xor(h1, [0, key.length]); 258 h2 = x64Xor(h2, [0, key.length]); 259 h1 = x64Add(h1, h2); 260 h2 = x64Add(h2, h1); 261 h1 = x64Fmix(h1); 262 h2 = x64Fmix(h2); 263 h1 = x64Add(h1, h2); 264 h2 = x64Add(h2, h1); 265 return ( 266 ("00000000" + (h1[0] >>> 0).toString(16)).slice(-8) + 267 ("00000000" + (h1[1] >>> 0).toString(16)).slice(-8) + 268 ("00000000" + (h2[0] >>> 0).toString(16)).slice(-8) + 269 ("00000000" + (h2[1] >>> 0).toString(16)).slice(-8) 270 ); 271 }; 272 273 var defaultOptions = { 274 preprocessor: null, 275 audio: { 276 timeout: 1000, 277 // On iOS 11, audio context can only be used in response to user interaction. 278 // We require users to explicitly enable audio fingerprinting on iOS 11. 279 // See https://stackoverflow.com/questions/46363048/onaudioprocess-not-called-on-ios11#46534088 280 excludeIOS11: true 281 }, 282 fonts: { 283 swfContainerId: "fingerprintjs2", 284 swfPath: "flash/compiled/FontList.swf", 285 userDefinedFonts: [], 286 extendedJsFonts: false 287 }, 288 screen: { 289 // To ensure consistent fingerprints when users rotate their mobile devices 290 detectScreenOrientation: true 291 }, 292 plugins: { 293 sortPluginsFor: [/palemoon/i], 294 excludeIE: false 295 }, 296 extraComponents: [], 297 excludes: { 298 // Unreliable on Windows, see https://github.com/Valve/fingerprintjs2/issues/375 299 enumerateDevices: true, 300 // devicePixelRatio depends on browser zoom, and it's impossible to detect browser zoom 301 pixelRatio: true, 302 // DNT depends on incognito mode for some browsers (Chrome) and it's impossible to detect incognito mode 303 doNotTrack: true, 304 // uses js fonts already 305 fontsFlash: true 306 }, 307 NOT_AVAILABLE: "not available", 308 ERROR: "error", 309 EXCLUDED: "excluded" 310 }; 311 312 var each = function(obj, iterator) { 313 if (Array.prototype.forEach && obj.forEach === Array.prototype.forEach) { 314 obj.forEach(iterator); 315 } else if (obj.length === +obj.length) { 316 for (var i = 0, l = obj.length; i < l; i++) { 317 iterator(obj[i], i, obj); 318 } 319 } else { 320 for (var key in obj) { 321 if (obj.hasOwnProperty(key)) { 322 iterator(obj[key], key, obj); 323 } 324 } 325 } 326 }; 327 328 var map = function(obj, iterator) { 329 var results = []; 330 // Not using strict equality so that this acts as a 331 // shortcut to checking for `null` and `undefined`. 332 if (obj == null) { 333 return results; 334 } 335 if (Array.prototype.map && obj.map === Array.prototype.map) { 336 return obj.map(iterator); 337 } 338 each(obj, function(value, index, list) { 339 results.push(iterator(value, index, list)); 340 }); 341 return results; 342 }; 343 344 var extendSoft = function(target, source) { 345 if (source == null) { 346 return target; 347 } 348 var value; 349 var key; 350 for (key in source) { 351 value = source[key]; 352 if (value != null && !Object.prototype.hasOwnProperty.call(target, key)) { 353 target[key] = value; 354 } 355 } 356 return target; 357 }; 358 359 // https://developer.mozilla.org/en-US/docs/Web/API/MediaDevices/enumerateDevices 360 var enumerateDevicesKey = function(done, options) { 361 if (!isEnumerateDevicesSupported()) { 362 return done(options.NOT_AVAILABLE); 363 } 364 navigator.mediaDevices 365 .enumerateDevices() 366 .then(function(devices) { 367 done( 368 devices.map(function(device) { 369 return ( 370 "id=" + 371 device.deviceId + 372 ";gid=" + 373 device.groupId + 374 ";" + 375 device.kind + 376 ";" + 377 device.label 378 ); 379 }) 380 ); 381 }) 382 .catch(function(error) { 383 done(error); 384 }); 385 }; 386 387 var isEnumerateDevicesSupported = function() { 388 return navigator.mediaDevices && navigator.mediaDevices.enumerateDevices; 389 }; 390 // Inspired by and based on https://github.com/cozylife/audio-fingerprint 391 var audioKey = function(done, options) { 392 var audioOptions = options.audio; 393 if ( 394 audioOptions.excludeIOS11 && 395 navigator.userAgent.match(/OS 11.+Version\/11.+Safari/) 396 ) { 397 // See comment for excludeUserAgent and https://stackoverflow.com/questions/46363048/onaudioprocess-not-called-on-ios11#46534088 398 return done(options.EXCLUDED); 399 } 400 401 var AudioContext = 402 window.OfflineAudioContext || window.webkitOfflineAudioContext; 403 404 if (AudioContext == null) { 405 return done(options.NOT_AVAILABLE); 406 } 407 408 var context = new AudioContext(1, 44100, 44100); 409 410 var oscillator = context.createOscillator(); 411 oscillator.type = "triangle"; 412 oscillator.frequency.setValueAtTime(10000, context.currentTime); 413 414 var compressor = context.createDynamicsCompressor(); 415 each( 416 [ 417 ["threshold", -50], 418 ["knee", 40], 419 ["ratio", 12], 420 ["reduction", -20], 421 ["attack", 0], 422 ["release", 0.25] 423 ], 424 function(item) { 425 if ( 426 compressor[item[0]] !== undefined && 427 typeof compressor[item[0]].setValueAtTime === "function" 428 ) { 429 compressor[item[0]].setValueAtTime(item[1], context.currentTime); 430 } 431 } 432 ); 433 434 oscillator.connect(compressor); 435 compressor.connect(context.destination); 436 oscillator.start(0); 437 context.startRendering(); 438 439 var audioTimeoutId = setTimeout(function() { 440 console.warn( 441 'Audio fingerprint timed out. Please report bug at https://github.com/Valve/fingerprintjs2 with your user agent: "' + 442 navigator.userAgent + 443 '".' 444 ); 445 context.oncomplete = function() {}; 446 context = null; 447 return done("audioTimeout"); 448 }, audioOptions.timeout); 449 450 context.oncomplete = function(event) { 451 var fingerprint;
452 try { 453 clearTimeout(audioTimeoutId); 454 fingerprint = event.renderedBuffer 455 .getChannelData(0) 456 .slice(4500, 5000) 457 .reduce(function(acc, val) { 458 return acc + Math.abs(val); 459 }, 0) 460 .toString(); 461 oscillator.disconnect(); 462 compressor.disconnect(); 463 } catch (error) { 464 done(error); 465 return; 466 } 467 done(fingerprint); 468 }; 469 }; 470 var UserAgent = function(done) { 471 done(navigator.userAgent); 472 }; 473 var languageKey = function(done, options) { 474 done( 475 navigator.language || 476 navigator.userLanguage || 477 navigator.browserLanguage || 478 navigator.systemLanguage || 479 options.NOT_AVAILABLE 480 ); 481 }; 482 var colorDepthKey = function(done, options) { 483 done(window.screen.colorDepth || options.NOT_AVAILABLE); 484 }; 485 var deviceMemoryKey = function(done, options) { 486 done(navigator.deviceMemory || options.NOT_AVAILABLE); 487 }; 488 var pixelRatioKey = function(done, options) { 489 done(window.devicePixelRatio || options.NOT_AVAILABLE); 490 }; 491 var screenResolutionKey = function(done, options) { 492 done(getScreenResolution(options)); 493 }; 494 var getScreenResolution = function(options) { 495 var resolution = [window.screen.width, window.screen.height]; 496 if (options.screen.detectScreenOrientation) { 497 resolution.sort().reverse(); 498 } 499 return resolution; 500 }; 501 var availableScreenResolutionKey = function(done, options) { 502 done(getAvailableScreenResolution(options)); 503 }; 504 var getAvailableScreenResolution = function(options) { 505 if (window.screen.availWidth && window.screen.availHeight) { 506 var available = [window.screen.availHeight, window.screen.availWidth]; 507 if (options.screen.detectScreenOrientation) { 508 available.sort().reverse(); 509 } 510 return available; 511 } 512 // headless browsers 513 return options.NOT_AVAILABLE; 514 }; 515 var timezoneOffset = function(done) { 516 done(new Date().getTimezoneOffset()); 517 }; 518 var timezone = function(done, options) { 519 if (window.Intl && window.Intl.DateTimeFormat) { 520 done(new window.Intl.DateTimeFormat().resolvedOptions().timeZone); 521 return; 522 } 523 done(options.NOT_AVAILABLE); 524 }; 525 var sessionStorageKey = function(done, options) { 526 done(hasSessionStorage(options)); 527 }; 528 var localStorageKey = function(done, options) { 529 done(hasLocalStorage(options)); 530 }; 531 var indexedDbKey = function(done, options) { 532 done(hasIndexedDB(options)); 533 }; 534 var addBehaviorKey = function(done) { 535 // body might not be defined at this point or removed programmatically 536 done(!!(document.body && document.body.addBehavior)); 537 }; 538 var openDatabaseKey = function(done) { 539 done(!!window.openDatabase); 540 }; 541 var cpuClassKey = function(done, options) { 542 done(getNavigatorCpuClass(options)); 543 }; 544 var platformKey = function(done, options) { 545 done(getNavigatorPlatform(options)); 546 }; 547 var doNotTrackKey = function(done, options) { 548 done(getDoNotTrack(options)); 549 }; 550 var canvasKey = function(done, options) { 551 if (isCanvasSupported()) { 552 done(getCanvasFp(options)); 553 return; 554 } 555 done(options.NOT_AVAILABLE); 556 }; 557 var webglKey = function(done, options) { 558 if (isWebGlSupported()) { 559 done(getWebglFp()); 560 return; 561 } 562 done(options.NOT_AVAILABLE); 563 }; 564 var webglVendorAndRendererKey = function(done) { 565 if (isWebGlSupported()) { 566 done(getWebglVendorAndRenderer()); 567 return; 568 } 569 done(); 570 }; 571 var adBlockKey = function(done) { 572 done(getAdBlock()); 573 }; 574 var hasLiedLanguagesKey = function(done) { 575 done(getHasLiedLanguages()); 576 }; 577 var hasLiedResolutionKey = function(done) { 578 done(getHasLiedResolution()); 579 }; 580 var hasLiedOsKey = function(done) { 581 done(getHasLiedOs()); 582 }; 583 var hasLiedBrowserKey = function(done) { 584 done(getHasLiedBrowser()); 585 }; 586 // flash fonts (will increase fingerprinting time 20X to ~ 130-150ms) 587 var flashFontsKey = function(done, options) { 588 // we do flash if swfobject is loaded 589 if (!hasSwfObjectLoaded()) { 590 return done("swf object not loaded"); 591 } 592 if (!hasMinFlashInstalled()) { 593 return done("flash not installed"); 594 } 595 if (!options.fonts.swfPath) { 596 return done("missing options.fonts.swfPath"); 597 } 598 loadSwfAndDetectFonts(function(fonts) { 599 done(fonts); 600 }, options); 601 }; 602 // kudos to http://www.lalit.org/lab/javascript-css-font-detect/ 603 var jsFontsKey = function(done, options) { 604 // a font will be compared against all the three default fonts. 605 // and if it doesn't match all 3 then that font is not available.
606 var baseFonts = ["monospace", "sans-serif", "serif"]; 607 608 var fontList = [ 609 "Andale Mono", 610 "Arial", 611 "Arial Black", 612 "Arial Hebrew", 613 "Arial MT", 614 "Arial Narrow", 615 "Arial Rounded MT Bold", 616 "Arial Unicode MS", 617 "Bitstream Vera Sans Mono", 618 "Book Antiqua", 619 "Bookman Old Style", 620 "Calibri", 621 "Cambria", 622 "Cambria Math", 623 "Century", 624 "Century Gothic", 625 "Century Schoolbook", 626 "Comic Sans", 627 "Comic Sans MS", 628 "Consolas", 629 "Courier", 630 "Courier New", 631 "Geneva", 632 "Georgia", 633 "Helvetica", 634 "Helvetica Neue", 635 "Impact", 636 "Lucida Bright", 637 "Lucida Calligraphy", 638 "Lucida Console", 639 "Lucida Fax", 640 "LUCIDA GRANDE", 641 "Lucida Handwriting", 642 "Lucida Sans", 643 "Lucida Sans Typewriter", 644 "Lucida Sans Unicode", 645 "Microsoft Sans Serif", 646 "Monaco", 647 "Monotype Corsiva", 648 "MS Gothic", 649 "MS Outlook", 650 "MS PGothic", 651 "MS Reference Sans Serif", 652 "MS Sans Serif", 653 "MS Serif", 654 "MYRIAD", 655 "MYRIAD PRO", 656 "Palatino", 657 "Palatino Linotype", 658 "Segoe Print", 659 "Segoe Script", 660 "Segoe UI", 661 "Segoe UI Light", 662 "Segoe UI Semibold", 663 "Segoe UI Symbol", 664 "Tahoma", 665 "Times", 666 "Times New Roman", 667 "Times New Roman PS", 668 "Trebuchet MS", 669 "Verdana", 670 "Wingdings", 671 "Wingdings 2", 672 "Wingdings 3" 673 ]; 674 675 if (options.fonts.extendedJsFonts) { 676 var extendedFontList = [ 677 "Abadi MT Condensed Light", 678 "Academy Engraved LET", 679 "ADOBE CASLON PRO", 680 "Adobe Garamond", 681 "ADOBE GARAMOND PRO", 682 "Agency FB", 683 "Aharoni", 684 "Albertus Extra Bold", 685 "Albertus Medium", 686 "Algerian", 687 "Amazone BT", 688 "American Typewriter", 689 "American Typewriter Condensed", 690 "AmerType Md BT", 691 "Andalus", 692 "Angsana New", 693 "AngsanaUPC", 694 "Antique Olive", 695 "Aparajita", 696 "Apple Chancery", 697 "Apple Color Emoji", 698 "Apple SD Gothic Neo", 699 "Arabic Typesetting", 700 "ARCHER", 701 "ARNO PRO", 702 "Arrus BT", 703 "Aurora Cn BT", 704 "AvantGarde Bk BT", 705 "AvantGarde Md BT", 706 "AVENIR", 707 "Ayuthaya", 708 "Bandy", 709 "Bangla Sangam MN", 710 "Bank Gothic", 711 "BankGothic Md BT", 712 "Baskerville", 713 "Baskerville Old Face", 714 "Batang", 715 "BatangChe", 716 "Bauer Bodoni", 717 "Bauhaus 93", 718 "Bazooka", 719 "Bell MT", 720 "Bembo", 721 "Benguiat Bk BT", 722 "Berlin Sans FB", 723 "Berlin Sans FB Demi", 724 "Bernard MT Condensed", 725 "BernhardFashion BT", 726 "BernhardMod BT", 727 "Big Caslon", 728 "BinnerD", 729 "Blackadder ITC", 730 "BlairMdITC TT", 731 "Bodoni 72", 732 "Bodoni 72 Oldstyle", 733 "Bodoni 72 Smallcaps", 734 "Bodoni MT", 735 "Bodoni MT Black", 736 "Bodoni MT Condensed", 737 "Bodoni MT Poster Compressed", 738 "Bookshelf Symbol 7", 739 "Boulder", 740 "Bradley Hand", 741 "Bradley Hand ITC", 742 "Bremen Bd BT", 743 "Britannic Bold", 744 "Broadway", 745 "Browallia New", 746 "BrowalliaUPC", 747 "Brush Script MT", 748 "Californian FB", 749 "Calisto MT", 750 "Calligrapher", 751 "Candara", 752 "CaslonOpnface BT", 753 "Castellar", 754 "Centaur", 755 "Cezanne", 756 "CG Omega", 757 "CG Times", 758 "Chalkboard", 759 "Chalkboard SE", 760 "Chalkduster", 761 "Charlesworth", 762 "Charter Bd BT", 763 "Charter BT", 764 "Chaucer", 765 "ChelthmITC Bk BT", 766 "Chiller", 767 "Clarendon", 768 "Clarendon Condensed", 769 "CloisterBlack BT", 770 "Cochin", 771 "Colonna MT", 772 "Constantia", 773 "Cooper Black", 774 "Copperplate", 775 "Copperplate Gothic", 776 "Copperplate Gothic Bold", 777 "Copperplate Gothic Light", 778 "CopperplGoth Bd BT", 779 "Corbel", 780 "Cordia New", 781 "CordiaUPC", 782 "Cornerstone", 783 "Coronet", 784 "Cuckoo", 785 "Curlz MT", 786 "DaunPenh", 787 "Dauphin", 788 "David", 789 "DB LCD Temp", 790 "DELICIOUS", 791 "Denmark", 792 "DFKai-SB", 793 "Didot", 794 "DilleniaUPC", 795 "DIN", 796 "DokChampa", 797 "Dotum", 798 "DotumChe", 799 "Ebrima", 800 "Edwardian Script ITC", 801 "Elephant", 802 "English 111 Vivace BT", 803 "Engravers MT", 804 "EngraversGothic BT", 805 "Eras Bold ITC", 806 "Eras Demi ITC", 807 "Eras Light ITC", 808 "Eras Medium ITC", 809 "EucrosiaUPC", 810 "Euphemia", 811 "Euphemia UCAS", 812 "EUROSTILE", 813 "Exotc350 Bd BT", 814 "FangSong", 815 "Felix Titling", 816 "Fixedsys", 817 "FONTIN", 818 "Footlight MT Light", 819 "Forte", 820 "FrankRuehl", 821 "Fransiscan", 822 "Freefrm721 Blk BT", 823 "FreesiaUPC", 824 "Freestyle Script", 825 "French Script MT", 826 "FrnkGothITC Bk BT", 827 "Fruitger", 828 "FRUTIGER", 829 "Futura", 830 "Futura Bk BT", 831 "Futura Lt BT", 832 "Futura Md BT", 833 "Futura ZBlk BT", 834 "FuturaBlack BT", 835 "Gabriola", 836 "Galliard BT", 837 "Gautami", 838 "Geeza Pro", 839 "Geometr231 BT", 840 "Geometr231 Hv BT", 841 "Geometr231 Lt BT", 842 "GeoSlab 703 Lt BT", 843 "GeoSlab 703 XBd BT", 844 "Gigi", 845 "Gill Sans", 846 "Gill Sans MT", 847 "Gill Sans MT Condensed", 848 "Gill Sans MT Ext Condensed Bold", 849 "Gill Sans Ultra Bold", 850 "Gill Sans Ultra Bold Condensed", 851 "Gisha", 852 "Gloucester MT Extra Condensed", 853 "GOTHAM", 854 "GOTHAM BOLD", 855 "Goudy Old Style", 856 "Goudy Stout",
857 "GoudyHandtooled BT", 858 "GoudyOLSt BT", 859 "Gujarati Sangam MN", 860 "Gulim", 861 "GulimChe", 862 "Gungsuh", 863 "GungsuhChe", 864 "Gurmukhi MN", 865 "Haettenschweiler", 866 "Harlow Solid Italic", 867 "Harrington", 868 "Heather", 869 "Heiti SC", 870 "Heiti TC", 871 "HELV", 872 "Herald", 873 "High Tower Text", 874 "Hiragino Kaku Gothic ProN", 875 "Hiragino Mincho ProN", 876 "Hoefler Text", 877 "Humanst 521 Cn BT", 878 "Humanst521 BT", 879 "Humanst521 Lt BT", 880 "Imprint MT Shadow", 881 "Incised901 Bd BT", 882 "Incised901 BT", 883 "Incised901 Lt BT", 884 "INCONSOLATA", 885 "Informal Roman", 886 "Informal011 BT", 887 "INTERSTATE", 888 "IrisUPC", 889 "Iskoola Pota", 890 "JasmineUPC", 891 "Jazz LET", 892 "Jenson", 893 "Jester", 894 "Jokerman", 895 "Juice ITC", 896 "Kabel Bk BT", 897 "Kabel Ult BT", 898 "Kailasa", 899 "KaiTi", 900 "Kalinga", 901 "Kannada Sangam MN", 902 "Kartika", 903 "Kaufmann Bd BT", 904 "Kaufmann BT", 905 "Khmer UI", 906 "KodchiangUPC", 907 "Kokila", 908 "Korinna BT", 909 "Kristen ITC", 910 "Krungthep", 911 "Kunstler Script", 912 "Lao UI", 913 "Latha", 914 "Leelawadee", 915 "Letter Gothic", 916 "Levenim MT", 917 "LilyUPC", 918 "Lithograph", 919 "Lithograph Light", 920 "Long Island", 921 "Lydian BT", 922 "Magneto", 923 "Maiandra GD", 924 "Malayalam Sangam MN", 925 "Malgun Gothic", 926 "Mangal", 927 "Marigold", 928 "Marion", 929 "Marker Felt", 930 "Market", 931 "Marlett", 932 "Matisse ITC", 933 "Matura MT Script Capitals", 934 "Meiryo", 935 "Meiryo UI", 936 "Microsoft Himalaya", 937 "Microsoft JhengHei", 938 "Microsoft New Tai Lue", 939 "Microsoft PhagsPa", 940 "Microsoft Tai Le", 941 "Microsoft Uighur", 942 "Microsoft YaHei", 943 "Microsoft Yi Baiti", 944 "MingLiU", 945 "MingLiU_HKSCS", 946 "MingLiU_HKSCS-ExtB", 947 "MingLiU-ExtB", 948 "Minion", 949 "Minion Pro", 950 "Miriam", 951 "Miriam Fixed", 952 "Mistral", 953 "Modern", 954 "Modern No. 20", 955 "Mona Lisa Solid ITC TT", 956 "Mongolian Baiti", 957 "MONO", 958 "MoolBoran", 959 "Mrs Eaves", 960 "MS LineDraw", 961 "MS Mincho", 962 "MS PMincho", 963 "MS Reference Specialty", 964 "MS UI Gothic", 965 "MT Extra", 966 "MUSEO", 967 "MV Boli", 968 "Nadeem", 969 "Narkisim", 970 "NEVIS", 971 "News Gothic", 972 "News GothicMT", 973 "NewsGoth BT", 974 "Niagara Engraved", 975 "Niagara Solid", 976 "Noteworthy", 977 "NSimSun", 978 "Nyala", 979 "OCR A Extended", 980 "Old Century", 981 "Old English Text MT", 982 "Onyx", 983 "Onyx BT", 984 "OPTIMA", 985 "Oriya Sangam MN", 986 "OSAKA", 987 "OzHandicraft BT", 988 "Palace Script MT", 989 "Papyrus", 990 "Parchment", 991 "Party LET", 992 "Pegasus", 993 "Perpetua", 994 "Perpetua Titling MT", 995 "PetitaBold", 996 "Pickwick", 997 "Plantagenet Cherokee", 998 "Playbill", 999 "PMingLiU", 1000 "PMingLiU-ExtB", 1001 "Poor Richard", 1002 "Poster", 1003 "PosterBodoni BT", 1004 "PRINCETOWN LET", 1005 "Pristina", 1006 "PTBarnum BT", 1007 "Pythagoras", 1008 "Raavi", 1009 "Rage Italic", 1010 "Ravie", 1011 "Ribbon131 Bd BT", 1012 "Rockwell", 1013 "Rockwell Condensed", 1014 "Rockwell Extra Bold", 1015 "Rod", 1016 "Roman", 1017 "Sakkal Majalla", 1018 "Santa Fe LET", 1019 "Savoye LET", 1020 "Sceptre", 1021 "Script", 1022 "Script MT Bold", 1023 "SCRIPTINA", 1024 "Serifa", 1025 "Serifa BT", 1026 "Serifa Th BT", 1027 "ShelleyVolante BT", 1028 "Sherwood", 1029 "Shonar Bangla", 1030 "Showcard Gothic", 1031 "Shruti", 1032 "Signboard", 1033 "SILKSCREEN", 1034 "SimHei", 1035 "Simplified Arabic", 1036 "Simplified Arabic Fixed", 1037 "SimSun", 1038 "SimSun-ExtB", 1039 "Sinhala Sangam MN", 1040 "Sketch Rockwell", 1041 "Skia", 1042 "Small Fonts", 1043 "Snap ITC", 1044 "Snell Roundhand", 1045 "Socket", 1046 "Souvenir Lt BT", 1047 "Staccato222 BT", 1048 "Steamer", 1049 "Stencil", 1050 "Storybook", 1051 "Styllo", 1052 "Subway", 1053 "Swis721 BlkEx BT", 1054 "Swiss911 XCm BT", 1055 "Sylfaen", 1056 "Synchro LET", 1057 "System", 1058 "Tamil Sangam MN", 1059 "Technical", 1060 "Teletype", 1061 "Telugu Sangam MN", 1062 "Tempus Sans ITC", 1063 "Terminal", 1064 "Thonburi", 1065 "Traditional Arabic", 1066 "Trajan", 1067 "TRAJAN PRO", 1068 "Tristan", 1069 "Tubular", 1070 "Tunga", 1071 "Tw Cen MT",
1072 "Tw Cen MT Condensed", 1073 "Tw Cen MT Condensed Extra Bold", 1074 "TypoUpright BT", 1075 "Unicorn", 1076 "Univers", 1077 "Univers CE 55 Medium", 1078 "Univers Condensed", 1079 "Utsaah", 1080 "Vagabond", 1081 "Vani", 1082 "Vijaya", 1083 "Viner Hand ITC", 1084 "VisualUI", 1085 "Vivaldi", 1086 "Vladimir Script", 1087 "Vrinda", 1088 "Westminster", 1089 "WHITNEY", 1090 "Wide Latin", 1091 "ZapfEllipt BT", 1092 "ZapfHumnst BT", 1093 "ZapfHumnst Dm BT", 1094 "Zapfino", 1095 "Zurich BlkEx BT", 1096 "Zurich Ex BT", 1097 "ZWAdobeF" 1098 ]; 1099 fontList = fontList.concat(extendedFontList); 1100 } 1101 1102 fontList = fontList.concat(options.fonts.userDefinedFonts); 1103 1104 // remove duplicate fonts 1105 fontList = fontList.filter(function(font, position) { 1106 return fontList.indexOf(font) === position; 1107 }); 1108 1109 // we use m or w because these two characters take up the maximum width. 1110 // And we use a LLi so that the same matching fonts can get separated 1111 var testString = "mmmmmmmmmmlli"; 1112 1113 // we test using 72px font size, we may use any size. I guess larger the better. 1114 var testSize = "72px"; 1115 1116 var h = document.getElementsByTagName("body")[0]; 1117 1118 // div to load spans for the base fonts 1119 var baseFontsDiv = document.createElement("div"); 1120 1121 // div to load spans for the fonts to detect 1122 var fontsDiv = document.createElement("div"); 1123 1124 var defaultWidth = {}; 1125 var defaultHeight = {}; 1126 1127 // creates a span where the fonts will be loaded 1128 var createSpan = function() { 1129 var s = document.createElement("span"); 1130 /* 1131 * We need this css as in some weird browser this 1132 * span elements shows up for a microSec which creates a 1133 * bad user experience 1134 */ 1135 s.style.position = "absolute"; 1136 s.style.left = "-9999px"; 1137 s.style.fontSize = testSize; 1138 1139 // css font reset to reset external styles 1140 s.style.fontStyle = "normal"; 1141 s.style.fontWeight = "normal"; 1142 s.style.letterSpacing = "normal"; 1143 s.style.lineBreak = "auto"; 1144 s.style.lineHeight = "normal"; 1145 s.style.textTransform = "none"; 1146 s.style.textAlign = "left"; 1147 s.style.textDecoration = "none"; 1148 s.style.textShadow = "none"; 1149 s.style.whiteSpace = "normal"; 1150 s.style.wordBreak = "normal"; 1151 s.style.wordSpacing = "normal"; 1152 1153 s.innerHTML = testString; 1154 return s; 1155 }; 1156 1157 // creates a span and load the font to detect and a base font for fallback 1158 var createSpanWithFonts = function(fontToDetect, baseFont) { 1159 var s = createSpan(); 1160 s.style.fontFamily = "'" + fontToDetect + "'," + baseFont; 1161 return s; 1162 }; 1163 1164 // creates spans for the base fonts and adds them to baseFontsDiv 1165 var initializeBaseFontsSpans = function() { 1166 var spans = []; 1167 for (var index = 0, length = baseFonts.length; index < length; index++) { 1168 var s = createSpan(); 1169 s.style.fontFamily = baseFonts[index]; 1170 baseFontsDiv.appendChild(s); 1171 spans.push(s); 1172 } 1173 return spans; 1174 }; 1175 1176 // creates spans for the fonts to detect and adds them to fontsDiv 1177 var initializeFontsSpans = function() { 1178 var spans = {}; 1179 for (var i = 0, l = fontList.length; i < l; i++) { 1180 var fontSpans = []; 1181 for ( 1182 var j = 0, numDefaultFonts = baseFonts.length; 1183 j < numDefaultFonts; 1184 j++ 1185 ) { 1186 var s = createSpanWithFonts(fontList[i], baseFonts[j]); 1187 fontsDiv.appendChild(s); 1188 fontSpans.push(s); 1189 } 1190 spans[fontList[i]] = fontSpans; // Stores {fontName : [spans for that font]} 1191 } 1192 return spans; 1193 }; 1194 1195 // checks if a font is available 1196 var isFontAvailable = function(fontSpans) { 1197 var detected = false; 1198 for (var i = 0; i < baseFonts.length; i++) { 1199 detected = 1200 fontSpans[i].offsetWidth !== defaultWidth[baseFonts[i]] || 1201 fontSpans[i].offsetHeight !== defaultHeight[baseFonts[i]]; 1202 if (detected) { 1203 return detected; 1204 } 1205 } 1206 return detected; 1207 }; 1208 1209 // create spans for base fonts 1210 var baseFontsSpans = initializeBaseFontsSpans(); 1211 1212 // add the spans to the DOM 1213 h.appendChild(baseFontsDiv); 1214 1215 // get the default width for the three base fonts 1216 for (var index = 0, length = baseFonts.length; index < length; index++) { 1217 defaultWidth[baseFonts[index]] = baseFontsSpans[index].offsetWidth; // width for the default font 1218 defaultHeight[baseFonts[index]] = baseFontsSpans[index].offsetHeight; // height for the default font 1219 } 1220 1221 // create spans for fonts to detect 1222 var fontsSpans = initializeFontsSpans(); 1223 1224 // add all the spans to the DOM 1225 h.appendChild(fontsDiv); 1226 1227 // check available fonts 1228 var available = []; 1229 for (var i = 0, l = fontList.length; i < l; i++) { 1230 if (isFontAvailable(fontsSpans[fontList[i]])) { 1231 available.push(fontList[i]); 1232 } 1233 } 1234 1235 // remove spans from DOM 1236 h.removeChild(fontsDiv); 1237 h.removeChild(baseFontsDiv); 1238 done(available); 1239 }; 1240 var pluginsComponent = function(done, options) { 1241 if (isIE()) { 1242 if (!options.plugins.excludeIE) { 1243 done(getIEPlugins(options)); 1244 } else { 1245 done(options.EXCLUDED); 1246 } 1247 } else { 1248 done(getRegularPlugins(options)); 1249 } 1250 }; 1251 var getRegularPlugins = function(options) { 1252 if (navigator.plugins == null) { 1253 return options.NOT_AVAILABLE; 1254 } 1255 1256 var plugins = []; 1257 // plugins isn't defined in Node envs. 1258 for (var i = 0, l = navigator.plugins.length; i < l; i++) { 1259 if (navigator.plugins[i]) { 1260 plugins.push(navigator.plugins[i]); 1261 } 1262 } 1263 1264 // sorting plugins only for those user agents, that we know randomize the plugins 1265 // every time we try to enumerate them 1266 if (pluginsShouldBeSorted(options)) { 1267 plugins = plugins.sort(function(a, b) { 1268 if (a.name > b.name) { 1269 return 1; 1270 } 1271 if (a.name < b.name) { 1272 return -1; 1273 } 1274 return 0; 1275 }); 1276 } 1277 return map(plugins, function(p) { 1278 var mimeTypes = map(p, function(mt) { 1279 return [mt.type, mt.suffixes]; 1280 }); 1281 return [p.name, p.description, mimeTypes]; 1282 }); 1283 }; 1284 var getIEPlugins = function(options) { 1285 var result = []; 1286 if ( 1287 (Object.getOwnPropertyDescriptor && 1288 Object.getOwnPropertyDescriptor(window, "ActiveXObject")) || 1289 "ActiveXObject" in window 1290 ) { 1291 var names = [ 1292 "AcroPDF.PDF", // Adobe PDF reader 7+ 1293 "Adodb.Stream", 1294 "AgControl.AgControl", // Silverlight 1295 "DevalVRXCtrl.DevalVRXCtrl.1", 1296 "MacromediaFlashPaper.MacromediaFlashPaper", 1297 "Msxml2.DOMDocument", 1298 "Msxml2.XMLHTTP", 1299 "PDF.PdfCtrl", // Adobe PDF reader 6 and earlier, brrr 1300 "QuickTime.QuickTime", // QuickTime 1301 "QuickTimeCheckObject.QuickTimeCheck.1", 1302 "RealPlayer", 1303 "RealPlayer.RealPlayer(tm) ActiveX Control (32-bit)", 1304 "RealVideo.RealVideo(tm) ActiveX Control (32-bit)", 1305 "Scripting.Dictionary", 1306 "SWCtl.SWCtl", // ShockWave player 1307 "Shell.UIHelper", 1308 "ShockwaveFlash.ShockwaveFlash", // flash plugin 1309 "Skype.Detection", 1310 "TDCCtl.TDCCtl", 1311 "WMPlayer.OCX", // Windows media player 1312 "rmocx.RealPlayer G2 Control", 1313 "rmocx.RealPlayer G2 Control.1" 1314 ]; 1315 // starting to detect plugins in IE 1316 result = map(names, function(name) { 1317 try { 1318 // eslint-disable-next-line no-new 1319 new window.ActiveXObject(name); 1320 return name; 1321 } catch (e) { 1322 return options.ERROR; 1323 } 1324 }); 1325 } else { 1326 result.push(options.NOT_AVAILABLE); 1327 } 1328 if (navigator.plugins) { 1329 result = result.concat(getRegularPlugins(options)); 1330 } 1331 return result; 1332 }; 1333 var pluginsShouldBeSorted = function(options) { 1334 var should = false;
1335 for (var i = 0, l = options.plugins.sortPluginsFor.length; i < l; i++) { 1336 var re = options.plugins.sortPluginsFor[i]; 1337 if (navigator.userAgent.match(re)) { 1338 should = true; 1339 break; 1340 } 1341 } 1342 return should; 1343 }; 1344 var touchSupportKey = function(done) { 1345 done(getTouchSupport()); 1346 }; 1347 var hardwareConcurrencyKey = function(done, options) { 1348 done(getHardwareConcurrency(options)); 1349 }; 1350 var hasSessionStorage = function(options) { 1351 try { 1352 return !!window.sessionStorage; 1353 } catch (e) { 1354 return options.ERROR; // SecurityError when referencing it means it exists 1355 } 1356 }; 1357 1358 // https://bugzilla.mozilla.org/show_bug.cgi?id=781447 1359 var hasLocalStorage = function(options) { 1360 try { 1361 return !!window.localStorage; 1362 } catch (e) { 1363 return options.ERROR; // SecurityError when referencing it means it exists 1364 } 1365 }; 1366 var hasIndexedDB = function(options) { 1367 try { 1368 return !!window.indexedDB; 1369 } catch (e) { 1370 return options.ERROR; // SecurityError when referencing it means it exists 1371 } 1372 }; 1373 var getHardwareConcurrency = function(options) { 1374 if (navigator.hardwareConcurrency) { 1375 return navigator.hardwareConcurrency; 1376 } 1377 return options.NOT_AVAILABLE; 1378 }; 1379 var getNavigatorCpuClass = function(options) { 1380 return navigator.cpuClass || options.NOT_AVAILABLE; 1381 }; 1382 var getNavigatorPlatform = function(options) { 1383 if (navigator.platform) { 1384 return navigator.platform; 1385 } else { 1386 return options.NOT_AVAILABLE; 1387 } 1388 }; 1389 var getDoNotTrack = function(options) { 1390 if (navigator.doNotTrack) { 1391 return navigator.doNotTrack; 1392 } else if (navigator.msDoNotTrack) { 1393 return navigator.msDoNotTrack; 1394 } else if (window.doNotTrack) { 1395 return window.doNotTrack; 1396 } else { 1397 return options.NOT_AVAILABLE; 1398 } 1399 }; 1400 // This is a crude and primitive touch screen detection. 1401 // It's not possible to currently reliably detect the availability of a touch screen 1402 // with a JS, without actually subscribing to a touch event. 1403 // http://www.stucox.com/blog/you-cant-detect-a-touchscreen/ 1404 // https://github.com/Modernizr/Modernizr/issues/548 1405 // method returns an array of 3 values: 1406 // maxTouchPoints, the success or failure of creating a TouchEvent, 1407 // and the availability of the 'ontouchstart' property 1408 1409 var getTouchSupport = function() { 1410 var maxTouchPoints = 0; 1411 var touchEvent; 1412 if (typeof navigator.maxTouchPoints !== "undefined") { 1413 maxTouchPoints = navigator.maxTouchPoints; 1414 } else if (typeof navigator.msMaxTouchPoints !== "undefined") { 1415 maxTouchPoints = navigator.msMaxTouchPoints; 1416 } 1417 try { 1418 document.createEvent("TouchEvent"); 1419 touchEvent = true; 1420 } catch (_) { 1421 touchEvent = false; 1422 } 1423 var touchStart = "ontouchstart" in window; 1424 return [maxTouchPoints, touchEvent, touchStart]; 1425 }; 1426 // https://www.browserleaks.com/canvas#how-does-it-work 1427 1428 var getCanvasFp = function(options) { 1429 var result = []; 1430 // Very simple now, need to make it more complex (geo shapes etc) 1431 var canvas = document.createElement("canvas"); 1432 canvas.width = 2000; 1433 canvas.height = 200; 1434 canvas.style.display = "inline"; 1435 var ctx = canvas.getContext("2d"); 1436 // detect browser support of canvas winding 1437 // http://blogs.adobe.com/webplatform/2013/01/30/winding-rules-in-canvas/ 1438 // https://github.com/Modernizr/Modernizr/blob/master/feature-detects/canvas/winding.js 1439 ctx.rect(0, 0, 10, 10); 1440 ctx.rect(2, 2, 6, 6); 1441 result.push( 1442 "canvas winding:" + 1443 (ctx.isPointInPath(5, 5, "evenodd") === false ? "yes" : "no") 1444 ); 1445 1446 ctx.textBaseline = "alphabetic"; 1447 ctx.fillStyle = "#f60"; 1448 ctx.fillRect(125, 1, 62, 20); 1449 ctx.fillStyle = "#069"; 1450 // https://github.com/Valve/fingerprintjs2/issues/66 1451 if (options.dontUseFakeFontInCanvas) { 1452 ctx.font = "11pt Arial"; 1453 } else { 1454 ctx.font = "11pt no-real-font-123"; 1455 } 1456 ctx.fillText("Cwm fjordbank glyphs vext quiz, \ud83d\ude03", 2, 15); 1457 ctx.fillStyle = "rgba(102, 204, 0, 0.2)"; 1458 ctx.font = "18pt Arial"; 1459 ctx.fillText("Cwm fjordbank glyphs vext quiz, \ud83d\ude03", 4, 45); 1460 1461 // canvas blending 1462 // http://blogs.adobe.com/webplatform/2013/01/28/blending-features-in-canvas/ 1463 // http://jsfiddle.net/NDYV8/16/ 1464 ctx.globalCompositeOperation = "multiply"; 1465 ctx.fillStyle = "rgb(255,0,255)"; 1466 ctx.beginPath(); 1467 ctx.arc(50, 50, 50, 0, Math.PI * 2, true); 1468 ctx.closePath(); 1469 ctx.fill(); 1470 ctx.fillStyle = "rgb(0,255,255)"; 1471 ctx.beginPath(); 1472 ctx.arc(100, 50, 50, 0, Math.PI * 2, true); 1473 ctx.closePath(); 1474 ctx.fill(); 1475 ctx.fillStyle = "rgb(255,255,0)"; 1476 ctx.beginPath(); 1477 ctx.arc(75, 100, 50, 0, Math.PI * 2, true); 1478 ctx.closePath(); 1479 ctx.fill(); 1480 ctx.fillStyle = "rgb(255,0,255)"; 1481 // canvas winding 1482 // http://blogs.adobe.com/webplatform/2013/01/30/winding-rules-in-canvas/ 1483 // http://jsfiddle.net/NDYV8/19/ 1484 ctx.arc(75, 75, 75, 0, Math.PI * 2, true); 1485 ctx.arc(75, 75, 25, 0, Math.PI * 2, true); 1486 ctx.fill("evenodd"); 1487 1488 if (canvas.toDataURL) { 1489 result.push("canvas fp:" + canvas.toDataURL()); 1490 } 1491 return result; 1492 }; 1493 var getWebglFp = function() { 1494 var gl; 1495 var fa2s = function(fa) { 1496 gl.clearColor(0.0, 0.0, 0.0, 1.0); 1497 gl.enable(gl.DEPTH_TEST); 1498 gl.depthFunc(gl.LEQUAL); 1499 gl.clear(gl.COLOR_BUFFER_BIT | gl.DEPTH_BUFFER_BIT); 1500 return "[" + fa[0] + ", " + fa[1] + "]"; 1501 }; 1502 var maxAnisotropy = function(gl) { 1503 var ext = 1504 gl.getExtension("EXT_texture_filter_anisotropic") || 1505 gl.getExtension("WEBKIT_EXT_texture_filter_anisotropic") || 1506 gl.getExtension("MOZ_EXT_texture_filter_anisotropic"); 1507 if (ext) { 1508 var anisotropy = gl.getParameter(ext.MAX_TEXTURE_MAX_ANISOTROPY_EXT); 1509 if (anisotropy === 0) { 1510 anisotropy = 2; 1511 } 1512 return anisotropy; 1513 } else { 1514 return null; 1515 } 1516 }; 1517 1518 gl = getWebglCanvas(); 1519 if (!gl) { 1520 return null; 1521 } 1522 // WebGL fingerprinting is a combination of techniques, found in MaxMind antifraud
1522script & Augur fingerprinting. 1523 // First it draws a gradient object with shaders and convers the image to the Base64 string. 1524 // Then it enumerates all WebGL extensions & capabilities and appends them to the Base64 string, resulting in a huge WebGL string, potentially very unique on each device 1525 // Since iOS supports webgl starting from version 8.1 and 8.1 runs on several graphics chips, the results may be different across ios devices, but we need to verify it. 1526 var result = []; 1527 var vShaderTemplate = 1528 "attribute vec2 attrVertex;varying vec2 varyinTexCoordinate;uniform vec2 uniformOffset;void main(){varyinTexCoordinate=attrVertex+uniformOffset;gl_Position=vec4(attrVertex,0,1);}"; 1529 var fShaderTemplate = 1530 "precision mediump float;varying vec2 varyinTexCoordinate;void main() {gl_FragColor=vec4(varyinTexCoordinate,0,1);}"; 1531 var vertexPosBuffer = gl.createBuffer(); 1532 gl.bindBuffer(gl.ARRAY_BUFFER, vertexPosBuffer); 1533 var vertices = new Float32Array([ 1534 -0.2, 1535 -0.9, 1536 0, 1537 0.4, 1538 -0.26, 1539 0, 1540 0, 1541 0.732134444, 1542 0 1543 ]); 1544 gl.bufferData(gl.ARRAY_BUFFER, vertices, gl.STATIC_DRAW); 1545 vertexPosBuffer.itemSize = 3; 1546 vertexPosBuffer.numItems = 3; 1547 var program = gl.createProgram(); 1548 var vshader = gl.createShader(gl.VERTEX_SHADER); 1549 gl.shaderSource(vshader, vShaderTemplate); 1550 gl.compileShader(vshader); 1551 var fshader = gl.createShader(gl.FRAGMENT_SHADER); 1552 gl.shaderSource(fshader, fShaderTemplate); 1553 gl.compileShader(fshader); 1554 gl.attachShader(program, vshader); 1555 gl.attachShader(program, fshader); 1556 gl.linkProgram(program); 1557 gl.useProgram(program); 1558 program.vertexPosAttrib = gl.getAttribLocation(program, "attrVertex"); 1559 program.offsetUniform = gl.getUniformLocation(program, "uniformOffset"); 1560 gl.enableVertexAttribArray(program.vertexPosArray); 1561 gl.vertexAttribPointer( 1562 program.vertexPosAttrib, 1563 vertexPosBuffer.itemSize, 1564 gl.FLOAT, 1565 !1, 1566 0, 1567 0 1568 ); 1569 gl.uniform2f(program.offsetUniform, 1, 1); 1570 gl.drawArrays(gl.TRIANGLE_STRIP, 0, vertexPosBuffer.numItems); 1571 try { 1572 result.push(gl.canvas.toDataURL()); 1573 } catch (e) { 1574 /* .toDataURL may be absent or broken (blocked by extension) */ 1575 } 1576 result.push("extensions:" + (gl.getSupportedExtensions() || []).join(";")); 1577 result.push( 1578 "webgl aliased line width range:" + 1579 fa2s(gl.getParameter(gl.ALIASED_LINE_WIDTH_RANGE)) 1580 ); 1581 result.push( 1582 "webgl aliased point size range:" + 1583 fa2s(gl.getParameter(gl.ALIASED_POINT_SIZE_RANGE)) 1584 ); 1585 result.push("webgl alpha bits:" + gl.getParameter(gl.ALPHA_BITS)); 1586 result.push( 1587 "webgl antialiasing:" + 1588 (gl.getContextAttributes().antialias ? "yes" : "no") 1589 ); 1590 result.push("webgl blue bits:" + gl.getParameter(gl.BLUE_BITS)); 1591 result.push("webgl depth bits:" + gl.getParameter(gl.DEPTH_BITS)); 1592 result.push("webgl green bits:" + gl.getParameter(gl.GREEN_BITS)); 1593 result.push("webgl max anisotropy:" + maxAnisotropy(gl)); 1594 result.push( 1595 "webgl max combined texture image units:" + 1596 gl.getParameter(gl.MAX_COMBINED_TEXTURE_IMAGE_UNITS) 1597 ); 1598 result.push( 1599 "webgl max cube map texture size:" + 1600 gl.getParameter(gl.MAX_CUBE_MAP_TEXTURE_SIZE) 1601 ); 1602 result.push( 1603 "webgl max fragment uniform vectors:" + 1604 gl.getParameter(gl.MAX_FRAGMENT_UNIFORM_VECTORS) 1605 ); 1606 result.push( 1607 "webgl max render buffer size:" + 1608 gl.getParameter(gl.MAX_RENDERBUFFER_SIZE) 1609 ); 1610 result.push( 1611 "webgl max texture image units:" + 1612 gl.getParameter(gl.MAX_TEXTURE_IMAGE_UNITS) 1613 ); 1614 result.push( 1615 "webgl max texture size:" + gl.getParameter(gl.MAX_TEXTURE_SIZE) 1616 ); 1617 result.push( 1618 "webgl max varying vectors:" + gl.getParameter(gl.MAX_VARYING_VECTORS) 1619 ); 1620 result.push( 1621 "webgl max vertex attribs:" + gl.getParameter(gl.MAX_VERTEX_ATTRIBS) 1622 ); 1623 result.push( 1624 "webgl max vertex texture image units:" + 1625 gl.getParameter(gl.MAX_VERTEX_TEXTURE_IMAGE_UNITS) 1626 ); 1627 result.push( 1628 "webgl max vertex uniform vectors:" + 1629 gl.getParameter(gl.MAX_VERTEX_UNIFORM_VECTORS) 1630 ); 1631 result.push( 1632 "webgl max viewport dims:" + fa2s(gl.getParameter(gl.MAX_VIEWPORT_DIMS)) 1633 ); 1634 result.push("webgl red bits:" + gl.getParameter(gl.RED_BITS)); 1635 result.push("webgl renderer:" + gl.getParameter(gl.RENDERER)); 1636 result.push( 1637 "webgl shading language version:" + 1638 gl.getParameter(gl.SHADING_LANGUAGE_VERSION) 1639 ); 1640 result.push("webgl stencil bits:" + gl.getParameter(gl.STENCIL_BITS)); 1641 result.push("webgl vendor:" + gl.getParameter(gl.VENDOR)); 1642 result.push("webgl version:" + gl.getParameter(gl.VERSION)); 1643 1644 try { 1645 // Add the unmasked vendor and unmasked renderer if the debug_renderer_info extension is available 1646 var extensionDebugRendererInfo = gl.getExtension( 1647 "WEBGL_debug_renderer_info" 1648 ); 1649 if (extensionDebugRendererInfo) { 1650 result.push( 1651 "webgl unmasked vendor:" + 1652 gl.getParameter(extensionDebugRendererInfo.UNMASKED_VENDOR_WEBGL) 1653 ); 1654 result.push( 1655 "webgl unmasked renderer:" + 1656 gl.getParameter(extensionDebugRendererInfo.UNMASKED_RENDERER_WEBGL) 1657 ); 1658 } 1659 } catch (e) { 1660 /* squelch */ 1661 } 1662 1663 if (!gl.getShaderPrecisionFormat) { 1664 return result; 1665 } 1666 1667 each(["FLOAT", "INT"], function(numType) { 1668 each(["VERTEX", "FRAGMENT"], function(shader) { 1669 each(["HIGH", "MEDIUM", "LOW"], function(numSize) { 1670 each(["precision", "rangeMin", "rangeMax"], function(key) { 1671 var format = gl.getShaderPrecisionFormat( 1672 gl[shader + "_SHADER"], 1673 gl[numSize + "_" + numType] 1674 )[key]; 1675 if (key !== "precision") { 1676 key = "precision " + key; 1677 } 1678 var line = [ 1679 "webgl ", 1680 shader.toLowerCase(), 1681 " shader ", 1682 numSize.toLowerCase(), 1683 " ", 1684 numType.toLowerCase(), 1685 " ", 1686 key, 1687 ":", 1688 format 1689 ].join(""); 1690 result.push(line); 1691 }); 1692 }); 1693 }); 1694 }); 1695 return result; 1696 }; 1697 var getWebglVendorAndRenderer = function() { 1698 /* This a subset of the WebGL fingerprint with a lot of entropy, while being reasonably browser-independent */ 1699 try { 1700 var glContext = getWebglCanvas(); 1701 var extensionDebugRendererInfo = glContext.getExtension( 1702 "WEBGL_debug_renderer_info" 1703 ); 1704 return ( 1705 glContext.getParameter( 1706 extensionDebugRendererInfo.UNMASKED_VENDOR_WEBGL 1707 ) + 1708 "~" + 1709 glContext.getParameter( 1710 extensionDebugRendererInfo.UNMASKED_RENDERER_WEBGL 1711 ) 1712 ); 1713 } catch (e) { 1714 return null; 1715 } 1716 }; 1717 var getAdBlock = function() { 1718 var ads = document.createElement("div"); 1719 ads.innerHTML = " "; 1720 ads.className = "adsbox"; 1721 var result = false;
1722 try { 1723 // body may not exist, that's why we need try/catch 1724 document.body.appendChild(ads); 1725 result = document.getElementsByClassName("adsbox")[0].offsetHeight === 0; 1726 document.body.removeChild(ads); 1727 } catch (e) { 1728 result = false; 1729 } 1730 return result; 1731 }; 1732 var getHasLiedLanguages = function() { 1733 // We check if navigator.language is equal to the first language of navigator.languages 1734 if (typeof navigator.languages !== "undefined") { 1735 try { 1736 var firstLanguages = navigator.languages[0].substr(0, 2); 1737 if (firstLanguages !== navigator.language.substr(0, 2)) { 1738 return true; 1739 } 1740 } catch (err) { 1741 return true; 1742 } 1743 } 1744 return false; 1745 }; 1746 var getHasLiedResolution = function() { 1747 return ( 1748 window.screen.width < window.screen.availWidth || 1749 window.screen.height < window.screen.availHeight 1750 ); 1751 }; 1752 var getHasLiedOs = function() { 1753 var userAgent = navigator.userAgent.toLowerCase(); 1754 var oscpu = navigator.oscpu; 1755 var platform = navigator.platform.toLowerCase(); 1756 var os; 1757 // We extract the OS from the user agent (respect the order of the if else if statement) 1758 if (userAgent.indexOf("windows phone") >= 0) { 1759 os = "Windows Phone"; 1760 } else if (userAgent.indexOf("win") >= 0) { 1761 os = "Windows"; 1762 } else if (userAgent.indexOf("android") >= 0) { 1763 os = "Android"; 1764 } else if (userAgent.indexOf("linux") >= 0) { 1765 os = "Linux"; 1766 } else if ( 1767 userAgent.indexOf("iphone") >= 0 || 1768 userAgent.indexOf("ipad") >= 0 1769 ) { 1770 os = "iOS"; 1771 } else if (userAgent.indexOf("mac") >= 0) { 1772 os = "Mac"; 1773 } else { 1774 os = "Other"; 1775 } 1776 // We detect if the person uses a mobile device 1777 var mobileDevice = 1778 "ontouchstart" in window || 1779 navigator.maxTouchPoints > 0 || 1780 navigator.msMaxTouchPoints > 0; 1781 1782 if ( 1783 mobileDevice && 1784 os !== "Windows Phone" && 1785 os !== "Android" && 1786 os !== "iOS" && 1787 os !== "Other" 1788 ) { 1789 return true; 1790 } 1791 1792 // We compare oscpu with the OS extracted from the UA 1793 if (typeof oscpu !== "undefined") { 1794 oscpu = oscpu.toLowerCase(); 1795 if ( 1796 oscpu.indexOf("win") >= 0 && 1797 os !== "Windows" && 1798 os !== "Windows Phone" 1799 ) { 1800 return true; 1801 } else if ( 1802 oscpu.indexOf("linux") >= 0 && 1803 os !== "Linux" && 1804 os !== "Android" 1805 ) { 1806 return true; 1807 } else if (oscpu.indexOf("mac") >= 0 && os !== "Mac" && os !== "iOS") { 1808 return true; 1809 } else if ( 1810 (oscpu.indexOf("win") === -1 && 1811 oscpu.indexOf("linux") === -1 && 1812 oscpu.indexOf("mac") === -1) !== 1813 (os === "Other") 1814 ) { 1815 return true; 1816 } 1817 } 1818 1819 // We compare platform with the OS extracted from the UA 1820 if ( 1821 platform.indexOf("win") >= 0 && 1822 os !== "Windows" && 1823 os !== "Windows Phone" 1824 ) { 1825 return true; 1826 } else if ( 1827 (platform.indexOf("linux") >= 0 || 1828 platform.indexOf("android") >= 0 || 1829 platform.indexOf("pike") >= 0) && 1830 os !== "Linux" && 1831 os !== "Android" 1832 ) { 1833 return true; 1834 } else if ( 1835 (platform.indexOf("mac") >= 0 || 1836 platform.indexOf("ipad") >= 0 || 1837 platform.indexOf("ipod") >= 0 || 1838 platform.indexOf("iphone") >= 0) && 1839 os !== "Mac" && 1840 os !== "iOS" 1841 ) { 1842 return true; 1843 } else if ( 1844 (platform.indexOf("win") === -1 && 1845 platform.indexOf("linux") === -1 && 1846 platform.indexOf("mac") === -1) !== 1847 (os === "Other") 1848 ) { 1849 return true; 1850 } 1851 1852 return ( 1853 typeof navigator.plugins === "undefined" && 1854 os !== "Windows" && 1855 os !== "Windows Phone" 1856 ); 1857 }; 1858 var getHasLiedBrowser = function() { 1859 var userAgent = navigator.userAgent.toLowerCase(); 1860 var productSub = navigator.productSub; 1861 1862 // we extract the browser from the user agent (respect the order of the tests) 1863 var browser; 1864 if (userAgent.indexOf("firefox") >= 0) { 1865 browser = "Firefox"; 1866 } else if ( 1867 userAgent.indexOf("opera") >= 0 || 1868 userAgent.indexOf("opr") >= 0 1869 ) { 1870 browser = "Opera"; 1871 } else if (userAgent.indexOf("chrome") >= 0) { 1872 browser = "Chrome"; 1873 } else if (userAgent.indexOf("safari") >= 0) { 1874 browser = "Safari"; 1875 } else if (userAgent.indexOf("trident") >= 0) { 1876 browser = "Internet Explorer"; 1877 } else { 1878 browser = "Other"; 1879 } 1880 1881 if ( 1882 (browser === "Chrome" || browser === "Safari" || browser === "Opera") && 1883 productSub !== "20030107" 1884 ) { 1885 return true; 1886 } 1887 1888 // eslint-disable-next-line no-eval 1889 var tempRes = eval.toString().length; 1890 if ( 1891 tempRes === 37 && 1892 browser !== "Safari" && 1893 browser !== "Firefox" && 1894 browser !== "Other" 1895 ) { 1896 return true; 1897 } else if ( 1898 tempRes === 39 && 1899 browser !== "Internet Explorer" && 1900 browser !== "Other" 1901 ) { 1902 return true; 1903 } else if ( 1904 tempRes === 33 && 1905 browser !== "Chrome" && 1906 browser !== "Opera" && 1907 browser !== "Other" 1908 ) { 1909 return true; 1910 } 1911 1912 // We create an error to see how it is handled 1913 var errFirefox; 1914 try { 1915 // eslint-disable-next-line no-throw-literal 1916 throw "a"; 1917 } catch (err) {
1918 try { 1919 err.toSource(); 1920 errFirefox = true; 1921 } catch (errOfErr) { 1922 errFirefox = false; 1923 } 1924 } 1925 return errFirefox && browser !== "Firefox" && browser !== "Other"; 1926 }; 1927 var isCanvasSupported = function() { 1928 var elem = document.createElement("canvas"); 1929 return !!(elem.getContext && elem.getContext("2d")); 1930 }; 1931 var isWebGlSupported = function() { 1932 // code taken from Modernizr 1933 if (!isCanvasSupported()) { 1934 return false; 1935 } 1936 1937 var glContext = getWebglCanvas(); 1938 return !!window.WebGLRenderingContext && !!glContext; 1939 }; 1940 var isIE = function() { 1941 if (navigator.appName === "Microsoft Internet Explorer") { 1942 return true; 1943 } else if ( 1944 navigator.appName === "Netscape" && 1945 /Trident/.test(navigator.userAgent) 1946 ) { 1947 // IE 11 1948 return true; 1949 } 1950 return false; 1951 }; 1952 var hasSwfObjectLoaded = function() { 1953 return typeof window.swfobject !== "undefined"; 1954 }; 1955 var hasMinFlashInstalled = function() { 1956 return window.swfobject.hasFlashPlayerVersion("9.0.0"); 1957 }; 1958 var addFlashDivNode = function(options) { 1959 var node = document.createElement("div"); 1960 node.setAttribute("id", options.fonts.swfContainerId); 1961 document.body.appendChild(node); 1962 }; 1963 var loadSwfAndDetectFonts = function(done, options) { 1964 var hiddenCallback = "___fp_swf_loaded"; 1965 window[hiddenCallback] = function(fonts) { 1966 done(fonts); 1967 }; 1968 var id = options.fonts.swfContainerId; 1969 addFlashDivNode(); 1970 var flashvars = { onReady: hiddenCallback }; 1971 var flashparams = { allowScriptAccess: "always", menu: "false" }; 1972 window.swfobject.embedSWF( 1973 options.fonts.swfPath, 1974 id, 1975 "1", 1976 "1", 1977 "9.0.0", 1978 false, 1979 flashvars, 1980 flashparams, 1981 {} 1982 ); 1983 }; 1984 var getWebglCanvas = function() { 1985 var canvas = document.createElement("canvas"); 1986 var gl = null; 1987 try { 1988 gl = 1989 canvas.getContext("webgl") || canvas.getContext("experimental-webgl"); 1990 } catch (e) { 1991 /* squelch */ 1992 } 1993 if (!gl) { 1994 gl = null; 1995 } 1996 return gl; 1997 }; 1998 1999 var components = [ 2000 { key: "userAgent", getData: UserAgent }, 2001 { key: "language", getData: languageKey }, 2002 { key: "colorDepth", getData: colorDepthKey }, 2003 { key: "deviceMemory", getData: deviceMemoryKey }, 2004 { key: "pixelRatio", getData: pixelRatioKey }, 2005 { key: "hardwareConcurrency", getData: hardwareConcurrencyKey }, 2006 { key: "screenResolution", getData: screenResolutionKey }, 2007 { key: "availableScreenResolution", getData: availableScreenResolutionKey }, 2008 { key: "timezoneOffset", getData: timezoneOffset }, 2009 { key: "timezone", getData: timezone }, 2010 { key: "sessionStorage", getData: sessionStorageKey }, 2011 { key: "localStorage", getData: localStorageKey }, 2012 { key: "indexedDb", getData: indexedDbKey }, 2013 { key: "addBehavior", getData: addBehaviorKey }, 2014 { key: "openDatabase", getData: openDatabaseKey }, 2015 { key: "cpuClass", getData: cpuClassKey }, 2016 { key: "platform", getData: platformKey }, 2017 { key: "doNotTrack", getData: doNotTrackKey }, 2018 { key: "plugins", getData: pluginsComponent }, 2019 //{ key: "canvas", getData: canvasKey }, 2020 //{ key: "webgl", getData: webglKey }, 2021 //{key: 'webglVendorAndRenderer', getData: webglVendorAndRendererKey}, 2022 { key: "hasLiedLanguages", getData: hasLiedLanguagesKey }, 2023 { key: "hasLiedResolution", getData: hasLiedResolutionKey }, 2024 { key: "hasLiedOs", getData: hasLiedOsKey }, 2025 { key: "hasLiedBrowser", getData: hasLiedBrowserKey }, 2026 { key: "touchSupport", getData: touchSupportKey }, 2027 { key: "fonts", getData: jsFontsKey, pauseBefore: true }, 2028 { key: "fontsFlash", getData: flashFontsKey, pauseBefore: true }, 2029 { key: "audio", getData: audioKey }, 2030 { key: "enumerateDevices", getData: enumerateDevicesKey } 2031 ]; 2032 2033 var Fingerprint2 = function(options) { 2034 throw new Error( 2035 "'new Fingerprint()' is deprecated, see https://github.com/Valve/fingerprintjs2#upgrade-guide-from-182-to-200" 2036 ); 2037 }; 2038 2039 Fingerprint2.get = function(options, callback) { 2040 if (!callback) { 2041 callback = options; 2042 options = {}; 2043 } else if (!options) { 2044 options = {}; 2045 } 2046 extendSoft(options, defaultOptions);
2047 options.components = options.extraComponents.concat(components); 2048 2049 var keys = { 2050 data: [], 2051 addPreprocessedComponent: function(key, value) { 2052 if (typeof options.preprocessor === "function") { 2053 value = options.preprocessor(key, value); 2054 } 2055 keys.data.push({ key: key, value: value }); 2056 } 2057 }; 2058 2059 var i = -1; 2060 var chainComponents = function(alreadyWaited) { 2061 i += 1; 2062 if (i >= options.components.length) { 2063 // on finish 2064 callback(keys.data); 2065 return; 2066 } 2067 var component = options.components[i]; 2068 2069 if (options.excludes[component.key]) { 2070 chainComponents(false); // skip 2071 return; 2072 } 2073 2074 if (!alreadyWaited && component.pauseBefore) { 2075 i -= 1; 2076 setTimeout(function() { 2077 chainComponents(true); 2078 }, 1); 2079 return; 2080 } 2081 2082 try { 2083 component.getData(function(value) { 2084 keys.addPreprocessedComponent(component.key, value); 2085 chainComponents(false); 2086 }, options); 2087 } catch (error) { 2088 // main body error 2089 keys.addPreprocessedComponent(component.key, String(error)); 2090 chainComponents(false); 2091 } 2092 }; 2093 2094 chainComponents(false); 2095 }; 2096 2097 Fingerprint2.getPromise = function(options) { 2098 return new Promise(function(resolve, reject) { 2099 Fingerprint2.get(options, resolve); 2100 }); 2101 }; 2102 2103 Fingerprint2.getV18 = function(options, callback) { 2104 if (callback == null) { 2105 callback = options; 2106 options = {}; 2107 } 2108 return Fingerprint2.get(options, function(components) { 2109 var newComponents = []; 2110 for (var i = 0; i < components.length; i++) { 2111 var component = components[i]; 2112 if (component.value === (options.NOT_AVAILABLE || "not available")) { 2113 newComponents.push({ key: component.key, value: "unknown" }); 2114 } else if (component.key === "plugins") { 2115 newComponents.push({ 2116 key: "plugins", 2117 value: map(component.value, function(p) { 2118 var mimeTypes = map(p[2], function(mt) { 2119 if (mt.join) { 2120 return mt.join("~"); 2121 } 2122 return mt; 2123 }).join(","); 2124 return [p[0], p[1], mimeTypes].join("::"); 2125 }) 2126 }); 2127 } else if (["canvas", "webgl"].indexOf(component.key) !== -1) { 2128 newComponents.push({ 2129 key: component.key, 2130 value: component.value.join("~") 2131 }); 2132 } else if ( 2133 [ 2134 "sessionStorage", 2135 "localStorage", 2136 "indexedDb", 2137 "addBehavior", 2138 "openDatabase" 2139 ].indexOf(component.key) !== -1 2140 ) { 2141 if (component.value) { 2142 newComponents.push({ key: component.key, value: 1 }); 2143 } else { 2144 // skip 2145 continue; 2146 } 2147 } else { 2148 if (component.value) { 2149 newComponents.push( 2150 component.value.join 2151 ? { key: component.key, value: component.value.join(";") } 2152 : component 2153 ); 2154 } else { 2155 newComponents.push({ key: component.key, value: component.value }); 2156 } 2157 } 2158 } 2159 var murmur = x64hash128( 2160 map(newComponents, function(component) { 2161 return component.value; 2162 }).join("~~~"), 2163 31 2164 ); 2165 callback(murmur, newComponents); 2166 }); 2167 }; 2168 2169 Fingerprint2.x64hash128 = x64hash128; 2170 Fingerprint2.VERSION = "2.0.0"; 2171 return Fingerprint2; 2172});
Line numbers count LF bytes from the start of the resource, as the search results do. Vendor segments are library code the classifier recognised; they are stored but not indexed. Bytes are shown as Latin1 characters, one per byte.