Path: blob/master/modules/browser/fingerprint_browser/fingerprint2.js
1154 views
/*1* Fingerprintjs2 2.0.6 - Modern & flexible browser fingerprint library v22* https://github.com/Valve/fingerprintjs23* Copyright (c) 2015 Valentin Vasilyev ([email protected])4* Licensed under the MIT (http://www.opensource.org/licenses/mit-license.php) license.5*6* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"7* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE8* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE9* ARE DISCLAIMED. IN NO EVENT SHALL VALENTIN VASILYEV BE LIABLE FOR ANY10* DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES11* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;12* LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND13* ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT14* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF15* THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.16*/17/* global define */18(function (name, context, definition) {19'use strict'20if (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() }21})('Fingerprint2', this, function () {22'use strict'2324/// MurmurHash3 related functions2526//27// Given two 64bit ints (as an array of two 32bit ints) returns the two28// added together as a 64bit int (as an array of two 32bit ints).29//30var x64Add = function (m, n) {31m = [m[0] >>> 16, m[0] & 0xffff, m[1] >>> 16, m[1] & 0xffff]32n = [n[0] >>> 16, n[0] & 0xffff, n[1] >>> 16, n[1] & 0xffff]33var o = [0, 0, 0, 0]34o[3] += m[3] + n[3]35o[2] += o[3] >>> 1636o[3] &= 0xffff37o[2] += m[2] + n[2]38o[1] += o[2] >>> 1639o[2] &= 0xffff40o[1] += m[1] + n[1]41o[0] += o[1] >>> 1642o[1] &= 0xffff43o[0] += m[0] + n[0]44o[0] &= 0xffff45return [(o[0] << 16) | o[1], (o[2] << 16) | o[3]]46}4748//49// Given two 64bit ints (as an array of two 32bit ints) returns the two50// multiplied together as a 64bit int (as an array of two 32bit ints).51//52var x64Multiply = function (m, n) {53m = [m[0] >>> 16, m[0] & 0xffff, m[1] >>> 16, m[1] & 0xffff]54n = [n[0] >>> 16, n[0] & 0xffff, n[1] >>> 16, n[1] & 0xffff]55var o = [0, 0, 0, 0]56o[3] += m[3] * n[3]57o[2] += o[3] >>> 1658o[3] &= 0xffff59o[2] += m[2] * n[3]60o[1] += o[2] >>> 1661o[2] &= 0xffff62o[2] += m[3] * n[2]63o[1] += o[2] >>> 1664o[2] &= 0xffff65o[1] += m[1] * n[3]66o[0] += o[1] >>> 1667o[1] &= 0xffff68o[1] += m[2] * n[2]69o[0] += o[1] >>> 1670o[1] &= 0xffff71o[1] += m[3] * n[1]72o[0] += o[1] >>> 1673o[1] &= 0xffff74o[0] += (m[0] * n[3]) + (m[1] * n[2]) + (m[2] * n[1]) + (m[3] * n[0])75o[0] &= 0xffff76return [(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 int80// representing a number of bit positions, returns the 64bit int (as an81// array of two 32bit ints) rotated left by that number of positions.82//83var x64Rotl = function (m, n) {84n %= 6485if (n === 32) {86return [m[1], m[0]]87} else if (n < 32) {88return [(m[0] << n) | (m[1] >>> (32 - n)), (m[1] << n) | (m[0] >>> (32 - n))]89} else {90n -= 3291return [(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 int96// representing a number of bit positions, returns the 64bit int (as an97// array of two 32bit ints) shifted left by that number of positions.98//99var x64LeftShift = function (m, n) {100n %= 64101if (n === 0) {102return m103} else if (n < 32) {104return [(m[0] << n) | (m[1] >>> (32 - n)), m[1] << n]105} else {106return [m[1] << (n - 32), 0]107}108}109//110// Given two 64bit ints (as an array of two 32bit ints) returns the two111// xored together as a 64bit int (as an array of two 32bit ints).112//113var x64Xor = function (m, n) {114return [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 the119// only place where we need to right shift 64bit ints.)120//121var x64Fmix = function (h) {122h = x64Xor(h, [0, h[0] >>> 1])123h = x64Multiply(h, [0xff51afd7, 0xed558ccd])124h = x64Xor(h, [0, h[0] >>> 1])125h = x64Multiply(h, [0xc4ceb9fe, 0x1a85ec53])126h = x64Xor(h, [0, h[0] >>> 1])127return h128}129130//131// Given a string and an optional seed as an int, returns a 128 bit132// hash using the x64 flavor of MurmurHash3, as an unsigned hex.133//134var x64hash128 = function (key, seed) {135key = key || ''136seed = seed || 0137var remainder = key.length % 16138var bytes = key.length - remainder139var h1 = [0, seed]140var h2 = [0, seed]141var k1 = [0, 0]142var k2 = [0, 0]143var c1 = [0x87c37b91, 0x114253d5]144var c2 = [0x4cf5ad43, 0x2745937f]145for (var i = 0; i < bytes; i = i + 16) {146k1 = [((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)]147k2 = [((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)]148k1 = x64Multiply(k1, c1)149k1 = x64Rotl(k1, 31)150k1 = x64Multiply(k1, c2)151h1 = x64Xor(h1, k1)152h1 = x64Rotl(h1, 27)153h1 = x64Add(h1, h2)154h1 = x64Add(x64Multiply(h1, [0, 5]), [0, 0x52dce729])155k2 = x64Multiply(k2, c2)156k2 = x64Rotl(k2, 33)157k2 = x64Multiply(k2, c1)158h2 = x64Xor(h2, k2)159h2 = x64Rotl(h2, 31)160h2 = x64Add(h2, h1)161h2 = x64Add(x64Multiply(h2, [0, 5]), [0, 0x38495ab5])162}163k1 = [0, 0]164k2 = [0, 0]165switch (remainder) {166case 15:167k2 = x64Xor(k2, x64LeftShift([0, key.charCodeAt(i + 14)], 48))168// fallthrough169case 14:170k2 = x64Xor(k2, x64LeftShift([0, key.charCodeAt(i + 13)], 40))171// fallthrough172case 13:173k2 = x64Xor(k2, x64LeftShift([0, key.charCodeAt(i + 12)], 32))174// fallthrough175case 12:176k2 = x64Xor(k2, x64LeftShift([0, key.charCodeAt(i + 11)], 24))177// fallthrough178case 11:179k2 = x64Xor(k2, x64LeftShift([0, key.charCodeAt(i + 10)], 16))180// fallthrough181case 10:182k2 = x64Xor(k2, x64LeftShift([0, key.charCodeAt(i + 9)], 8))183// fallthrough184case 9:185k2 = x64Xor(k2, [0, key.charCodeAt(i + 8)])186k2 = x64Multiply(k2, c2)187k2 = x64Rotl(k2, 33)188k2 = x64Multiply(k2, c1)189h2 = x64Xor(h2, k2)190// fallthrough191case 8:192k1 = x64Xor(k1, x64LeftShift([0, key.charCodeAt(i + 7)], 56))193// fallthrough194case 7:195k1 = x64Xor(k1, x64LeftShift([0, key.charCodeAt(i + 6)], 48))196// fallthrough197case 6:198k1 = x64Xor(k1, x64LeftShift([0, key.charCodeAt(i + 5)], 40))199// fallthrough200case 5:201k1 = x64Xor(k1, x64LeftShift([0, key.charCodeAt(i + 4)], 32))202// fallthrough203case 4:204k1 = x64Xor(k1, x64LeftShift([0, key.charCodeAt(i + 3)], 24))205// fallthrough206case 3:207k1 = x64Xor(k1, x64LeftShift([0, key.charCodeAt(i + 2)], 16))208// fallthrough209case 2:210k1 = x64Xor(k1, x64LeftShift([0, key.charCodeAt(i + 1)], 8))211// fallthrough212case 1:213k1 = x64Xor(k1, [0, key.charCodeAt(i)])214k1 = x64Multiply(k1, c1)215k1 = x64Rotl(k1, 31)216k1 = x64Multiply(k1, c2)217h1 = x64Xor(h1, k1)218// fallthrough219}220h1 = x64Xor(h1, [0, key.length])221h2 = x64Xor(h2, [0, key.length])222h1 = x64Add(h1, h2)223h2 = x64Add(h2, h1)224h1 = x64Fmix(h1)225h2 = x64Fmix(h2)226h1 = x64Add(h1, h2)227h2 = x64Add(h2, h1)228return ('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}230231var defaultOptions = {232preprocessor: null,233audio: {234timeout: 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#46534088238excludeIOS11: true239},240fonts: {241swfContainerId: 'fingerprintjs2',242swfPath: 'flash/compiled/FontList.swf',243userDefinedFonts: [],244extendedJsFonts: false245},246screen: {247// To ensure consistent fingerprints when users rotate their mobile devices248detectScreenOrientation: true249},250plugins: {251sortPluginsFor: [/palemoon/i],252excludeIE: false253},254extraComponents: [],255excludes: {256// Unreliable on Windows, see https://github.com/Valve/fingerprintjs2/issues/375257'enumerateDevices': true,258// devicePixelRatio depends on browser zoom, and it's impossible to detect browser zoom259'pixelRatio': true,260// DNT depends on incognito mode for some browsers (Chrome) and it's impossible to detect incognito mode261'doNotTrack': true,262// uses js fonts already263'fontsFlash': true264},265NOT_AVAILABLE: 'not available',266ERROR: 'error',267EXCLUDED: 'excluded'268}269270var each = function (obj, iterator) {271if (Array.prototype.forEach && obj.forEach === Array.prototype.forEach) {272obj.forEach(iterator)273} else if (obj.length === +obj.length) {274for (var i = 0, l = obj.length; i < l; i++) {275iterator(obj[i], i, obj)276}277} else {278for (var key in obj) {279if (obj.hasOwnProperty(key)) {280iterator(obj[key], key, obj)281}282}283}284}285286var map = function (obj, iterator) {287var results = []288// Not using strict equality so that this acts as a289// shortcut to checking for `null` and `undefined`.290if (obj == null) {291return results292}293if (Array.prototype.map && obj.map === Array.prototype.map) { return obj.map(iterator) }294each(obj, function (value, index, list) {295results.push(iterator(value, index, list))296})297return results298}299300var extendSoft = function (target, source) {301if (source == null) { return target }302var value303var key304for (key in source) {305value = source[key]306if (value != null && !(Object.prototype.hasOwnProperty.call(target, key))) {307target[key] = value308}309}310return target311}312313// https://developer.mozilla.org/en-US/docs/Web/API/MediaDevices/enumerateDevices314var enumerateDevicesKey = function (done, options) {315if (!isEnumerateDevicesSupported()) {316return done(options.NOT_AVAILABLE)317}318navigator.mediaDevices.enumerateDevices().then(function (devices) {319done(devices.map(function (device) {320return 'id=' + device.deviceId + ';gid=' + device.groupId + ';' + device.kind + ';' + device.label321}))322})323.catch(function (error) {324done(error)325})326}327328var isEnumerateDevicesSupported = function () {329return (navigator.mediaDevices && navigator.mediaDevices.enumerateDevices)330}331// Inspired by and based on https://github.com/cozylife/audio-fingerprint332var audioKey = function (done, options) {333var audioOptions = options.audio334if (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#46534088336return done(options.EXCLUDED)337}338339var AudioContext = window.OfflineAudioContext || window.webkitOfflineAudioContext340341if (AudioContext == null) {342return done(options.NOT_AVAILABLE)343}344345var context = new AudioContext(1, 44100, 44100)346347var oscillator = context.createOscillator()348oscillator.type = 'triangle'349oscillator.frequency.setValueAtTime(10000, context.currentTime)350351var compressor = context.createDynamicsCompressor()352each([353['threshold', -50],354['knee', 40],355['ratio', 12],356['reduction', -20],357['attack', 0],358['release', 0.25]359], function (item) {360if (compressor[item[0]] !== undefined && typeof compressor[item[0]].setValueAtTime === 'function') {361compressor[item[0]].setValueAtTime(item[1], context.currentTime)362}363})364365oscillator.connect(compressor)366compressor.connect(context.destination)367oscillator.start(0)368context.startRendering()369370var audioTimeoutId = setTimeout(function () {371console.warn('Audio fingerprint timed out. Please report bug at https://github.com/Valve/fingerprintjs2 with your user agent: "' + navigator.userAgent + '".')372context.oncomplete = function () {}373context = null374return done('audioTimeout')375}, audioOptions.timeout)376377context.oncomplete = function (event) {378var fingerprint379try {380clearTimeout(audioTimeoutId)381fingerprint = event.renderedBuffer.getChannelData(0)382.slice(4500, 5000)383.reduce(function (acc, val) { return acc + Math.abs(val) }, 0)384.toString()385oscillator.disconnect()386compressor.disconnect()387} catch (error) {388done(error)389return390}391done(fingerprint)392}393}394var UserAgent = function (done) {395done(navigator.userAgent)396}397var languageKey = function (done, options) {398done(navigator.language || navigator.userLanguage || navigator.browserLanguage || navigator.systemLanguage || options.NOT_AVAILABLE)399}400var colorDepthKey = function (done, options) {401done(window.screen.colorDepth || options.NOT_AVAILABLE)402}403var deviceMemoryKey = function (done, options) {404done(navigator.deviceMemory || options.NOT_AVAILABLE)405}406var pixelRatioKey = function (done, options) {407done(window.devicePixelRatio || options.NOT_AVAILABLE)408}409var screenResolutionKey = function (done, options) {410done(getScreenResolution(options))411}412var getScreenResolution = function (options) {413var resolution = [window.screen.width, window.screen.height]414if (options.screen.detectScreenOrientation) {415resolution.sort().reverse()416}417return resolution418}419var availableScreenResolutionKey = function (done, options) {420done(getAvailableScreenResolution(options))421}422var getAvailableScreenResolution = function (options) {423if (window.screen.availWidth && window.screen.availHeight) {424var available = [window.screen.availHeight, window.screen.availWidth]425if (options.screen.detectScreenOrientation) {426available.sort().reverse()427}428return available429}430// headless browsers431return options.NOT_AVAILABLE432}433var timezoneOffset = function (done) {434done(new Date().getTimezoneOffset())435}436var timezone = function (done, options) {437if (window.Intl && window.Intl.DateTimeFormat) {438done(new window.Intl.DateTimeFormat().resolvedOptions().timeZone)439return440}441done(options.NOT_AVAILABLE)442}443var sessionStorageKey = function (done, options) {444done(hasSessionStorage(options))445}446var localStorageKey = function (done, options) {447done(hasLocalStorage(options))448}449var indexedDbKey = function (done, options) {450done(hasIndexedDB(options))451}452var addBehaviorKey = function (done) {453// body might not be defined at this point or removed programmatically454done(!!(document.body && document.body.addBehavior))455}456var openDatabaseKey = function (done) {457done(!!window.openDatabase)458}459var cpuClassKey = function (done, options) {460done(getNavigatorCpuClass(options))461}462var platformKey = function (done, options) {463done(getNavigatorPlatform(options))464}465var doNotTrackKey = function (done, options) {466done(getDoNotTrack(options))467}468var canvasKey = function (done, options) {469if (isCanvasSupported()) {470done(getCanvasFp(options))471return472}473done(options.NOT_AVAILABLE)474}475var webglKey = function (done, options) {476if (isWebGlSupported()) {477done(getWebglFp())478return479}480done(options.NOT_AVAILABLE)481}482var webglVendorAndRendererKey = function (done) {483if (isWebGlSupported()) {484done(getWebglVendorAndRenderer())485return486}487done()488}489var adBlockKey = function (done) {490done(getAdBlock())491}492var hasLiedLanguagesKey = function (done) {493done(getHasLiedLanguages())494}495var hasLiedResolutionKey = function (done) {496done(getHasLiedResolution())497}498var hasLiedOsKey = function (done) {499done(getHasLiedOs())500}501var hasLiedBrowserKey = function (done) {502done(getHasLiedBrowser())503}504// flash fonts (will increase fingerprinting time 20X to ~ 130-150ms)505var flashFontsKey = function (done, options) {506// we do flash if swfobject is loaded507if (!hasSwfObjectLoaded()) {508return done('swf object not loaded')509}510if (!hasMinFlashInstalled()) {511return done('flash not installed')512}513if (!options.fonts.swfPath) {514return done('missing options.fonts.swfPath')515}516loadSwfAndDetectFonts(function (fonts) {517done(fonts)518}, options)519}520// kudos to http://www.lalit.org/lab/javascript-css-font-detect/521var 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.524var baseFonts = ['monospace', 'sans-serif', 'serif']525526var 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]540541if (options.fonts.extendedJsFonts) {542var 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 Condensed', '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']574fontList = fontList.concat(extendedFontList)575}576577fontList = fontList.concat(options.fonts.userDefinedFonts)578579// remove duplicate fonts580fontList = fontList.filter(function (font, position) {581return fontList.indexOf(font) === position582})583584// 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 separated586var testString = 'mmmmmmmmmmlli'587588// we test using 72px font size, we may use any size. I guess larger the better.589var testSize = '72px'590591var h = document.getElementsByTagName('body')[0]592593// div to load spans for the base fonts594var baseFontsDiv = document.createElement('div')595596// div to load spans for the fonts to detect597var fontsDiv = document.createElement('div')598599var defaultWidth = {}600var defaultHeight = {}601602// creates a span where the fonts will be loaded603var createSpan = function () {604var s = document.createElement('span')605/*606* We need this css as in some weird browser this607* span elements shows up for a microSec which creates a608* bad user experience609*/610s.style.position = 'absolute'611s.style.left = '-9999px'612s.style.fontSize = testSize613614// css font reset to reset external styles615s.style.fontStyle = 'normal'616s.style.fontWeight = 'normal'617s.style.letterSpacing = 'normal'618s.style.lineBreak = 'auto'619s.style.lineHeight = 'normal'620s.style.textTransform = 'none'621s.style.textAlign = 'left'622s.style.textDecoration = 'none'623s.style.textShadow = 'none'624s.style.whiteSpace = 'normal'625s.style.wordBreak = 'normal'626s.style.wordSpacing = 'normal'627628s.innerHTML = testString629return s630}631632// creates a span and load the font to detect and a base font for fallback633var createSpanWithFonts = function (fontToDetect, baseFont) {634var s = createSpan()635s.style.fontFamily = "'" + fontToDetect + "'," + baseFont636return s637}638639// creates spans for the base fonts and adds them to baseFontsDiv640var initializeBaseFontsSpans = function () {641var spans = []642for (var index = 0, length = baseFonts.length; index < length; index++) {643var s = createSpan()644s.style.fontFamily = baseFonts[index]645baseFontsDiv.appendChild(s)646spans.push(s)647}648return spans649}650651// creates spans for the fonts to detect and adds them to fontsDiv652var initializeFontsSpans = function () {653var spans = {}654for (var i = 0, l = fontList.length; i < l; i++) {655var fontSpans = []656for (var j = 0, numDefaultFonts = baseFonts.length; j < numDefaultFonts; j++) {657var s = createSpanWithFonts(fontList[i], baseFonts[j])658fontsDiv.appendChild(s)659fontSpans.push(s)660}661spans[fontList[i]] = fontSpans // Stores {fontName : [spans for that font]}662}663return spans664}665666// checks if a font is available667var isFontAvailable = function (fontSpans) {668var detected = false669for (var i = 0; i < baseFonts.length; i++) {670detected = (fontSpans[i].offsetWidth !== defaultWidth[baseFonts[i]] || fontSpans[i].offsetHeight !== defaultHeight[baseFonts[i]])671if (detected) {672return detected673}674}675return detected676}677678// create spans for base fonts679var baseFontsSpans = initializeBaseFontsSpans()680681// add the spans to the DOM682h.appendChild(baseFontsDiv)683684// get the default width for the three base fonts685for (var index = 0, length = baseFonts.length; index < length; index++) {686defaultWidth[baseFonts[index]] = baseFontsSpans[index].offsetWidth // width for the default font687defaultHeight[baseFonts[index]] = baseFontsSpans[index].offsetHeight // height for the default font688}689690// create spans for fonts to detect691var fontsSpans = initializeFontsSpans()692693// add all the spans to the DOM694h.appendChild(fontsDiv)695696// check available fonts697var available = []698for (var i = 0, l = fontList.length; i < l; i++) {699if (isFontAvailable(fontsSpans[fontList[i]])) {700available.push(fontList[i])701}702}703704// remove spans from DOM705h.removeChild(fontsDiv)706h.removeChild(baseFontsDiv)707done(available)708}709var pluginsComponent = function (done, options) {710if (isIE()) {711if (!options.plugins.excludeIE) {712done(getIEPlugins(options))713} else {714done(options.EXCLUDED)715}716} else {717done(getRegularPlugins(options))718}719}720var getRegularPlugins = function (options) {721if (navigator.plugins == null) {722return options.NOT_AVAILABLE723}724725var plugins = []726// plugins isn't defined in Node envs.727for (var i = 0, l = navigator.plugins.length; i < l; i++) {728if (navigator.plugins[i]) { plugins.push(navigator.plugins[i]) }729}730731// sorting plugins only for those user agents, that we know randomize the plugins732// every time we try to enumerate them733if (pluginsShouldBeSorted(options)) {734plugins = plugins.sort(function (a, b) {735if (a.name > b.name) { return 1 }736if (a.name < b.name) { return -1 }737return 0738})739}740return map(plugins, function (p) {741var mimeTypes = map(p, function (mt) {742return [mt.type, mt.suffixes]743})744return [p.name, p.description, mimeTypes]745})746}747var getIEPlugins = function (options) {748var result = []749if ((Object.getOwnPropertyDescriptor && Object.getOwnPropertyDescriptor(window, 'ActiveXObject')) || ('ActiveXObject' in window)) {750var names = [751'AcroPDF.PDF', // Adobe PDF reader 7+752'Adodb.Stream',753'AgControl.AgControl', // Silverlight754'DevalVRXCtrl.DevalVRXCtrl.1',755'MacromediaFlashPaper.MacromediaFlashPaper',756'Msxml2.DOMDocument',757'Msxml2.XMLHTTP',758'PDF.PdfCtrl', // Adobe PDF reader 6 and earlier, brrr759'QuickTime.QuickTime', // QuickTime760'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 player766'Shell.UIHelper',767'ShockwaveFlash.ShockwaveFlash', // flash plugin768'Skype.Detection',769'TDCCtl.TDCCtl',770'WMPlayer.OCX', // Windows media player771'rmocx.RealPlayer G2 Control',772'rmocx.RealPlayer G2 Control.1'773]774// starting to detect plugins in IE775result = map(names, function (name) {776try {777// eslint-disable-next-line no-new778new window.ActiveXObject(name)779return name780} catch (e) {781return options.ERROR782}783})784} else {785result.push(options.NOT_AVAILABLE)786}787if (navigator.plugins) {788result = result.concat(getRegularPlugins(options))789}790return result791}792var pluginsShouldBeSorted = function (options) {793var should = false794for (var i = 0, l = options.plugins.sortPluginsFor.length; i < l; i++) {795var re = options.plugins.sortPluginsFor[i]796if (navigator.userAgent.match(re)) {797should = true798break799}800}801return should802}803var touchSupportKey = function (done) {804done(getTouchSupport())805}806var hardwareConcurrencyKey = function (done, options) {807done(getHardwareConcurrency(options))808}809var hasSessionStorage = function (options) {810try {811return !!window.sessionStorage812} catch (e) {813return options.ERROR // SecurityError when referencing it means it exists814}815}816817// https://bugzilla.mozilla.org/show_bug.cgi?id=781447818var hasLocalStorage = function (options) {819try {820return !!window.localStorage821} catch (e) {822return options.ERROR // SecurityError when referencing it means it exists823}824}825var hasIndexedDB = function (options) {826try {827return !!window.indexedDB828} catch (e) {829return options.ERROR // SecurityError when referencing it means it exists830}831}832var getHardwareConcurrency = function (options) {833if (navigator.hardwareConcurrency) {834return navigator.hardwareConcurrency835}836return options.NOT_AVAILABLE837}838var getNavigatorCpuClass = function (options) {839return navigator.cpuClass || options.NOT_AVAILABLE840}841var getNavigatorPlatform = function (options) {842if (navigator.platform) {843return navigator.platform844} else {845return options.NOT_AVAILABLE846}847}848var getDoNotTrack = function (options) {849if (navigator.doNotTrack) {850return navigator.doNotTrack851} else if (navigator.msDoNotTrack) {852return navigator.msDoNotTrack853} else if (window.doNotTrack) {854return window.doNotTrack855} else {856return options.NOT_AVAILABLE857}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 screen861// 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/548864// method returns an array of 3 values:865// maxTouchPoints, the success or failure of creating a TouchEvent,866// and the availability of the 'ontouchstart' property867868var getTouchSupport = function () {869var maxTouchPoints = 0870var touchEvent871if (typeof navigator.maxTouchPoints !== 'undefined') {872maxTouchPoints = navigator.maxTouchPoints873} else if (typeof navigator.msMaxTouchPoints !== 'undefined') {874maxTouchPoints = navigator.msMaxTouchPoints875}876try {877document.createEvent('TouchEvent')878touchEvent = true879} catch (_) {880touchEvent = false881}882var touchStart = 'ontouchstart' in window883return [maxTouchPoints, touchEvent, touchStart]884}885// https://www.browserleaks.com/canvas#how-does-it-work886887var getCanvasFp = function (options) {888var result = []889// Very simple now, need to make it more complex (geo shapes etc)890var canvas = document.createElement('canvas')891canvas.width = 2000892canvas.height = 200893canvas.style.display = 'inline'894var ctx = canvas.getContext('2d')895// detect browser support of canvas winding896// http://blogs.adobe.com/webplatform/2013/01/30/winding-rules-in-canvas/897// https://github.com/Modernizr/Modernizr/blob/master/feature-detects/canvas/winding.js898ctx.rect(0, 0, 10, 10)899ctx.rect(2, 2, 6, 6)900result.push('canvas winding:' + ((ctx.isPointInPath(5, 5, 'evenodd') === false) ? 'yes' : 'no'))901902ctx.textBaseline = 'alphabetic'903ctx.fillStyle = '#f60'904ctx.fillRect(125, 1, 62, 20)905ctx.fillStyle = '#069'906// https://github.com/Valve/fingerprintjs2/issues/66907if (options.dontUseFakeFontInCanvas) {908ctx.font = '11pt Arial'909} else {910ctx.font = '11pt no-real-font-123'911}912ctx.fillText('Cwm fjordbank glyphs vext quiz, \ud83d\ude03', 2, 15)913ctx.fillStyle = 'rgba(102, 204, 0, 0.2)'914ctx.font = '18pt Arial'915ctx.fillText('Cwm fjordbank glyphs vext quiz, \ud83d\ude03', 4, 45)916917// canvas blending918// http://blogs.adobe.com/webplatform/2013/01/28/blending-features-in-canvas/919// http://jsfiddle.net/NDYV8/16/920ctx.globalCompositeOperation = 'multiply'921ctx.fillStyle = 'rgb(255,0,255)'922ctx.beginPath()923ctx.arc(50, 50, 50, 0, Math.PI * 2, true)924ctx.closePath()925ctx.fill()926ctx.fillStyle = 'rgb(0,255,255)'927ctx.beginPath()928ctx.arc(100, 50, 50, 0, Math.PI * 2, true)929ctx.closePath()930ctx.fill()931ctx.fillStyle = 'rgb(255,255,0)'932ctx.beginPath()933ctx.arc(75, 100, 50, 0, Math.PI * 2, true)934ctx.closePath()935ctx.fill()936ctx.fillStyle = 'rgb(255,0,255)'937// canvas winding938// http://blogs.adobe.com/webplatform/2013/01/30/winding-rules-in-canvas/939// http://jsfiddle.net/NDYV8/19/940ctx.arc(75, 75, 75, 0, Math.PI * 2, true)941ctx.arc(75, 75, 25, 0, Math.PI * 2, true)942ctx.fill('evenodd')943944if (canvas.toDataURL) { result.push('canvas fp:' + canvas.toDataURL()) }945return result946}947var getWebglFp = function () {948var gl949var fa2s = function (fa) {950gl.clearColor(0.0, 0.0, 0.0, 1.0)951gl.enable(gl.DEPTH_TEST)952gl.depthFunc(gl.LEQUAL)953gl.clear(gl.COLOR_BUFFER_BIT | gl.DEPTH_BUFFER_BIT)954return '[' + fa[0] + ', ' + fa[1] + ']'955}956var maxAnisotropy = function (gl) {957var ext = gl.getExtension('EXT_texture_filter_anisotropic') || gl.getExtension('WEBKIT_EXT_texture_filter_anisotropic') || gl.getExtension('MOZ_EXT_texture_filter_anisotropic')958if (ext) {959var anisotropy = gl.getParameter(ext.MAX_TEXTURE_MAX_ANISOTROPY_EXT)960if (anisotropy === 0) {961anisotropy = 2962}963return anisotropy964} else {965return null966}967}968969gl = getWebglCanvas()970if (!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 device974// 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.975var result = []976var vShaderTemplate = 'attribute vec2 attrVertex;varying vec2 varyinTexCoordinate;uniform vec2 uniformOffset;void main(){varyinTexCoordinate=attrVertex+uniformOffset;gl_Position=vec4(attrVertex,0,1);}'977var fShaderTemplate = 'precision mediump float;varying vec2 varyinTexCoordinate;void main() {gl_FragColor=vec4(varyinTexCoordinate,0,1);}'978var vertexPosBuffer = gl.createBuffer()979gl.bindBuffer(gl.ARRAY_BUFFER, vertexPosBuffer)980var vertices = new Float32Array([-0.2, -0.9, 0, 0.4, -0.26, 0, 0, 0.732134444, 0])981gl.bufferData(gl.ARRAY_BUFFER, vertices, gl.STATIC_DRAW)982vertexPosBuffer.itemSize = 3983vertexPosBuffer.numItems = 3984var program = gl.createProgram()985var vshader = gl.createShader(gl.VERTEX_SHADER)986gl.shaderSource(vshader, vShaderTemplate)987gl.compileShader(vshader)988var fshader = gl.createShader(gl.FRAGMENT_SHADER)989gl.shaderSource(fshader, fShaderTemplate)990gl.compileShader(fshader)991gl.attachShader(program, vshader)992gl.attachShader(program, fshader)993gl.linkProgram(program)994gl.useProgram(program)995program.vertexPosAttrib = gl.getAttribLocation(program, 'attrVertex')996program.offsetUniform = gl.getUniformLocation(program, 'uniformOffset')997gl.enableVertexAttribArray(program.vertexPosArray)998gl.vertexAttribPointer(program.vertexPosAttrib, vertexPosBuffer.itemSize, gl.FLOAT, !1, 0, 0)999gl.uniform2f(program.offsetUniform, 1, 1)1000gl.drawArrays(gl.TRIANGLE_STRIP, 0, vertexPosBuffer.numItems)1001try {1002result.push(gl.canvas.toDataURL())1003} catch (e) {1004/* .toDataURL may be absent or broken (blocked by extension) */1005}1006result.push('extensions:' + (gl.getSupportedExtensions() || []).join(';'))1007result.push('webgl aliased line width range:' + fa2s(gl.getParameter(gl.ALIASED_LINE_WIDTH_RANGE)))1008result.push('webgl aliased point size range:' + fa2s(gl.getParameter(gl.ALIASED_POINT_SIZE_RANGE)))1009result.push('webgl alpha bits:' + gl.getParameter(gl.ALPHA_BITS))1010result.push('webgl antialiasing:' + (gl.getContextAttributes().antialias ? 'yes' : 'no'))1011result.push('webgl blue bits:' + gl.getParameter(gl.BLUE_BITS))1012result.push('webgl depth bits:' + gl.getParameter(gl.DEPTH_BITS))1013result.push('webgl green bits:' + gl.getParameter(gl.GREEN_BITS))1014result.push('webgl max anisotropy:' + maxAnisotropy(gl))1015result.push('webgl max combined texture image units:' + gl.getParameter(gl.MAX_COMBINED_TEXTURE_IMAGE_UNITS))1016result.push('webgl max cube map texture size:' + gl.getParameter(gl.MAX_CUBE_MAP_TEXTURE_SIZE))1017result.push('webgl max fragment uniform vectors:' + gl.getParameter(gl.MAX_FRAGMENT_UNIFORM_VECTORS))1018result.push('webgl max render buffer size:' + gl.getParameter(gl.MAX_RENDERBUFFER_SIZE))1019result.push('webgl max texture image units:' + gl.getParameter(gl.MAX_TEXTURE_IMAGE_UNITS))1020result.push('webgl max texture size:' + gl.getParameter(gl.MAX_TEXTURE_SIZE))1021result.push('webgl max varying vectors:' + gl.getParameter(gl.MAX_VARYING_VECTORS))1022result.push('webgl max vertex attribs:' + gl.getParameter(gl.MAX_VERTEX_ATTRIBS))1023result.push('webgl max vertex texture image units:' + gl.getParameter(gl.MAX_VERTEX_TEXTURE_IMAGE_UNITS))1024result.push('webgl max vertex uniform vectors:' + gl.getParameter(gl.MAX_VERTEX_UNIFORM_VECTORS))1025result.push('webgl max viewport dims:' + fa2s(gl.getParameter(gl.MAX_VIEWPORT_DIMS)))1026result.push('webgl red bits:' + gl.getParameter(gl.RED_BITS))1027result.push('webgl renderer:' + gl.getParameter(gl.RENDERER))1028result.push('webgl shading language version:' + gl.getParameter(gl.SHADING_LANGUAGE_VERSION))1029result.push('webgl stencil bits:' + gl.getParameter(gl.STENCIL_BITS))1030result.push('webgl vendor:' + gl.getParameter(gl.VENDOR))1031result.push('webgl version:' + gl.getParameter(gl.VERSION))10321033try {1034// Add the unmasked vendor and unmasked renderer if the debug_renderer_info extension is available1035var extensionDebugRendererInfo = gl.getExtension('WEBGL_debug_renderer_info')1036if (extensionDebugRendererInfo) {1037result.push('webgl unmasked vendor:' + gl.getParameter(extensionDebugRendererInfo.UNMASKED_VENDOR_WEBGL))1038result.push('webgl unmasked renderer:' + gl.getParameter(extensionDebugRendererInfo.UNMASKED_RENDERER_WEBGL))1039}1040} catch (e) { /* squelch */ }10411042if (!gl.getShaderPrecisionFormat) {1043return result1044}10451046each(['FLOAT', 'INT'], function (numType) {1047each(['VERTEX', 'FRAGMENT'], function (shader) {1048each(['HIGH', 'MEDIUM', 'LOW'], function (numSize) {1049each(['precision', 'rangeMin', 'rangeMax'], function (key) {1050var format = gl.getShaderPrecisionFormat(gl[shader + '_SHADER'], gl[numSize + '_' + numType])[key]1051if (key !== 'precision') {1052key = 'precision ' + key1053}1054var line = ['webgl ', shader.toLowerCase(), ' shader ', numSize.toLowerCase(), ' ', numType.toLowerCase(), ' ', key, ':', format].join('')1055result.push(line)1056})1057})1058})1059})1060return result1061}1062var getWebglVendorAndRenderer = function () {1063/* This a subset of the WebGL fingerprint with a lot of entropy, while being reasonably browser-independent */1064try {1065var glContext = getWebglCanvas()1066var extensionDebugRendererInfo = glContext.getExtension('WEBGL_debug_renderer_info')1067return glContext.getParameter(extensionDebugRendererInfo.UNMASKED_VENDOR_WEBGL) + '~' + glContext.getParameter(extensionDebugRendererInfo.UNMASKED_RENDERER_WEBGL)1068} catch (e) {1069return null1070}1071}1072var getAdBlock = function () {1073var ads = document.createElement('div')1074ads.innerHTML = ' '1075ads.className = 'adsbox'1076var result = false1077try {1078// body may not exist, that's why we need try/catch1079document.body.appendChild(ads)1080result = document.getElementsByClassName('adsbox')[0].offsetHeight === 01081document.body.removeChild(ads)1082} catch (e) {1083result = false1084}1085return result1086}1087var getHasLiedLanguages = function () {1088// We check if navigator.language is equal to the first language of navigator.languages1089if (typeof navigator.languages !== 'undefined') {1090try {1091var firstLanguages = navigator.languages[0].substr(0, 2)1092if (firstLanguages !== navigator.language.substr(0, 2)) {1093return true1094}1095} catch (err) {1096return true1097}1098}1099return false1100}1101var getHasLiedResolution = function () {1102return window.screen.width < window.screen.availWidth || window.screen.height < window.screen.availHeight1103}1104var getHasLiedOs = function () {1105var userAgent = navigator.userAgent.toLowerCase()1106var oscpu = navigator.oscpu1107var platform = navigator.platform.toLowerCase()1108var os1109// We extract the OS from the user agent (respect the order of the if else if statement)1110if (userAgent.indexOf('windows phone') >= 0) {1111os = 'Windows Phone'1112} else if (userAgent.indexOf('win') >= 0) {1113os = 'Windows'1114} else if (userAgent.indexOf('android') >= 0) {1115os = 'Android'1116} else if (userAgent.indexOf('linux') >= 0) {1117os = 'Linux'1118} else if (userAgent.indexOf('iphone') >= 0 || userAgent.indexOf('ipad') >= 0) {1119os = 'iOS'1120} else if (userAgent.indexOf('mac') >= 0) {1121os = 'Mac'1122} else {1123os = 'Other'1124}1125// We detect if the person uses a mobile device1126var mobileDevice = (('ontouchstart' in window) ||1127(navigator.maxTouchPoints > 0) ||1128(navigator.msMaxTouchPoints > 0))11291130if (mobileDevice && os !== 'Windows Phone' && os !== 'Android' && os !== 'iOS' && os !== 'Other') {1131return true1132}11331134// We compare oscpu with the OS extracted from the UA1135if (typeof oscpu !== 'undefined') {1136oscpu = oscpu.toLowerCase()1137if (oscpu.indexOf('win') >= 0 && os !== 'Windows' && os !== 'Windows Phone') {1138return true1139} else if (oscpu.indexOf('linux') >= 0 && os !== 'Linux' && os !== 'Android') {1140return true1141} else if (oscpu.indexOf('mac') >= 0 && os !== 'Mac' && os !== 'iOS') {1142return true1143} else if ((oscpu.indexOf('win') === -1 && oscpu.indexOf('linux') === -1 && oscpu.indexOf('mac') === -1) !== (os === 'Other')) {1144return true1145}1146}11471148// We compare platform with the OS extracted from the UA1149if (platform.indexOf('win') >= 0 && os !== 'Windows' && os !== 'Windows Phone') {1150return true1151} else if ((platform.indexOf('linux') >= 0 || platform.indexOf('android') >= 0 || platform.indexOf('pike') >= 0) && os !== 'Linux' && os !== 'Android') {1152return true1153} else if ((platform.indexOf('mac') >= 0 || platform.indexOf('ipad') >= 0 || platform.indexOf('ipod') >= 0 || platform.indexOf('iphone') >= 0) && os !== 'Mac' && os !== 'iOS') {1154return true1155} else if ((platform.indexOf('win') === -1 && platform.indexOf('linux') === -1 && platform.indexOf('mac') === -1) !== (os === 'Other')) {1156return true1157}11581159return typeof navigator.plugins === 'undefined' && os !== 'Windows' && os !== 'Windows Phone'1160}1161var getHasLiedBrowser = function () {1162var userAgent = navigator.userAgent.toLowerCase()1163var productSub = navigator.productSub11641165// we extract the browser from the user agent (respect the order of the tests)1166var browser1167if (userAgent.indexOf('firefox') >= 0) {1168browser = 'Firefox'1169} else if (userAgent.indexOf('opera') >= 0 || userAgent.indexOf('opr') >= 0) {1170browser = 'Opera'1171} else if (userAgent.indexOf('chrome') >= 0) {1172browser = 'Chrome'1173} else if (userAgent.indexOf('safari') >= 0) {1174browser = 'Safari'1175} else if (userAgent.indexOf('trident') >= 0) {1176browser = 'Internet Explorer'1177} else {1178browser = 'Other'1179}11801181if ((browser === 'Chrome' || browser === 'Safari' || browser === 'Opera') && productSub !== '20030107') {1182return true1183}11841185// eslint-disable-next-line no-eval1186var tempRes = eval.toString().length1187if (tempRes === 37 && browser !== 'Safari' && browser !== 'Firefox' && browser !== 'Other') {1188return true1189} else if (tempRes === 39 && browser !== 'Internet Explorer' && browser !== 'Other') {1190return true1191} else if (tempRes === 33 && browser !== 'Chrome' && browser !== 'Opera' && browser !== 'Other') {1192return true1193}11941195// We create an error to see how it is handled1196var errFirefox1197try {1198// eslint-disable-next-line no-throw-literal1199throw 'a'1200} catch (err) {1201try {1202err.toSource()1203errFirefox = true1204} catch (errOfErr) {1205errFirefox = false1206}1207}1208return errFirefox && browser !== 'Firefox' && browser !== 'Other'1209}1210var isCanvasSupported = function () {1211var elem = document.createElement('canvas')1212return !!(elem.getContext && elem.getContext('2d'))1213}1214var isWebGlSupported = function () {1215// code taken from Modernizr1216if (!isCanvasSupported()) {1217return false1218}12191220var glContext = getWebglCanvas()1221return !!window.WebGLRenderingContext && !!glContext1222}1223var isIE = function () {1224if (navigator.appName === 'Microsoft Internet Explorer') {1225return true1226} else if (navigator.appName === 'Netscape' && /Trident/.test(navigator.userAgent)) { // IE 111227return true1228}1229return false1230}1231var hasSwfObjectLoaded = function () {1232return typeof window.swfobject !== 'undefined'1233}1234var hasMinFlashInstalled = function () {1235return window.swfobject.hasFlashPlayerVersion('9.0.0')1236}1237var addFlashDivNode = function (options) {1238var node = document.createElement('div')1239node.setAttribute('id', options.fonts.swfContainerId)1240document.body.appendChild(node)1241}1242var loadSwfAndDetectFonts = function (done, options) {1243var hiddenCallback = '___fp_swf_loaded'1244window[hiddenCallback] = function (fonts) {1245done(fonts)1246}1247var id = options.fonts.swfContainerId1248addFlashDivNode()1249var flashvars = { onReady: hiddenCallback }1250var flashparams = { allowScriptAccess: 'always', menu: 'false' }1251window.swfobject.embedSWF(options.fonts.swfPath, id, '1', '1', '9.0.0', false, flashvars, flashparams, {})1252}1253var getWebglCanvas = function () {1254var canvas = document.createElement('canvas')1255var gl = null1256try {1257gl = canvas.getContext('webgl') || canvas.getContext('experimental-webgl')1258} catch (e) { /* squelch */ }1259if (!gl) { gl = null }1260return gl1261}12621263var 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]12971298var Fingerprint2 = function (options) {1299throw new Error("'new Fingerprint()' is deprecated, see https://github.com/Valve/fingerprintjs2#upgrade-guide-from-182-to-200")1300}13011302Fingerprint2.get = function (options, callback) {1303if (!callback) {1304callback = options1305options = {}1306} else if (!options) {1307options = {}1308}1309extendSoft(options, defaultOptions)1310options.components = options.extraComponents.concat(components)13111312var keys = {1313data: [],1314addPreprocessedComponent: function (key, value) {1315if (typeof options.preprocessor === 'function') {1316value = options.preprocessor(key, value)1317}1318keys.data.push({key: key, value: value})1319}1320}13211322var i = -11323var chainComponents = function (alreadyWaited) {1324i += 11325if (i >= options.components.length) { // on finish1326callback(keys.data)1327return1328}1329var component = options.components[i]13301331if (options.excludes[component.key]) {1332chainComponents(false) // skip1333return1334}13351336if (!alreadyWaited && component.pauseBefore) {1337i -= 11338setTimeout(function () {1339chainComponents(true)1340}, 1)1341return1342}13431344try {1345component.getData(function (value) {1346keys.addPreprocessedComponent(component.key, value)1347chainComponents(false)1348}, options)1349} catch (error) {1350// main body error1351keys.addPreprocessedComponent(component.key, String(error))1352chainComponents(false)1353}1354}13551356chainComponents(false)1357}13581359Fingerprint2.getPromise = function (options) {1360return new Promise(function (resolve, reject) {1361Fingerprint2.get(options, resolve)1362})1363}13641365Fingerprint2.getV18 = function (options, callback) {1366if (callback == null) {1367callback = options1368options = {}1369}1370return Fingerprint2.get(options, function (components) {1371var newComponents = []1372for (var i = 0; i < components.length; i++) {1373var component = components[i]1374if (component.value === (options.NOT_AVAILABLE || 'not available')) {1375newComponents.push({key: component.key, value: 'unknown'})1376} else if (component.key === 'plugins') {1377newComponents.push({key: 'plugins',1378value: map(component.value, function (p) {1379var mimeTypes = map(p[2], function (mt) {1380if (mt.join) { return mt.join('~') }1381return mt1382}).join(',')1383return [p[0], p[1], mimeTypes].join('::')1384})})1385} else if (['canvas', 'webgl'].indexOf(component.key) !== -1) {1386newComponents.push({key: component.key, value: component.value.join('~')})1387} else if (['sessionStorage', 'localStorage', 'indexedDb', 'addBehavior', 'openDatabase'].indexOf(component.key) !== -1) {1388if (component.value) {1389newComponents.push({key: component.key, value: 1})1390} else {1391// skip1392continue1393}1394} else {1395if (component.value) {1396newComponents.push(component.value.join ? {key: component.key, value: component.value.join(';')} : component)1397} else {1398newComponents.push({key: component.key, value: component.value})1399}1400}1401}1402var murmur = x64hash128(map(newComponents, function (component) { return component.value }).join('~~~'), 31)1403callback(murmur, newComponents)1404})1405}14061407Fingerprint2.x64hash128 = x64hash1281408Fingerprint2.VERSION = '2.0.6'1409return Fingerprint21410})141114121413