vendor: 6,659 bytes, lines 1-167
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 (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' 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.charCo
vendor: 15,720 bytes, lines 196-557
196deAt(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/Valve/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 languageKey = function (done, options) { 399 done(navigator.language || navigator.userLanguage || navigator.browserLanguage || navigator.systemLanguage || options.NOT_AVAILABLE) 400 } 401 var colorDepthKey = function (done, options) { 402 done(window.screen.colorDepth || options.NOT_AVAILABLE) 403 } 404 var deviceMemoryKey = function (done, options) { 405 done(navigator.deviceMemory || options.NOT_AVAILABLE) 406 } 407 var pixelRatioKey = function (done, options) { 408 done(window.devicePixelRatio || options.NOT_AVAILABLE) 409 } 410 var screenResolutionKey = function (done, options) { 411 done(getScreenResolution(options)) 412 } 413 var getScreenResolution = function (options) { 414 var resolution = [window.screen.width, window.screen.height] 415 if (options.screen.detectScreenOrientation) { 416 resolution.sort().reverse() 417 } 418 return resolution 419 } 420 var availableScreenResolutionKey = function (done, options) { 421 done(getAvailableScreenResolution(options)) 422 } 423 var getAvailableScreenResolution = function (options) { 424 if (window.screen.availWidth && window.screen.availHeight) { 425 var available = [window.screen.availHeight, window.screen.availWidth] 426 if (options.screen.detectScreenOrientation) { 427 available.sort().reverse() 428 } 429 return available 430 } 431 // headless browsers 432 return options.NOT_AVAILABLE 433 } 434 var timezoneOffset = function (done) { 435 done(new Date().getTimezoneOffset()) 436 } 437 var timezone = function (done, options) { 438 if (window.Intl && window.Intl.DateTimeFormat) { 439 done(new window.Intl.DateTimeFormat().resolvedOptions().timeZone) 440 return 441 } 442 done(options.NOT_AVAILABLE) 443 } 444 var sessionStorageKey = function (done, options) { 445 done(hasSessionStorage(options)) 446 } 447 var localStorageKey = function (done, options) { 448 done(hasLocalStorage(options)) 449 } 450 var indexedDbKey = function (done, options) { 451 done(hasIndexedDB(options)) 452 } 453 var addBehaviorKey = function (done) { 454 // body might not be defined at this point or removed programmatically 455 done(!!(document.body && document.body.addBehavior)) 456 } 457 var openDatabaseKey = function (done) { 458 done(!!window.openDatabase) 459 } 460 var cpuClassKey = function (done, options) { 461 done(getNavigatorCpuClass(options)) 462 } 463 var platformKey = function (done, options) { 464 done(getNavigatorPlatform(options)) 465 } 466 var doNotTrackKey = function (done, options) { 467 done(getDoNotTrack(options)) 468 } 469 var canvasKey = function (done, options) { 470 if (isCanvasSupported()) { 471 done(getCanvasFp(options)) 472 return 473 } 474 done(options.NOT_AVAILABLE) 475 } 476 var webglKey = function (done, options) { 477 if (isWebGlSupported()) { 478 done(getWebglFp()) 479 return 480 } 481 done(options.NOT_AVAILABLE) 482 } 483 var webglVendorAndRendererKey = function (done) { 484 if (isWebGlSupported()) { 485 done(getWebglVendorAndRenderer()) 486 return 487 } 488 done() 489 } 490 var adBlockKey = function (done) { 491 done(getAdBlock()) 492 } 493 var hasLiedLanguagesKey = function (done) { 494 done(getHasLiedLanguages()) 495 } 496 var hasLiedResolutionKey = function (done) { 497 done(getHasLiedResolution()) 498 } 499 var hasLiedOsKey = function (done) { 500 done(getHasLiedOs()) 501 } 502 var hasLiedBrowserKey = function (done) { 503 done(getHasLiedBrowser()) 504 } 505 // flash fonts (will increase fingerprinting time 20X to ~ 130-150ms) 506 var flashFontsKey = function (done, options) { 507 // we do flash if swfobject is loaded 508 if (!hasSwfObjectLoaded()) { 509 return done('swf object not loaded') 510 } 511 if (!hasMinFlashInstalled()) { 512 return done('flash not installed') 513 } 514 if (!options.fonts.swfPath) { 515 return done('missing options.fonts.swfPath') 516 } 517 loadSwfAndDetectFonts(function (fonts) { 518 done(fonts) 519 }, options) 520 } 521 // kudos to http://www.lalit.org/lab/javascript-css-font-detect/ 522 var jsFontsKey = function (done, options) { 523 // a font will be compared against all the three default fonts. 524 // and if it doesn't match all 3 then that font is not available. 525 var baseFonts = ['monospace', 'sans-serif', 'serif'] 526 527 var fontList = [ 528 'Andale Mono', 'Arial', 'Arial Black', 'Arial Hebrew', 'Arial MT', 'Arial Narrow', 'Arial Rounded MT Bold', 'Arial Unicode MS', 529 'Bitstream Vera Sans Mono', 'Book Antiqua', 'Bookman Old Style', 530 'Calibri', 'Cambria', 'Cambria Math', 'Century', 'Century Gothic', 'Century Schoolbook', 'Comic Sans', 'Comic Sans MS', 'Consolas', 'Courier', 'Courier New', 531 'Geneva', 'Georgia', 532 'Helvetica', 'Helvetica Neue', 533 'Impact', 534 'Lucida Bright', 'Lucida Calligraphy', 'Lucida Console', 'Lucida Fax', 'LUCIDA GRANDE', 'Lucida Handwriting', 'Lucida Sans', 'Lucida Sans Typewriter', 'Lucida Sans Unicode', 535 'Microsoft Sans Serif', 'Monaco', 'Monotype Corsiva', 'MS Gothic', 'MS Outlook', 'MS PGothic', 'MS Reference Sans Serif', 'MS Sans Serif', 'MS Serif', 'MYRIAD', 'MYRIAD PRO', 536 'Palatino', 'Palatino Linotype', 537 'Segoe Print', 'Segoe Script', 'Segoe UI', 'Segoe UI Light', 'Segoe UI Semibold', 'Segoe UI Symbol', 538 'Tahoma', 'Times', 'Times New Roman', 'Times New Roman PS', 'Trebuchet MS', 539 'Verdana', 'Wingdings', 'Wingdings 2', 'Wingdings 3' 540 ] 541 542 if (options.fonts.extendedJsFonts) { 543 var extendedFontList = [ 544 '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', 545 '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', 546 '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', 547 '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', 548 'Blackadder ITC', 'BlairMdITC TT', 'Bodoni 72', 'Bodoni 72 Oldstyle', 'Bodoni 72 Smallcaps', 'Bodoni MT', 'Bodoni MT Black', 'Bodoni MT Condensed', 'Bodoni MT Poster Compressed', 549 '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', 550 'CaslonOpnface BT', 'Castellar', 'Centaur', 'Cezanne', 'CG Omega', 'CG Times', 'Chalkboard', 'Chalkboard SE', 'Chalkduster', 'Charlesworth', 'Charter Bd BT', 'Charter BT', 'Chaucer', 551 'ChelthmITC Bk BT', 'Chiller', 'Clarendon', 'Clarendon Condensed', 'CloisterBlack BT', 'Cochin', 'Colonna MT', 'Constantia', 'Cooper Black', 'Copperplate', 'Copperplate Gothic', 'Copperplate Gothic Bold', 552 'Copperplate Gothic Light', 'CopperplGoth Bd BT', 'Corbel', 'Cordia New', 'CordiaUPC', 'Cornerstone', 'Coronet', 'Cuckoo', 'Curlz MT', 'DaunPenh', 'Dauphin', 'David', 'DB LCD Temp', 'DELICIOUS', 'Denmark', 553 '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', 554 'EucrosiaUPC', 'Euphemia', 'Euphemia UCAS', 'EUROSTILE', 'Exotc350 Bd BT', 'FangSong', 'Felix Titling', 'Fixedsys', 'FONTIN', 'Footlight MT Light', 'Forte', 555 'FrankRuehl', 'Fransiscan', 'Freefrm721 Blk BT', 'FreesiaUPC', 'Freestyle Script', 'French Script MT', 'FrnkGothITC Bk BT', 'Fruitger', 'FRUTIGER', 556 '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', 557 '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',
558 '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', 559 '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', 560 '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', 561 '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', 562 'Lydian BT', 'Magneto', 'Maiandra GD', 'Malayalam Sangam MN', 'Malgun Gothic', 563 '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', 564 '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', 565 'MONO', 'MoolBoran', 'Mrs Eaves', 'MS LineDraw', 'MS Mincho', 'MS PMincho', 'MS Reference Specialty', 'MS UI Gothic', 'MT Extra', 'MUSEO', 'MV Boli', 566 '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', 567 'OSAKA', 'OzHandicraft BT', 'Palace Script MT', 'Papyrus', 'Parchment', 'Party LET', 'Pegasus', 'Perpetua', 'Perpetua Titling MT', 'PetitaBold', 'Pickwick', 'Plantagenet Cherokee', 'Playbill', 'PMingLiU', 'PMingLiU-ExtB', 568 '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', 569 'Santa Fe LET', 'Savoye LET', 'Sceptre', 'Script', 'Script MT Bold', 'SCRIPTINA', 'Serifa', 'Serifa BT', 'Serifa Th BT', 'ShelleyVolante BT', 'Sherwood', 570 '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', 571 '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', 572 'Terminal', 'Thonburi', 'Traditional Arabic', 'Trajan', 'TRAJAN PRO', 'Tristan', 'Tubular', 'Tunga', 'Tw Cen MT', 'Tw Cen MT C
vendor: 33,813 bytes, lines 572-1411
572ondensed', 'Tw Cen MT Condensed Extra Bold', 573 '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', 574 'ZapfEllipt BT', 'ZapfHumnst BT', 'ZapfHumnst Dm BT', 'Zapfino', 'Zurich BlkEx BT', 'Zurich Ex BT', 'ZWAdobeF'] 575 fontList = fontList.concat(extendedFontList) 576 } 577 578 fontList = fontList.concat(options.fonts.userDefinedFonts) 579 580 // remove duplicate fonts 581 fontList = fontList.filter(function (font, position) { 582 return fontList.indexOf(font) === position 583 }) 584 585 // we use m or w because these two characters take up the maximum width. 586 // And we use a LLi so that the same matching fonts can get separated 587 var testString = 'mmmmmmmmmmlli' 588 589 // we test using 72px font size, we may use any size. I guess larger the better. 590 var testSize = '72px' 591 592 var h = document.getElementsByTagName('body')[0] 593 594 // div to load spans for the base fonts 595 var baseFontsDiv = document.createElement('div') 596 597 // div to load spans for the fonts to detect 598 var fontsDiv = document.createElement('div') 599 600 var defaultWidth = {} 601 var defaultHeight = {} 602 603 // creates a span where the fonts will be loaded 604 var createSpan = function () { 605 var s = document.createElement('span') 606 /* 607 * We need this css as in some weird browser this 608 * span elements shows up for a microSec which creates a 609 * bad user experience 610 */ 611 s.style.position = 'absolute' 612 s.style.left = '-9999px' 613 s.style.fontSize = testSize 614 615 // css font reset to reset external styles 616 s.style.fontStyle = 'normal' 617 s.style.fontWeight = 'normal' 618 s.style.letterSpacing = 'normal' 619 s.style.lineBreak = 'auto' 620 s.style.lineHeight = 'normal' 621 s.style.textTransform = 'none' 622 s.style.textAlign = 'left' 623 s.style.textDecoration = 'none' 624 s.style.textShadow = 'none' 625 s.style.whiteSpace = 'normal' 626 s.style.wordBreak = 'normal' 627 s.style.wordSpacing = 'normal' 628 629 s.innerHTML = testString 630 return s 631 } 632 633 // creates a span and load the font to detect and a base font for fallback 634 var createSpanWithFonts = function (fontToDetect, baseFont) { 635 var s = createSpan() 636 s.style.fontFamily = "'" + fontToDetect + "'," + baseFont 637 return s 638 } 639 640 // creates spans for the base fonts and adds them to baseFontsDiv 641 var initializeBaseFontsSpans = function () { 642 var spans = [] 643 for (var index = 0, length = baseFonts.length; index < length; index++) { 644 var s = createSpan() 645 s.style.fontFamily = baseFonts[index] 646 baseFontsDiv.appendChild(s) 647 spans.push(s) 648 } 649 return spans 650 } 651 652 // creates spans for the fonts to detect and adds them to fontsDiv 653 var initializeFontsSpans = function () { 654 var spans = {} 655 for (var i = 0, l = fontList.length; i < l; i++) { 656 var fontSpans = [] 657 for (var j = 0, numDefaultFonts = baseFonts.length; j < numDefaultFonts; j++) { 658 var s = createSpanWithFonts(fontList[i], baseFonts[j]) 659 fontsDiv.appendChild(s) 660 fontSpans.push(s) 661 } 662 spans[fontList[i]] = fontSpans // Stores {fontName : [spans for that font]} 663 } 664 return spans 665 } 666 667 // checks if a font is available 668 var isFontAvailable = function (fontSpans) { 669 var detected = false 670 for (var i = 0; i < baseFonts.length; i++) { 671 detected = (fontSpans[i].offsetWidth !== defaultWidth[baseFonts[i]] || fontSpans[i].offsetHeight !== defaultHeight[baseFonts[i]]) 672 if (detected) { 673 return detected 674 } 675 } 676 return detected 677 } 678 679 // create spans for base fonts 680 var baseFontsSpans = initializeBaseFontsSpans() 681 682 // add the spans to the DOM 683 h.appendChild(baseFontsDiv) 684 685 // get the default width for the three base fonts 686 for (var index = 0, length = baseFonts.length; index < length; index++) { 687 defaultWidth[baseFonts[index]] = baseFontsSpans[index].offsetWidth // width for the default font 688 defaultHeight[baseFonts[index]] = baseFontsSpans[index].offsetHeight // height for the default font 689 } 690 691 // create spans for fonts to detect 692 var fontsSpans = initializeFontsSpans() 693 694 // add all the spans to the DOM 695 h.appendChild(fontsDiv) 696 697 // check available fonts 698 var available = [] 699 for (var i = 0, l = fontList.length; i < l; i++) { 700 if (isFontAvailable(fontsSpans[fontList[i]])) { 701 available.push(fontList[i]) 702 } 703 } 704 705 // remove spans from DOM 706 h.removeChild(fontsDiv) 707 h.removeChild(baseFontsDiv) 708 done(available) 709 } 710 var pluginsComponent = function (done, options) { 711 if (isIE()) { 712 if (!options.plugins.excludeIE) { 713 done(getIEPlugins(options)) 714 } else { 715 done(options.EXCLUDED) 716 } 717 } else { 718 done(getRegularPlugins(options)) 719 } 720 } 721 var getRegularPlugins = function (options) { 722 if (navigator.plugins == null) { 723 return options.NOT_AVAILABLE 724 } 725 726 var plugins = [] 727 // plugins isn't defined in Node envs. 728 for (var i = 0, l = navigator.plugins.length; i < l; i++) { 729 if (navigator.plugins[i]) { plugins.push(navigator.plugins[i]) } 730 } 731 732 // sorting plugins only for those user agents, that we know randomize the plugins 733 // every time we try to enumerate them 734 if (pluginsShouldBeSorted(options)) { 735 plugins = plugins.sort(function (a, b) { 736 if (a.name > b.name) { return 1 } 737 if (a.name < b.name) { return -1 } 738 return 0 739 }) 740 } 741 return map(plugins, function (p) { 742 var mimeTypes = map(p, function (mt) { 743 return [mt.type, mt.suffixes] 744 }) 745 return [p.name, p.description, mimeTypes] 746 }) 747 } 748 var getIEPlugins = function (options) { 749 var result = [] 750 if ((Object.getOwnPropertyDescriptor && Object.getOwnPropertyDescriptor(window, 'ActiveXObject')) || ('ActiveXObject' in window)) { 751 var names = [ 752 'AcroPDF.PDF', // Adobe PDF reader 7+ 753 'Adodb.Stream', 754 'AgControl.AgControl', // Silverlight 755 'DevalVRXCtrl.DevalVRXCtrl.1', 756 'MacromediaFlashPaper.MacromediaFlashPaper', 757 'Msxml2.DOMDocument', 758 'Msxml2.XMLHTTP', 759 'PDF.PdfCtrl', // Adobe PDF reader 6 and earlier, brrr 760 'QuickTime.QuickTime', // QuickTime 761 'QuickTimeCheckObject.QuickTimeCheck.1', 762 'RealPlayer', 763 'RealPlayer.RealPlayer(tm) ActiveX Control (32-bit)', 764 'RealVideo.RealVideo(tm) ActiveX Control (32-bit)', 765 'Scripting.Dictionary', 766 'SWCtl.SWCtl', // ShockWave player 767 'Shell.UIHelper', 768 'ShockwaveFlash.ShockwaveFlash', // flash plugin 769 'Skype.Detection', 770 'TDCCtl.TDCCtl', 771 'WMPlayer.OCX', // Windows media player 772 'rmocx.RealPlayer G2 Control', 773 'rmocx.RealPlayer G2 Control.1' 774 ] 775 // starting to detect plugins in IE 776 result = map(names, function (name) { 777 try { 778 // eslint-disable-next-line no-new 779 new window.ActiveXObject(name) 780 return name 781 } catch (e) { 782 return options.ERROR 783 } 784 }) 785 } else { 786 result.push(options.NOT_AVAILABLE) 787 } 788 if (navigator.plugins) { 789 result = result.concat(getRegularPlugins(options)) 790 } 791 return result 792 } 793 var pluginsShouldBeSorted = function (options) { 794 var should = false 795 for (var i = 0, l = options.plugins.sortPluginsFor.length; i < l; i++) { 796 var re = options.plugins.sortPluginsFor[i] 797 if (navigator.userAgent.match(re)) { 798 should = true 799 break 800 } 801 } 802 return should 803 } 804 var touchSupportKey = function (done) { 805 done(getTouchSupport()) 806 } 807 var hardwareConcurrencyKey = function (done, options) { 808 done(getHardwareConcurrency(options)) 809 } 810 var hasSessionStorage = function (options) { 811 try { 812 return !!window.sessionStorage 813 } catch (e) { 814 return options.ERROR // SecurityError when referencing it means it exists 815 } 816 } 817 818 // https://bugzilla.mozilla.org/show_bug.cgi?id=781447 819 var hasLocalStorage = function (options) { 820 try { 821 return !!window.localStorage 822 } catch (e) { 823 return options.ERROR // SecurityError when referencing it means it exists 824 } 825 } 826 var hasIndexedDB = function (options) { 827 try { 828 return !!window.indexedDB 829 } catch (e) { 830 return options.ERROR // SecurityError when referencing it means it exists 831 } 832 } 833 var getHardwareConcurrency = function (options) { 834 if (navigator.hardwareConcurrency) { 835 return navigator.hardwareConcurrency 836 } 837 return options.NOT_AVAILABLE 838 } 839 var getNavigatorCpuClass = function (options) { 840 return navigator.cpuClass || options.NOT_AVAILABLE 841 } 842 var getNavigatorPlatform = function (options) { 843 if (navigator.platform) { 844 return navigator.platform 845 } else { 846 return options.NOT_AVAILABLE 847 } 848 } 849 var getDoNotTrack = function (options) { 850 if (navigator.doNotTrack) { 851 return navigator.doNotTrack 852 } else if (navigator.msDoNotTrack) { 853 return navigator.msDoNotTrack 854 } else if (window.doNotTrack) { 855 return window.doNotTrack 856 } else { 857 return options.NOT_AVAILABLE 858 } 859 } 860 // This is a crude and primitive touch screen detection. 861 // It's not possible to currently reliably detect the availability of a touch screen 862 // with a JS, without actually subscribing to a touch event. 863 // http://www.stucox.com/blog/you-cant-detect-a-touchscreen/ 864 // https://github.com/Modernizr/Modernizr/issues/548 865 // method returns an array of 3 values: 866 // maxTouchPoints, the success or failure of creating a TouchEvent, 867 // and the availability of the 'ontouchstart' property 868 869 var getTouchSupport = function () { 870 var maxTouchPoints = 0 871 var touchEvent 872 if (typeof navigator.maxTouchPoints !== 'undefined') { 873 maxTouchPoints = navigator.maxTouchPoints 874 } else if (typeof navigator.msMaxTouchPoints !== 'undefined') { 875 maxTouchPoints = navigator.msMaxTouchPoints 876 } 877 try { 878 document.createEvent('TouchEvent') 879 touchEvent = true 880 } catch (_) { 881 touchEvent = false 882 } 883 var touchStart = 'ontouchstart' in window 884 return [maxTouchPoints, touchEvent, touchStart] 885 } 886 // https://www.browserleaks.com/canvas#how-does-it-work 887 888 var getCanvasFp = function (options) { 889 var result = [] 890 // Very simple now, need to make it more complex (geo shapes etc) 891 var canvas = document.createElement('canvas') 892 canvas.width = 2000 893 canvas.height = 200 894 canvas.style.display = 'inline' 895 var ctx = canvas.getContext('2d') 896 // detect browser support of canvas winding 897 // http://blogs.adobe.com/webplatform/2013/01/30/winding-rules-in-canvas/ 898 // https://github.com/Modernizr/Modernizr/blob/master/feature-detects/canvas/winding.js 899 ctx.rect(0, 0, 10, 10) 900 ctx.rect(2, 2, 6, 6) 901 result.push('canvas winding:' + ((ctx.isPointInPath(5, 5, 'evenodd') === false) ? 'yes' : 'no')) 902 903 ctx.textBaseline = 'alphabetic' 904 ctx.fillStyle = '#f60' 905 ctx.fillRect(125, 1, 62, 20) 906 ctx.fillStyle = '#069' 907 // https://github.com/Valve/fingerprintjs2/issues/66 908 if (options.dontUseFakeFontInCanvas) { 909 ctx.font = '11pt Arial' 910 } else { 911 ctx.font = '11pt no-real-font-123' 912 } 913 ctx.fillText('Cwm fjordbank glyphs vext quiz, \ud83d\ude03', 2, 15) 914 ctx.fillStyle = 'rgba(102, 204, 0, 0.2)' 915 ctx.font = '18pt Arial' 916 ctx.fillText('Cwm fjordbank glyphs vext quiz, \ud83d\ude03', 4, 45) 917 918 // canvas blending 919 // http://blogs.adobe.com/webplatform/2013/01/28/blending-features-in-canvas/ 920 // http://jsfiddle.net/NDYV8/16/ 921 ctx.globalCompositeOperation = 'multiply' 922 ctx.fillStyle = 'rgb(255,0,255)' 923 ctx.beginPath() 924 ctx.arc(50, 50, 50, 0, Math.PI * 2, true) 925 ctx.closePath() 926 ctx.fill() 927 ctx.fillStyle = 'rgb(0,255,255)' 928 ctx.beginPath() 929 ctx.arc(100, 50, 50, 0, Math.PI * 2, true) 930 ctx.closePath() 931 ctx.fill() 932 ctx.fillStyle = 'rgb(255,255,0)' 933 ctx.beginPath() 934 ctx.arc(75, 100, 50, 0, Math.PI * 2, true) 935 ctx.closePath() 936 ctx.fill() 937 ctx.fillStyle = 'rgb(255,0,255)' 938 // canvas winding 939 // http://blogs.adobe.com/webplatform/2013/01/30/winding-rules-in-canvas/ 940 // http://jsfiddle.net/NDYV8/19/ 941 ctx.arc(75, 75, 75, 0, Math.PI * 2, true) 942 ctx.arc(75, 75, 25, 0, Math.PI * 2, true) 943 ctx.fill('evenodd') 944 945 if (canvas.toDataURL) { result.push('canvas fp:' + canvas.toDataURL()) } 946 return result 947 } 948 var getWebglFp = function () { 949 var gl 950 var fa2s = function (fa) { 951 gl.clearColor(0.0, 0.0, 0.0, 1.0) 952 gl.enable(gl.DEPTH_TEST) 953 gl.depthFunc(gl.LEQUAL) 954 gl.clear(gl.COLOR_BUFFER_BIT | gl.DEPTH_BUFFER_BIT) 955 return '[' + fa[0] + ', ' + fa[1] + ']' 956 } 957 var maxAnisotropy = function (gl) { 958 var ext = gl.getExtension('EXT_texture_filter_anisotropic') || gl.getExtension('WEBKIT_EXT_texture_filter_anisotropic') || gl.getExtension('MOZ_EXT_texture_filter_anisotropic') 959 if (ext) { 960 var anisotropy = gl.getParameter(ext.MAX_TEXTURE_MAX_ANISOTROPY_EXT) 961 if (anisotropy === 0) { 962 anisotropy = 2 963 } 964 return anisotropy 965 } else { 966 return null 967 } 968 } 969 970 gl = getWebglCanvas() 971 if (!gl) { return null } 972 // WebGL fingerprinting is a combination of techniques, found in MaxMind antifraud script & Augur fingerprinting. 973 // First it draws a gradient object with shaders and convers the image to the Base64 string. 974 // 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 975 // 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. 976 var result = [] 977 var vShaderTemplate = 'attribute vec2 attrVertex;varying vec2 varyinTexCoordinate;uniform vec2 uniformOffset;void main(){varyinTexCoordinate=attrVertex+uniformOffset;gl_Position=vec4(attrVertex,0,1);}' 978 var fShaderTemplate = 'precision mediump float;varying vec2 varyinTexCoordinate;void main() {gl_FragColor=vec4(varyinTexCoordinate,0,1);}' 979 var vertexPosBuffer = gl.createBuffer() 980 gl.bindBuffer(gl.ARRAY_BUFFER, vertexPosBuffer) 981 var vertices = new Float32Array([-0.2, -0.9, 0, 0.4, -0.26, 0, 0, 0.732134444, 0]) 982 gl.bufferData(gl.ARRAY_BUFFER, vertices, gl.STATIC_DRAW) 983 vertexPosBuffer.itemSize = 3 984 vertexPosBuffer.numItems = 3 985 var program = gl.createProgram() 986 var vshader = gl.createShader(gl.VERTEX_SHADER) 987 gl.shaderSource(vshader, vShaderTemplate) 988 gl.compileShader(vshader) 989 var fshader = gl.createShader(gl.FRAGMENT_SHADER) 990 gl.shaderSource(fshader, fShaderTemplate) 991 gl.compileShader(fshader) 992 gl.attachShader(program, vshader) 993 gl.attachShader(program, fshader) 994 gl.linkProgram(program) 995 gl.useProgram(program) 996 program.vertexPosAttrib = gl.getAttribLocation(program, 'attrVertex') 997 program.offsetUniform = gl.getUniformLocation(program, 'uniformOffset') 998 gl.enableVertexAttribArray(program.vertexPosArray) 999 gl.vertexAttribPointer(program.vertexPosAttrib, vertexPosBuffer.itemSize, gl.FLOAT, !1, 0, 0) 1000 gl.uniform2f(program.offsetUniform, 1, 1) 1001 gl.drawArrays(gl.TRIANGLE_STRIP, 0, vertexPosBuffer.numItems) 1002 try { 1003 result.push(gl.canvas.toDataURL()) 1004 } catch (e) { 1005 /* .toDataURL may be absent or broken (blocked by extension) */ 1006 } 1007 result.push('extensions:' + (gl.getSupportedExtensions() || []).join(';')) 1008 result.push('webgl aliased line width range:' + fa2s(gl.getParameter(gl.ALIASED_LINE_WIDTH_RANGE))) 1009 result.push('webgl aliased point size range:' + fa2s(gl.getParameter(gl.ALIASED_POINT_SIZE_RANGE))) 1010 result.push('webgl alpha bits:' + gl.getParameter(gl.ALPHA_BITS)) 1011 result.push('webgl antialiasing:' + (gl.getContextAttributes().antialias ? 'yes' : 'no')) 1012 result.push('webgl blue bits:' + gl.getParameter(gl.BLUE_BITS)) 1013 result.push('webgl depth bits:' + gl.getParameter(gl.DEPTH_BITS)) 1014 result.push('webgl green bits:' + gl.getParameter(gl.GREEN_BITS)) 1015 result.push('webgl max anisotropy:' + maxAnisotropy(gl)) 1016 result.push('webgl max combined texture image units:' + gl.getParameter(gl.MAX_COMBINED_TEXTURE_IMAGE_UNITS)) 1017 result.push('webgl max cube map texture size:' + gl.getParameter(gl.MAX_CUBE_MAP_TEXTURE_SIZE)) 1018 result.push('webgl max fragment uniform vectors:' + gl.getParameter(gl.MAX_FRAGMENT_UNIFORM_VECTORS)) 1019 result.push('webgl max render buffer size:' + gl.getParameter(gl.MAX_RENDERBUFFER_SIZE)) 1020 result.push('webgl max texture image units:' + gl.getParameter(gl.MAX_TEXTURE_IMAGE_UNITS)) 1021 result.push('webgl max texture size:' + gl.getParameter(gl.MAX_TEXTURE_SIZE)) 1022 result.push('webgl max varying vectors:' + gl.getParameter(gl.MAX_VARYING_VECTORS)) 1023 result.push('webgl max vertex attribs:' + gl.getParameter(gl.MAX_VERTEX_ATTRIBS)) 1024 result.push('webgl max vertex texture image units:' + gl.getParameter(gl.MAX_VERTEX_TEXTURE_IMAGE_UNITS)) 1025 result.push('webgl max vertex uniform vectors:' + gl.getParameter(gl.MAX_VERTEX_UNIFORM_VECTORS)) 1026 result.push('webgl max viewport dims:' + fa2s(gl.getParameter(gl.MAX_VIEWPORT_DIMS))) 1027 result.push('webgl red bits:' + gl.getParameter(gl.RED_BITS)) 1028 result.push('webgl renderer:' + gl.getParameter(gl.RENDERER)) 1029 result.push('webgl shading language version:' + gl.getParameter(gl.SHADING_LANGUAGE_VERSION)) 1030 result.push('webgl stencil bits:' + gl.getParameter(gl.STENCIL_BITS)) 1031 result.push('webgl vendor:' + gl.getParameter(gl.VENDOR)) 1032 result.push('webgl version:' + gl.getParameter(gl.VERSION)) 1033 1034 try { 1035 // Add the unmasked vendor and unmasked renderer if the debug_renderer_info extension is available 1036 var extensionDebugRendererInfo = gl.getExtension('WEBGL_debug_renderer_info') 1037 if (extensionDebugRendererInfo) { 1038 result.push('webgl unmasked vendor:' + gl.getParameter(extensionDebugRendererInfo.UNMASKED_VENDOR_WEBGL)) 1039 result.push('webgl unmasked renderer:' + gl.getParameter(extensionDebugRendererInfo.UNMASKED_RENDERER_WEBGL)) 1040 } 1041 } catch (e) { /* squelch */ } 1042 1043 if (!gl.getShaderPrecisionFormat) { 1044 return result 1045 } 1046 1047 each(['FLOAT', 'INT'], function (numType) { 1048 each(['VERTEX', 'FRAGMENT'], function (shader) { 1049 each(['HIGH', 'MEDIUM', 'LOW'], function (numSize) { 1050 each(['precision', 'rangeMin', 'rangeMax'], function (key) { 1051 var format = gl.getShaderPrecisionFormat(gl[shader + '_SHADER'], gl[numSize + '_' + numType])[key] 1052 if (key !== 'precision') { 1053 key = 'precision ' + key 1054 } 1055 var line = ['webgl ', shader.toLowerCase(), ' shader ', numSize.toLowerCase(), ' ', numType.toLowerCase(), ' ', key, ':', format].join('') 1056 result.push(line) 1057 }) 1058 }) 1059 }) 1060 }) 1061 return result 1062 } 1063 var getWebglVendorAndRenderer = function () { 1064 /* This a subset of the WebGL fingerprint with a lot of entropy, while being reasonably browser-independent */ 1065 try { 1066 var glContext = getWebglCanvas() 1067 var extensionDebugRendererInfo = glContext.getExtension('WEBGL_debug_renderer_info') 1068 return glContext.getParameter(extensionDebugRendererInfo.UNMASKED_VENDOR_WEBGL) + '~' + glContext.getParameter(extensionDebugRendererInfo.UNMASKED_RENDERER_WEBGL) 1069 } catch (e) { 1070 return null 1071 } 1072 } 1073 var getAdBlock = function () { 1074 var ads = document.createElement('div') 1075 ads.innerHTML = ' ' 1076 ads.className = 'adsbox' 1077 var result = false 1078 try { 1079 // body may not exist, that's why we need try/catch 1080 document.body.appendChild(ads) 1081 result = document.getElementsByClassName('adsbox')[0].offsetHeight === 0 1082 document.body.removeChild(ads) 1083 } catch (e) { 1084 result = false 1085 } 1086 return result 1087 } 1088 var getHasLiedLanguages = function () { 1089 // We check if navigator.language is equal to the first language of navigator.languages 1090 if (typeof navigator.languages !== 'undefined') { 1091 try { 1092 var firstLanguages = navigator.languages[0].substr(0, 2) 1093 if (firstLanguages !== navigator.language.substr(0, 2)) { 1094 return true 1095 } 1096 } catch (err) { 1097 return true 1098 } 1099 } 1100 return false 1101 } 1102 var getHasLiedResolution = function () { 1103 return window.screen.width < window.screen.availWidth || window.screen.height < window.screen.availHeight 1104 } 1105 var getHasLiedOs = function () { 1106 var userAgent = navigator.userAgent.toLowerCase() 1107 var oscpu = navigator.oscpu 1108 var platform = navigator.platform.toLowerCase() 1109 var os 1110 // We extract the OS from the user agent (respect the order of the if else if statement) 1111 if (userAgent.indexOf('windows phone') >= 0) { 1112 os = 'Windows Phone' 1113 } else if (userAgent.indexOf('win') >= 0) { 1114 os = 'Windows' 1115 } else if (userAgent.indexOf('android') >= 0) { 1116 os = 'Android' 1117 } else if (userAgent.indexOf('linux') >= 0) { 1118 os = 'Linux' 1119 } else if (userAgent.indexOf('iphone') >= 0 || userAgent.indexOf('ipad') >= 0) { 1120 os = 'iOS' 1121 } else if (userAgent.indexOf('mac') >= 0) { 1122 os = 'Mac' 1123 } else { 1124 os = 'Other' 1125 } 1126 // We detect if the person uses a mobile device 1127 var mobileDevice = (('ontouchstart' in window) || 1128 (navigator.maxTouchPoints > 0) || 1129 (navigator.msMaxTouchPoints > 0)) 1130 1131 if (mobileDevice && os !== 'Windows Phone' && os !== 'Android' && os !== 'iOS' && os !== 'Other') { 1132 return true 1133 } 1134 1135 // We compare oscpu with the OS extracted from the UA 1136 if (typeof oscpu !== 'undefined') { 1137 oscpu = oscpu.toLowerCase() 1138 if (oscpu.indexOf('win') >= 0 && os !== 'Windows' && os !== 'Windows Phone') { 1139 return true 1140 } else if (oscpu.indexOf('linux') >= 0 && os !== 'Linux' && os !== 'Android') { 1141 return true 1142 } else if (oscpu.indexOf('mac') >= 0 && os !== 'Mac' && os !== 'iOS') { 1143 return true 1144 } else if ((oscpu.indexOf('win') === -1 && oscpu.indexOf('linux') === -1 && oscpu.indexOf('mac') === -1) !== (os === 'Other')) { 1145 return true 1146 } 1147 } 1148 1149 // We compare platform with the OS extracted from the UA 1150 if (platform.indexOf('win') >= 0 && os !== 'Windows' && os !== 'Windows Phone') { 1151 return true 1152 } else if ((platform.indexOf('linux') >= 0 || platform.indexOf('android') >= 0 || platform.indexOf('pike') >= 0) && os !== 'Linux' && os !== 'Android') { 1153 return true 1154 } else if ((platform.indexOf('mac') >= 0 || platform.indexOf('ipad') >= 0 || platform.indexOf('ipod') >= 0 || platform.indexOf('iphone') >= 0) && os !== 'Mac' && os !== 'iOS') { 1155 return true 1156 } else if ((platform.indexOf('win') === -1 && platform.indexOf('linux') === -1 && platform.indexOf('mac') === -1) !== (os === 'Other')) { 1157 return true 1158 } 1159 1160 return typeof navigator.plugins === 'undefined' && os !== 'Windows' && os !== 'Windows Phone' 1161 } 1162 var getHasLiedBrowser = function () { 1163 var userAgent = navigator.userAgent.toLowerCase() 1164 var productSub = navigator.productSub 1165 1166 // we extract the browser from the user agent (respect the order of the tests) 1167 var browser 1168 if (userAgent.indexOf('firefox') >= 0) { 1169 browser = 'Firefox' 1170 } else if (userAgent.indexOf('opera') >= 0 || userAgent.indexOf('opr') >= 0) { 1171 browser = 'Opera' 1172 } else if (userAgent.indexOf('chrome') >= 0) { 1173 browser = 'Chrome' 1174 } else if (userAgent.indexOf('safari') >= 0) { 1175 browser = 'Safari' 1176 } else if (userAgent.indexOf('trident') >= 0) { 1177 browser = 'Internet Explorer' 1178 } else { 1179 browser = 'Other' 1180 } 1181 1182 if ((browser === 'Chrome' || browser === 'Safari' || browser === 'Opera') && productSub !== '20030107') { 1183 return true 1184 } 1185 1186 // eslint-disable-next-line no-eval 1187 var tempRes = eval.toString().length 1188 if (tempRes === 37 && browser !== 'Safari' && browser !== 'Firefox' && browser !== 'Other') { 1189 return true 1190 } else if (tempRes === 39 && browser !== 'Internet Explorer' && browser !== 'Other') { 1191 return true 1192 } else if (tempRes === 33 && browser !== 'Chrome' && browser !== 'Opera' && browser !== 'Other') { 1193 return true 1194 } 1195 1196 // We create an error to see how it is handled 1197 var errFirefox 1198 try { 1199 // eslint-disable-next-line no-throw-literal 1200 throw 'a' 1201 } catch (err) { 1202 try { 1203 err.toSource() 1204 errFirefox = true 1205 } catch (errOfErr) { 1206 errFirefox = false 1207 } 1208 } 1209 return errFirefox && browser !== 'Firefox' && browser !== 'Other' 1210 } 1211 var isCanvasSupported = function () { 1212 var elem = document.createElement('canvas') 1213 return !!(elem.getContext && elem.getContext('2d')) 1214 } 1215 var isWebGlSupported = function () { 1216 // code taken from Modernizr 1217 if (!isCanvasSupported()) { 1218 return false 1219 } 1220 1221 var glContext = getWebglCanvas() 1222 return !!window.WebGLRenderingContext && !!glContext 1223 } 1224 var isIE = function () { 1225 if (navigator.appName === 'Microsoft Internet Explorer') { 1226 return true 1227 } else if (navigator.appName === 'Netscape' && /Trident/.test(navigator.userAgent)) { // IE 11 1228 return true 1229 } 1230 return false 1231 } 1232 var hasSwfObjectLoaded = function () { 1233 return typeof window.swfobject !== 'undefined' 1234 } 1235 var hasMinFlashInstalled = function () { 1236 return window.swfobject.hasFlashPlayerVersion('9.0.0') 1237 } 1238 var addFlashDivNode = function (options) { 1239 var node = document.createElement('div') 1240 node.setAttribute('id', options.fonts.swfContainerId) 1241 document.body.appendChild(node) 1242 } 1243 var loadSwfAndDetectFonts = function (done, options) { 1244 var hiddenCallback = '___fp_swf_loaded' 1245 window[hiddenCallback] = function (fonts) { 1246 done(fonts) 1247 } 1248 var id = options.fonts.swfContainerId 1249 addFlashDivNode() 1250 var flashvars = { onReady: hiddenCallback } 1251 var flashparams = { allowScriptAccess: 'always', menu: 'false' } 1252 window.swfobject.embedSWF(options.fonts.swfPath, id, '1', '1', '9.0.0', false, flashvars, flashparams, {}) 1253 } 1254 var getWebglCanvas = function () { 1255 var canvas = document.createElement('canvas') 1256 var gl = null 1257 try { 1258 gl = canvas.getContext('webgl') || canvas.getContext('experimental-webgl') 1259 } catch (e) { /* squelch */ } 1260 if (!gl) { gl = null } 1261 return gl 1262 } 1263 1264 var components = [ 1265 {key: 'userAgent', getData: UserAgent}, 1266 {key: 'language', getData: languageKey}, 1267 {key: 'colorDepth', getData: colorDepthKey}, 1268 {key: 'deviceMemory', getData: deviceMemoryKey}, 1269 {key: 'pixelRatio', getData: pixelRatioKey}, 1270 {key: 'hardwareConcurrency', getData: hardwareConcurrencyKey}, 1271 {key: 'screenResolution', getData: screenResolutionKey}, 1272 {key: 'availableScreenResolution', getData: availableScreenResolutionKey}, 1273 {key: 'timezoneOffset', getData: timezoneOffset}, 1274 {key: 'timezone', getData: timezone}, 1275 {key: 'sessionStorage', getData: sessionStorageKey}, 1276 {key: 'localStorage', getData: localStorageKey}, 1277 {key: 'indexedDb', getData: indexedDbKey}, 1278 {key: 'addBehavior', getData: addBehaviorKey}, 1279 {key: 'openDatabase', getData: openDatabaseKey}, 1280 {key: 'cpuClass', getData: cpuClassKey}, 1281 {key: 'platform', getData: platformKey}, 1282 {key: 'doNotTrack', getData: doNotTrackKey}, 1283 {key: 'plugins', getData: pluginsComponent}, 1284 {key: 'canvas', getData: canvasKey}, 1285 {key: 'webgl', getData: webglKey}, 1286 {key: 'webglVendorAndRenderer', getData: webglVendorAndRendererKey}, 1287 {key: 'adBlock', getData: adBlockKey}, 1288 {key: 'hasLiedLanguages', getData: hasLiedLanguagesKey}, 1289 {key: 'hasLiedResolution', getData: hasLiedResolutionKey}, 1290 {key: 'hasLiedOs', getData: hasLiedOsKey}, 1291 {key: 'hasLiedBrowser', getData: hasLiedBrowserKey}, 1292 {key: 'touchSupport', getData: touchSupportKey}, 1293 {key: 'fonts', getData: jsFontsKey, pauseBefore: true}, 1294 {key: 'fontsFlash', getData: flashFontsKey, pauseBefore: true}, 1295 {key: 'audio', getData: audioKey}, 1296 {key: 'enumerateDevices', getData: enumerateDevicesKey} 1297 ] 1298 1299 var Fingerprint2 = function (options) { 1300 throw new Error("'new Fingerprint()' is deprecated, see https://github.com/Valve/fingerprintjs2#upgrade-guide-from-182-to-200") 1301 } 1302 1303 Fingerprint2.get = function (options, callback) { 1304 if (!callback) { 1305 callback = options 1306 options = {} 1307 } else if (!options) { 1308 options = {} 1309 } 1310 extendSoft(options, defaultOptions) 1311 options.components = options.extraComponents.concat(components) 1312 1313 var keys = { 1314 data: [], 1315 addPreprocessedComponent: function (key, value) { 1316 if (typeof options.preprocessor === 'function') { 1317 value = options.preprocessor(key, value) 1318 } 1319 keys.data.push({key: key, value: value}) 1320 } 1321 } 1322 1323 var i = -1 1324 var chainComponents = function (alreadyWaited) { 1325 i += 1 1326 if (i >= options.components.length) { // on finish 1327 callback(keys.data) 1328 return 1329 } 1330 var component = options.components[i] 1331 1332 if (options.excludes[component.key]) { 1333 chainComponents(false) // skip 1334 return 1335 } 1336 1337 if (!alreadyWaited && component.pauseBefore) { 1338 i -= 1 1339 setTimeout(function () { 1340 chainComponents(true) 1341 }, 1) 1342 return 1343 } 1344 1345 try { 1346 component.getData(function (value) { 1347 keys.addPreprocessedComponent(component.key, value) 1348 chainComponents(false) 1349 }, options) 1350 } catch (error) { 1351 // main body error 1352 keys.addPreprocessedComponent(component.key, String(error)) 1353 chainComponents(false) 1354 } 1355 } 1356 1357 chainComponents(false) 1358 } 1359 1360 Fingerprint2.getPromise = function (options) { 1361 return new Promise(function (resolve, reject) { 1362 Fingerprint2.get(options, resolve) 1363 }) 1364 } 1365 1366 Fingerprint2.getV18 = function (options, callback) { 1367 if (callback == null) { 1368 callback = options 1369 options = {} 1370 } 1371 return Fingerprint2.get(options, function (components) { 1372 var newComponents = [] 1373 for (var i = 0; i < components.length; i++) { 1374 var component = components[i] 1375 if (component.value === (options.NOT_AVAILABLE || 'not available')) { 1376 newComponents.push({key: component.key, value: 'unknown'}) 1377 } else if (component.key === 'plugins') { 1378 newComponents.push({key: 'plugins', 1379 value: map(component.value, function (p) { 1380 var mimeTypes = map(p[2], function (mt) { 1381 if (mt.join) { return mt.join('~') } 1382 return mt 1383 }).join(',') 1384 return [p[0], p[1], mimeTypes].join('::') 1385 })}) 1386 } else if (['canvas', 'webgl'].indexOf(component.key) !== -1) { 1387 newComponents.push({key: component.key, value: component.value.join('~')}) 1388 } else if (['sessionStorage', 'localStorage', 'indexedDb', 'addBehavior', 'openDatabase'].indexOf(component.key) !== -1) { 1389 if (component.value) { 1390 newComponents.push({key: component.key, value: 1}) 1391 } else { 1392 // skip 1393 continue 1394 } 1395 } else { 1396 if (component.value) { 1397 newComponents.push(component.value.join ? {key: component.key, value: component.value.join(';')} : component) 1398 } else { 1399 newComponents.push({key: component.key, value: component.value}) 1400 } 1401 } 1402 } 1403 var murmur = x64hash128(map(newComponents, function (component) { return component.value }).join('~~~'), 31) 1404 callback(murmur, newComponents) 1405 }) 1406 } 1407 1408 Fingerprint2.x64hash128 = x64hash128 1409 Fingerprint2.VERSION = '2.0.0' 1410 return Fingerprint2 1411 })
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.