iotcat 4 years ago
parent 7d5dc00168
commit 14abe20968
  1. 4
      .gitignore
  2. 104
      README.md
  3. 4
      compile.sh
  4. 26
      demo.html
  5. 21
      dist/LICENSE
  6. 440
      dist/fp.js
  7. 3
      dist/fp.min.js
  8. 2
      dist/fp.min.js.map
  9. 1466
      fingerprint2.js
  10. 440
      fp.js
  11. 1
      fp.min.js
  12. 1596
      fp_old.js
  13. 1
      hao
  14. 9
      package.json

4
.gitignore vendored

@ -1,2 +1,4 @@
config.php config.php
/tmp /tmp
auth.html
test.html

@ -2,108 +2,64 @@
[![FOSSA Status](https://app.fossa.com/api/projects/git%2Bgithub.com%2FIoTcat%2Ffp.svg?type=shield)](https://app.fossa.com/projects/git%2Bgithub.com%2FIoTcat%2Ffp?ref=badge_shield) [![FOSSA Status](https://app.fossa.com/api/projects/git%2Bgithub.com%2FIoTcat%2Ffp.svg?type=shield)](https://app.fossa.com/projects/git%2Bgithub.com%2FIoTcat%2Ffp?ref=badge_shield)
Let fp work with Cookie, cross-domain, make it stable and reliable! The simplified usage of [fingerprintjs/fingerprintjs2](https://github.com/fingerprintjs/fingerprintjs2).
## What is fp? ## What is fp?
fp is a concise web front-end solution to generate an unique 'fingerprint' for each visitor basing on visitor's device and browser. fp is a concise web front-end solution to generate an unique 'fingerprint' for each visitor basing on visitor's device and browser. fp is the simplified usage of [fingerprintjs/fingerprintjs2](https://github.com/fingerprintjs/fingerprintjs2). Without complex configuration, fp can provide an optimized fingerprint for user-agents in form of 6 letters hash in a short time.
## Quick start ## Quick start
[Click here to see how it works!](https://fp.yimian.xyz/demo.html) [Click here to see how it works!](./demo.html)
## How to use fp? ## How to use fp?
To use fp, you must include the fp.js or fp.min.js first. To use fp, you must include the fp.js or fp.min.js first.
A simple example: A simple example:
```html ```html
<script type="text/javascript" src="./fp.min.js"></script> <script type="text/javascript" src="https://cdn.jsdelivr.net/npm/fp3/dist/fp.min.js"></script>
<script type="text/javascript"> <script type="text/javascript">
fp(function(myFp){ ;(async function(){
alert(myFp); alert(await fp);
}); })()
</script> </script>
``` ```
## Advanced Usage This will generate a six-letter hash like this (For difficult client this hash is unique):
```bash
### Get fp df3kd0
```html
<script type="text/javascript" src="./fp.min.js"></script>
<script type="text/javascript">
fp(function(myFp, key, acc, detail, createdTime, timeUsed, detailObj){
console.log('My fp: ' + myFp);
console.log('fp key: ' + key);
console.log('Accuracy: ' + acc);
console.log('fp Details: ' + detail);
console.log('fp Created Time: ' + createdTime);
console.log('Time Usage to calculate: ' + timeUsed);
console.log(detailObj);
});
</script>
``` ```
The first two letters `df` indicates the fingerprint of the device, such as the PC, phone etc. Ideally, these two letter will not vary while you switch the browser on the same device.
### Recover fp from key (cross-domain purpose) The middle two letters `3k` and the last two letters `d0` are the fingerprint of the browser. The different is that, the last one could vary with the change of timezone, plugin etc., while the middle one is more stable and will not vary under these stuation. However, the middle part is more likely to be "not unique" when you have a great amount of clients.
```html
<script type="text/javascript" src="./fp.min.js"></script>
<script type="text/javascript">
fp(/*fp key*/'eyJfZnAiOiI1YjI4Y2U5ZCIsIl9mcF9yZWZfIjoiZVZiSmJVVjlWNGFzS0JZOE10THRZQmJVTkVkSkk5WUpJZElnYlJJUVpWY0lhZGJKZVFZNWI5TkVMRmJKWlZJZ09KZTlkMEwxWkZVZExOVUZJVWJOWkpJNVpvWkZhOVpVY0ZjSklCY0pZeEl4Y0pNQmJKYlJkeFlCYkJZQlk0YlViQlpoSUJaRllGWUZiY0xaY1JZQllRVHRRTlpsUmxkSlFkSUZiOVlZUUJRNVZGUUZRaFRWV0ZlTmRJWVZRVlpKYjVUQVprUmhOUmRFTkJNSmJvWmhaNVpKWmhaTmFvY0phTWN3VEpaSlpSVTlJWkxOWk1jeGJGY0JiRmExTEpUeFk1TE5MSlFsSVZPUU5ZTyIsIl9mcF9MYXN0Q2hhbmdlVGltZSI6IjE1NjE1MTkxNzYifQ==',
function(myFp, key, acc, detail, createdTime, timeUsed, detailObj){
console.log('My fp: ' + myFp);
console.log('fp key: ' + key);
console.log('Accuracy: ' + acc);
console.log('fp Details: ' + detail);
console.log('fp Created Time: ' + createdTime);
console.log('Time Usage to calculate: ' + timeUsed);
console.log(detailObj);
});
</script>
```
### Reset fp In practice, you can use `substr()` to decompose fp into each part.
```html ```js
<script type="text/javascript" src="./fp.min.js"></script> ;(async function(){
<script type="text/javascript"> var fp_device = (await fp).substr(0, 2); //df in df3kd0
fp('reset', function(myFp, key, acc, detail, createdTime, timeUsed, detailObj){ var fp_browser = (await fp).substr(2, 2); //3k in df3kd0
console.log('My fp: ' + myFp); var fp_unique = (await fp).substr(4); //d0 in df3kd0
console.log('fp key: ' + key); })()
console.log('Accuracy: ' + acc); ````
console.log('fp Details: ' + detail);
console.log('fp Created Time: ' + createdTime);
console.log('Time Usage to calculate: ' + timeUsed);
console.log(detailObj);
});
</script>
```
### Cookie
fp preloads [iotcat/cookie-js](https://github.com/iotcat/cookie-js), support all of its functions.
**usage**
`cookie.set(key, val, days)`: set a cookie, with key name, value, days to live(defaule: 10 years)
`cookie.get(key)`: get cookie with key
`cookie.del(key)`: delete cookie with key
**example** ## Advanced Usage
```js
//set a cookie named iotcat,its value is hero,live for 30 days
cookie.set("iotcat", "hero", 30);
//get the value of the cookie iotcat
alert(cookie.get("iotcat"));
//delete the cookie of iotcat ### Get fp details
cookie.del("iotcat"); ```html
<script type="text/javascript" src="https://cdn.jsdelivr.net/npm/fp3/dist/fp.min.js"></script>
<script type="text/javascript">
;(async function(){
console.log(await fp_details);
})()
</script>
``` ```
## CDN ## CDN
- jsdelivr: `https://cdn.jsdelivr.net/npm/fp3/dist/fp.min.js`
- China: `https://cdn.yimian.xyz/fp/fp.min.js` - China: `https://cdn.yimian.xyz/fp/fp.min.js`
## Background
This project is developed from https://github.com/Valve/fingerprintjs2
## License ## License
[![FOSSA Status](https://app.fossa.io/api/projects/git%2Bgithub.com%2FIoTcat%2Ffp.svg?type=large)](https://app.fossa.io/projects/git%2Bgithub.com%2FIoTcat%2Ffp?ref=badge_large) [![FOSSA Status](https://app.fossa.io/api/projects/git%2Bgithub.com%2FIoTcat%2Ffp.svg?type=large)](https://app.fossa.io/projects/git%2Bgithub.com%2FIoTcat%2Ffp?ref=badge_large)

@ -0,0 +1,4 @@
npm i -g uglify-js
cp fp.js dist/
uglifyjs dist/fp.js -m -o dist/fp.min.js --source-map url='dist/fp.min.js.map'

@ -54,28 +54,30 @@
<p> <button type="button" id="btn">Your fp: <strong id="fp"></strong></button></p> <p> <button type="button" id="btn">Your fp: <strong id="fp"></strong></button></p>
<p>Time Used: <var id="time"></var> ms &nbsp;Accuracy: <var id="acc"></var> %</p> <p>Time Used: <var id="time"></var> ms &nbsp;
<p>Created Time: <var id="cTime"></var></p>
<p><strong>Detailed information: </strong></p> <p><strong>Detailed information: </strong></p>
<pre id="details"></pre> <pre id="details"></pre>
<script src="fp.min.js"></script> <script src="./fp.js"></script>
<script> <script>
document.querySelector("#btn").addEventListener("click", function () { document.querySelector("#btn").addEventListener("click", function () {
location.reload(); location.reload();
}) })
fp((f, key, acc, detail, CreateTime, TimeUsed, detailObj)=>{ ;(async function(){
var ts = new Date().valueOf();
document.querySelector("#fp").textContent = await fp;
document.querySelector("#time").textContent = new Date().valueOf() - ts;
var details = await fp_details;
var s = '';
details.forEach(function(obj){
s += obj.key + ': '+obj.value + '\n';
});
document.querySelector("#details").textContent = s;
})()
document.querySelector("#acc").textContent = String(acc * 100).substr(0, 6);
document.querySelector("#time").textContent = TimeUsed
document.querySelector("#fp").textContent = f
document.querySelector("#details").textContent = detail;
document.querySelector("#cTime").textContent = CreateTime;
console.log('fp_key: ' + key);
console.log(detailObj);
})
</script> </script>

21
dist/LICENSE vendored

@ -1,21 +0,0 @@
MIT License
Copyright (c) 2018 IoTgod
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.

440
dist/fp.js vendored

@ -1,49 +1,55 @@
/* /*
* @package: fp
* @version: v3.0.1
* @Author: IoTcat (https://iotcat.me) * @Author: IoTcat (https://iotcat.me)
* @Date: 2019-06-26 11:34:32 * @Date: 2020-08-15 11:34:32
* @Last Modified by: * @Last Modified by: iotcat
* @Last Modified time: 2019-06-26 11:38:03 * @Last Modified time: 2020-08-15 11:34:32
*/ */
var cookie = { /*
set: function (name, value, Days) { * Fingerprintjs2 2.1.2 - Modern & flexible browser fingerprint library v2
if(Days == undefined) var Days = 3000; * https://github.com/Valve/fingerprintjs2
var exp = new Date(); * Copyright (c) 2020 Valentin Vasilyev (valentin@fingerprintjs.com)
exp.setTime(exp.getTime() + Days * 24 * 60 * 60 * 1000); * Licensed under the MIT (http://www.opensource.org/licenses/mit-license.php) license.
document.cookie = name + '=' + escape(value) + ';expires=' + exp.toGMTString() + ";path=/"; *
}, * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
get: function (name) { * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
var arr, reg = new RegExp('(^| )' + name + '=([^;]*)(;|$)'); * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
if (arr = document.cookie.match(reg)) { * ARE DISCLAIMED. IN NO EVENT SHALL VALENTIN VASILYEV BE LIABLE FOR ANY
return unescape(arr[2]); * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
} else { * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
return null; * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
} * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
}, * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
del: function (name) { * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
var exp = new Date(); */
exp.setTime(exp.getTime() - 1); /*
var arr, reg = new RegExp('(^| )' + name + '=([^;]*)(;|$)'); * This software contains code from open-source projects:
if (arr = document.cookie.match(reg)) { * MurmurHash3 by Karan Lyons (https://github.com/karanlyons/murmurHash3.js)
var cval = unescape(arr[2]); */
} else {
var cval = null;
}
if (cval != null) {
document.cookie = name + '=' + cval + ';expires=' + exp.toGMTString();
}
}
};
/* global define */ /* global define */
(function (name, context, definition) { (function (name, context, definition) {
'use strict' 'use strict'
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() } 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() }
})('fp', this, function () { })('Fingerprint2', this, function () {
'use strict' 'use strict'
var MaxDiff = 0.8; // detect if object is array
// only implement if no native implementation is available
if (typeof Array.isArray === 'undefined') {
Array.isArray = function (obj) {
return Object.prototype.toString.call(obj) === '[object Array]'
}
};
/// MurmurHash3 related functions
//
// Given two 64bit ints (as an array of two 32bit ints) returns the two
// added together as a 64bit int (as an array of two 32bit ints).
//
var x64Add = function (m, n) { var x64Add = function (m, n) {
m = [m[0] >>> 16, m[0] & 0xffff, m[1] >>> 16, m[1] & 0xffff] m = [m[0] >>> 16, m[0] & 0xffff, m[1] >>> 16, m[1] & 0xffff]
n = [n[0] >>> 16, n[0] & 0xffff, n[1] >>> 16, n[1] & 0xffff] n = [n[0] >>> 16, n[0] & 0xffff, n[1] >>> 16, n[1] & 0xffff]
@ -62,7 +68,10 @@ var cookie = {
return [(o[0] << 16) | o[1], (o[2] << 16) | o[3]] return [(o[0] << 16) | o[1], (o[2] << 16) | o[3]]
} }
//
// Given two 64bit ints (as an array of two 32bit ints) returns the two
// multiplied together as a 64bit int (as an array of two 32bit ints).
//
var x64Multiply = function (m, n) { var x64Multiply = function (m, n) {
m = [m[0] >>> 16, m[0] & 0xffff, m[1] >>> 16, m[1] & 0xffff] m = [m[0] >>> 16, m[0] & 0xffff, m[1] >>> 16, m[1] & 0xffff]
n = [n[0] >>> 16, n[0] & 0xffff, n[1] >>> 16, n[1] & 0xffff] n = [n[0] >>> 16, n[0] & 0xffff, n[1] >>> 16, n[1] & 0xffff]
@ -89,7 +98,11 @@ var cookie = {
o[0] &= 0xffff o[0] &= 0xffff
return [(o[0] << 16) | o[1], (o[2] << 16) | o[3]] return [(o[0] << 16) | o[1], (o[2] << 16) | o[3]]
} }
//
// Given a 64bit int (as an array of two 32bit ints) and an int
// representing a number of bit positions, returns the 64bit int (as an
// array of two 32bit ints) rotated left by that number of positions.
//
var x64Rotl = function (m, n) { var x64Rotl = function (m, n) {
n %= 64 n %= 64
if (n === 32) { if (n === 32) {
@ -101,7 +114,11 @@ var cookie = {
return [(m[1] << n) | (m[0] >>> (32 - n)), (m[0] << n) | (m[1] >>> (32 - n))] return [(m[1] << n) | (m[0] >>> (32 - n)), (m[0] << n) | (m[1] >>> (32 - n))]
} }
} }
//
// Given a 64bit int (as an array of two 32bit ints) and an int
// representing a number of bit positions, returns the 64bit int (as an
// array of two 32bit ints) shifted left by that number of positions.
//
var x64LeftShift = function (m, n) { var x64LeftShift = function (m, n) {
n %= 64 n %= 64
if (n === 0) { if (n === 0) {
@ -112,11 +129,18 @@ var cookie = {
return [m[1] << (n - 32), 0] return [m[1] << (n - 32), 0]
} }
} }
//
// Given two 64bit ints (as an array of two 32bit ints) returns the two
// xored together as a 64bit int (as an array of two 32bit ints).
//
var x64Xor = function (m, n) { var x64Xor = function (m, n) {
return [m[0] ^ n[0], m[1] ^ n[1]] return [m[0] ^ n[0], m[1] ^ n[1]]
} }
//
// Given a block, returns murmurHash3's final x64 mix of that block.
// (`[0, h[0] >>> 1]` is a 33 bit unsigned right shift. This is the
// only place where we need to right shift 64bit ints.)
//
var x64Fmix = function (h) { var x64Fmix = function (h) {
h = x64Xor(h, [0, h[0] >>> 1]) h = x64Xor(h, [0, h[0] >>> 1])
h = x64Multiply(h, [0xff51afd7, 0xed558ccd]) h = x64Multiply(h, [0xff51afd7, 0xed558ccd])
@ -126,7 +150,10 @@ var cookie = {
return h return h
} }
//
// Given a string and an optional seed as an int, returns a 128 bit
// hash using the x64 flavor of MurmurHash3, as an unsigned hex.
//
var x64hash128 = function (key, seed) { var x64hash128 = function (key, seed) {
key = key || '' key = key || ''
seed = seed || 0 seed = seed || 0
@ -315,10 +342,9 @@ var cookie = {
done(devices.map(function (device) { done(devices.map(function (device) {
return 'id=' + device.deviceId + ';gid=' + device.groupId + ';' + device.kind + ';' + device.label return 'id=' + device.deviceId + ';gid=' + device.groupId + ';' + device.kind + ';' + device.label
})) }))
})['catch'](function (error) {
done(error)
}) })
.catch(function (error) {
done(error)
})
} }
var isEnumerateDevicesSupported = function () { var isEnumerateDevicesSupported = function () {
@ -364,6 +390,7 @@ var cookie = {
context.startRendering() context.startRendering()
var audioTimeoutId = setTimeout(function () { var audioTimeoutId = setTimeout(function () {
console.warn('Audio fingerprint timed out. Please report bug at https://github.com/Valve/fingerprintjs2 with your user agent: "' + navigator.userAgent + '".')
context.oncomplete = function () { } context.oncomplete = function () { }
context = null context = null
return done('audioTimeout') return done('audioTimeout')
@ -1038,6 +1065,7 @@ var cookie = {
} catch (e) { /* squelch */ } } catch (e) { /* squelch */ }
if (!gl.getShaderPrecisionFormat) { if (!gl.getShaderPrecisionFormat) {
loseWebglContext(gl)
return result return result
} }
@ -1055,6 +1083,7 @@ var cookie = {
}) })
}) })
}) })
loseWebglContext(gl)
return result return result
} }
var getWebglVendorAndRenderer = function () { var getWebglVendorAndRenderer = function () {
@ -1062,7 +1091,9 @@ var cookie = {
try { try {
var glContext = getWebglCanvas() var glContext = getWebglCanvas()
var extensionDebugRendererInfo = glContext.getExtension('WEBGL_debug_renderer_info') var extensionDebugRendererInfo = glContext.getExtension('WEBGL_debug_renderer_info')
return glContext.getParameter(extensionDebugRendererInfo.UNMASKED_VENDOR_WEBGL) + '~' + glContext.getParameter(extensionDebugRendererInfo.UNMASKED_RENDERER_WEBGL) var params = glContext.getParameter(extensionDebugRendererInfo.UNMASKED_VENDOR_WEBGL) + '~' + glContext.getParameter(extensionDebugRendererInfo.UNMASKED_RENDERER_WEBGL)
loseWebglContext(glContext)
return params
} catch (e) { } catch (e) {
return null return null
} }
@ -1108,25 +1139,25 @@ var cookie = {
// We extract the OS from the user agent (respect the order of the if else if statement) // We extract the OS from the user agent (respect the order of the if else if statement)
if (userAgent.indexOf('windows phone') >= 0) { if (userAgent.indexOf('windows phone') >= 0) {
os = 'Windows Phone' os = 'Windows Phone'
} else if (userAgent.indexOf('win') >= 0) { } else if (userAgent.indexOf('windows') >= 0 || userAgent.indexOf('win16') >= 0 || userAgent.indexOf('win32') >= 0 || userAgent.indexOf('win64') >= 0 || userAgent.indexOf('win95') >= 0 || userAgent.indexOf('win98') >= 0 || userAgent.indexOf('winnt') >= 0 || userAgent.indexOf('wow64') >= 0) {
os = 'Windows' os = 'Windows'
} else if (userAgent.indexOf('android') >= 0) { } else if (userAgent.indexOf('android') >= 0) {
os = 'Android' os = 'Android'
} else if (userAgent.indexOf('linux') >= 0 || userAgent.indexOf('cros') >= 0) { } else if (userAgent.indexOf('linux') >= 0 || userAgent.indexOf('cros') >= 0 || userAgent.indexOf('x11') >= 0) {
os = 'Linux' os = 'Linux'
} else if (userAgent.indexOf('iphone') >= 0 || userAgent.indexOf('ipad') >= 0) { } else if (userAgent.indexOf('iphone') >= 0 || userAgent.indexOf('ipad') >= 0 || userAgent.indexOf('ipod') >= 0 || userAgent.indexOf('crios') >= 0 || userAgent.indexOf('fxios') >= 0) {
os = 'iOS' os = 'iOS'
} else if (userAgent.indexOf('mac') >= 0) { } else if (userAgent.indexOf('macintosh') >= 0 || userAgent.indexOf('mac_powerpc)') >= 0) {
os = 'Mac' os = 'Mac'
} else { } else {
os = 'Other' os = 'Other'
} }
// We detect if the person uses a mobile device // We detect if the person uses a touch device
var mobileDevice = (('ontouchstart' in window) || var mobileDevice = (('ontouchstart' in window) ||
(navigator.maxTouchPoints > 0) || (navigator.maxTouchPoints > 0) ||
(navigator.msMaxTouchPoints > 0)) (navigator.msMaxTouchPoints > 0))
if (mobileDevice && os !== 'Windows Phone' && os !== 'Android' && os !== 'iOS' && os !== 'Other') { if (mobileDevice && os !== 'Windows' && os !== 'Windows Phone' && os !== 'Android' && os !== 'iOS' && os !== 'Other' && userAgent.indexOf('cros') === -1) {
return true return true
} }
@ -1151,12 +1182,17 @@ var cookie = {
return true return true
} else if ((platform.indexOf('mac') >= 0 || platform.indexOf('ipad') >= 0 || platform.indexOf('ipod') >= 0 || platform.indexOf('iphone') >= 0) && os !== 'Mac' && os !== 'iOS') { } else if ((platform.indexOf('mac') >= 0 || platform.indexOf('ipad') >= 0 || platform.indexOf('ipod') >= 0 || platform.indexOf('iphone') >= 0) && os !== 'Mac' && os !== 'iOS') {
return true return true
} else if (platform.indexOf('arm') >= 0 && os === 'Windows Phone') {
return false
} else if (platform.indexOf('pike') >= 0 && userAgent.indexOf('opera mini') >= 0) {
return false
} else { } else {
var platformIsOther = platform.indexOf('win') < 0 && var platformIsOther = platform.indexOf('win') < 0 &&
platform.indexOf('linux') < 0 && platform.indexOf('linux') < 0 &&
platform.indexOf('mac') < 0 && platform.indexOf('mac') < 0 &&
platform.indexOf('iphone') < 0 && platform.indexOf('iphone') < 0 &&
platform.indexOf('ipad') < 0 platform.indexOf('ipad') < 0 &&
platform.indexOf('ipod') < 0
if (platformIsOther !== (os === 'Other')) { if (platformIsOther !== (os === 'Other')) {
return true return true
} }
@ -1170,15 +1206,25 @@ var cookie = {
// we extract the browser from the user agent (respect the order of the tests) // we extract the browser from the user agent (respect the order of the tests)
var browser var browser
if (userAgent.indexOf('firefox') >= 0) { if (userAgent.indexOf('edge/') >= 0 || userAgent.indexOf('iemobile/') >= 0) {
// Unreliable, different versions use EdgeHTML, Webkit, Blink, etc.
return false
} else if (userAgent.indexOf('opera mini') >= 0) {
// Unreliable, different modes use Presto, WebView, Webkit, etc.
return false
} else if (userAgent.indexOf('firefox/') >= 0) {
browser = 'Firefox' browser = 'Firefox'
} else if (userAgent.indexOf('opera') >= 0 || userAgent.indexOf('opr') >= 0) { } else if (userAgent.indexOf('opera/') >= 0 || userAgent.indexOf(' opr/') >= 0) {
browser = 'Opera' browser = 'Opera'
} else if (userAgent.indexOf('chrome') >= 0) { } else if (userAgent.indexOf('chrome/') >= 0) {
browser = 'Chrome' browser = 'Chrome'
} else if (userAgent.indexOf('safari') >= 0) { } else if (userAgent.indexOf('safari/') >= 0) {
browser = 'Safari' if (userAgent.indexOf('android 1.') >= 0 || userAgent.indexOf('android 2.') >= 0 || userAgent.indexOf('android 3.') >= 0 || userAgent.indexOf('android 4.') >= 0) {
} else if (userAgent.indexOf('trident') >= 0) { browser = 'AOSP'
} else {
browser = 'Safari'
}
} else if (userAgent.indexOf('trident/') >= 0) {
browser = 'Internet Explorer' browser = 'Internet Explorer'
} else { } else {
browser = 'Other' browser = 'Other'
@ -1194,7 +1240,7 @@ var cookie = {
return true return true
} else if (tempRes === 39 && browser !== 'Internet Explorer' && browser !== 'Other') { } else if (tempRes === 39 && browser !== 'Internet Explorer' && browser !== 'Other') {
return true return true
} else if (tempRes === 33 && browser !== 'Chrome' && browser !== 'Opera' && browser !== 'Other') { } else if (tempRes === 33 && browser !== 'Chrome' && browser !== 'AOSP' && browser !== 'Opera' && browser !== 'Other') {
return true return true
} }
@ -1224,7 +1270,9 @@ var cookie = {
} }
var glContext = getWebglCanvas() var glContext = getWebglCanvas()
return !!window.WebGLRenderingContext && !!glContext var isSupported = !!window.WebGLRenderingContext && !!glContext
loseWebglContext(glContext)
return isSupported
} }
var isIE = function () { var isIE = function () {
if (navigator.appName === 'Microsoft Internet Explorer') { if (navigator.appName === 'Microsoft Internet Explorer') {
@ -1265,6 +1313,12 @@ var cookie = {
if (!gl) { gl = null } if (!gl) { gl = null }
return gl return gl
} }
var loseWebglContext = function (context) {
var loseContextExtension = context.getExtension('WEBGL_lose_context')
if (loseContextExtension != null) {
loseContextExtension.loseContext()
}
}
var components = [ var components = [
{ key: 'userAgent', getData: UserAgent }, { key: 'userAgent', getData: UserAgent },
@ -1391,7 +1445,9 @@ var cookie = {
return [p[0], p[1], mimeTypes].join('::') return [p[0], p[1], mimeTypes].join('::')
}) })
}) })
} else if (['canvas', 'webgl'].indexOf(component.key) !== -1) { } else if (['canvas', 'webgl'].indexOf(component.key) !== -1 && Array.isArray(component.value)) {
// sometimes WebGL returns error in headless browsers (during CI testing for example)
// so we need to join only if the values are array
newComponents.push({ key: component.key, value: component.value.join('~') }) newComponents.push({ key: component.key, value: component.value.join('~') })
} else if (['sessionStorage', 'localStorage', 'indexedDb', 'addBehavior', 'openDatabase'].indexOf(component.key) !== -1) { } else if (['sessionStorage', 'localStorage', 'indexedDb', 'addBehavior', 'openDatabase'].indexOf(component.key) !== -1) {
if (component.value) { if (component.value) {
@ -1407,190 +1463,120 @@ var cookie = {
newComponents.push({ key: component.key, value: component.value }) newComponents.push({ key: component.key, value: component.value })
} }
} }
}; }
var murmur = x64hash128(map(newComponents, function (component) { return component.value }).join('~~~'), 31); var murmur = x64hash128(map(newComponents, function (component) { return component.value }).join('~~~'), 31)
callback(murmur, newComponents); callback(murmur, newComponents)
}); })
}; }
var _fp_val = null;
var _fp_detail = "";
var _fp_acc = null;
var _fp_LastChangeTime = null;
var _fp_TimeUsed = null;
var _fp_detailObj = {};
var _fp_key = null;
var _fp_ready = false;
var d1 = null;
function ini(components) {
var murmur = x64hash128(components.map(function (pair) { return pair.value }).join(), 15);
murmur = murmur.substr(0, 8);
var rate = 0;
for (var index in components) {
var obj = components[index]
var line = obj.key + " = " + String(obj.value).substr(0, 100);
_fp_detail += line + "\n";
_fp_detailObj[obj.key] = String(obj.value).substr(0, 100);
}
if (cookie.get('_fp_ref_')) rate = levenshteinenator(Compress(window.btoa(JSON.stringify(_fp_detailObj))), cookie.get('_fp_ref_'));
else {
rate = 0;
}
//rate = (rate < 0) ? 0 : rate;
_fp_acc = rate;
if (rate < MaxDiff) {
cookie.set('_fp_ref_', Compress(window.btoa(JSON.stringify(_fp_detailObj))));
}
var d2 = new Date();
var time = d2 - d1;
_fp_TimeUsed = time; Fingerprint2.x64hash128 = x64hash128
Fingerprint2.VERSION = '2.1.2'
return Fingerprint2
})
if (rate < MaxDiff) { /*
cookie.set('_fp', murmur); * @Author: IoTcat (https://iotcat.me)
cookie.set('_fp_LastChangeTime', Date.parse(d2) / 1000); * @Date: 2020-08-15 11:34:32
_fp_LastChangeTime = d2; * @Last Modified by: iotcat
_fp_val = murmur; * @Last Modified time: 2020-08-15 11:34:32
*/
//fp
;(function (name, context, definition) {
'use strict'
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() }
})('fp', this, function () {
var startTime = new Date().valueOf();
var options_low = {
excludes: {
'enumerateDevices': true,
'pixelRatio': true,
'doNotTrack': true,
'fontsFlash': true,
'fonts': true,
'language': true,
"availableScreenResolution": true,
"timezoneOffset": true,
"timezone": true,
"plugins": true,
"canvas": true,
"webgl": true,
"adBlock": true,
"audio": true
} }
else { };
_fp_LastChangeTime = new Date(cookie.get('_fp_LastChangeTime') * 1000); var options_high = {
_fp_val = cookie.get('_fp'); excludes: {
'enumerateDevices': true,
'pixelRatio': true,
'doNotTrack': true,
'fontsFlash': true,
'fonts': true,
"adBlock": true,
} }
console.log('\n' + ' %c fp v2.0.1 %c ' + _fp_val + '::' + String(_fp_acc * 100).substr(0, 4) + '%::' + _fp_TimeUsed + 'ms %c https://fp.yimian.xyz \n', 'color: #00FFFF; background: #030307; padding:5px 0;', 'color: #fadfa3; background: #030307; padding:5px 0;', 'background: #4682B4; padding:5px 0;'); };
}
function Compress(strNormalString) {
var strCompressedString = "";
for (var i = 0; i < strNormalString.length; i += Math.round(strNormalString.length / 289)) { var exec = function(f){
strCompressedString += strNormalString.charAt(i); if (window.requestIdleCallback) {
requestIdleCallback(function () {
f();
})
} else {
setTimeout(f, 500)
} }
return strCompressedString;
} }
function generateKey(){ var getLowFp = function(highFp, components, resolve, reject){
var obj = { var lowFp = Fingerprint2.x64hash128(JSON.stringify(components), 15).substring(0, 4);
_fp: cookie.get('_fp'), var fullFp = lowFp + highFp;
_fp_ref_: cookie.get('_fp_ref_'), console.log('\n' + ' %c fp v3.0.1 %c ' + fullFp + '::' + (new Date().valueOf() - startTime) + 'ms %c https://fp.yimian.xyz/ \n', 'color: #00FFFF; background: #030307; padding:5px 0;', 'color: #fadfa3; background: #030307; padding:5px 0;', 'background: #4682B4; padding:5px 0;');
_fp_LastChangeTime: cookie.get('_fp_LastChangeTime') resolve(fullFp);
};
_fp_key = window.btoa(JSON.stringify(obj));
return _fp_key;
} }
var getHighFp = function(components, resolve, reject){
var levenshteinenator = (function () { var highFp = Fingerprint2.x64hash128(JSON.stringify(components), 15).substring(0, 2);
function levenshteinenator(a, b) { components.forEach(function(obj, index){
var cost; if(options_high.excludes.hasOwnProperty(obj.key)){
var m = a.length; components.splice(index, 1);
var n = b.length;
if (m < n) {
var c = a; a = b; b = c;
var o = m; m = n; n = o;
}
var r = []; r[0] = [];
for (var c = 0; c < n + 1; ++c) {
r[0][c] = c;
} }
});
for (var i = 1; i < m + 1; ++i) { getLowFp(highFp, components, resolve, reject);
r[i] = []; r[i][0] = i;
for (var j = 1; j < n + 1; ++j) {
cost = a.charAt(i - 1) === b.charAt(j - 1) ? 0 : 1;
r[i][j] = minimator(r[i - 1][j] + 1, r[i][j - 1] + 1, r[i - 1][j - 1] + cost);
}
}
return 1 - r[m - 1][n - 1] / Math.max(m, n);
}
function minimator(x, y, z) {
if (x <= y && x <= z) return x;
if (y <= x && y <= z) return y;
return z;
}
return levenshteinenator;
}());
var key_check = function (key){
var obj;
try{
obj = JSON.parse(window.atob(key));
}catch(e){
return false;
}
if(obj._fp === undefined || obj._fp_ref_ === undefined || obj._fp_LastChangeTime === undefined){
return false;
}
return true;
}
var fp_link = function (key, f){
if(key != _fp_key && !_fp_ready && key_check(key)){
key = window.atob(key);
var obj = JSON.parse(key);
cookie.set('_fp', obj._fp);
cookie.set('_fp_ref_', obj._fp_ref_);
cookie.set('_fp_LastChangeTime', obj._fp_LastChangeTime);
fp_reset();
}
fp_get(f);
} }
var fp_reset = function (){ return new Promise(function(resolve, reject){
exec(function(){
d1 = new Date(); startTime = new Date().valueOf();
Fingerprint2.get(ini); Fingerprint2.get(options_high, function (components) {
_fp_ready = true; getHighFp(components, resolve, reject);
} })
});
});
})
var fp_get = function (f) { //fp_details
if (!_fp_val) { ;(function (name, context, definition) {
setTimeout(fp_get, 1, f); 'use strict'
return; 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() }
} })('fp_details', this, function () {
f(_fp_val, generateKey(), _fp_acc, _fp_detail, _fp_LastChangeTime, _fp_TimeUsed, _fp_detailObj); var options= {
excludes: {}
}; };
var fp = function (k, f){ var exec = function(f){
if(typeof k === 'function'){ if (window.requestIdleCallback) {
if(!_fp_ready) { requestIdleCallback(function () {
fp_reset(); f();
} })
fp_get(k); } else {
return; setTimeout(f, 500)
}
if(typeof k === 'string'){
if(f == undefined) f = function(){};
if(k == 'reset'){
cookie.del('_fp');
cookie.del('_fp_ref_');
cookie.del('_fp_LastChangeTime');
fp_reset();
fp_get(f);
return;
}else{
fp_link(k, f);
}
return;
} }
} }
return fp; return new Promise(function(resolve, reject){
}); exec(function(){
Fingerprint2.get(options, function (components) {
resolve(components);
})
});
});
})

3
dist/fp.min.js vendored

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because it is too large Load Diff

440
fp.js

@ -1,49 +1,55 @@
/* /*
* @package: fp
* @version: v3.0.1
* @Author: IoTcat (https://iotcat.me) * @Author: IoTcat (https://iotcat.me)
* @Date: 2019-06-26 11:34:32 * @Date: 2020-08-15 11:34:32
* @Last Modified by: * @Last Modified by: iotcat
* @Last Modified time: 2019-06-26 11:38:03 * @Last Modified time: 2020-08-15 11:34:32
*/ */
var cookie = { /*
set: function (name, value, Days) { * Fingerprintjs2 2.1.2 - Modern & flexible browser fingerprint library v2
if(Days == undefined) var Days = 3000; * https://github.com/Valve/fingerprintjs2
var exp = new Date(); * Copyright (c) 2020 Valentin Vasilyev (valentin@fingerprintjs.com)
exp.setTime(exp.getTime() + Days * 24 * 60 * 60 * 1000); * Licensed under the MIT (http://www.opensource.org/licenses/mit-license.php) license.
document.cookie = name + '=' + escape(value) + ';expires=' + exp.toGMTString() + ";path=/"; *
}, * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
get: function (name) { * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
var arr, reg = new RegExp('(^| )' + name + '=([^;]*)(;|$)'); * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
if (arr = document.cookie.match(reg)) { * ARE DISCLAIMED. IN NO EVENT SHALL VALENTIN VASILYEV BE LIABLE FOR ANY
return unescape(arr[2]); * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
} else { * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
return null; * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
} * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
}, * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
del: function (name) { * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
var exp = new Date(); */
exp.setTime(exp.getTime() - 1); /*
var arr, reg = new RegExp('(^| )' + name + '=([^;]*)(;|$)'); * This software contains code from open-source projects:
if (arr = document.cookie.match(reg)) { * MurmurHash3 by Karan Lyons (https://github.com/karanlyons/murmurHash3.js)
var cval = unescape(arr[2]); */
} else {
var cval = null;
}
if (cval != null) {
document.cookie = name + '=' + cval + ';expires=' + exp.toGMTString();
}
}
};
/* global define */ /* global define */
(function (name, context, definition) { (function (name, context, definition) {
'use strict' 'use strict'
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() } 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() }
})('fp', this, function () { })('Fingerprint2', this, function () {
'use strict' 'use strict'
var MaxDiff = 0.8; // detect if object is array
// only implement if no native implementation is available
if (typeof Array.isArray === 'undefined') {
Array.isArray = function (obj) {
return Object.prototype.toString.call(obj) === '[object Array]'
}
};
/// MurmurHash3 related functions
//
// Given two 64bit ints (as an array of two 32bit ints) returns the two
// added together as a 64bit int (as an array of two 32bit ints).
//
var x64Add = function (m, n) { var x64Add = function (m, n) {
m = [m[0] >>> 16, m[0] & 0xffff, m[1] >>> 16, m[1] & 0xffff] m = [m[0] >>> 16, m[0] & 0xffff, m[1] >>> 16, m[1] & 0xffff]
n = [n[0] >>> 16, n[0] & 0xffff, n[1] >>> 16, n[1] & 0xffff] n = [n[0] >>> 16, n[0] & 0xffff, n[1] >>> 16, n[1] & 0xffff]
@ -62,7 +68,10 @@ var cookie = {
return [(o[0] << 16) | o[1], (o[2] << 16) | o[3]] return [(o[0] << 16) | o[1], (o[2] << 16) | o[3]]
} }
//
// Given two 64bit ints (as an array of two 32bit ints) returns the two
// multiplied together as a 64bit int (as an array of two 32bit ints).
//
var x64Multiply = function (m, n) { var x64Multiply = function (m, n) {
m = [m[0] >>> 16, m[0] & 0xffff, m[1] >>> 16, m[1] & 0xffff] m = [m[0] >>> 16, m[0] & 0xffff, m[1] >>> 16, m[1] & 0xffff]
n = [n[0] >>> 16, n[0] & 0xffff, n[1] >>> 16, n[1] & 0xffff] n = [n[0] >>> 16, n[0] & 0xffff, n[1] >>> 16, n[1] & 0xffff]
@ -89,7 +98,11 @@ var cookie = {
o[0] &= 0xffff o[0] &= 0xffff
return [(o[0] << 16) | o[1], (o[2] << 16) | o[3]] return [(o[0] << 16) | o[1], (o[2] << 16) | o[3]]
} }
//
// Given a 64bit int (as an array of two 32bit ints) and an int
// representing a number of bit positions, returns the 64bit int (as an
// array of two 32bit ints) rotated left by that number of positions.
//
var x64Rotl = function (m, n) { var x64Rotl = function (m, n) {
n %= 64 n %= 64
if (n === 32) { if (n === 32) {
@ -101,7 +114,11 @@ var cookie = {
return [(m[1] << n) | (m[0] >>> (32 - n)), (m[0] << n) | (m[1] >>> (32 - n))] return [(m[1] << n) | (m[0] >>> (32 - n)), (m[0] << n) | (m[1] >>> (32 - n))]
} }
} }
//
// Given a 64bit int (as an array of two 32bit ints) and an int
// representing a number of bit positions, returns the 64bit int (as an
// array of two 32bit ints) shifted left by that number of positions.
//
var x64LeftShift = function (m, n) { var x64LeftShift = function (m, n) {
n %= 64 n %= 64
if (n === 0) { if (n === 0) {
@ -112,11 +129,18 @@ var cookie = {
return [m[1] << (n - 32), 0] return [m[1] << (n - 32), 0]
} }
} }
//
// Given two 64bit ints (as an array of two 32bit ints) returns the two
// xored together as a 64bit int (as an array of two 32bit ints).
//
var x64Xor = function (m, n) { var x64Xor = function (m, n) {
return [m[0] ^ n[0], m[1] ^ n[1]] return [m[0] ^ n[0], m[1] ^ n[1]]
} }
//
// Given a block, returns murmurHash3's final x64 mix of that block.
// (`[0, h[0] >>> 1]` is a 33 bit unsigned right shift. This is the
// only place where we need to right shift 64bit ints.)
//
var x64Fmix = function (h) { var x64Fmix = function (h) {
h = x64Xor(h, [0, h[0] >>> 1]) h = x64Xor(h, [0, h[0] >>> 1])
h = x64Multiply(h, [0xff51afd7, 0xed558ccd]) h = x64Multiply(h, [0xff51afd7, 0xed558ccd])
@ -126,7 +150,10 @@ var cookie = {
return h return h
} }
//
// Given a string and an optional seed as an int, returns a 128 bit
// hash using the x64 flavor of MurmurHash3, as an unsigned hex.
//
var x64hash128 = function (key, seed) { var x64hash128 = function (key, seed) {
key = key || '' key = key || ''
seed = seed || 0 seed = seed || 0
@ -315,10 +342,9 @@ var cookie = {
done(devices.map(function (device) { done(devices.map(function (device) {
return 'id=' + device.deviceId + ';gid=' + device.groupId + ';' + device.kind + ';' + device.label return 'id=' + device.deviceId + ';gid=' + device.groupId + ';' + device.kind + ';' + device.label
})) }))
})['catch'](function (error) {
done(error)
}) })
.catch(function (error) {
done(error)
})
} }
var isEnumerateDevicesSupported = function () { var isEnumerateDevicesSupported = function () {
@ -364,6 +390,7 @@ var cookie = {
context.startRendering() context.startRendering()
var audioTimeoutId = setTimeout(function () { var audioTimeoutId = setTimeout(function () {
console.warn('Audio fingerprint timed out. Please report bug at https://github.com/Valve/fingerprintjs2 with your user agent: "' + navigator.userAgent + '".')
context.oncomplete = function () { } context.oncomplete = function () { }
context = null context = null
return done('audioTimeout') return done('audioTimeout')
@ -1038,6 +1065,7 @@ var cookie = {
} catch (e) { /* squelch */ } } catch (e) { /* squelch */ }
if (!gl.getShaderPrecisionFormat) { if (!gl.getShaderPrecisionFormat) {
loseWebglContext(gl)
return result return result
} }
@ -1055,6 +1083,7 @@ var cookie = {
}) })
}) })
}) })
loseWebglContext(gl)
return result return result
} }
var getWebglVendorAndRenderer = function () { var getWebglVendorAndRenderer = function () {
@ -1062,7 +1091,9 @@ var cookie = {
try { try {
var glContext = getWebglCanvas() var glContext = getWebglCanvas()
var extensionDebugRendererInfo = glContext.getExtension('WEBGL_debug_renderer_info') var extensionDebugRendererInfo = glContext.getExtension('WEBGL_debug_renderer_info')
return glContext.getParameter(extensionDebugRendererInfo.UNMASKED_VENDOR_WEBGL) + '~' + glContext.getParameter(extensionDebugRendererInfo.UNMASKED_RENDERER_WEBGL) var params = glContext.getParameter(extensionDebugRendererInfo.UNMASKED_VENDOR_WEBGL) + '~' + glContext.getParameter(extensionDebugRendererInfo.UNMASKED_RENDERER_WEBGL)
loseWebglContext(glContext)
return params
} catch (e) { } catch (e) {
return null return null
} }
@ -1108,25 +1139,25 @@ var cookie = {
// We extract the OS from the user agent (respect the order of the if else if statement) // We extract the OS from the user agent (respect the order of the if else if statement)
if (userAgent.indexOf('windows phone') >= 0) { if (userAgent.indexOf('windows phone') >= 0) {
os = 'Windows Phone' os = 'Windows Phone'
} else if (userAgent.indexOf('win') >= 0) { } else if (userAgent.indexOf('windows') >= 0 || userAgent.indexOf('win16') >= 0 || userAgent.indexOf('win32') >= 0 || userAgent.indexOf('win64') >= 0 || userAgent.indexOf('win95') >= 0 || userAgent.indexOf('win98') >= 0 || userAgent.indexOf('winnt') >= 0 || userAgent.indexOf('wow64') >= 0) {
os = 'Windows' os = 'Windows'
} else if (userAgent.indexOf('android') >= 0) { } else if (userAgent.indexOf('android') >= 0) {
os = 'Android' os = 'Android'
} else if (userAgent.indexOf('linux') >= 0 || userAgent.indexOf('cros') >= 0) { } else if (userAgent.indexOf('linux') >= 0 || userAgent.indexOf('cros') >= 0 || userAgent.indexOf('x11') >= 0) {
os = 'Linux' os = 'Linux'
} else if (userAgent.indexOf('iphone') >= 0 || userAgent.indexOf('ipad') >= 0) { } else if (userAgent.indexOf('iphone') >= 0 || userAgent.indexOf('ipad') >= 0 || userAgent.indexOf('ipod') >= 0 || userAgent.indexOf('crios') >= 0 || userAgent.indexOf('fxios') >= 0) {
os = 'iOS' os = 'iOS'
} else if (userAgent.indexOf('mac') >= 0) { } else if (userAgent.indexOf('macintosh') >= 0 || userAgent.indexOf('mac_powerpc)') >= 0) {
os = 'Mac' os = 'Mac'
} else { } else {
os = 'Other' os = 'Other'
} }
// We detect if the person uses a mobile device // We detect if the person uses a touch device
var mobileDevice = (('ontouchstart' in window) || var mobileDevice = (('ontouchstart' in window) ||
(navigator.maxTouchPoints > 0) || (navigator.maxTouchPoints > 0) ||
(navigator.msMaxTouchPoints > 0)) (navigator.msMaxTouchPoints > 0))
if (mobileDevice && os !== 'Windows Phone' && os !== 'Android' && os !== 'iOS' && os !== 'Other') { if (mobileDevice && os !== 'Windows' && os !== 'Windows Phone' && os !== 'Android' && os !== 'iOS' && os !== 'Other' && userAgent.indexOf('cros') === -1) {
return true return true
} }
@ -1151,12 +1182,17 @@ var cookie = {
return true return true
} else if ((platform.indexOf('mac') >= 0 || platform.indexOf('ipad') >= 0 || platform.indexOf('ipod') >= 0 || platform.indexOf('iphone') >= 0) && os !== 'Mac' && os !== 'iOS') { } else if ((platform.indexOf('mac') >= 0 || platform.indexOf('ipad') >= 0 || platform.indexOf('ipod') >= 0 || platform.indexOf('iphone') >= 0) && os !== 'Mac' && os !== 'iOS') {
return true return true
} else if (platform.indexOf('arm') >= 0 && os === 'Windows Phone') {
return false
} else if (platform.indexOf('pike') >= 0 && userAgent.indexOf('opera mini') >= 0) {
return false
} else { } else {
var platformIsOther = platform.indexOf('win') < 0 && var platformIsOther = platform.indexOf('win') < 0 &&
platform.indexOf('linux') < 0 && platform.indexOf('linux') < 0 &&
platform.indexOf('mac') < 0 && platform.indexOf('mac') < 0 &&
platform.indexOf('iphone') < 0 && platform.indexOf('iphone') < 0 &&
platform.indexOf('ipad') < 0 platform.indexOf('ipad') < 0 &&
platform.indexOf('ipod') < 0
if (platformIsOther !== (os === 'Other')) { if (platformIsOther !== (os === 'Other')) {
return true return true
} }
@ -1170,15 +1206,25 @@ var cookie = {
// we extract the browser from the user agent (respect the order of the tests) // we extract the browser from the user agent (respect the order of the tests)
var browser var browser
if (userAgent.indexOf('firefox') >= 0) { if (userAgent.indexOf('edge/') >= 0 || userAgent.indexOf('iemobile/') >= 0) {
// Unreliable, different versions use EdgeHTML, Webkit, Blink, etc.
return false
} else if (userAgent.indexOf('opera mini') >= 0) {
// Unreliable, different modes use Presto, WebView, Webkit, etc.
return false
} else if (userAgent.indexOf('firefox/') >= 0) {
browser = 'Firefox' browser = 'Firefox'
} else if (userAgent.indexOf('opera') >= 0 || userAgent.indexOf('opr') >= 0) { } else if (userAgent.indexOf('opera/') >= 0 || userAgent.indexOf(' opr/') >= 0) {
browser = 'Opera' browser = 'Opera'
} else if (userAgent.indexOf('chrome') >= 0) { } else if (userAgent.indexOf('chrome/') >= 0) {
browser = 'Chrome' browser = 'Chrome'
} else if (userAgent.indexOf('safari') >= 0) { } else if (userAgent.indexOf('safari/') >= 0) {
browser = 'Safari' if (userAgent.indexOf('android 1.') >= 0 || userAgent.indexOf('android 2.') >= 0 || userAgent.indexOf('android 3.') >= 0 || userAgent.indexOf('android 4.') >= 0) {
} else if (userAgent.indexOf('trident') >= 0) { browser = 'AOSP'
} else {
browser = 'Safari'
}
} else if (userAgent.indexOf('trident/') >= 0) {
browser = 'Internet Explorer' browser = 'Internet Explorer'
} else { } else {
browser = 'Other' browser = 'Other'
@ -1194,7 +1240,7 @@ var cookie = {
return true return true
} else if (tempRes === 39 && browser !== 'Internet Explorer' && browser !== 'Other') { } else if (tempRes === 39 && browser !== 'Internet Explorer' && browser !== 'Other') {
return true return true
} else if (tempRes === 33 && browser !== 'Chrome' && browser !== 'Opera' && browser !== 'Other') { } else if (tempRes === 33 && browser !== 'Chrome' && browser !== 'AOSP' && browser !== 'Opera' && browser !== 'Other') {
return true return true
} }
@ -1224,7 +1270,9 @@ var cookie = {
} }
var glContext = getWebglCanvas() var glContext = getWebglCanvas()
return !!window.WebGLRenderingContext && !!glContext var isSupported = !!window.WebGLRenderingContext && !!glContext
loseWebglContext(glContext)
return isSupported
} }
var isIE = function () { var isIE = function () {
if (navigator.appName === 'Microsoft Internet Explorer') { if (navigator.appName === 'Microsoft Internet Explorer') {
@ -1265,6 +1313,12 @@ var cookie = {
if (!gl) { gl = null } if (!gl) { gl = null }
return gl return gl
} }
var loseWebglContext = function (context) {
var loseContextExtension = context.getExtension('WEBGL_lose_context')
if (loseContextExtension != null) {
loseContextExtension.loseContext()
}
}
var components = [ var components = [
{ key: 'userAgent', getData: UserAgent }, { key: 'userAgent', getData: UserAgent },
@ -1391,7 +1445,9 @@ var cookie = {
return [p[0], p[1], mimeTypes].join('::') return [p[0], p[1], mimeTypes].join('::')
}) })
}) })
} else if (['canvas', 'webgl'].indexOf(component.key) !== -1) { } else if (['canvas', 'webgl'].indexOf(component.key) !== -1 && Array.isArray(component.value)) {
// sometimes WebGL returns error in headless browsers (during CI testing for example)
// so we need to join only if the values are array
newComponents.push({ key: component.key, value: component.value.join('~') }) newComponents.push({ key: component.key, value: component.value.join('~') })
} else if (['sessionStorage', 'localStorage', 'indexedDb', 'addBehavior', 'openDatabase'].indexOf(component.key) !== -1) { } else if (['sessionStorage', 'localStorage', 'indexedDb', 'addBehavior', 'openDatabase'].indexOf(component.key) !== -1) {
if (component.value) { if (component.value) {
@ -1407,190 +1463,120 @@ var cookie = {
newComponents.push({ key: component.key, value: component.value }) newComponents.push({ key: component.key, value: component.value })
} }
} }
}; }
var murmur = x64hash128(map(newComponents, function (component) { return component.value }).join('~~~'), 31); var murmur = x64hash128(map(newComponents, function (component) { return component.value }).join('~~~'), 31)
callback(murmur, newComponents); callback(murmur, newComponents)
}); })
}; }
var _fp_val = null;
var _fp_detail = "";
var _fp_acc = null;
var _fp_LastChangeTime = null;
var _fp_TimeUsed = null;
var _fp_detailObj = {};
var _fp_key = null;
var _fp_ready = false;
var d1 = null;
function ini(components) {
var murmur = x64hash128(components.map(function (pair) { return pair.value }).join(), 15);
murmur = murmur.substr(0, 8);
var rate = 0;
for (var index in components) {
var obj = components[index]
var line = obj.key + " = " + String(obj.value).substr(0, 100);
_fp_detail += line + "\n";
_fp_detailObj[obj.key] = String(obj.value).substr(0, 100);
}
if (cookie.get('_fp_ref_')) rate = levenshteinenator(Compress(window.btoa(JSON.stringify(_fp_detailObj))), cookie.get('_fp_ref_'));
else {
rate = 0;
}
//rate = (rate < 0) ? 0 : rate;
_fp_acc = rate;
if (rate < MaxDiff) {
cookie.set('_fp_ref_', Compress(window.btoa(JSON.stringify(_fp_detailObj))));
}
var d2 = new Date();
var time = d2 - d1;
_fp_TimeUsed = time; Fingerprint2.x64hash128 = x64hash128
Fingerprint2.VERSION = '2.1.2'
return Fingerprint2
})
if (rate < MaxDiff) { /*
cookie.set('_fp', murmur); * @Author: IoTcat (https://iotcat.me)
cookie.set('_fp_LastChangeTime', Date.parse(d2) / 1000); * @Date: 2020-08-15 11:34:32
_fp_LastChangeTime = d2; * @Last Modified by: iotcat
_fp_val = murmur; * @Last Modified time: 2020-08-15 11:34:32
*/
//fp
;(function (name, context, definition) {
'use strict'
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() }
})('fp', this, function () {
var startTime = new Date().valueOf();
var options_low = {
excludes: {
'enumerateDevices': true,
'pixelRatio': true,
'doNotTrack': true,
'fontsFlash': true,
'fonts': true,
'language': true,
"availableScreenResolution": true,
"timezoneOffset": true,
"timezone": true,
"plugins": true,
"canvas": true,
"webgl": true,
"adBlock": true,
"audio": true
} }
else { };
_fp_LastChangeTime = new Date(cookie.get('_fp_LastChangeTime') * 1000); var options_high = {
_fp_val = cookie.get('_fp'); excludes: {
'enumerateDevices': true,
'pixelRatio': true,
'doNotTrack': true,
'fontsFlash': true,
'fonts': true,
"adBlock": true,
} }
console.log('\n' + ' %c fp v2.0.1 %c ' + _fp_val + '::' + String(_fp_acc * 100).substr(0, 4) + '%::' + _fp_TimeUsed + 'ms %c https://fp.yimian.xyz \n', 'color: #00FFFF; background: #030307; padding:5px 0;', 'color: #fadfa3; background: #030307; padding:5px 0;', 'background: #4682B4; padding:5px 0;'); };
}
function Compress(strNormalString) {
var strCompressedString = "";
for (var i = 0; i < strNormalString.length; i += Math.round(strNormalString.length / 289)) { var exec = function(f){
strCompressedString += strNormalString.charAt(i); if (window.requestIdleCallback) {
requestIdleCallback(function () {
f();
})
} else {
setTimeout(f, 500)
} }
return strCompressedString;
} }
function generateKey(){ var getLowFp = function(highFp, components, resolve, reject){
var obj = { var lowFp = Fingerprint2.x64hash128(JSON.stringify(components), 15).substring(0, 4);
_fp: cookie.get('_fp'), var fullFp = lowFp + highFp;
_fp_ref_: cookie.get('_fp_ref_'), console.log('\n' + ' %c fp v3.0.1 %c ' + fullFp + '::' + (new Date().valueOf() - startTime) + 'ms %c https://fp.yimian.xyz/ \n', 'color: #00FFFF; background: #030307; padding:5px 0;', 'color: #fadfa3; background: #030307; padding:5px 0;', 'background: #4682B4; padding:5px 0;');
_fp_LastChangeTime: cookie.get('_fp_LastChangeTime') resolve(fullFp);
};
_fp_key = window.btoa(JSON.stringify(obj));
return _fp_key;
} }
var getHighFp = function(components, resolve, reject){
var levenshteinenator = (function () { var highFp = Fingerprint2.x64hash128(JSON.stringify(components), 15).substring(0, 2);
function levenshteinenator(a, b) { components.forEach(function(obj, index){
var cost; if(options_high.excludes.hasOwnProperty(obj.key)){
var m = a.length; components.splice(index, 1);
var n = b.length;
if (m < n) {
var c = a; a = b; b = c;
var o = m; m = n; n = o;
}
var r = []; r[0] = [];
for (var c = 0; c < n + 1; ++c) {
r[0][c] = c;
} }
});
for (var i = 1; i < m + 1; ++i) { getLowFp(highFp, components, resolve, reject);
r[i] = []; r[i][0] = i;
for (var j = 1; j < n + 1; ++j) {
cost = a.charAt(i - 1) === b.charAt(j - 1) ? 0 : 1;
r[i][j] = minimator(r[i - 1][j] + 1, r[i][j - 1] + 1, r[i - 1][j - 1] + cost);
}
}
return 1 - r[m - 1][n - 1] / Math.max(m, n);
}
function minimator(x, y, z) {
if (x <= y && x <= z) return x;
if (y <= x && y <= z) return y;
return z;
}
return levenshteinenator;
}());
var key_check = function (key){
var obj;
try{
obj = JSON.parse(window.atob(key));
}catch(e){
return false;
}
if(obj._fp === undefined || obj._fp_ref_ === undefined || obj._fp_LastChangeTime === undefined){
return false;
}
return true;
}
var fp_link = function (key, f){
if(key != _fp_key && !_fp_ready && key_check(key)){
key = window.atob(key);
var obj = JSON.parse(key);
cookie.set('_fp', obj._fp);
cookie.set('_fp_ref_', obj._fp_ref_);
cookie.set('_fp_LastChangeTime', obj._fp_LastChangeTime);
fp_reset();
}
fp_get(f);
} }
var fp_reset = function (){ return new Promise(function(resolve, reject){
exec(function(){
d1 = new Date(); startTime = new Date().valueOf();
Fingerprint2.get(ini); Fingerprint2.get(options_high, function (components) {
_fp_ready = true; getHighFp(components, resolve, reject);
} })
});
});
})
var fp_get = function (f) { //fp_details
if (!_fp_val) { ;(function (name, context, definition) {
setTimeout(fp_get, 1, f); 'use strict'
return; 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() }
} })('fp_details', this, function () {
f(_fp_val, generateKey(), _fp_acc, _fp_detail, _fp_LastChangeTime, _fp_TimeUsed, _fp_detailObj); var options= {
excludes: {}
}; };
var fp = function (k, f){ var exec = function(f){
if(typeof k === 'function'){ if (window.requestIdleCallback) {
if(!_fp_ready) { requestIdleCallback(function () {
fp_reset(); f();
} })
fp_get(k); } else {
return; setTimeout(f, 500)
}
if(typeof k === 'string'){
if(f == undefined) f = function(){};
if(k == 'reset'){
cookie.del('_fp');
cookie.del('_fp_ref_');
cookie.del('_fp_LastChangeTime');
fp_reset();
fp_get(f);
return;
}else{
fp_link(k, f);
}
return;
} }
} }
return fp; return new Promise(function(resolve, reject){
}); exec(function(){
Fingerprint2.get(options, function (components) {
resolve(components);
})
});
});
})

1
fp.min.js vendored

File diff suppressed because one or more lines are too long

File diff suppressed because it is too large Load Diff

1
hao

@ -1 +0,0 @@
..

@ -0,0 +1,9 @@
{
"name": "fp3",
"version": "3.0.1",
"description": "fp is a concise web front-end solution to generate an unique 'fingerprint' for each visitor basing on visitor's device and browser.",
"main": "fp.js",
"repository": "git@github.com:IoTcat/fp.git",
"author": "iotcat <i@iotcat.me>",
"license": "MIT"
}
Loading…
Cancel
Save