1/* 2* Fingerprintjs2 2.0.6 - 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 (typeof window !== 'undefined' && typeof define === 'function' && define.amd) { define(definition) } else if (typeof module !== 'undefined' && module.exports) { module.exports = definition() } else if (context.exports) { context.exports = definition() } else { context[name] = definition() } 22})('Fingerprint2', this, function () { 23 'use strict'
vendor: 8,046 bytes, lines 23-257
23 24 25 /// MurmurHash3 related functions 26 27 // 28 // Given two 64bit ints (as an array of two 32bit ints) returns the two 29 // added together as a 64bit int (as an array of two 32bit ints). 30 // 31 var x64Add = function (m, n) { 32 m = [m[0] >>> 16, m[0] & 0xffff, m[1] >>> 16, m[1] & 0xffff] 33 n = [n[0] >>> 16, n[0] & 0xffff, n[1] >>> 16, n[1] & 0xffff] 34 var o = [0, 0, 0, 0] 35 o[3] += m[3] + n[3] 36 o[2] += o[3] >>> 16 37 o[3] &= 0xffff 38 o[2] += m[2] + n[2] 39 o[1] += o[2] >>> 16 40 o[2] &= 0xffff 41 o[1] += m[1] + n[1] 42 o[0] += o[1] >>> 16 43 o[1] &= 0xffff 44 o[0] += m[0] + n[0] 45 o[0] &= 0xffff 46 return [(o[0] << 16) | o[1], (o[2] << 16) | o[3]] 47 } 48 49 // 50 // Given two 64bit ints (as an array of two 32bit ints) returns the two 51 // multiplied together as a 64bit int (as an array of two 32bit ints). 52 // 53 var x64Multiply = function (m, n) { 54 m = [m[0] >>> 16, m[0] & 0xffff, m[1] >>> 16, m[1] & 0xffff] 55 n = [n[0] >>> 16, n[0] & 0xffff, n[1] >>> 16, n[1] & 0xffff] 56 var o = [0, 0, 0, 0] 57 o[3] += m[3] * n[3] 58 o[2] += o[3] >>> 16 59 o[3] &= 0xffff 60 o[2] += m[2] * n[3] 61 o[1] += o[2] >>> 16 62 o[2] &= 0xffff 63 o[2] += m[3] * n[2] 64 o[1] += o[2] >>> 16 65 o[2] &= 0xffff 66 o[1] += m[1] * n[3] 67 o[0] += o[1] >>> 16 68 o[1] &= 0xffff 69 o[1] += m[2] * n[2] 70 o[0] += o[1] >>> 16 71 o[1] &= 0xffff 72 o[1] += m[3] * n[1] 73 o[0] += o[1] >>> 16 74 o[1] &= 0xffff 75 o[0] += (m[0] * n[3]) + (m[1] * n[2]) + (m[2] * n[1]) + (m[3] * n[0]) 76 o[0] &= 0xffff 77 return [(o[0] << 16) | o[1], (o[2] << 16) | o[3]] 78 } 79 // 80 // Given a 64bit int (as an array of two 32bit ints) and an int 81 // representing a number of bit positions, returns the 64bit int (as an 82 // array of two 32bit ints) rotated left by that number of positions. 83 // 84 var x64Rotl = function (m, n) { 85 n %= 64 86 if (n === 32) { 87 return [m[1], m[0]] 88 } else if (n < 32) { 89 return [(m[0] << n) | (m[1] >>> (32 - n)), (m[1] << n) | (m[0] >>> (32 - n))] 90 } else { 91 n -= 32 92 return [(m[1] << n) | (m[0] >>> (32 - n)), (m[0] << n) | (m[1] >>> (32 - n))] 93 } 94 } 95 // 96 // Given a 64bit int (as an array of two 32bit ints) and an int 97 // representing a number of bit positions, returns the 64bit int (as an 98 // array of two 32bit ints) shifted left by that number of positions. 99 // 100 var x64LeftShift = function (m, n) { 101 n %= 64 102 if (n === 0) { 103 return m 104 } else if (n < 32) { 105 return [(m[0] << n) | (m[1] >>> (32 - n)), m[1] << n] 106 } else { 107 return [m[1] << (n - 32), 0] 108 } 109 } 110 // 111 // Given two 64bit ints (as an array of two 32bit ints) returns the two 112 // xored together as a 64bit int (as an array of two 32bit ints). 113 // 114 var x64Xor = function (m, n) { 115 return [m[0] ^ n[0], m[1] ^ n[1]] 116 } 117 // 118 // Given a block, returns murmurHash3's final x64 mix of that block. 119 // (`[0, h[0] >>> 1]` is a 33 bit unsigned right shift. This is the 120 // only place where we need to right shift 64bit ints.) 121 // 122 var x64Fmix = function (h) { 123 h = x64Xor(h, [0, h[0] >>> 1]) 124 h = x64Multiply(h, [0xff51afd7, 0xed558ccd]) 125 h = x64Xor(h, [0, h[0] >>> 1]) 126 h = x64Multiply(h, [0xc4ceb9fe, 0x1a85ec53]) 127 h = x64Xor(h, [0, h[0] >>> 1]) 128 return h 129 } 130 131 // 132 // Given a string and an optional seed as an int, returns a 128 bit 133 // hash using the x64 flavor of MurmurHash3, as an unsigned hex. 134 // 135 var x64hash128 = function (key, seed) { 136 key = key || '' 137 seed = seed || 0 138 var remainder = key.length % 16 139 var bytes = key.length - remainder 140 var h1 = [0, seed] 141 var h2 = [0, seed] 142 var k1 = [0, 0] 143 var k2 = [0, 0] 144 var c1 = [0x87c37b91, 0x114253d5] 145 var c2 = [0x4cf5ad43, 0x2745937f] 146 for (var i = 0; i < bytes; i = i + 16) { 147 k1 = [((key.charCodeAt(i + 4) & 0xff)) | ((key.charCodeAt(i + 5) & 0xff) << 8) | ((key.charCodeAt(i + 6) & 0xff) << 16) | ((key.charCodeAt(i + 7) & 0xff) << 24), ((key.charCodeAt(i) & 0xff)) | ((key.charCodeAt(i + 1) & 0xff) << 8) | ((key.charCodeAt(i + 2) & 0xff) << 16) | ((key.charCodeAt(i + 3) & 0xff) << 24)] 148 k2 = [((key.charCodeAt(i + 12) & 0xff)) | ((key.charCodeAt(i + 13) & 0xff) << 8) | ((key.charCodeAt(i + 14) & 0xff) << 16) | ((key.charCodeAt(i + 15) & 0xff) << 24), ((key.charCodeAt(i + 8) & 0xff)) | ((key.charCodeAt(i + 9) & 0xff) << 8) | ((key.charCodeAt(i + 10) & 0xff) << 16) | ((key.charCodeAt(i + 11) & 0xff) << 24)] 149 k1 = x64Multiply(k1, c1) 150 k1 = x64Rotl(k1, 31) 151 k1 = x64Multiply(k1, c2) 152 h1 = x64Xor(h1, k1) 153 h1 = x64Rotl(h1, 27) 154 h1 = x64Add(h1, h2) 155 h1 = x64Add(x64Multiply(h1, [0, 5]), [0, 0x52dce729]) 156 k2 = x64Multiply(k2, c2) 157 k2 = x64Rotl(k2, 33) 158 k2 = x64Multiply(k2, c1) 159 h2 = x64Xor(h2, k2) 160 h2 = x64Rotl(h2, 31) 161 h2 = x64Add(h2, h1) 162 h2 = x64Add(x64Multiply(h2, [0, 5]), [0, 0x38495ab5]) 163 } 164 k1 = [0, 0] 165 k2 = [0, 0] 166 switch (remainder) { 167 case 15: 168 k2 = x64Xor(k2, x64LeftShift([0, key.charCodeAt(i + 14)], 48)) 169 // fallthrough 170 case 14: 171 k2 = x64Xor(k2, x64LeftShift([0, key.charCodeAt(i + 13)], 40)) 172 // fallthrough 173 case 13: 174 k2 = x64Xor(k2, x64LeftShift([0, key.charCodeAt(i + 12)], 32)) 175 // fallthrough 176 case 12: 177 k2 = x64Xor(k2, x64LeftShift([0, key.charCodeAt(i + 11)], 24)) 178 // fallthrough 179 case 11: 180 k2 = x64Xor(k2, x64LeftShift([0, key.charCodeAt(i + 10)], 16)) 181 // fallthrough 182 case 10: 183 k2 = x64Xor(k2, x64LeftShift([0, key.charCodeAt(i + 9)], 8)) 184 // fallthrough 185 case 9: 186 k2 = x64Xor(k2, [0, key.charCodeAt(i + 8)]) 187 k2 = x64Multiply(k2, c2) 188 k2 = x64Rotl(k2, 33) 189 k2 = x64Multiply(k2, c1) 190 h2 = x64Xor(h2, k2) 191 // fallthrough 192 case 8: 193 k1 = x64Xor(k1, x64LeftShift([0, key.charCodeAt(i + 7)], 56)) 194 // fallthrough 195 case 7: 196 k1 = x64Xor(k1, x64LeftShift([0, key.charCodeAt(i + 6)], 48)) 197 // fallthrough 198 case 6: 199 k1 = x64Xor(k1, x64LeftShift([0, key.charCodeAt(i + 5)], 40)) 200 // fallthrough 201 case 5: 202 k1 = x64Xor(k1, x64LeftShift([0, key.charCodeAt(i + 4)], 32)) 203 // fallthrough 204 case 4: 205 k1 = x64Xor(k1, x64LeftShift([0, key.charCodeAt(i + 3)], 24)) 206 // fallthrough 207 case 3: 208 k1 = x64Xor(k1, x64LeftShift([0, key.charCodeAt(i + 2)], 16)) 209 // fallthrough 210 case 2: 211 k1 = x64Xor(k1, x64LeftShift([0, key.charCodeAt(i + 1)], 8)) 212 // fallthrough 213 case 1: 214 k1 = x64Xor(k1, [0, key.charCodeAt(i)]) 215 k1 = x64Multiply(k1, c1) 216 k1 = x64Rotl(k1, 31) 217 k1 = x64Multiply(k1, c2) 218 h1 = x64Xor(h1, k1) 219 // fallthrough 220 } 221 h1 = x64Xor(h1, [0, key.length]) 222 h2 = x64Xor(h2, [0, key.length]) 223 h1 = x64Add(h1, h2) 224 h2 = x64Add(h2, h1) 225 h1 = x64Fmix(h1) 226 h2 = x64Fmix(h2) 227 h1 = x64Add(h1, h2) 228 h2 = x64Add(h2, h1) 229 return ('00000000' + (h1[0] >>> 0).toString(16)).slice(-8) + ('00000000' + (h1[1] >>> 0).toString(16)).slice(-8) + ('00000000' + (h2[0] >>> 0).toString(16)).slice(-8) + ('00000000' + (h2[1] >>> 0).toString(16)).slice(-8) 230 } 231 232 var defaultOptions = { 233 preprocessor: null, 234 audio: { 235 timeout: 1000, 236 // On iOS 11, audio context can only be used in response to user interaction. 237 // We require users to explicitly enable audio fingerprinting on iOS 11. 238 // See https://stackoverflow.com/questions/46363048/onaudioprocess-not-called-on-ios11#46534088 239 excludeIOS11: true 240 }, 241 fonts: { 242 swfContainerId: 'fingerprintjs2', 243 swfPath: 'flash/compiled/FontList.swf', 244 userDefinedFonts: [], 245 extendedJsFonts: false 246 }, 247 screen: { 248 // To ensure consistent fingerprints when users rotate their mobile devices 249 detectScreenOrientation: true 250 }, 251 plugins: { 252 sortPluginsFor: [/palemoon/i], 253 excludeIE: false 254 }, 255 extraComponents: [], 256 excludes: { 257 // Unreliable on Windows, see https://github.com/
vendor: 6,800 bytes, lines 257-458
257Valve/fingerprintjs2/issues/375 258 'enumerateDevices': true, 259 // devicePixelRatio depends on browser zoom, and it's impossible to detect browser zoom 260 'pixelRatio': true, 261 // DNT depends on incognito mode for some browsers (Chrome) and it's impossible to detect incognito mode 262 'doNotTrack': true, 263 // uses js fonts already 264 'fontsFlash': true 265 }, 266 NOT_AVAILABLE: 'not available', 267 ERROR: 'error', 268 EXCLUDED: 'excluded' 269 } 270 271 var each = function (obj, iterator) { 272 if (Array.prototype.forEach && obj.forEach === Array.prototype.forEach) { 273 obj.forEach(iterator) 274 } else if (obj.length === +obj.length) { 275 for (var i = 0, l = obj.length; i < l; i++) { 276 iterator(obj[i], i, obj) 277 } 278 } else { 279 for (var key in obj) { 280 if (obj.hasOwnProperty(key)) { 281 iterator(obj[key], key, obj) 282 } 283 } 284 } 285 } 286 287 var map = function (obj, iterator) { 288 var results = [] 289 // Not using strict equality so that this acts as a 290 // shortcut to checking for `null` and `undefined`. 291 if (obj == null) { 292 return results 293 } 294 if (Array.prototype.map && obj.map === Array.prototype.map) { return obj.map(iterator) } 295 each(obj, function (value, index, list) { 296 results.push(iterator(value, index, list)) 297 }) 298 return results 299 } 300 301 var extendSoft = function (target, source) { 302 if (source == null) { return target } 303 var value 304 var key 305 for (key in source) { 306 value = source[key] 307 if (value != null && !(Object.prototype.hasOwnProperty.call(target, key))) { 308 target[key] = value 309 } 310 } 311 return target 312 } 313 314 // https://developer.mozilla.org/en-US/docs/Web/API/MediaDevices/enumerateDevices 315 var enumerateDevicesKey = function (done, options) { 316 if (!isEnumerateDevicesSupported()) { 317 return done(options.NOT_AVAILABLE) 318 } 319 navigator.mediaDevices.enumerateDevices().then(function (devices) { 320 done(devices.map(function (device) { 321 return 'id=' + device.deviceId + ';gid=' + device.groupId + ';' + device.kind + ';' + device.label 322 })) 323 }) 324 .catch(function (error) { 325 done(error) 326 }) 327 } 328 329 var isEnumerateDevicesSupported = function () { 330 return (navigator.mediaDevices && navigator.mediaDevices.enumerateDevices) 331 } 332 // Inspired by and based on https://github.com/cozylife/audio-fingerprint 333 var audioKey = function (done, options) { 334 var audioOptions = options.audio 335 if (audioOptions.excludeIOS11 && navigator.userAgent.match(/OS 11.+Version\/11.+Safari/)) { 336 // See comment for excludeUserAgent and https://stackoverflow.com/questions/46363048/onaudioprocess-not-called-on-ios11#46534088 337 return done(options.EXCLUDED) 338 } 339 340 var AudioContext = window.OfflineAudioContext || window.webkitOfflineAudioContext 341 342 if (AudioContext == null) { 343 return done(options.NOT_AVAILABLE) 344 } 345 346 var context = new AudioContext(1, 44100, 44100) 347 348 var oscillator = context.createOscillator() 349 oscillator.type = 'triangle' 350 oscillator.frequency.setValueAtTime(10000, context.currentTime) 351 352 var compressor = context.createDynamicsCompressor() 353 each([ 354 ['threshold', -50], 355 ['knee', 40], 356 ['ratio', 12], 357 ['reduction', -20], 358 ['attack', 0], 359 ['release', 0.25] 360 ], function (item) { 361 if (compressor[item[0]] !== undefined && typeof compressor[item[0]].setValueAtTime === 'function') { 362 compressor[item[0]].setValueAtTime(item[1], context.currentTime) 363 } 364 }) 365 366 oscillator.connect(compressor) 367 compressor.connect(context.destination) 368 oscillator.start(0) 369 context.startRendering() 370 371 var audioTimeoutId = setTimeout(function () { 372 console.warn('Audio fingerprint timed out. Please report bug at https://github.com/Valve/fingerprintjs2 with your user agent: "' + navigator.userAgent + '".') 373 context.oncomplete = function () { } 374 context = null 375 return done('audioTimeout') 376 }, audioOptions.timeout) 377 378 context.oncomplete = function (event) { 379 var fingerprint 380 try { 381 clearTimeout(audioTimeoutId) 382 fingerprint = event.renderedBuffer.getChannelData(0) 383 .slice(4500, 5000) 384 .reduce(function (acc, val) { return acc + Math.abs(val) }, 0) 385 .toString() 386 oscillator.disconnect() 387 compressor.disconnect() 388 } catch (error) { 389 done(error) 390 return 391 } 392 done(fingerprint) 393 } 394 } 395 var UserAgent = function (done) { 396 done(navigator.userAgent) 397 } 398 var webdriver = function (done, options) { 399 done(navigator.webdriver == null ? options.NOT_AVAILABLE : navigator.webdriver) 400 } 401 var languageKey = function (done, options) { 402 done(navigator.language || navigator.userLanguage || navigator.browserLanguage || navigator.systemLanguage || options.NOT_AVAILABLE) 403 } 404 var colorDepthKey = function (done, options) { 405 done(window.screen.colorDepth || options.NOT_AVAILABLE) 406 } 407 var deviceMemoryKey = function (done, options) { 408 done(navigator.deviceMemory || options.NOT_AVAILABLE) 409 } 410 var pixelRatioKey = function (done, options) { 411 done(window.devicePixelRatio || options.NOT_AVAILABLE) 412 } 413 var screenResolutionKey = function (done, options) { 414 done(getScreenResolution(options)) 415 } 416 var getScreenResolution = function (options) { 417 var resolution = [window.screen.width, window.screen.height] 418 if (options.screen.detectScreenOrientation) { 419 resolution.sort().reverse() 420 } 421 return resolution 422 } 423 var availableScreenResolutionKey = function (done, options) { 424 done(getAvailableScreenResolution(options)) 425 } 426 var getAvailableScreenResolution = function (options) { 427 if (window.screen.availWidth && window.screen.availHeight) { 428 var available = [window.screen.availHeight, window.screen.availWidth] 429 if (options.screen.detectScreenOrientation) { 430 available.sort().reverse() 431 } 432 return available 433 } 434 // headless browsers 435 return options.NOT_AVAILABLE 436 } 437 var timezoneOffset = function (done) { 438 done(new Date().getTimezoneOffset()) 439 } 440 var timezone = function (done, options) { 441 if (window.Intl && window.Intl.DateTimeFormat) { 442 done(new window.Intl.DateTimeFormat().resolvedOptions().timeZone) 443 return 444 } 445 done(options.NOT_AVAILABLE) 446 } 447 var sessionStorageKey = function (done, options) { 448 done(hasSessionStorage(options)) 449 } 450 var localStorageKey = function (done, options) { 451 done(hasLocalStorage(options)) 452 } 453 var indexedDbKey = function (done, options) { 454 done(hasIndexedDB(options)) 455 } 456 var addBehaviorKey = function (done) { 457 // body might not be defined at this point or removed programmatically 458 done(!!(document.body && document.body.addBehavior)
vendor: 18,093 bytes, lines 458-830
458) 459 } 460 var openDatabaseKey = function (done) { 461 done(!!window.openDatabase) 462 } 463 var cpuClassKey = function (done, options) { 464 done(getNavigatorCpuClass(options)) 465 } 466 var platformKey = function (done, options) { 467 done(getNavigatorPlatform(options)) 468 } 469 var doNotTrackKey = function (done, options) { 470 done(getDoNotTrack(options)) 471 } 472 var canvasKey = function (done, options) { 473 if (isCanvasSupported()) { 474 done(getCanvasFp(options)) 475 return 476 } 477 done(options.NOT_AVAILABLE) 478 } 479 var webglKey = function (done, options) { 480 if (isWebGlSupported()) { 481 done(getWebglFp()) 482 return 483 } 484 done(options.NOT_AVAILABLE) 485 } 486 var webglVendorAndRendererKey = function (done) { 487 if (isWebGlSupported()) { 488 done(getWebglVendorAndRenderer()) 489 return 490 } 491 done() 492 } 493 var adBlockKey = function (done) { 494 done(getAdBlock()) 495 } 496 var hasLiedLanguagesKey = function (done) { 497 done(getHasLiedLanguages()) 498 } 499 var hasLiedResolutionKey = function (done) { 500 done(getHasLiedResolution()) 501 } 502 var hasLiedOsKey = function (done) { 503 done(getHasLiedOs()) 504 } 505 var hasLiedBrowserKey = function (done) { 506 done(getHasLiedBrowser()) 507 } 508 // flash fonts (will increase fingerprinting time 20X to ~ 130-150ms) 509 var flashFontsKey = function (done, options) { 510 // we do flash if swfobject is loaded 511 if (!hasSwfObjectLoaded()) { 512 return done('swf object not loaded') 513 } 514 if (!hasMinFlashInstalled()) { 515 return done('flash not installed') 516 } 517 if (!options.fonts.swfPath) { 518 return done('missing options.fonts.swfPath') 519 } 520 loadSwfAndDetectFonts(function (fonts) { 521 done(fonts) 522 }, options) 523 } 524 // kudos to http://www.lalit.org/lab/javascript-css-font-detect/ 525 var jsFontsKey = function (done, options) { 526 // a font will be compared against all the three default fonts. 527 // and if it doesn't match all 3 then that font is not available. 528 var baseFonts = ['monospace', 'sans-serif', 'serif'] 529 530 var fontList = [ 531 'Andale Mono', 'Arial', 'Arial Black', 'Arial Hebrew', 'Arial MT', 'Arial Narrow', 'Arial Rounded MT Bold', 'Arial Unicode MS', 532 'Bitstream Vera Sans Mono', 'Book Antiqua', 'Bookman Old Style', 533 'Calibri', 'Cambria', 'Cambria Math', 'Century', 'Century Gothic', 'Century Schoolbook', 'Comic Sans', 'Comic Sans MS', 'Consolas', 'Courier', 'Courier New', 534 'Geneva', 'Georgia', 535 'Helvetica', 'Helvetica Neue', 536 'Impact', 537 'Lucida Bright', 'Lucida Calligraphy', 'Lucida Console', 'Lucida Fax', 'LUCIDA GRANDE', 'Lucida Handwriting', 'Lucida Sans', 'Lucida Sans Typewriter', 'Lucida Sans Unicode', 538 'Microsoft Sans Serif', 'Monaco', 'Monotype Corsiva', 'MS Gothic', 'MS Outlook', 'MS PGothic', 'MS Reference Sans Serif', 'MS Sans Serif', 'MS Serif', 'MYRIAD', 'MYRIAD PRO', 539 'Palatino', 'Palatino Linotype', 540 'Segoe Print', 'Segoe Script', 'Segoe UI', 'Segoe UI Light', 'Segoe UI Semibold', 'Segoe UI Symbol', 541 'Tahoma', 'Times', 'Times New Roman', 'Times New Roman PS', 'Trebuchet MS', 542 'Verdana', 'Wingdings', 'Wingdings 2', 'Wingdings 3' 543 ] 544 545 if (options.fonts.extendedJsFonts) { 546 var extendedFontList = [ 547 'Abadi MT Condensed Light', 'Academy Engraved LET', 'ADOBE CASLON PRO', 'Adobe Garamond', 'ADOBE GARAMOND PRO', 'Agency FB', 'Aharoni', 'Albertus Extra Bold', 'Albertus Medium', 'Algerian', 'Amazone BT', 'American Typewriter', 548 'American Typewriter Condensed', 'AmerType Md BT', 'Andalus', 'Angsana New', 'AngsanaUPC', 'Antique Olive', 'Aparajita', 'Apple Chancery', 'Apple Color Emoji', 'Apple SD Gothic Neo', 'Arabic Typesetting', 'ARCHER', 549 'ARNO PRO', 'Arrus BT', 'Aurora Cn BT', 'AvantGarde Bk BT', 'AvantGarde Md BT', 'AVENIR', 'Ayuthaya', 'Bandy', 'Bangla Sangam MN', 'Bank Gothic', 'BankGothic Md BT', 'Baskerville', 550 'Baskerville Old Face', 'Batang', 'BatangChe', 'Bauer Bodoni', 'Bauhaus 93', 'Bazooka', 'Bell MT', 'Bembo', 'Benguiat Bk BT', 'Berlin Sans FB', 'Berlin Sans FB Demi', 'Bernard MT Condensed', 'BernhardFashion BT', 'BernhardMod BT', 'Big Caslon', 'BinnerD', 551 'Blackadder ITC', 'BlairMdITC TT', 'Bodoni 72', 'Bodoni 72 Oldstyle', 'Bodoni 72 Smallcaps', 'Bodoni MT', 'Bodoni MT Black', 'Bodoni MT Condensed', 'Bodoni MT Poster Compressed', 552 'Bookshelf Symbol 7', 'Boulder', 'Bradley Hand', 'Bradley Hand ITC', 'Bremen Bd BT', 'Britannic Bold', 'Broadway', 'Browallia New', 'BrowalliaUPC', 'Brush Script MT', 'Californian FB', 'Calisto MT', 'Calligrapher', 'Candara', 553 'CaslonOpnface BT', 'Castellar', 'Centaur', 'Cezanne', 'CG Omega', 'CG Times', 'Chalkboard', 'Chalkboard SE', 'Chalkduster', 'Charlesworth', 'Charter Bd BT', 'Charter BT', 'Chaucer', 554 'ChelthmITC Bk BT', 'Chiller', 'Clarendon', 'Clarendon Condensed', 'CloisterBlack BT', 'Cochin', 'Colonna MT', 'Constantia', 'Cooper Black', 'Copperplate', 'Copperplate Gothic', 'Copperplate Gothic Bold', 555 'Copperplate Gothic Light', 'CopperplGoth Bd BT', 'Corbel', 'Cordia New', 'CordiaUPC', 'Cornerstone', 'Coronet', 'Cuckoo', 'Curlz MT', 'DaunPenh', 'Dauphin', 'David', 'DB LCD Temp', 'DELICIOUS', 'Denmark', 556 'DFKai-SB', 'Didot', 'DilleniaUPC', 'DIN', 'DokChampa', 'Dotum', 'DotumChe', 'Ebrima', 'Edwardian Script ITC', 'Elephant', 'English 111 Vivace BT', 'Engravers MT', 'EngraversGothic BT', 'Eras Bold ITC', 'Eras Demi ITC', 'Eras Light ITC', 'Eras Medium ITC', 557 'EucrosiaUPC', 'Euphemia', 'Euphemia UCAS', 'EUROSTILE', 'Exotc350 Bd BT', 'FangSong', 'Felix Titling', 'Fixedsys', 'FONTIN', 'Footlight MT Light', 'Forte', 558 'FrankRuehl', 'Fransiscan', 'Freefrm721 Blk BT', 'FreesiaUPC', 'Freestyle Script', 'French Script MT', 'FrnkGothITC Bk BT', 'Fruitger', 'FRUTIGER', 559 'Futura', 'Futura Bk BT', 'Futura Lt BT', 'Futura Md BT', 'Futura ZBlk BT', 'FuturaBlack BT', 'Gabriola', 'Galliard BT', 'Gautami', 'Geeza Pro', 'Geometr231 BT', 'Geometr231 Hv BT', 'Geometr231 Lt BT', 'GeoSlab 703 Lt BT', 560 'GeoSlab 703 XBd BT', 'Gigi', 'Gill Sans', 'Gill Sans MT', 'Gill Sans MT Condensed', 'Gill Sans MT Ext Condensed Bold', 'Gill Sans Ultra Bold', 'Gill Sans Ultra Bold Condensed', 'Gisha', 'Gloucester MT Extra Condensed', 'GOTHAM', 'GOTHAM BOLD', 561 'Goudy Old Style', 'Goudy Stout', 'GoudyHandtooled BT', 'GoudyOLSt BT', 'Gujarati Sangam MN', 'Gulim', 'GulimChe', 'Gungsuh', 'GungsuhChe', 'Gurmukhi MN', 'Haettenschweiler', 'Harlow Solid Italic', 'Harrington', 'Heather', 'Heiti SC', 'Heiti TC', 'HELV', 562 'Herald', 'High Tower Text', 'Hiragino Kaku Gothic ProN', 'Hiragino Mincho ProN', 'Hoefler Text', 'Humanst 521 Cn BT', 'Humanst521 BT', 'Humanst521 Lt BT', 'Imprint MT Shadow', 'Incised901 Bd BT', 'Incised901 BT', 563 'Incised901 Lt BT', 'INCONSOLATA', 'Informal Roman', 'Informal011 BT', 'INTERSTATE', 'IrisUPC', 'Iskoola Pota', 'JasmineUPC', 'Jazz LET', 'Jenson', 'Jester', 'Jokerman', 'Juice ITC', 'Kabel Bk BT', 'Kabel Ult BT', 'Kailasa', 'KaiTi', 'Kalinga', 'Kannada Sangam MN', 564 'Kartika', 'Kaufmann Bd BT', 'Kaufmann BT', 'Khmer UI', 'KodchiangUPC', 'Kokila', 'Korinna BT', 'Kristen ITC', 'Krungthep', 'Kunstler Script', 'Lao UI', 'Latha', 'Leelawadee', 'Letter Gothic', 'Levenim MT', 'LilyUPC', 'Lithograph', 'Lithograph Light', 'Long Island', 565 'Lydian BT', 'Magneto', 'Maiandra GD', 'Malayalam Sangam MN', 'Malgun Gothic', 566 'Mangal', 'Marigold', 'Marion', 'Marker Felt', 'Market', 'Marlett', 'Matisse ITC', 'Matura MT Script Capitals', 'Meiryo', 'Meiryo UI', 'Microsoft Himalaya', 'Microsoft JhengHei', 'Microsoft New Tai Lue', 'Microsoft PhagsPa', 'Microsoft Tai Le', 567 'Microsoft Uighur', 'Microsoft YaHei', 'Microsoft Yi Baiti', 'MingLiU', 'MingLiU_HKSCS', 'MingLiU_HKSCS-ExtB', 'MingLiU-ExtB', 'Minion', 'Minion Pro', 'Miriam', 'Miriam Fixed', 'Mistral', 'Modern', 'Modern No. 20', 'Mona Lisa Solid ITC TT', 'Mongolian Baiti', 568 'MONO', 'MoolBoran', 'Mrs Eaves', 'MS LineDraw', 'MS Mincho', 'MS PMincho', 'MS Reference Specialty', 'MS UI Gothic', 'MT Extra', 'MUSEO', 'MV Boli', 569 'Nadeem', 'Narkisim', 'NEVIS', 'News Gothic', 'News GothicMT', 'NewsGoth BT', 'Niagara Engraved', 'Niagara Solid', 'Noteworthy', 'NSimSun', 'Nyala', 'OCR A Extended', 'Old Century', 'Old English Text MT', 'Onyx', 'Onyx BT', 'OPTIMA', 'Oriya Sangam MN', 570 'OSAKA', 'OzHandicraft BT', 'Palace Script MT', 'Papyrus', 'Parchment', 'Party LET', 'Pegasus', 'Perpetua', 'Perpetua Titling MT', 'PetitaBold', 'Pickwick', 'Plantagenet Cherokee', 'Playbill', 'PMingLiU', 'PMingLiU-ExtB', 571 'Poor Richard', 'Poster', 'PosterBodoni BT', 'PRINCETOWN LET', 'Pristina', 'PTBarnum BT', 'Pythagoras', 'Raavi', 'Rage Italic', 'Ravie', 'Ribbon131 Bd BT', 'Rockwell', 'Rockwell Condensed', 'Rockwell Extra Bold', 'Rod', 'Roman', 'Sakkal Majalla', 572 'Santa Fe LET', 'Savoye LET', 'Sceptre', 'Script', 'Script MT Bold', 'SCRIPTINA', 'Serifa', 'Serifa BT', 'Serifa Th BT', 'ShelleyVolante BT', 'Sherwood', 573 'Shonar Bangla', 'Showcard Gothic', 'Shruti', 'Signboard', 'SILKSCREEN', 'SimHei', 'Simplified Arabic', 'Simplified Arabic Fixed', 'SimSun', 'SimSun-ExtB', 'Sinhala Sangam MN', 'Sketch Rockwell', 'Skia', 'Small Fonts', 'Snap ITC', 'Snell Roundhand', 'Socket', 574 'Souvenir Lt BT', 'Staccato222 BT', 'Steamer', 'Stencil', 'Storybook', 'Styllo', 'Subway', 'Swis721 BlkEx BT', 'Swiss911 XCm BT', 'Sylfaen', 'Synchro LET', 'System', 'Tamil Sangam MN', 'Technical', 'Teletype', 'Telugu Sangam MN', 'Tempus Sans ITC', 575 'Terminal', 'Thonburi', 'Traditional Arabic', 'Trajan', 'TRAJAN PRO', 'Tristan', 'Tubular', 'Tunga', 'Tw Cen MT', 'Tw Cen MT Condensed', 'Tw Cen MT Condensed Extra Bold', 576 'TypoUpright BT', 'Unicorn', 'Univers', 'Univers CE 55 Medium', 'Univers Condensed', 'Utsaah', 'Vagabond', 'Vani', 'Vijaya', 'Viner Hand ITC', 'VisualUI', 'Vivaldi', 'Vladimir Script', 'Vrinda', 'Westminster', 'WHITNEY', 'Wide Latin', 577 'ZapfEllipt BT', 'ZapfHumnst BT', 'ZapfHumnst Dm BT', 'Zapfino', 'Zurich BlkEx BT', 'Zurich Ex BT', 'ZWAdobeF'] 578 fontList = fontList.concat(extendedFontList) 579 } 580 581 fontList = fontList.concat(options.fonts.userDefinedFonts) 582 583 // remove duplicate fonts 584 fontList = fontList.filter(function (font, position) { 585 return fontList.indexOf(font) === position 586 }) 587 588 // we use m or w because these two characters take up the maximum width. 589 // And we use a LLi so that the same matching fonts can get separated 590 var testString = 'mmmmmmmmmmlli' 591 592 // we test using 72px font size, we may use any size. I guess larger the better. 593 var testSize = '72px' 594 595 var h = document.getElementsByTagName('body')[0] 596 597 // div to load spans for the base fonts 598 var baseFontsDiv = document.createElement('div') 599 600 // div to load spans for the fonts to detect 601 var fontsDiv = document.createElement('div') 602 603 var defaultWidth = {} 604 var defaultHeight = {} 605 606 // creates a span where the fonts will be loaded 607 var createSpan = function () { 608 var s = document.createElement('span') 609 /* 610 * We need this css as in some weird browser this 611 * span elements shows up for a microSec which creates a 612 * bad user experience 613 */ 614 s.style.position = 'absolute' 615 s.style.left = '-9999px' 616 s.style.fontSize = testSize 617 618 // css font reset to reset external styles 619 s.style.fontStyle = 'normal' 620 s.style.fontWeight = 'normal' 621 s.style.letterSpacing = 'normal' 622 s.style.lineBreak = 'auto' 623 s.style.lineHeight = 'normal' 624 s.style.textTransform = 'none' 625 s.style.textAlign = 'left' 626 s.style.textDecoration = 'none' 627 s.style.textShadow = 'none' 628 s.style.whiteSpace = 'normal' 629 s.style.wordBreak = 'normal' 630 s.style.wordSpacing = 'normal' 631 632 s.innerHTML = testString 633 return s 634 } 635 636 // creates a span and load the font to detect and a base font for fallback 637 var createSpanWithFonts = function (fontToDetect, baseFont) { 638 var s = createSpan() 639 s.style.fontFamily = "'" + fontToDetect + "'," + baseFont 640 return s 641 } 642 643 // creates spans for the base fonts and adds them to baseFontsDiv 644 var initializeBaseFontsSpans = function () { 645 var spans = [] 646 for (var index = 0, length = baseFonts.length; index < length; index++) { 647 var s = createSpan() 648 s.style.fontFamily = baseFonts[index] 649 baseFontsDiv.appendChild(s) 650 spans.push(s) 651 } 652 return spans 653 } 654 655 // creates spans for the fonts to detect and adds them to fontsDiv 656 var initializeFontsSpans = function () { 657 var spans = {} 658 for (var i = 0, l = fontList.length; i < l; i++) { 659 var fontSpans = [] 660 for (var j = 0, numDefaultFonts = baseFonts.length; j < numDefaultFonts; j++) { 661 var s = createSpanWithFonts(fontList[i], baseFonts[j]) 662 fontsDiv.appendChild(s) 663 fontSpans.push(s) 664 } 665 spans[fontList[i]] = fontSpans // Stores {fontName : [spans for that font]} 666 } 667 return spans 668 } 669 670 // checks if a font is available 671 var isFontAvailable = function (fontSpans) { 672 var detected = false 673 for (var i = 0; i < baseFonts.length; i++) { 674 detected = (fontSpans[i].offsetWidth !== defaultWidth[baseFonts[i]] || fontSpans[i].offsetHeight !== defaultHeight[baseFonts[i]]) 675 if (detected) { 676 return detected 677 } 678 } 679 return detected 680 } 681 682 // create spans for base fonts 683 var baseFontsSpans = initializeBaseFontsSpans() 684 685 // add the spans to the DOM 686 h.appendChild(baseFontsDiv) 687 688 // get the default width for the three base fonts 689 for (var index = 0, length = baseFonts.length; index < length; index++) { 690 defaultWidth[baseFonts[index]] = baseFontsSpans[index].offsetWidth // width for the default font 691 defaultHeight[baseFonts[index]] = baseFontsSpans[index].offsetHeight // height for the default font 692 } 693 694 // create spans for fonts to detect 695 var fontsSpans = initializeFontsSpans() 696 697 // add all the spans to the DOM 698 h.appendChild(fontsDiv) 699 700 // check available fonts 701 var available = [] 702 for (var i = 0, l = fontList.length; i < l; i++) { 703 if (isFontAvailable(fontsSpans[fontList[i]])) { 704 available.push(fontList[i]) 705 } 706 } 707 708 // remove spans from DOM 709 h.removeChild(fontsDiv) 710 h.removeChild(baseFontsDiv) 711 done(available) 712 } 713 var pluginsComponent = function (done, options) { 714 if (isIE()) { 715 if (!options.plugins.excludeIE) { 716 done(getIEPlugins(options)) 717 } else { 718 done(options.EXCLUDED) 719 } 720 } else { 721 done(getRegularPlugins(options)) 722 } 723 } 724 var getRegularPlugins = function (options) { 725 if (navigator.plugins == null) { 726 return options.NOT_AVAILABLE 727 } 728 729 var plugins = [] 730 // plugins isn't defined in Node envs. 731 for (var i = 0, l = navigator.plugins.length; i < l; i++) { 732 if (navigator.plugins[i]) { plugins.push(navigator.plugins[i]) } 733 } 734 735 // sorting plugins only for those user agents, that we know randomize the plugins 736 // every time we try to enumerate them 737 if (pluginsShouldBeSorted(options)) { 738 plugins = plugins.sort(function (a, b) { 739 if (a.name > b.name) { return 1 } 740 if (a.name < b.name) { return -1 } 741 return 0 742 }) 743 } 744 return map(plugins, function (p) { 745 var mimeTypes = map(p, function (mt) { 746 return [mt.type, mt.suffixes] 747 }) 748 return [p.name, p.description, mimeTypes] 749 }) 750 } 751 var getIEPlugins = function (options) { 752 var result = [] 753 if ((Object.getOwnPropertyDescriptor && Object.getOwnPropertyDescriptor(window, 'ActiveXObject')) || ('ActiveXObject' in window)) { 754 var names = [ 755 'AcroPDF.PDF', // Adobe PDF reader 7+ 756 'Adodb.Stream', 757 'AgControl.AgControl', // Silverlight 758 'DevalVRXCtrl.DevalVRXCtrl.1', 759 'MacromediaFlashPaper.MacromediaFlashPaper', 760 'Msxml2.DOMDocument', 761 'Msxml2.XMLHTTP', 762 'PDF.PdfCtrl', // Adobe PDF reader 6 and earlier, brrr 763 'QuickTime.QuickTime', // QuickTime 764 'QuickTimeCheckObject.QuickTimeCheck.1', 765 'RealPlayer', 766 'RealPlayer.RealPlayer(tm) ActiveX Control (32-bit)', 767 'RealVideo.RealVideo(tm) ActiveX Control (32-bit)', 768 'Scripting.Dictionary', 769 'SWCtl.SWCtl', // ShockWave player 770 'Shell.UIHelper', 771 'ShockwaveFlash.ShockwaveFlash', // flash plugin 772 'Skype.Detection', 773 'TDCCtl.TDCCtl', 774 'WMPlayer.OCX', // Windows media player 775 'rmocx.RealPlayer G2 Control', 776 'rmocx.RealPlayer G2 Control.1' 777 ] 778 // starting to detect plugins in IE 779 result = map(names, function (name) { 780 try { 781 // eslint-disable-next-line no-new 782 new window.ActiveXObject(name) 783 return name 784 } catch (e) { 785 return options.ERROR 786 } 787 }) 788 } else { 789 result.push(options.NOT_AVAILABLE) 790 } 791 if (navigator.plugins) { 792 result = result.concat(getRegularPlugins(options)) 793 } 794 return result 795 } 796 var pluginsShouldBeSorted = function (options) { 797 var should = false 798 for (var i = 0, l = options.plugins.sortPluginsFor.length; i < l; i++) { 799 var re = options.plugins.sortPluginsFor[i] 800 if (navigator.userAgent.match(re)) { 801 should = true 802 break 803 } 804 } 805 return should 806 } 807 var touchSupportKey = function (done) { 808 done(getTouchSupport()) 809 } 810 var hardwareConcurrencyKey = function (done, options) { 811 done(getHardwareConcurrency(options)) 812 } 813 var hasSessionStorage = function (options) { 814 try { 815 return !!window.sessionStorage 816 } catch (e) { 817 return options.ERROR // SecurityError when referencing it means it exists 818 } 819 } 820 821 // https://bugzilla.mozilla.org/show_bug.cgi?id=781447 822 var hasLocalStorage = function (options) { 823 try { 824 return !!window.localStorage 825 } catch (e) { 826 return options.ERROR // SecurityError when referencing it means it exists 827 } 828 } 829 var hasIndexedDB = function (options) { 830
vendor: 2,586 bytes, lines 830-903
830try { 831 return !!window.indexedDB 832 } catch (e) { 833 return options.ERROR // SecurityError when referencing it means it exists 834 } 835 } 836 var getHardwareConcurrency = function (options) { 837 if (navigator.hardwareConcurrency) { 838 return navigator.hardwareConcurrency 839 } 840 return options.NOT_AVAILABLE 841 } 842 var getNavigatorCpuClass = function (options) { 843 return navigator.cpuClass || options.NOT_AVAILABLE 844 } 845 var getNavigatorPlatform = function (options) { 846 if (navigator.platform) { 847 return navigator.platform 848 } else { 849 return options.NOT_AVAILABLE 850 } 851 } 852 var getDoNotTrack = function (options) { 853 if (navigator.doNotTrack) { 854 return navigator.doNotTrack 855 } else if (navigator.msDoNotTrack) { 856 return navigator.msDoNotTrack 857 } else if (window.doNotTrack) { 858 return window.doNotTrack 859 } else { 860 return options.NOT_AVAILABLE 861 } 862 } 863 // This is a crude and primitive touch screen detection. 864 // It's not possible to currently reliably detect the availability of a touch screen 865 // with a JS, without actually subscribing to a touch event. 866 // http://www.stucox.com/blog/you-cant-detect-a-touchscreen/ 867 // https://github.com/Modernizr/Modernizr/issues/548 868 // method returns an array of 3 values: 869 // maxTouchPoints, the success or failure of creating a TouchEvent, 870 // and the availability of the 'ontouchstart' property 871 872 var getTouchSupport = function () { 873 var maxTouchPoints = 0 874 var touchEvent 875 if (typeof navigator.maxTouchPoints !== 'undefined') { 876 maxTouchPoints = navigator.maxTouchPoints 877 } else if (typeof navigator.msMaxTouchPoints !== 'undefined') { 878 maxTouchPoints = navigator.msMaxTouchPoints 879 } 880 try { 881 document.createEvent('TouchEvent') 882 touchEvent = true 883 } catch (_) { 884 touchEvent = false 885 } 886 var touchStart = 'ontouchstart' in window 887 return [maxTouchPoints, touchEvent, touchStart] 888 } 889 // https://www.browserleaks.com/canvas#how-does-it-work 890 891 var getCanvasFp = function (options) { 892 var result = [] 893 // Very simple now, need to make it more complex (geo shapes etc) 894 var canvas = document.createElement('canvas') 895 canvas.width = 2000 896 canvas.height = 200 897 canvas.style.display = 'inline' 898 var ctx = canvas.getContext('2d') 899 // detect browser support of canvas winding 900 // http://blogs.adobe.com/webplatform/2013/01/30/winding-rules-in-canvas/ 901 // https://github.com/Modernizr/Modernizr/blob/master/feature-detects/canvas/winding.js 902 ctx.rect(0, 0, 10, 10) 903 ctx.rect(2, 2, 6, 6)
904 result.push('canvas winding:' + ((ctx.isPointInPath(5, 5, 'evenodd') === false) ? 'yes' : 'no')) 905 906 ctx.textBaseline = 'alphabetic' 907 ctx.fillStyle = '#f60' 908 ctx.fillRect(125, 1, 62, 20) 909 ctx.fillStyle = '#069' 910 // https://github.com/Valve/fingerprintjs2
vendor: 7,162 bytes, lines 910-1047
910/issues/66 911 if (options.dontUseFakeFontInCanvas) { 912 ctx.font = '11pt Arial' 913 } else { 914 ctx.font = '11pt no-real-font-123' 915 } 916 ctx.fillText('Cwm fjordbank glyphs vext quiz, \ud83d\ude03', 2, 15) 917 ctx.fillStyle = 'rgba(102, 204, 0, 0.2)' 918 ctx.font = '18pt Arial' 919 ctx.fillText('Cwm fjordbank glyphs vext quiz, \ud83d\ude03', 4, 45) 920 921 // canvas blending 922 // http://blogs.adobe.com/webplatform/2013/01/28/blending-features-in-canvas/ 923 // http://jsfiddle.net/NDYV8/16/ 924 ctx.globalCompositeOperation = 'multiply' 925 ctx.fillStyle = 'rgb(255,0,255)' 926 ctx.beginPath() 927 ctx.arc(50, 50, 50, 0, Math.PI * 2, true) 928 ctx.closePath() 929 ctx.fill() 930 ctx.fillStyle = 'rgb(0,255,255)' 931 ctx.beginPath() 932 ctx.arc(100, 50, 50, 0, Math.PI * 2, true) 933 ctx.closePath() 934 ctx.fill() 935 ctx.fillStyle = 'rgb(255,255,0)' 936 ctx.beginPath() 937 ctx.arc(75, 100, 50, 0, Math.PI * 2, true) 938 ctx.closePath() 939 ctx.fill() 940 ctx.fillStyle = 'rgb(255,0,255)' 941 // canvas winding 942 // http://blogs.adobe.com/webplatform/2013/01/30/winding-rules-in-canvas/ 943 // http://jsfiddle.net/NDYV8/19/ 944 ctx.arc(75, 75, 75, 0, Math.PI * 2, true) 945 ctx.arc(75, 75, 25, 0, Math.PI * 2, true) 946 ctx.fill('evenodd') 947 948 if (canvas.toDataURL) { result.push('canvas fp:' + canvas.toDataURL()) } 949 return result 950 } 951 var getWebglFp = function () { 952 var gl 953 var fa2s = function (fa) { 954 gl.clearColor(0.0, 0.0, 0.0, 1.0) 955 gl.enable(gl.DEPTH_TEST) 956 gl.depthFunc(gl.LEQUAL) 957 gl.clear(gl.COLOR_BUFFER_BIT | gl.DEPTH_BUFFER_BIT) 958 return '[' + fa[0] + ', ' + fa[1] + ']' 959 } 960 var maxAnisotropy = function (gl) { 961 var ext = gl.getExtension('EXT_texture_filter_anisotropic') || gl.getExtension('WEBKIT_EXT_texture_filter_anisotropic') || gl.getExtension('MOZ_EXT_texture_filter_anisotropic') 962 if (ext) { 963 var anisotropy = gl.getParameter(ext.MAX_TEXTURE_MAX_ANISOTROPY_EXT) 964 if (anisotropy === 0) { 965 anisotropy = 2 966 } 967 return anisotropy 968 } else { 969 return null 970 } 971 } 972 973 gl = getWebglCanvas() 974 if (!gl) { return null } 975 // WebGL fingerprinting is a combination of techniques, found in MaxMind antifraud script & Augur fingerprinting. 976 // First it draws a gradient object with shaders and convers the image to the Base64 string. 977 // 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 978 // 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. 979 var result = [] 980 var vShaderTemplate = 'attribute vec2 attrVertex;varying vec2 varyinTexCoordinate;uniform vec2 uniformOffset;void main(){varyinTexCoordinate=attrVertex+uniformOffset;gl_Position=vec4(attrVertex,0,1);}' 981 var fShaderTemplate = 'precision mediump float;varying vec2 varyinTexCoordinate;void main() {gl_FragColor=vec4(varyinTexCoordinate,0,1);}' 982 var vertexPosBuffer = gl.createBuffer() 983 gl.bindBuffer(gl.ARRAY_BUFFER, vertexPosBuffer) 984 var vertices = new Float32Array([-0.2, -0.9, 0, 0.4, -0.26, 0, 0, 0.732134444, 0]) 985 gl.bufferData(gl.ARRAY_BUFFER, vertices, gl.STATIC_DRAW) 986 vertexPosBuffer.itemSize = 3 987 vertexPosBuffer.numItems = 3 988 var program = gl.createProgram() 989 var vshader = gl.createShader(gl.VERTEX_SHADER) 990 gl.shaderSource(vshader, vShaderTemplate) 991 gl.compileShader(vshader) 992 var fshader = gl.createShader(gl.FRAGMENT_SHADER) 993 gl.shaderSource(fshader, fShaderTemplate) 994 gl.compileShader(fshader) 995 gl.attachShader(program, vshader) 996 gl.attachShader(program, fshader) 997 gl.linkProgram(program) 998 gl.useProgram(program) 999 program.vertexPosAttrib = gl.getAttribLocation(program, 'attrVertex') 1000 program.offsetUniform = gl.getUniformLocation(program, 'uniformOffset') 1001 gl.enableVertexAttribArray(program.vertexPosArray) 1002 gl.vertexAttribPointer(program.vertexPosAttrib, vertexPosBuffer.itemSize, gl.FLOAT, !1, 0, 0) 1003 gl.uniform2f(program.offsetUniform, 1, 1) 1004 gl.drawArrays(gl.TRIANGLE_STRIP, 0, vertexPosBuffer.numItems) 1005 try { 1006 result.push(gl.canvas.toDataURL()) 1007 } catch (e) { 1008 /* .toDataURL may be absent or broken (blocked by extension) */ 1009 } 1010 result.push('extensions:' + (gl.getSupportedExtensions() || []).join(';')) 1011 result.push('webgl aliased line width range:' + fa2s(gl.getParameter(gl.ALIASED_LINE_WIDTH_RANGE))) 1012 result.push('webgl aliased point size range:' + fa2s(gl.getParameter(gl.ALIASED_POINT_SIZE_RANGE))) 1013 result.push('webgl alpha bits:' + gl.getParameter(gl.ALPHA_BITS)) 1014 result.push('webgl antialiasing:' + (gl.getContextAttributes().antialias ? 'yes' : 'no')) 1015 result.push('webgl blue bits:' + gl.getParameter(gl.BLUE_BITS)) 1016 result.push('webgl depth bits:' + gl.getParameter(gl.DEPTH_BITS)) 1017 result.push('webgl green bits:' + gl.getParameter(gl.GREEN_BITS)) 1018 result.push('webgl max anisotropy:' + maxAnisotropy(gl)) 1019 result.push('webgl max combined texture image units:' + gl.getParameter(gl.MAX_COMBINED_TEXTURE_IMAGE_UNITS)) 1020 result.push('webgl max cube map texture size:' + gl.getParameter(gl.MAX_CUBE_MAP_TEXTURE_SIZE)) 1021 result.push('webgl max fragment uniform vectors:' + gl.getParameter(gl.MAX_FRAGMENT_UNIFORM_VECTORS)) 1022 result.push('webgl max render buffer size:' + gl.getParameter(gl.MAX_RENDERBUFFER_SIZE)) 1023 result.push('webgl max texture image units:' + gl.getParameter(gl.MAX_TEXTURE_IMAGE_UNITS)) 1024 result.push('webgl max texture size:' + gl.getParameter(gl.MAX_TEXTURE_SIZE)) 1025 result.push('webgl max varying vectors:' + gl.getParameter(gl.MAX_VARYING_VECTORS)) 1026 result.push('webgl max vertex attribs:' + gl.getParameter(gl.MAX_VERTEX_ATTRIBS)) 1027 result.push('webgl max vertex texture image units:' + gl.getParameter(gl.MAX_VERTEX_TEXTURE_IMAGE_UNITS)) 1028 result.push('webgl max vertex uniform vectors:' + gl.getParameter(gl.MAX_VERTEX_UNIFORM_VECTORS)) 1029 result.push('webgl max viewport dims:' + fa2s(gl.getParameter(gl.MAX_VIEWPORT_DIMS))) 1030 result.push('webgl red bits:' + gl.getParameter(gl.RED_BITS)) 1031 result.push('webgl renderer:' + gl.getParameter(gl.RENDERER)) 1032 result.push('webgl shading language version:' + gl.getParameter(gl.SHADING_LANGUAGE_VERSION)) 1033 result.push('webgl stencil bits:' + gl.getParameter(gl.STENCIL_BITS)) 1034 result.push('webgl vendor:' + gl.getParameter(gl.VENDOR)) 1035 result.push('webgl version:' + gl.getParameter(gl.VERSION)) 1036 1037 try { 1038 // Add the unmasked vendor and unmasked renderer if the debug_renderer_info extension is available 1039 var extensionDebugRendererInfo = gl.getExtension('WEBGL_debug_renderer_info') 1040 if (extensionDebugRendererInfo) { 1041 result.push('webgl unmasked vendor:' + gl.getParameter(extensionDebugRendererInfo.UNMASKED_VENDOR_WEBGL)) 1042 result.push('webgl unmasked renderer:' + gl.getParameter(extensionDebugRendererInfo.UNMASKED_RENDERER_WEBGL)) 1043 } 1044 } catch (e) { /* squelch */ } 1045 1046 if (!gl.getShaderPrecisionFormat) { 1047
vendor: 13,926 bytes, lines 1047-1425
1047return result 1048 } 1049 1050 each(['FLOAT', 'INT'], function (numType) { 1051 each(['VERTEX', 'FRAGMENT'], function (shader) { 1052 each(['HIGH', 'MEDIUM', 'LOW'], function (numSize) { 1053 each(['precision', 'rangeMin', 'rangeMax'], function (key) { 1054 var format = gl.getShaderPrecisionFormat(gl[shader + '_SHADER'], gl[numSize + '_' + numType])[key] 1055 if (key !== 'precision') { 1056 key = 'precision ' + key 1057 } 1058 var line = ['webgl ', shader.toLowerCase(), ' shader ', numSize.toLowerCase(), ' ', numType.toLowerCase(), ' ', key, ':', format].join('') 1059 result.push(line) 1060 }) 1061 }) 1062 }) 1063 }) 1064 return result 1065 } 1066 var getWebglVendorAndRenderer = function () { 1067 /* This a subset of the WebGL fingerprint with a lot of entropy, while being reasonably browser-independent */ 1068 try { 1069 var glContext = getWebglCanvas() 1070 var extensionDebugRendererInfo = glContext.getExtension('WEBGL_debug_renderer_info') 1071 return glContext.getParameter(extensionDebugRendererInfo.UNMASKED_VENDOR_WEBGL) + '~' + glContext.getParameter(extensionDebugRendererInfo.UNMASKED_RENDERER_WEBGL) 1072 } catch (e) { 1073 return null 1074 } 1075 } 1076 var getAdBlock = function () { 1077 var ads = document.createElement('div') 1078 ads.innerHTML = ' ' 1079 ads.className = 'adsbox' 1080 var result = false 1081 try { 1082 // body may not exist, that's why we need try/catch 1083 document.body.appendChild(ads) 1084 result = document.getElementsByClassName('adsbox')[0].offsetHeight === 0 1085 document.body.removeChild(ads) 1086 } catch (e) { 1087 result = false 1088 } 1089 return result 1090 } 1091 var getHasLiedLanguages = function () { 1092 // We check if navigator.language is equal to the first language of navigator.languages 1093 // navigator.languages is undefined on IE11 (and potentially older IEs) 1094 if (typeof navigator.languages !== 'undefined') { 1095 try { 1096 var firstLanguages = navigator.languages[0].substr(0, 2) 1097 if (firstLanguages !== navigator.language.substr(0, 2)) { 1098 return true 1099 } 1100 } catch (err) { 1101 return true 1102 } 1103 } 1104 return false 1105 } 1106 var getHasLiedResolution = function () { 1107 return window.screen.width < window.screen.availWidth || window.screen.height < window.screen.availHeight 1108 } 1109 var getHasLiedOs = function () { 1110 var userAgent = navigator.userAgent.toLowerCase() 1111 var oscpu = navigator.oscpu 1112 var platform = navigator.platform.toLowerCase() 1113 var os 1114 // We extract the OS from the user agent (respect the order of the if else if statement) 1115 if (userAgent.indexOf('windows phone') >= 0) { 1116 os = 'Windows Phone' 1117 } else if (userAgent.indexOf('win') >= 0) { 1118 os = 'Windows' 1119 } else if (userAgent.indexOf('android') >= 0) { 1120 os = 'Android' 1121 } else if (userAgent.indexOf('linux') >= 0 || userAgent.indexOf('cros') >= 0) { 1122 os = 'Linux' 1123 } else if (userAgent.indexOf('iphone') >= 0 || userAgent.indexOf('ipad') >= 0) { 1124 os = 'iOS' 1125 } else if (userAgent.indexOf('mac') >= 0) { 1126 os = 'Mac' 1127 } else { 1128 os = 'Other' 1129 } 1130 // We detect if the person uses a mobile device 1131 var mobileDevice = (('ontouchstart' in window) || 1132 (navigator.maxTouchPoints > 0) || 1133 (navigator.msMaxTouchPoints > 0)) 1134 1135 if (mobileDevice && os !== 'Windows Phone' && os !== 'Android' && os !== 'iOS' && os !== 'Other') { 1136 return true 1137 } 1138 1139 // We compare oscpu with the OS extracted from the UA 1140 if (typeof oscpu !== 'undefined') { 1141 oscpu = oscpu.toLowerCase() 1142 if (oscpu.indexOf('win') >= 0 && os !== 'Windows' && os !== 'Windows Phone') { 1143 return true 1144 } else if (oscpu.indexOf('linux') >= 0 && os !== 'Linux' && os !== 'Android') { 1145 return true 1146 } else if (oscpu.indexOf('mac') >= 0 && os !== 'Mac' && os !== 'iOS') { 1147 return true 1148 } else if ((oscpu.indexOf('win') === -1 && oscpu.indexOf('linux') === -1 && oscpu.indexOf('mac') === -1) !== (os === 'Other')) { 1149 return true 1150 } 1151 } 1152 1153 // We compare platform with the OS extracted from the UA 1154 if (platform.indexOf('win') >= 0 && os !== 'Windows' && os !== 'Windows Phone') { 1155 return true 1156 } else if ((platform.indexOf('linux') >= 0 || platform.indexOf('android') >= 0 || platform.indexOf('pike') >= 0) && os !== 'Linux' && os !== 'Android') { 1157 return true 1158 } else if ((platform.indexOf('mac') >= 0 || platform.indexOf('ipad') >= 0 || platform.indexOf('ipod') >= 0 || platform.indexOf('iphone') >= 0) && os !== 'Mac' && os !== 'iOS') { 1159 return true 1160 } else { 1161 var platformIsOther = platform.indexOf('win') < 0 && 1162 platform.indexOf('linux') < 0 && 1163 platform.indexOf('mac') < 0 && 1164 platform.indexOf('iphone') < 0 && 1165 platform.indexOf('ipad') < 0 1166 if (platformIsOther !== (os === "Other")) { 1167 return true; 1168 } 1169 } 1170 1171 return typeof navigator.plugins === 'undefined' && os !== 'Windows' && os !== 'Windows Phone' 1172 } 1173 var getHasLiedBrowser = function () { 1174 var userAgent = navigator.userAgent.toLowerCase() 1175 var productSub = navigator.productSub 1176 1177 // we extract the browser from the user agent (respect the order of the tests) 1178 var browser 1179 if (userAgent.indexOf('firefox') >= 0) { 1180 browser = 'Firefox' 1181 } else if (userAgent.indexOf('opera') >= 0 || userAgent.indexOf('opr') >= 0) { 1182 browser = 'Opera' 1183 } else if (userAgent.indexOf('chrome') >= 0) { 1184 browser = 'Chrome' 1185 } else if (userAgent.indexOf('safari') >= 0) { 1186 browser = 'Safari' 1187 } else if (userAgent.indexOf('trident') >= 0) { 1188 browser = 'Internet Explorer' 1189 } else { 1190 browser = 'Other' 1191 } 1192 1193 if ((browser === 'Chrome' || browser === 'Safari' || browser === 'Opera') && productSub !== '20030107') { 1194 return true 1195 } 1196 1197 // eslint-disable-next-line no-eval 1198 var tempRes = eval.toString().length 1199 if (tempRes === 37 && browser !== 'Safari' && browser !== 'Firefox' && browser !== 'Other') { 1200 return true 1201 } else if (tempRes === 39 && browser !== 'Internet Explorer' && browser !== 'Other') { 1202 return true 1203 } else if (tempRes === 33 && browser !== 'Chrome' && browser !== 'Opera' && browser !== 'Other') { 1204 return true 1205 } 1206 1207 // We create an error to see how it is handled 1208 var errFirefox 1209 try { 1210 // eslint-disable-next-line no-throw-literal 1211 throw 'a' 1212 } catch (err) { 1213 try { 1214 err.toSource() 1215 errFirefox = true 1216 } catch (errOfErr) { 1217 errFirefox = false 1218 } 1219 } 1220 return errFirefox && browser !== 'Firefox' && browser !== 'Other' 1221 } 1222 var isCanvasSupported = function () { 1223 var elem = document.createElement('canvas') 1224 return !!(elem.getContext && elem.getContext('2d')) 1225 } 1226 var isWebGlSupported = function () { 1227 // code taken from Modernizr 1228 if (!isCanvasSupported()) { 1229 return false 1230 } 1231 1232 var glContext = getWebglCanvas() 1233 return !!window.WebGLRenderingContext && !!glContext 1234 } 1235 var isIE = function () { 1236 if (navigator.appName === 'Microsoft Internet Explorer') { 1237 return true 1238 } else if (navigator.appName === 'Netscape' && /Trident/.test(navigator.userAgent)) { // IE 11 1239 return true 1240 } 1241 return false 1242 } 1243 var hasSwfObjectLoaded = function () { 1244 return typeof window.swfobject !== 'undefined' 1245 } 1246 var hasMinFlashInstalled = function () { 1247 return window.swfobject.hasFlashPlayerVersion('9.0.0') 1248 } 1249 var addFlashDivNode = function (options) { 1250 var node = document.createElement('div') 1251 node.setAttribute('id', options.fonts.swfContainerId) 1252 document.body.appendChild(node) 1253 } 1254 var loadSwfAndDetectFonts = function (done, options) { 1255 var hiddenCallback = '___fp_swf_loaded' 1256 window[hiddenCallback] = function (fonts) { 1257 done(fonts) 1258 } 1259 var id = options.fonts.swfContainerId 1260 addFlashDivNode() 1261 var flashvars = { onReady: hiddenCallback } 1262 var flashparams = { allowScriptAccess: 'always', menu: 'false' } 1263 window.swfobject.embedSWF(options.fonts.swfPath, id, '1', '1', '9.0.0', false, flashvars, flashparams, {}) 1264 } 1265 var getWebglCanvas = function () { 1266 var canvas = document.createElement('canvas') 1267 var gl = null 1268 try { 1269 gl = canvas.getContext('webgl') || canvas.getContext('experimental-webgl') 1270 } catch (e) { /* squelch */ } 1271 if (!gl) { gl = null } 1272 return gl 1273 } 1274 1275 var components = [ 1276 { key: 'userAgent', getData: UserAgent }, 1277 { key: 'webdriver', getData: webdriver }, 1278 { key: 'language', getData: languageKey }, 1279 { key: 'colorDepth', getData: colorDepthKey }, 1280 { key: 'deviceMemory', getData: deviceMemoryKey }, 1281 { key: 'pixelRatio', getData: pixelRatioKey }, 1282 { key: 'hardwareConcurrency', getData: hardwareConcurrencyKey }, 1283 { key: 'screenResolution', getData: screenResolutionKey }, 1284 { key: 'availableScreenResolution', getData: availableScreenResolutionKey }, 1285 { key: 'timezoneOffset', getData: timezoneOffset }, 1286 { key: 'timezone', getData: timezone }, 1287 { key: 'sessionStorage', getData: sessionStorageKey }, 1288 { key: 'localStorage', getData: localStorageKey }, 1289 { key: 'indexedDb', getData: indexedDbKey }, 1290 { key: 'addBehavior', getData: addBehaviorKey }, 1291 { key: 'openDatabase', getData: openDatabaseKey }, 1292 { key: 'cpuClass', getData: cpuClassKey }, 1293 { key: 'platform', getData: platformKey }, 1294 { key: 'doNotTrack', getData: doNotTrackKey }, 1295 { key: 'plugins', getData: pluginsComponent }, 1296 { key: 'canvas', getData: canvasKey }, 1297 { key: 'webgl', getData: webglKey }, 1298 { key: 'webglVendorAndRenderer', getData: webglVendorAndRendererKey }, 1299 { key: 'adBlock', getData: adBlockKey }, 1300 { key: 'hasLiedLanguages', getData: hasLiedLanguagesKey }, 1301 { key: 'hasLiedResolution', getData: hasLiedResolutionKey }, 1302 { key: 'hasLiedOs', getData: hasLiedOsKey }, 1303 { key: 'hasLiedBrowser', getData: hasLiedBrowserKey }, 1304 { key: 'touchSupport', getData: touchSupportKey }, 1305 { key: 'fonts', getData: jsFontsKey, pauseBefore: true }, 1306 { key: 'fontsFlash', getData: flashFontsKey, pauseBefore: true }, 1307 { key: 'audio', getData: audioKey }, 1308 { key: 'enumerateDevices', getData: enumerateDevicesKey } 1309 ] 1310 1311 var Fingerprint2 = function (options) { 1312 throw new Error("'new Fingerprint()' is deprecated, see https://github.com/Valve/fingerprintjs2#upgrade-guide-from-182-to-200") 1313 } 1314 1315 Fingerprint2.get = function (options, callback) { 1316 if (!callback) { 1317 callback = options 1318 options = {} 1319 } else if (!options) { 1320 options = {} 1321 } 1322 extendSoft(options, defaultOptions) 1323 options.components = options.extraComponents.concat(components) 1324 1325 var keys = { 1326 data: [], 1327 addPreprocessedComponent: function (key, value) { 1328 if (typeof options.preprocessor === 'function') { 1329 value = options.preprocessor(key, value) 1330 } 1331 keys.data.push({ key: key, value: value }) 1332 } 1333 } 1334 1335 var i = -1 1336 var chainComponents = function (alreadyWaited) { 1337 i += 1 1338 if (i >= options.components.length) { // on finish 1339 callback(keys.data) 1340 return 1341 } 1342 var component = options.components[i] 1343 1344 if (options.excludes[component.key]) { 1345 chainComponents(false) // skip 1346 return 1347 } 1348 1349 if (!alreadyWaited && component.pauseBefore) { 1350 i -= 1 1351 setTimeout(function () { 1352 chainComponents(true) 1353 }, 1) 1354 return 1355 } 1356 1357 try { 1358 component.getData(function (value) { 1359 keys.addPreprocessedComponent(component.key, value) 1360 chainComponents(false) 1361 }, options) 1362 } catch (error) { 1363 // main body error 1364 keys.addPreprocessedComponent(component.key, String(error)) 1365 chainComponents(false) 1366 } 1367 } 1368 1369 chainComponents(false) 1370 } 1371 1372 Fingerprint2.getPromise = function (options) { 1373 return new Promise(function (resolve, reject) { 1374 Fingerprint2.get(options, resolve) 1375 }) 1376 } 1377 1378 Fingerprint2.getV18 = function (options, callback) { 1379 if (callback == null) { 1380 callback = options 1381 options = {} 1382 } 1383 return Fingerprint2.get(options, function (components) { 1384 var newComponents = [] 1385 for (var i = 0; i < components.length; i++) { 1386 var component = components[i] 1387 if (component.value === (options.NOT_AVAILABLE || 'not available')) { 1388 newComponents.push({ key: component.key, value: 'unknown' }) 1389 } else if (component.key === 'plugins') { 1390 newComponents.push({ 1391 key: 'plugins', 1392 value: map(component.value, function (p) { 1393 var mimeTypes = map(p[2], function (mt) { 1394 if (mt.join) { return mt.join('~') } 1395 return mt 1396 }).join(',') 1397 return [p[0], p[1], mimeTypes].join('::') 1398 }) 1399 }) 1400 } else if (['canvas', 'webgl'].indexOf(component.key) !== -1) { 1401 newComponents.push({ key: component.key, value: component.value.join('~') }) 1402 } else if (['sessionStorage', 'localStorage', 'indexedDb', 'addBehavior', 'openDatabase'].indexOf(component.key) !== -1) { 1403 if (component.value) { 1404 newComponents.push({ key: component.key, value: 1 }) 1405 } else { 1406 // skip 1407 continue 1408 } 1409 } else { 1410 if (component.value) { 1411 newComponents.push(component.value.join ? { key: component.key, value: component.value.join(';') } : component) 1412 } else { 1413 newComponents.push({ key: component.key, value: component.value }) 1414 } 1415 } 1416 } 1417 var murmur = x64hash128(map(newComponents, function (component) { return component.value }).join('~~~'), 31) 1418 callback(murmur, newComponents) 1419 }) 1420 } 1421 1422 Fingerprint2.x64hash128 = x64hash128 1423 Fingerprint2.VERSION = '2.0.6' 1424 return Fingerprint2 1425})
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.