PageSourceSearch

https://www.k105.com/wp-content/plugins/weblator_polling/public/as…bine_fingerprint_signature.js?ver=1.3.5.6_5

js k105.com collected 2026-10-02 06:55:41 UTC 62,116 bytes, 1,450 lines download raw bytes

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

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.