PageSourceSearch

https://slottywaycasino.net.pl/wp-content/themes/slottyway/Slottyw…D%D0%B0%D0%B7%D0%B2%D0%B0%D0%BD%D0%B8%D1%8F

js slottywaycasino.net.pl collected 2026-10-09 22:39:38 UTC 58,348 bytes, 1,425 lines download raw bytes

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

Line numbers count LF bytes from the start of the resource, as the search results do. Vendor segments are library code the classifier recognised; they are stored but not indexed. Bytes are shown as Latin1 characters, one per byte.