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