Switching locatejs over for vytal
11
.babelrc
|
|
@ -1,11 +0,0 @@
|
||||||
{
|
|
||||||
"presets": [
|
|
||||||
// "@babel/preset-env"
|
|
||||||
"@babel/preset-react"
|
|
||||||
// "react-app"
|
|
||||||
],
|
|
||||||
"plugins": [
|
|
||||||
// "@babel/plugin-proposal-class-properties",
|
|
||||||
"react-hot-loader/babel"
|
|
||||||
]
|
|
||||||
}
|
|
||||||
|
|
@ -1,6 +0,0 @@
|
||||||
{
|
|
||||||
"extends": "react-app",
|
|
||||||
"globals": {
|
|
||||||
"chrome": "readonly"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
45
.eslintrc.js
Normal file
|
|
@ -0,0 +1,45 @@
|
||||||
|
module.exports = {
|
||||||
|
env: {
|
||||||
|
browser: true,
|
||||||
|
es2021: true,
|
||||||
|
webextensions: true,
|
||||||
|
},
|
||||||
|
extends: ['plugin:react/recommended', 'airbnb'],
|
||||||
|
plugins: ['react'],
|
||||||
|
settings: {
|
||||||
|
'import/resolver': {
|
||||||
|
node: {
|
||||||
|
extensions: ['.js', '.jsx'],
|
||||||
|
moduleDirectory: ['node_modules', 'src/'],
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
rules: {
|
||||||
|
'no-plusplus': 'off',
|
||||||
|
'comma-dangle': 'off',
|
||||||
|
'operator-linebreak': 'off',
|
||||||
|
'no-use-before-define': 'off',
|
||||||
|
'linebreak-style': 'off',
|
||||||
|
'react/jsx-filename-extension': [1, { extensions: ['.jsx', '.js'] }],
|
||||||
|
'jsx-a11y/label-has-associated-control': 'off',
|
||||||
|
'one-var': 'off',
|
||||||
|
'one-var-declaration-per-line': 'off',
|
||||||
|
'object-curly-newline': 'off',
|
||||||
|
'implicit-arrow-linebreak': 'off',
|
||||||
|
'import/extensions': [
|
||||||
|
'error',
|
||||||
|
'ignorePackages',
|
||||||
|
{
|
||||||
|
js: 'never',
|
||||||
|
jsx: 'never',
|
||||||
|
},
|
||||||
|
],
|
||||||
|
'react/jsx-one-expression-per-line': 'off',
|
||||||
|
'react/prop-types': 'off',
|
||||||
|
'react/react-in-jsx-scope': 'off',
|
||||||
|
'no-bitwise': 'off',
|
||||||
|
'react/no-array-index-key': 'off',
|
||||||
|
'nonblock-statement-body-position': 'off',
|
||||||
|
'react/button-has-type': 'off'
|
||||||
|
},
|
||||||
|
};
|
||||||
13
.gitignore
vendored
|
|
@ -1,7 +1,11 @@
|
||||||
# See https://help.github.com/ignore-files/ for more about ignoring files.
|
# See https://help.github.com/articles/ignoring-files/ for more about ignoring files.
|
||||||
|
/images
|
||||||
|
App.test.js
|
||||||
|
|
||||||
# dependencies
|
# dependencies
|
||||||
/node_modules
|
/node_modules
|
||||||
|
/.pnp
|
||||||
|
.pnp.js
|
||||||
|
|
||||||
# testing
|
# testing
|
||||||
/coverage
|
/coverage
|
||||||
|
|
@ -15,7 +19,8 @@
|
||||||
.env.development.local
|
.env.development.local
|
||||||
.env.test.local
|
.env.test.local
|
||||||
.env.production.local
|
.env.production.local
|
||||||
.history
|
|
||||||
|
|
||||||
# secrets
|
npm-debug.log*
|
||||||
secrets.*.js
|
yarn-debug.log*
|
||||||
|
yarn-error.log*
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -1,8 +0,0 @@
|
||||||
{
|
|
||||||
"singleQuote": true,
|
|
||||||
"trailingComma": "es5",
|
|
||||||
"requirePragma": false,
|
|
||||||
"arrowParens": "always",
|
|
||||||
"semi": false,
|
|
||||||
"editor.formatOnSave": true
|
|
||||||
}
|
|
||||||
15
.vscode/launch.json
vendored
Normal file
|
|
@ -0,0 +1,15 @@
|
||||||
|
{
|
||||||
|
// Use IntelliSense to learn about possible attributes.
|
||||||
|
// Hover to view descriptions of existing attributes.
|
||||||
|
// For more information, visit: https://go.microsoft.com/fwlink/?linkid=830387
|
||||||
|
"version": "0.2.0",
|
||||||
|
"configurations": [
|
||||||
|
{
|
||||||
|
"type": "pwa-chrome",
|
||||||
|
"request": "launch",
|
||||||
|
"name": "Launch Chrome against localhost",
|
||||||
|
"url": "http://localhost:8080",
|
||||||
|
"webRoot": "${workspaceFolder}"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
3
.vscode/settings.json
vendored
|
|
@ -1,3 +0,0 @@
|
||||||
{
|
|
||||||
"editor.formatOnSave": true
|
|
||||||
}
|
|
||||||
6
LICENSE
|
|
@ -1,6 +1,6 @@
|
||||||
The MIT License (MIT)
|
MIT License
|
||||||
|
|
||||||
Copyright (c) 2019 Michael Xieyang Liu
|
Copyright (c) 2022 z0ccc
|
||||||
|
|
||||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||||
of this software and associated documentation files (the "Software"), to deal
|
of this software and associated documentation files (the "Software"), to deal
|
||||||
|
|
@ -18,4 +18,4 @@ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
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
|
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||||
SOFTWARE.
|
SOFTWARE.
|
||||||
|
|
|
||||||
151
README.md
Executable file → Normal file
|
|
@ -1,151 +1,52 @@
|
||||||
<img src="src/assets/img/icon-128.png" width="64"/>
|
# LocateJS
|
||||||
|
|
||||||
# Chrome Extension Boilerplate with React 17 and Webpack 5
|
Check it out here: [https://locatejs.com/](https://locatejs.com/).
|
||||||
|
|
||||||
[](https://www.npmjs.com/package/chrome-extension-boilerplate-react)
|
## About
|
||||||
[](https://www.npmjs.com/package/chrome-extension-boilerplate-react)
|
|
||||||
[](https://www.npmjs.com/package/chrome-extension-boilerplate-react)
|
|
||||||
|
|
||||||
[](https://david-dm.org/lxieyang/chrome-extension-boilerplate-react)
|
LocateJS predicts your location by analyzing your connection and system data. This scan allows you to understand how your location can be pinpointed even while using a VPN, location spoofer or private mode.
|
||||||
[](https://david-dm.org/lxieyang/chrome-extension-boilerplate-react?type=dev)
|
|
||||||
|
|
||||||
## Announcements
|
Accuracy of the location prediction is dependent on how well you have concealed your location data. The prediction can be anywhere from spot-on to inaccurate depending on how much identifiable and authentic info your exposing.
|
||||||
|
|
||||||
- **_This boilerplate adopts [Manifest V3](https://developer.chrome.com/docs/extensions/mv3/intro/mv3-overview/)!_** For V2 users, please check out the [manifest-v2](https://github.com/lxieyang/chrome-extension-boilerplate-react/tree/manifest-v2) branch, or use version [3.x](https://www.npmjs.com/package/chrome-extension-boilerplate-react/v/3.3.0).
|
## How to hide location
|
||||||
- Check out the [Manifest V3 Migration Guide](https://developer.chrome.com/docs/extensions/mv3/intro/mv3-migration/).
|
|
||||||
- Recently added [devtools](https://developer.chrome.com/docs/extensions/mv3/devtools/) Support! Thanks [GeekaholicLin](https://github.com/lxieyang/chrome-extension-boilerplate-react/issues/17)!
|
|
||||||
- Recently updated from **[React](https://reactjs.org)** ~~16~~ to **17**!
|
|
||||||
- Recently updated from **[Webpack Dev Server](https://webpack.js.org/configuration/dev-server/)** ~~3.x~~ to **4.x** and **[Webpack](https://webpack.js.org/)** ~~4~~ to **5**!
|
|
||||||
- Recently added [TypeScript](https://www.typescriptlang.org/) Support!
|
|
||||||
|
|
||||||
## Features
|
The best way to hide your location is to use [Tor](https://www.torproject.org/). Tor will completely hide both your connection and system data.
|
||||||
|
|
||||||
This is a basic Chrome Extensions boilerplate to help you write modular and modern Javascript code, load CSS easily and [automatic reload the browser on code changes](https://webpack.github.io/docs/webpack-dev-server.html#automatic-refresh).
|
As it's not always practical to use Tor (slow speeds, captcha loops, etc) the following are instructions on how to obscure your location without Tor.
|
||||||
|
|
||||||
This boilerplate is updated with:
|
To hide your connection data you will need to use a VPN or proxy
|
||||||
|
|
||||||
- [Chrome Extension Manifest V3](https://developer.chrome.com/docs/extensions/mv3/intro/mv3-overview/)
|
To hide your system data you will need to change your system settings. You can change your timezone to one with the same timezone offset so that the date on your computer stays the same.
|
||||||
- [React 17](https://reactjs.org)
|
|
||||||
- [Webpack 5](https://webpack.js.org/)
|
|
||||||
- [Webpack Dev Server 4](https://webpack.js.org/configuration/dev-server/)
|
|
||||||
- [React Hot Loader](https://github.com/gaearon/react-hot-loader)
|
|
||||||
- [eslint-config-react-app](https://www.npmjs.com/package/eslint-config-react-app)
|
|
||||||
- [Prettier](https://prettier.io/)
|
|
||||||
- [TypeScript](https://www.typescriptlang.org/)
|
|
||||||
|
|
||||||
This boilerplate is heavily inspired by and adapted from [https://github.com/samuelsimoes/chrome-extension-webpack-boilerplate](https://github.com/samuelsimoes/chrome-extension-webpack-boilerplate), with additional support for React 17 features, Webpack 5, and Webpack Dev Server 4.
|
Timezone/location spoofing extensions will not help as they cannot spoof data in [web workers](https://developer.mozilla.org/en-US/docs/Web/API/Web_Workers_API/Using_web_workers).
|
||||||
|
|
||||||
Please open up an issue to nudge me to keep the npm packages up-to-date. FYI, it takes time to make different packages with different versions work together nicely.
|
You should select a language that has no country associated with it (‘en’ instead of ‘en-US’). You should remove all other languages.
|
||||||
|
|
||||||
## Installing and Running
|
Although adding multiple random languages will help obscure your location, it's not the best idea as it will give you a very unique [device fingerprint](https://en.wikipedia.org/wiki/Device_fingerprint).
|
||||||
|
|
||||||
### Procedures:
|
It's also a good idea to make sure your connection data matches your system data because if there are contradictions it would reveal that you are attempting to hide your location.
|
||||||
|
|
||||||
1. Check if your [Node.js](https://nodejs.org/) version is >= **14**.
|
## FAQ
|
||||||
2. Clone this repository.
|
|
||||||
3. Change the package's `name`, `description`, and `repository` fields in `package.json`.
|
|
||||||
4. Change the name of your extension on `src/manifest.json`.
|
|
||||||
5. Run `npm install` to install the dependencies.
|
|
||||||
6. Run `npm start`
|
|
||||||
7. Load your extension on Chrome following:
|
|
||||||
1. Access `chrome://extensions/`
|
|
||||||
2. Check `Developer mode`
|
|
||||||
3. Click on `Load unpacked extension`
|
|
||||||
4. Select the `build` folder.
|
|
||||||
8. Happy hacking.
|
|
||||||
|
|
||||||
## Structure
|
### Why did nothing change when ‘Only use system data for prediction’ was selected?
|
||||||
|
|
||||||
All your extension's code must be placed in the `src` folder.
|
If the values do not change when this option is selected it means that the connection values were already removed from the prediction due to being detected as inaccurate.
|
||||||
|
|
||||||
The boilerplate is already prepared to have a popup, an options page, a background page, and a new tab page (which replaces the new tab page of your browser). But feel free to customize these.
|
### What do the green and red circles mean?
|
||||||
|
|
||||||
## TypeScript
|
A red circle indicates that the data in that row has been tampered with. If the true value cannot be derived then it is assumed to be inaccurate and will be left out of the location prediction. You can click on the red circle to see what types of issues have been detected.
|
||||||
|
|
||||||
This boilerplate now supports TypeScript! The `Options` Page is implemented using TypeScript. Please refer to `src/pages/Options/` for example usages.
|
A green circle indicates that no object tampering has been detected and that the value is legitimate.
|
||||||
|
|
||||||
## Webpack auto-reload and HRM
|
## Dev
|
||||||
|
|
||||||
To make your workflow much more efficient this boilerplate uses the [webpack server](https://webpack.github.io/docs/webpack-dev-server.html) to development (started with `npm start`) with auto reload feature that reloads the browser automatically every time that you save some file in your editor.
|
This application is built with Javascript and React.
|
||||||
|
|
||||||
You can run the dev mode on other port if you want. Just specify the env var `port` like this:
|
Clone this repo and run these commands to start the development server.
|
||||||
|
|
||||||
```
|
```
|
||||||
$ PORT=6002 npm run start
|
yarn
|
||||||
|
yarn run start
|
||||||
```
|
```
|
||||||
|
|
||||||
## Content Scripts
|
The website can then be accessed at http://localhost:3000/.
|
||||||
|
|
||||||
Although this boilerplate uses the webpack dev server, it's also prepared to write all your bundles files on the disk at every code change, so you can point, on your extension manifest, to your bundles that you want to use as [content scripts](https://developer.chrome.com/extensions/content_scripts), but you need to exclude these entry points from hot reloading [(why?)](https://github.com/samuelsimoes/chrome-extension-webpack-boilerplate/issues/4#issuecomment-261788690). To do so you need to expose which entry points are content scripts on the `webpack.config.js` using the `chromeExtensionBoilerplate -> notHotReload` config. Look the example below.
|
|
||||||
|
|
||||||
Let's say that you want use the `myContentScript` entry point as content script, so on your `webpack.config.js` you will configure the entry point and exclude it from hot reloading, like this:
|
|
||||||
|
|
||||||
```js
|
|
||||||
{
|
|
||||||
…
|
|
||||||
entry: {
|
|
||||||
myContentScript: "./src/js/myContentScript.js"
|
|
||||||
},
|
|
||||||
chromeExtensionBoilerplate: {
|
|
||||||
notHotReload: ["myContentScript"]
|
|
||||||
}
|
|
||||||
…
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
and on your `src/manifest.json`:
|
|
||||||
|
|
||||||
```json
|
|
||||||
{
|
|
||||||
"content_scripts": [
|
|
||||||
{
|
|
||||||
"matches": ["https://www.google.com/*"],
|
|
||||||
"js": ["myContentScript.bundle.js"]
|
|
||||||
}
|
|
||||||
]
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
## Intelligent Code Completion
|
|
||||||
|
|
||||||
Thanks to [@hudidit](https://github.com/lxieyang/chrome-extension-boilerplate-react/issues/4)'s kind suggestions, this boilerplate supports chrome-specific intelligent code completion using [@types/chrome](https://www.npmjs.com/package/@types/chrome).
|
|
||||||
|
|
||||||
## Packing
|
|
||||||
|
|
||||||
After the development of your extension run the command
|
|
||||||
|
|
||||||
```
|
|
||||||
$ NODE_ENV=production npm run build
|
|
||||||
```
|
|
||||||
|
|
||||||
Now, the content of `build` folder will be the extension ready to be submitted to the Chrome Web Store. Just take a look at the [official guide](https://developer.chrome.com/webstore/publish) to more infos about publishing.
|
|
||||||
|
|
||||||
## Secrets
|
|
||||||
|
|
||||||
If you are developing an extension that talks with some API you probably are using different keys for testing and production. Is a good practice you not commit your secret keys and expose to anyone that have access to the repository.
|
|
||||||
|
|
||||||
To this task this boilerplate import the file `./secrets.<THE-NODE_ENV>.js` on your modules through the module named as `secrets`, so you can do things like this:
|
|
||||||
|
|
||||||
_./secrets.development.js_
|
|
||||||
|
|
||||||
```js
|
|
||||||
export default { key: '123' };
|
|
||||||
```
|
|
||||||
|
|
||||||
_./src/popup.js_
|
|
||||||
|
|
||||||
```js
|
|
||||||
import secrets from 'secrets';
|
|
||||||
ApiCall({ key: secrets.key });
|
|
||||||
```
|
|
||||||
|
|
||||||
:point_right: The files with name `secrets.*.js` already are ignored on the repository.
|
|
||||||
|
|
||||||
## Resources:
|
|
||||||
|
|
||||||
- [Webpack documentation](https://webpack.js.org/concepts/)
|
|
||||||
- [Chrome Extension documentation](https://developer.chrome.com/extensions/getstarted)
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
Michael Xieyang Liu | [Website](https://lxieyang.github.io)
|
|
||||||
|
|
|
||||||
19779
package-lock.json
generated
97
package.json
Executable file → Normal file
|
|
@ -1,58 +1,55 @@
|
||||||
{
|
{
|
||||||
"name": "vytal",
|
"homepage": "https://locatejs.com",
|
||||||
"version": "1.0.0",
|
"name": "locatejs",
|
||||||
"description": "Vytal",
|
"version": "0.1.0",
|
||||||
"license": "MIT",
|
"private": true,
|
||||||
"repository": {
|
|
||||||
"type": "git",
|
|
||||||
"url": "https://github.com/z0ccc/Vytal.git"
|
|
||||||
},
|
|
||||||
"scripts": {
|
|
||||||
"build": "node utils/build.js",
|
|
||||||
"start": "node utils/webserver.js",
|
|
||||||
"prettier": "prettier --write '**/*.{js,jsx,ts,tsx,json,css,scss,md}'"
|
|
||||||
},
|
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@hot-loader/react-dom": "^17.0.2",
|
"@babel/plugin-syntax-dynamic-import": "^7.8.3",
|
||||||
|
"@testing-library/jest-dom": "^5.11.4",
|
||||||
|
"@testing-library/react": "^11.1.0",
|
||||||
|
"@testing-library/user-event": "^12.1.10",
|
||||||
|
"countries-and-timezones": "^3.2.2",
|
||||||
|
"country-language": "^0.1.7",
|
||||||
|
"gh-pages": "^3.2.3",
|
||||||
"react": "^17.0.2",
|
"react": "^17.0.2",
|
||||||
"react-dom": "^17.0.2",
|
"react-dom": "^17.0.2",
|
||||||
"react-hot-loader": "^4.13.0"
|
"react-github-btn": "^1.2.1",
|
||||||
|
"react-modal": "^3.14.3",
|
||||||
|
"react-scripts": "4.0.3",
|
||||||
|
"web-vitals": "^1.0.1"
|
||||||
|
},
|
||||||
|
"scripts": {
|
||||||
|
"predeploy": "yarn run build",
|
||||||
|
"deploy": "echo 'locatejs.com' > ./build/CNAME && gh-pages -d build",
|
||||||
|
"start": "react-scripts start",
|
||||||
|
"build": "react-scripts build",
|
||||||
|
"test": "react-scripts test",
|
||||||
|
"eject": "react-scripts eject"
|
||||||
|
},
|
||||||
|
"eslintConfig": {
|
||||||
|
"extends": [
|
||||||
|
"react-app",
|
||||||
|
"react-app/jest"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"browserslist": {
|
||||||
|
"production": [
|
||||||
|
">0.2%",
|
||||||
|
"not dead",
|
||||||
|
"not op_mini all"
|
||||||
|
],
|
||||||
|
"development": [
|
||||||
|
"last 1 chrome version",
|
||||||
|
"last 1 firefox version",
|
||||||
|
"last 1 safari version"
|
||||||
|
]
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"@babel/core": "^7.17.0",
|
"eslint": "^7.32.0",
|
||||||
"@babel/plugin-proposal-class-properties": "^7.16.7",
|
"eslint-config-airbnb": "^18.2.1",
|
||||||
"@babel/preset-env": "^7.16.11",
|
"eslint-plugin-import": "^2.24.2",
|
||||||
"@babel/preset-react": "^7.16.7",
|
"eslint-plugin-jsx-a11y": "^6.4.1",
|
||||||
"@types/chrome": "^0.0.177",
|
"eslint-plugin-react": "^7.25.1",
|
||||||
"@types/react": "^17.0.39",
|
"eslint-plugin-react-hooks": "^4.2.0"
|
||||||
"@types/react-dom": "^17.0.11",
|
|
||||||
"babel-eslint": "^10.1.0",
|
|
||||||
"babel-loader": "^8.2.3",
|
|
||||||
"babel-preset-react-app": "^10.0.1",
|
|
||||||
"clean-webpack-plugin": "^4.0.0",
|
|
||||||
"copy-webpack-plugin": "^7.0.0",
|
|
||||||
"css-loader": "^6.6.0",
|
|
||||||
"eslint": "^8.8.0",
|
|
||||||
"eslint-config-react-app": "^7.0.0",
|
|
||||||
"eslint-plugin-flowtype": "^8.0.3",
|
|
||||||
"eslint-plugin-import": "^2.25.4",
|
|
||||||
"eslint-plugin-jsx-a11y": "^6.5.1",
|
|
||||||
"eslint-plugin-react": "^7.28.0",
|
|
||||||
"eslint-plugin-react-hooks": "^4.3.0",
|
|
||||||
"file-loader": "^6.2.0",
|
|
||||||
"fs-extra": "^10.0.0",
|
|
||||||
"html-loader": "^3.1.0",
|
|
||||||
"html-webpack-plugin": "^5.5.0",
|
|
||||||
"node-sass": "^6.0.1",
|
|
||||||
"prettier": "^2.5.1",
|
|
||||||
"sass-loader": "^12.4.0",
|
|
||||||
"source-map-loader": "^3.0.1",
|
|
||||||
"style-loader": "^3.3.1",
|
|
||||||
"terser-webpack-plugin": "^5.3.1",
|
|
||||||
"ts-loader": "^9.2.6",
|
|
||||||
"typescript": "^4.5.5",
|
|
||||||
"webpack": "^5.68.0",
|
|
||||||
"webpack-cli": "^4.9.2",
|
|
||||||
"webpack-dev-server": "^4.7.4"
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
BIN
public/favicon.ico
Normal file
|
After Width: | Height: | Size: 17 KiB |
89
public/frame.html
Normal file
|
|
@ -0,0 +1,89 @@
|
||||||
|
<!DOCTYPE html>
|
||||||
|
|
||||||
|
<script>
|
||||||
|
const checkDatePrototype = () => {
|
||||||
|
if (!Date.prototype.setDate.toString().includes('[native code]')) {
|
||||||
|
return 'Failed Date.prototype.setDate.toString()';
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
};
|
||||||
|
|
||||||
|
const checkIntlConstructor = () => {
|
||||||
|
if (!Object.getPrototypeOf(Intl.DateTimeFormat.prototype).constructor.toString().includes('Object')) {
|
||||||
|
return 'Failed Object.getPrototypeOf(Intl.DateTimeFormat.prototype).constructor.toString()';
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
};
|
||||||
|
|
||||||
|
const checkIntlPrototype = () => {
|
||||||
|
if (!Intl.DateTimeFormat.prototype.resolvedOptions.toString().includes('[native code]')) {
|
||||||
|
return 'Failed Intl.DateTimeFormat.prototype.resolvedOptions.toString()';
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
};
|
||||||
|
|
||||||
|
const checkNavigatorProperties = (key) => {
|
||||||
|
if (Object.getOwnPropertyDescriptor(navigator, key) !== undefined) {
|
||||||
|
return 'Failed Object.getOwnPropertyDescriptor(navigator, key)';
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
};
|
||||||
|
|
||||||
|
const checkNavigatorValue = (key) => {
|
||||||
|
if (
|
||||||
|
Object.getOwnPropertyDescriptor(Navigator.prototype, key).value !==
|
||||||
|
undefined
|
||||||
|
) {
|
||||||
|
return 'Failed object.getOwnPropertyDescriptor(Navigator.prototype, key).value';
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
};
|
||||||
|
|
||||||
|
const checkNavigatorPrototype = (key) => {
|
||||||
|
try {
|
||||||
|
// eslint-disable-next-line no-unused-vars
|
||||||
|
const check = Navigator.prototype[key];
|
||||||
|
return 'Failed Navigator.prototype[key]';
|
||||||
|
} catch (err) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
const getNavigatorValue = (type) =>
|
||||||
|
[checkNavigatorProperties(type),
|
||||||
|
checkNavigatorValue(type),
|
||||||
|
checkNavigatorPrototype(type)].filter(Boolean);
|
||||||
|
|
||||||
|
const data = {
|
||||||
|
type: 'frameData',
|
||||||
|
data: {
|
||||||
|
locale: {
|
||||||
|
value: Intl.DateTimeFormat().resolvedOptions().locale,
|
||||||
|
issues: [checkIntlPrototype(), checkIntlConstructor()].filter(Boolean)
|
||||||
|
},
|
||||||
|
timeZone: {
|
||||||
|
value: Intl.DateTimeFormat().resolvedOptions().timeZone,
|
||||||
|
issues: [checkIntlPrototype(), checkIntlConstructor()].filter(Boolean)
|
||||||
|
},
|
||||||
|
timezoneOffset: {
|
||||||
|
value: new Date().getTimezoneOffset(),
|
||||||
|
issues: [checkDatePrototype()].filter(Boolean),
|
||||||
|
},
|
||||||
|
dateString: {
|
||||||
|
value: new Date().toString(),
|
||||||
|
issues: [checkDatePrototype()].filter(Boolean)
|
||||||
|
},
|
||||||
|
dateLocale: {
|
||||||
|
value: new Date().toLocaleString(),
|
||||||
|
issues: [checkDatePrototype()].filter(Boolean),
|
||||||
|
},
|
||||||
|
userAgent: {
|
||||||
|
value: navigator.userAgent,
|
||||||
|
issues: getNavigatorValue('userAgent'),
|
||||||
|
},
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
parent.postMessage(data, '*');
|
||||||
|
|
||||||
|
</script>
|
||||||
93
public/index.html
Normal file
|
|
@ -0,0 +1,93 @@
|
||||||
|
|
||||||
|
<!DOCTYPE html>
|
||||||
|
<script>
|
||||||
|
const initialDataObj = {
|
||||||
|
locale: {
|
||||||
|
value: Intl.DateTimeFormat().resolvedOptions().locale,
|
||||||
|
issues: [],
|
||||||
|
},
|
||||||
|
timeZone: {
|
||||||
|
value: Intl.DateTimeFormat().resolvedOptions().timeZone,
|
||||||
|
issues: [],
|
||||||
|
},
|
||||||
|
timezoneOffset: {
|
||||||
|
value: new Date().getTimezoneOffset(),
|
||||||
|
issues: [],
|
||||||
|
},
|
||||||
|
dateString: {
|
||||||
|
value: new Date().toString(),
|
||||||
|
issues: [],
|
||||||
|
},
|
||||||
|
dateLocale: {
|
||||||
|
value: new Date().toLocaleString(),
|
||||||
|
issues: [],
|
||||||
|
},
|
||||||
|
userAgent: {
|
||||||
|
value: navigator.userAgent,
|
||||||
|
issues: [],
|
||||||
|
},
|
||||||
|
};
|
||||||
|
</script>
|
||||||
|
<html lang="en">
|
||||||
|
<head>
|
||||||
|
<meta charset="utf-8" />
|
||||||
|
<link rel="icon" href="%PUBLIC_URL%/favicon.ico" />
|
||||||
|
<meta name="viewport" content="width=device-width, initial-scale=1" />
|
||||||
|
<meta name="theme-color" content="#943ec5" />
|
||||||
|
<meta
|
||||||
|
name="description"
|
||||||
|
content="Check if your location is actually hidden."
|
||||||
|
/>
|
||||||
|
<meta name="author" content="z0ccc" />
|
||||||
|
<meta property="og:title" content="LocateJS" />
|
||||||
|
<meta property="og:url" content="https://locatejs.com" />
|
||||||
|
<meta
|
||||||
|
property="og:image:secure"
|
||||||
|
content="https://locatejs.com/preview.png"
|
||||||
|
/>
|
||||||
|
<meta
|
||||||
|
property="og:img"
|
||||||
|
content="https://locatejs.com/preview.png"
|
||||||
|
/>
|
||||||
|
<meta
|
||||||
|
property="og:description"
|
||||||
|
content="Check if your location is actually hidden."
|
||||||
|
/>
|
||||||
|
<meta
|
||||||
|
name="twitter:card"
|
||||||
|
content="https://locatejs.com/preview.png"
|
||||||
|
/>
|
||||||
|
<meta name="twitter:title" content="LocateJS" />
|
||||||
|
<meta
|
||||||
|
name="twitter:description"
|
||||||
|
content="Check if your location is actually hidden."
|
||||||
|
/>
|
||||||
|
<meta
|
||||||
|
name="twitter:image"
|
||||||
|
content="https://locatejs.com/preview.png"
|
||||||
|
/>
|
||||||
|
<link rel="manifest" href="%PUBLIC_URL%/manifest.json" />
|
||||||
|
<link rel="apple-touch-icon" href="%PUBLIC_URL%/logo192.png" />
|
||||||
|
<!--
|
||||||
|
manifest.json provides metadata used when your web app is installed on a
|
||||||
|
user's mobile device or desktop. See https://developers.google.com/web/fundamentals/web-app-manifest/
|
||||||
|
-->
|
||||||
|
<link rel="manifest" href="%PUBLIC_URL%/manifest.json" />
|
||||||
|
<title>LocateJS - Check if your location is actually hidden</title>
|
||||||
|
</head>
|
||||||
|
<body>
|
||||||
|
<noscript>You need to enable JavaScript to run this app.</noscript>
|
||||||
|
<div id="root"></div>
|
||||||
|
<!--
|
||||||
|
This HTML file is a template.
|
||||||
|
If you open it directly in the browser, you will see an empty page.
|
||||||
|
|
||||||
|
You can add webfonts, meta tags, or analytics to this file.
|
||||||
|
The build step will place the bundled scripts into the <body> tag.
|
||||||
|
|
||||||
|
To begin the development, run `npm start` or `yarn start`.
|
||||||
|
To create a production bundle, use `npm run build` or `yarn build`.
|
||||||
|
-->
|
||||||
|
<iframe id="iframe" sandbox="allow-same-origin" style="display: none"></iframe>
|
||||||
|
</body>
|
||||||
|
</html>
|
||||||
BIN
public/logo192.png
Normal file
|
After Width: | Height: | Size: 6.1 KiB |
BIN
public/logo512.png
Normal file
|
After Width: | Height: | Size: 22 KiB |
25
public/manifest.json
Normal file
|
|
@ -0,0 +1,25 @@
|
||||||
|
{
|
||||||
|
"short_name": "LocateJS",
|
||||||
|
"name": "LocateJS",
|
||||||
|
"icons": [
|
||||||
|
{
|
||||||
|
"src": "favicon.ico",
|
||||||
|
"sizes": "64x64 32x32 24x24 16x16",
|
||||||
|
"type": "image/x-icon"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"src": "logo192.png",
|
||||||
|
"type": "image/png",
|
||||||
|
"sizes": "192x192"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"src": "logo512.png",
|
||||||
|
"type": "image/png",
|
||||||
|
"sizes": "512x512"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"start_url": ".",
|
||||||
|
"display": "standalone",
|
||||||
|
"theme_color": "rgb(87, 35, 117)",
|
||||||
|
"background_color": "rgb(87, 35, 117)"
|
||||||
|
}
|
||||||
BIN
public/preview.png
Normal file
|
After Width: | Height: | Size: 173 KiB |
3
public/robots.txt
Normal file
|
|
@ -0,0 +1,3 @@
|
||||||
|
# https://www.robotstxt.org/robotstxt.html
|
||||||
|
User-agent: *
|
||||||
|
Disallow:
|
||||||
79
public/worker.js
Normal file
|
|
@ -0,0 +1,79 @@
|
||||||
|
const checkDatePrototype = () => {
|
||||||
|
if (!Date.prototype.setDate.toString().includes('[native code]')) {
|
||||||
|
return 'Failed Date.prototype.setDate.toString()';
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
};
|
||||||
|
|
||||||
|
const checkIntlConstructor = () => {
|
||||||
|
if (
|
||||||
|
!Object.getPrototypeOf(Intl.DateTimeFormat.prototype)
|
||||||
|
.constructor.toString()
|
||||||
|
.includes('Object')
|
||||||
|
) {
|
||||||
|
return 'Failed Object.getPrototypeOf(Intl.DateTimeFormat.prototype).constructor.toString()';
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
};
|
||||||
|
|
||||||
|
const checkIntlPrototype = () => {
|
||||||
|
if (
|
||||||
|
!Intl.DateTimeFormat.prototype.resolvedOptions
|
||||||
|
.toString()
|
||||||
|
.includes('[native code]')
|
||||||
|
) {
|
||||||
|
return 'Failed Intl.DateTimeFormat.prototype.resolvedOptions.toString()';
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
};
|
||||||
|
|
||||||
|
const checkNavigatorProperties = (key) => {
|
||||||
|
if (Object.getOwnPropertyDescriptor(navigator, key) !== undefined) {
|
||||||
|
return 'Failed Object.getOwnPropertyDescriptor(navigator, key)';
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
};
|
||||||
|
|
||||||
|
const checkNavigatorPrototype = (key) => {
|
||||||
|
try {
|
||||||
|
// eslint-disable-next-line no-unused-vars
|
||||||
|
const check = Navigator.prototype[key];
|
||||||
|
return 'Failed Navigator.prototype[key]';
|
||||||
|
} catch (err) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
const getNavigatorValue = (type) =>
|
||||||
|
[checkNavigatorProperties(type), checkNavigatorPrototype(type)].filter(
|
||||||
|
Boolean
|
||||||
|
);
|
||||||
|
|
||||||
|
const data = {
|
||||||
|
locale: {
|
||||||
|
value: Intl.DateTimeFormat().resolvedOptions().locale,
|
||||||
|
issues: [checkIntlPrototype(), checkIntlConstructor()].filter(Boolean),
|
||||||
|
},
|
||||||
|
timeZone: {
|
||||||
|
value: Intl.DateTimeFormat().resolvedOptions().timeZone,
|
||||||
|
issues: [checkIntlPrototype(), checkIntlConstructor()].filter(Boolean),
|
||||||
|
},
|
||||||
|
timezoneOffset: {
|
||||||
|
value: new Date().getTimezoneOffset(),
|
||||||
|
issues: [checkDatePrototype()].filter(Boolean),
|
||||||
|
},
|
||||||
|
dateString: {
|
||||||
|
value: new Date().toString(),
|
||||||
|
issues: [checkDatePrototype()].filter(Boolean),
|
||||||
|
},
|
||||||
|
dateLocale: {
|
||||||
|
value: new Date().toLocaleString(),
|
||||||
|
issues: [checkDatePrototype()].filter(Boolean),
|
||||||
|
},
|
||||||
|
userAgent: {
|
||||||
|
value: navigator.userAgent,
|
||||||
|
issues: getNavigatorValue('userAgent'),
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
postMessage(data);
|
||||||
|
Before Width: | Height: | Size: 7.6 KiB |
|
Before Width: | Height: | Size: 1.8 KiB |
|
|
@ -1 +0,0 @@
|
||||||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 448 512"><path fill="#fff" d="M256 64C256 46.33 270.3 32 288 32H415.1C415.1 32 415.1 32 415.1 32C420.3 32 424.5 32.86 428.2 34.43C431.1 35.98 435.5 38.27 438.6 41.3C438.6 41.35 438.6 41.4 438.7 41.44C444.9 47.66 447.1 55.78 448 63.9C448 63.94 448 63.97 448 64V192C448 209.7 433.7 224 416 224C398.3 224 384 209.7 384 192V141.3L214.6 310.6C202.1 323.1 181.9 323.1 169.4 310.6C156.9 298.1 156.9 277.9 169.4 265.4L338.7 96H288C270.3 96 256 81.67 256 64V64zM0 128C0 92.65 28.65 64 64 64H160C177.7 64 192 78.33 192 96C192 113.7 177.7 128 160 128H64V416H352V320C352 302.3 366.3 288 384 288C401.7 288 416 302.3 416 320V416C416 451.3 387.3 480 352 480H64C28.65 480 0 451.3 0 416V128z"/></svg>
|
|
||||||
|
Before Width: | Height: | Size: 736 B |
|
|
@ -1,17 +0,0 @@
|
||||||
<svg
|
|
||||||
xmlns="http://www.w3.org/2000/svg"
|
|
||||||
preserveAspectRatio="xMidYMid meet"
|
|
||||||
version="1"
|
|
||||||
viewBox="0 0 4389 1056"
|
|
||||||
>
|
|
||||||
<g fill="#fff" stroke="none" transform="matrix(.1 0 0 -.1 0 1056)">
|
|
||||||
<path d="M4890 10554c-19-2-102-11-185-20-814-84-1627-377-2332-841-666-437-1242-1047-1652-1748-345-590-582-1279-670-1950-34-261-44-422-44-710 0-388 28-679 99-1035C523 2160 2153 526 4240 106c360-73 640-99 1040-99 488 0 847 44 1305 160 665 168 1310 477 1860 891 1004 754 1703 1833 1983 3062 146 636 168 1351 61 2010-273 1699-1362 3160-2913 3911-540 261-1091 420-1716 495-100 12-240 17-535 19-220 2-416 1-435-1zm870-978c947-115 1772-494 2460-1130 303-281 615-672 815-1025 491-866 669-1875 504-2861-113-681-390-1322-814-1885-233-310-552-627-869-862-1529-1139-3637-1136-5161 7-391 294-746 669-1012 1070-367 553-599 1170-683 1814-113 869 22 1705 399 2472 103 210 151 294 287 499 316 476 726 886 1199 1199 340 225 629 368 999 495 349 120 681 187 1106 225 112 10 641-3 770-18z"></path>
|
|
||||||
<path d="M5045 8784c-729-59-1353-298-1925-740-149-114-480-445-594-594-405-524-638-1090-722-1755-22-172-25-595-6-760 86-742 352-1362 822-1916 176-207 457-462 565-513 153-73 342-56 483 44 140 98 210 245 199 420-9 156-62 251-211 377-479 408-770 907-876 1504-31 174-39 528-16 719 93 759 503 1435 1121 1847 275 183 609 315 946 372 442 76 912 34 1329-116 593-214 1089-664 1375-1248 194-396 270-726 270-1165-1-320-34-522-135-815-143-414-360-722-786-1116-311-287-141-788 284-834 71-8 180 13 256 51 97 47 353 285 519 482 474 563 751 1223 818 1954 17 188 6 625-19 798-99 656-347 1220-760 1730-118 144-361 385-514 507-520 416-1102 662-1770 748-118 16-543 28-653 19z"></path>
|
|
||||||
<path d="M5175 6514c-92-14-198-39-260-61-458-165-766-607-768-1103-1-309 89-544 306-792 141-160 207-284 259-481 21-80 22-101 27-972l6-890 24-60c79-199 237-332 440-372 282-56 561 117 657 407 17 51 19 120 24 935 6 867 6 881 28 958 51 183 137 343 254 474 207 230 308 486 308 782 0 328-118 612-349 842-171 170-340 263-576 314-69 16-321 28-380 19z"></path>
|
|
||||||
<path d="M39255 8795c-156-35-252-89-375-214-79-79-103-111-137-181-23-47-50-112-60-145-17-57-18-203-21-2915-2-2552-1-2863 13-2933 31-154 97-272 219-392 93-91 176-144 291-183l80-27 1972-3c2216-3 2045-8 2213 74 82 41 109 61 196 148s107 114 147 196c56 113 77 207 77 338-1 222-69 380-232 538-87 85-141 122-228 159-131 55-109 54-1270 57-591 2-1274 4-1517 6l-443 2-2 2433-3 2432-23 73c-86 279-301 480-574 536-92 19-240 20-323 1z"></path>
|
|
||||||
<path d="M12765 8744c-101-17-243-74-332-136-160-109-287-325-313-531-10-84-2-193 21-280 9-37 516-1310 1838-4620 424-1062 439-1096 540-1209 63-71 201-159 301-192 130-43 243-53 364-32 163 27 276 83 399 196 130 120 126 111 585 1263 231 578 735 1842 1120 2807 530 1328 705 1776 718 1840 59 295-87 628-344 784-226 137-480 151-719 41-147-68-257-165-333-294-21-36-377-914-790-1950l-753-1884-756 1890c-416 1039-772 1916-791 1949-48 81-170 202-255 252-114 67-240 104-365 107-58 2-118 1-135-1z"></path>
|
|
||||||
<path d="M26688 8740c-304-52-566-312-619-614-18-104-7-273 24-369 75-231 263-414 512-498l70-24 792-3 793-3 2-2442 3-2442 23-70c87-258 284-451 528-516 108-29 287-29 396-1 270 72 486 299 544 571 11 55 14 457 14 2484v2417h753c474 0 776 4 816 11 164 26 285 91 416 223 151 152 214 307 215 523 0 116-11 179-47 277-66 176-234 350-413 426-140 59 58 54-2465 56-1270 1-2331-2-2357-6z"></path>
|
|
||||||
<path d="M34355 8735c-232-43-433-192-538-402-33-64-2231-5575-2255-5652-13-42-17-90-17-196 1-131 3-146 32-227 78-220 214-370 416-455 112-47 194-64 307-65 143 0 264 32 382 103 119 70 223 181 285 304 12 22 357 879 768 1905s750 1868 754 1872c3 4 339-826 745-1845 406-1018 755-1885 775-1925 133-274 456-451 752-413 109 14 155 29 279 88 79 38 107 59 180 132 71 71 94 103 133 181 89 180 112 363 68 530-22 86-2217 5595-2263 5682-150 281-487 442-803 383z"></path>
|
|
||||||
<path d="M19760 8661c-129-35-236-96-337-194-264-253-298-670-80-966 23-31 467-625 987-1321l945-1265 5-1255 5-1255 24-70c65-194 179-338 343-437 106-63 202-90 343-95 153-6 238 11 364 73 169 83 316 254 379 441l27 78 5 1250 5 1250 1021 1280c561 704 1039 1305 1062 1335 94 127 137 261 135 430-2 240-95 431-283 581-141 113-312 167-495 156-199-13-348-76-478-205-29-28-421-512-872-1075l-819-1025-763 1018c-419 561-781 1040-805 1066-133 145-329 224-548 223-68 0-128-6-170-18z"></path>
|
|
||||||
</g>
|
|
||||||
</svg>
|
|
||||||
|
Before Width: | Height: | Size: 4.2 KiB |
166
src/components/App.css
Normal file
|
|
@ -0,0 +1,166 @@
|
||||||
|
:root {
|
||||||
|
--main: #943ec5;
|
||||||
|
--text: rgb(0, 0, 0, 0.8);
|
||||||
|
--border: #ddd;
|
||||||
|
--grey: #c4c4c4;
|
||||||
|
--lightGrey: #f8f8f8;
|
||||||
|
--issueBackground: #f8d7da;
|
||||||
|
--issueText: #721c24;
|
||||||
|
--fill: #9fa6b2;
|
||||||
|
--link: #943ec5;
|
||||||
|
}
|
||||||
|
|
||||||
|
body {
|
||||||
|
margin: 0;
|
||||||
|
font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", "Roboto", "Oxygen",
|
||||||
|
"Ubuntu", "Cantarell", "Fira Sans", "Droid Sans", "Helvetica Neue",
|
||||||
|
sans-serif;
|
||||||
|
-webkit-font-smoothing: antialiased;
|
||||||
|
-moz-osx-font-smoothing: grayscale;
|
||||||
|
line-height: 24px;
|
||||||
|
font-size: 14px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.background {
|
||||||
|
position: fixed;
|
||||||
|
width: 100%;
|
||||||
|
height: 100%;
|
||||||
|
background: var(--main);
|
||||||
|
background: linear-gradient(
|
||||||
|
165deg,
|
||||||
|
rgba(87, 35, 117, 1) 0%,
|
||||||
|
rgba(148, 62, 197, 1) 40%,
|
||||||
|
rgba(169, 100, 208, 1) 100%
|
||||||
|
);
|
||||||
|
z-index: -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
.App {
|
||||||
|
min-height: 100vh;
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
}
|
||||||
|
|
||||||
|
h1 {
|
||||||
|
margin: 0 0 10px 0;
|
||||||
|
font-weight: 600;
|
||||||
|
font-size: 16px;
|
||||||
|
word-break: break-all;
|
||||||
|
}
|
||||||
|
|
||||||
|
p {
|
||||||
|
margin: 10px 0 0 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
b {
|
||||||
|
font-weight: 600;
|
||||||
|
}
|
||||||
|
|
||||||
|
.tableWrap {
|
||||||
|
border: 1px solid var(--border);
|
||||||
|
border-radius: 6px;
|
||||||
|
}
|
||||||
|
|
||||||
|
table {
|
||||||
|
width: 100%;
|
||||||
|
table-layout: fixed;
|
||||||
|
border-collapse: collapse;
|
||||||
|
font-size: inherit;
|
||||||
|
color: inherit;
|
||||||
|
line-height: inherit;
|
||||||
|
}
|
||||||
|
|
||||||
|
tr:not(:last-child) {
|
||||||
|
border-bottom: 1px solid var(--border);
|
||||||
|
}
|
||||||
|
|
||||||
|
td {
|
||||||
|
padding: 10px;
|
||||||
|
word-break: break-all;
|
||||||
|
}
|
||||||
|
|
||||||
|
td:first-child {
|
||||||
|
width: 100px;
|
||||||
|
font-weight: 600;
|
||||||
|
word-break: normal;
|
||||||
|
}
|
||||||
|
|
||||||
|
td:nth-child(3) {
|
||||||
|
width: 40px;
|
||||||
|
font-weight: 600;
|
||||||
|
word-break: normal;
|
||||||
|
}
|
||||||
|
|
||||||
|
ul {
|
||||||
|
padding-left: 20px;
|
||||||
|
margin: 0px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.link {
|
||||||
|
color: var(--link);
|
||||||
|
text-decoration: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
.link:hover {
|
||||||
|
text-decoration: underline;
|
||||||
|
}
|
||||||
|
|
||||||
|
.checkBox {
|
||||||
|
margin: 0 0 10px 0;
|
||||||
|
display: block;
|
||||||
|
}
|
||||||
|
|
||||||
|
input[type="checkbox"] {
|
||||||
|
margin: 0 6px 0 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.button {
|
||||||
|
display: block;
|
||||||
|
background-color: var(--main);
|
||||||
|
color: #fff;
|
||||||
|
border-radius: 6px;
|
||||||
|
box-sizing: border-box;
|
||||||
|
text-align: center;
|
||||||
|
width: 100%;
|
||||||
|
height: 46px;
|
||||||
|
border: none;
|
||||||
|
cursor: pointer;
|
||||||
|
font-family: inherit;
|
||||||
|
font-size: inherit;
|
||||||
|
-webkit-appearance: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
.button:hover {
|
||||||
|
opacity: 0.7;
|
||||||
|
}
|
||||||
|
|
||||||
|
.boxWrap {
|
||||||
|
border: 1px solid var(--border);
|
||||||
|
border-radius: 6px;
|
||||||
|
padding: 10px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.hash {
|
||||||
|
text-align: center;
|
||||||
|
font-weight: 600;
|
||||||
|
}
|
||||||
|
|
||||||
|
@media screen and (max-width: 575px) {
|
||||||
|
body {
|
||||||
|
font-size: 13px;
|
||||||
|
}
|
||||||
|
|
||||||
|
h1 {
|
||||||
|
font-size: 15px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.contentIcon {
|
||||||
|
flex: none;
|
||||||
|
margin: 0 18px 0 0;
|
||||||
|
width: 28px !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
td:first-child {
|
||||||
|
width: 70px;
|
||||||
|
}
|
||||||
|
}
|
||||||
13
src/components/App.js
Normal file
|
|
@ -0,0 +1,13 @@
|
||||||
|
import './App.css';
|
||||||
|
import GitHub from './GitHub';
|
||||||
|
import MainColumn from './MainColumn';
|
||||||
|
|
||||||
|
const App = () => (
|
||||||
|
<div className="App">
|
||||||
|
<GitHub />
|
||||||
|
<div className="background" />
|
||||||
|
<MainColumn />
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
|
||||||
|
export default App;
|
||||||
5
src/components/Block.js
Normal file
|
|
@ -0,0 +1,5 @@
|
||||||
|
const ContentBlock = ({ children }) => (
|
||||||
|
<div className="contentBlock">{children}</div>
|
||||||
|
);
|
||||||
|
|
||||||
|
export default ContentBlock;
|
||||||
23
src/components/Blocks.css
Normal file
|
|
@ -0,0 +1,23 @@
|
||||||
|
.contentBlock {
|
||||||
|
color: var(--text);
|
||||||
|
background-color: #fff;
|
||||||
|
border-radius: 6px;
|
||||||
|
box-sizing: border-box;
|
||||||
|
padding: 20px;
|
||||||
|
box-shadow: rgba(0, 0, 0, 0.1) 0px 4px 12px;
|
||||||
|
margin: 0 0 24px 0;
|
||||||
|
min-width: 500px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.loadBlock {
|
||||||
|
margin: 0 12px 12px 12px !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
@media screen and (max-width: 575px) {
|
||||||
|
.contentBlock {
|
||||||
|
padding: 18px;
|
||||||
|
width: 100%;
|
||||||
|
min-width: 0;
|
||||||
|
margin: 0 0 12px 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
103
src/components/Blocks.js
Normal file
|
|
@ -0,0 +1,103 @@
|
||||||
|
import './Blocks.css';
|
||||||
|
import { useState, useEffect } from 'react';
|
||||||
|
import DataContext from './Context';
|
||||||
|
import DataBlock from './DataBlock';
|
||||||
|
import GeolocationBlock from './GeolocationBlock';
|
||||||
|
import delayedData from '../utils/data';
|
||||||
|
|
||||||
|
const getWebWorker = () => {
|
||||||
|
let w;
|
||||||
|
if (typeof w === 'undefined') {
|
||||||
|
w = new Worker('/worker.js');
|
||||||
|
}
|
||||||
|
return w;
|
||||||
|
};
|
||||||
|
|
||||||
|
const Blocks = () => {
|
||||||
|
const [workerData, setWorkerData] = useState();
|
||||||
|
const [frameData, setFrameData] = useState();
|
||||||
|
|
||||||
|
// eslint-disable-next-line no-undef
|
||||||
|
const initialData = initialDataObj;
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
const frame = document.createElement('iframe');
|
||||||
|
document.body.appendChild(frame);
|
||||||
|
frame.style.display = 'none';
|
||||||
|
frame.src = '/frame.html';
|
||||||
|
const receiveMessage = (event) => {
|
||||||
|
if (event.data.type === 'frameData') {
|
||||||
|
setFrameData(event.data.data);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
window.addEventListener('message', receiveMessage, false);
|
||||||
|
|
||||||
|
if (window.Worker.length) {
|
||||||
|
getWebWorker().onmessage = (event) => {
|
||||||
|
setWorkerData(event.data);
|
||||||
|
};
|
||||||
|
} else {
|
||||||
|
setWorkerData(true);
|
||||||
|
}
|
||||||
|
}, []);
|
||||||
|
|
||||||
|
return (
|
||||||
|
<>
|
||||||
|
{initialData && delayedData && frameData && workerData ? (
|
||||||
|
<DataContext.Provider
|
||||||
|
value={{
|
||||||
|
initialData,
|
||||||
|
delayedData,
|
||||||
|
frameData,
|
||||||
|
workerData,
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
<div className="centerBlockInner">
|
||||||
|
<DataBlock
|
||||||
|
title="Intl.DateTimeFormat().resolvedOptions().timeZone"
|
||||||
|
type="timeZone"
|
||||||
|
/>
|
||||||
|
<DataBlock
|
||||||
|
title="Intl.DateTimeFormat().resolvedOptions().locale"
|
||||||
|
type="locale"
|
||||||
|
/>
|
||||||
|
<DataBlock title="navigator.userAgent" type="userAgent" />
|
||||||
|
</div>
|
||||||
|
<div className="centerBlockInner">
|
||||||
|
<DataBlock title="new Date().toString()" type="dateString" />
|
||||||
|
<DataBlock title="new Date().toLocaleString()" type="dateLocale" />
|
||||||
|
<DataBlock
|
||||||
|
title="new Date().getTimezoneOffset()"
|
||||||
|
type="timezoneOffset"
|
||||||
|
/>
|
||||||
|
<GeolocationBlock />
|
||||||
|
</div>
|
||||||
|
<div className="centerBlockMobile">
|
||||||
|
<DataBlock
|
||||||
|
title="Intl.DateTimeFormat().resolvedOptions().timeZone"
|
||||||
|
type="timeZone"
|
||||||
|
/>
|
||||||
|
<DataBlock
|
||||||
|
title="Intl.DateTimeFormat().resolvedOptions().locale"
|
||||||
|
type="locale"
|
||||||
|
/>
|
||||||
|
<DataBlock title="new Date().toString()" type="dateString" />
|
||||||
|
<DataBlock title="new Date().toLocaleString()" type="dateLocale" />
|
||||||
|
<DataBlock
|
||||||
|
title="new Date().getTimezoneOffset()"
|
||||||
|
type="timezoneOffset"
|
||||||
|
/>
|
||||||
|
<DataBlock title="navigator.userAgent" type="userAgent" />
|
||||||
|
<GeolocationBlock />
|
||||||
|
</div>
|
||||||
|
</DataContext.Provider>
|
||||||
|
) : (
|
||||||
|
<div className="contentBlock loadBlock">
|
||||||
|
<center>Loading...</center>
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
|
</>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
export default Blocks;
|
||||||
29
src/components/ConnectionBlock.js
Normal file
|
|
@ -0,0 +1,29 @@
|
||||||
|
import { useContext } from 'react';
|
||||||
|
import DataContext from './Context';
|
||||||
|
import Block from './Block';
|
||||||
|
import { getConnection } from '../utils/connection';
|
||||||
|
import TableRow from './TableRow';
|
||||||
|
|
||||||
|
const ConnectionBlock = () => {
|
||||||
|
const { connectionData } = useContext(DataContext);
|
||||||
|
return (
|
||||||
|
<Block>
|
||||||
|
<h1>Connection</h1>
|
||||||
|
<div className="tableWrap">
|
||||||
|
<table>
|
||||||
|
<tbody>
|
||||||
|
{getConnection(connectionData).map((item) => (
|
||||||
|
<TableRow key={item.key} title={item.key} value={item.value} issues={item.issues} />
|
||||||
|
))}
|
||||||
|
</tbody>
|
||||||
|
</table>
|
||||||
|
</div>
|
||||||
|
<p>
|
||||||
|
Your IP address reveals information about your
|
||||||
|
connection. Using a VPN or Tor will hide your connection info.
|
||||||
|
</p>
|
||||||
|
</Block>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
export default ConnectionBlock;
|
||||||
5
src/components/Context.js
Normal file
|
|
@ -0,0 +1,5 @@
|
||||||
|
import { createContext } from 'react';
|
||||||
|
|
||||||
|
const DataContext = createContext();
|
||||||
|
|
||||||
|
export default DataContext;
|
||||||
30
src/components/DataBlock.js
Normal file
|
|
@ -0,0 +1,30 @@
|
||||||
|
import { useContext } from 'react';
|
||||||
|
import DataContext from './Context';
|
||||||
|
import Block from './Block';
|
||||||
|
import TableRow from './TableRow';
|
||||||
|
|
||||||
|
const DataBlock = ({ title, type }) => {
|
||||||
|
const { initialData, delayedData, frameData, workerData } = useContext(DataContext);
|
||||||
|
|
||||||
|
return (
|
||||||
|
<Block>
|
||||||
|
<h1>{title}</h1>
|
||||||
|
<div className="tableWrap">
|
||||||
|
<table>
|
||||||
|
<tbody>
|
||||||
|
<TableRow title="Initial" value={initialData[type].value} issues={initialData[type].issues} />
|
||||||
|
<TableRow title="Delayed" value={delayedData[type].value} issues={delayedData[type].issues} />
|
||||||
|
<TableRow title="Frame" value={frameData[type].value} issues={frameData[type].issues} />
|
||||||
|
<TableRow title="Web worker" value={window.Worker.length ? workerData[type].value : null} issues={window.Worker.length ? workerData[type].issues : ['Web workers blocked']} />
|
||||||
|
</tbody>
|
||||||
|
</table>
|
||||||
|
</div>
|
||||||
|
<p>
|
||||||
|
Date and language data can be used to identify your
|
||||||
|
location. Changing the settings on your computer can prevent this.
|
||||||
|
</p>
|
||||||
|
</Block>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
export default DataBlock;
|
||||||
60
src/components/GeolocationBlock.js
Normal file
|
|
@ -0,0 +1,60 @@
|
||||||
|
import { useState } from 'react';
|
||||||
|
import Block from './Block';
|
||||||
|
import getGeolocation from '../utils/geolocation';
|
||||||
|
import TableRow from './TableRow';
|
||||||
|
|
||||||
|
const GeolocationBlock = () => {
|
||||||
|
const [geolocationData, setGeolocationData] = useState();
|
||||||
|
const [buttonValue, setbuttonValue] = useState('Allow Geolocation API');
|
||||||
|
return (
|
||||||
|
<Block>
|
||||||
|
<h1>HTML Geolocation API</h1>
|
||||||
|
{geolocationData ? (
|
||||||
|
<>
|
||||||
|
{typeof geolocationData === 'string' ? (
|
||||||
|
<div className="boxWrap">
|
||||||
|
<div className="hash">{`${geolocationData}`}</div>
|
||||||
|
</div>
|
||||||
|
) : (
|
||||||
|
<>
|
||||||
|
<p style={{ marginBottom: '10px' }}>
|
||||||
|
This data is not included in location prediction
|
||||||
|
</p>
|
||||||
|
<div className="tableWrap">
|
||||||
|
<table>
|
||||||
|
<tbody>
|
||||||
|
{geolocationData.map((item) => (
|
||||||
|
<TableRow
|
||||||
|
key={item.key}
|
||||||
|
title={item.key}
|
||||||
|
value={item.value}
|
||||||
|
issues={item.issues}
|
||||||
|
/>
|
||||||
|
))}
|
||||||
|
</tbody>
|
||||||
|
</table>
|
||||||
|
</div>
|
||||||
|
</>
|
||||||
|
)}
|
||||||
|
</>
|
||||||
|
) : (
|
||||||
|
<input
|
||||||
|
type="submit"
|
||||||
|
onClick={() => {
|
||||||
|
getGeolocation(setGeolocationData);
|
||||||
|
setbuttonValue('Loading...');
|
||||||
|
}}
|
||||||
|
className="button"
|
||||||
|
value={buttonValue}
|
||||||
|
/>
|
||||||
|
)}
|
||||||
|
<p>
|
||||||
|
HTML Geolocation API is used to get the geographical position of a user.
|
||||||
|
Since this can compromise privacy, its not available unless the user
|
||||||
|
approves it.
|
||||||
|
</p>
|
||||||
|
</Block>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
export default GeolocationBlock;
|
||||||
18
src/components/GitHub.css
Normal file
|
|
@ -0,0 +1,18 @@
|
||||||
|
.gitHubButton {
|
||||||
|
position: absolute;
|
||||||
|
top: 10px;
|
||||||
|
right: 10px;
|
||||||
|
}
|
||||||
|
|
||||||
|
@media screen and (max-width: 900px) {
|
||||||
|
.gitHubButton {
|
||||||
|
position: absolute;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@media screen and (max-width: 575px) {
|
||||||
|
.gitHubButton {
|
||||||
|
top: 8px;
|
||||||
|
right: 8px;
|
||||||
|
}
|
||||||
|
}
|
||||||
16
src/components/GitHub.js
Normal file
|
|
@ -0,0 +1,16 @@
|
||||||
|
import './GitHub.css';
|
||||||
|
import GitHubButton from 'react-github-btn';
|
||||||
|
|
||||||
|
const GitHub = () => (
|
||||||
|
<div className="gitHubButton">
|
||||||
|
<GitHubButton
|
||||||
|
href="https://github.com/z0ccc/LocateJS"
|
||||||
|
data-color-scheme="no-preference: light; light: light; dark: light;"
|
||||||
|
aria-label="Star z0ccc/LocateJS on GitHub"
|
||||||
|
>
|
||||||
|
Star
|
||||||
|
</GitHubButton>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
|
||||||
|
export default GitHub;
|
||||||
17
src/components/Logo.css
Normal file
|
|
@ -0,0 +1,17 @@
|
||||||
|
.logoWrap {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: center;
|
||||||
|
}
|
||||||
|
|
||||||
|
.logo {
|
||||||
|
width: 225px;
|
||||||
|
margin: 24px 0 18px 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
@media screen and (max-width: 575px) {
|
||||||
|
.logo {
|
||||||
|
width: 160px;
|
||||||
|
margin: 18px 0 12px 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
12
src/components/Logo.js
Normal file
|
|
@ -0,0 +1,12 @@
|
||||||
|
import './Logo.css';
|
||||||
|
import { ReactComponent as LogoImg } from '../images/logo.svg';
|
||||||
|
|
||||||
|
const Logo = () => (
|
||||||
|
<div className="logoWrap">
|
||||||
|
<a href="." className="logo" alt="LocateJS logo">
|
||||||
|
<LogoImg />
|
||||||
|
</a>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
|
||||||
|
export default Logo;
|
||||||
25
src/components/MainColumn.css
Normal file
|
|
@ -0,0 +1,25 @@
|
||||||
|
.centerBlockOuter {
|
||||||
|
display: flex;
|
||||||
|
justify-content: center;
|
||||||
|
gap: 24px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.centerBlockInner {
|
||||||
|
max-width: 500px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.centerBlockMobile {
|
||||||
|
max-width: 500px;
|
||||||
|
padding: 0 12px;
|
||||||
|
display: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
@media screen and (max-width: 1100px) {
|
||||||
|
.centerBlockInner {
|
||||||
|
display: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
.centerBlockMobile {
|
||||||
|
display: block;
|
||||||
|
}
|
||||||
|
}
|
||||||
14
src/components/MainColumn.js
Normal file
|
|
@ -0,0 +1,14 @@
|
||||||
|
import './MainColumn.css';
|
||||||
|
import Logo from './Logo';
|
||||||
|
import Blocks from './Blocks';
|
||||||
|
|
||||||
|
const MainColumn = () => (
|
||||||
|
<>
|
||||||
|
<Logo />
|
||||||
|
<div className="centerBlockOuter">
|
||||||
|
<Blocks />
|
||||||
|
</div>
|
||||||
|
</>
|
||||||
|
);
|
||||||
|
|
||||||
|
export default MainColumn;
|
||||||
16
src/components/PredictionBlock.css
Normal file
|
|
@ -0,0 +1,16 @@
|
||||||
|
.mapImg {
|
||||||
|
width: 100%;
|
||||||
|
border-radius: 6px;
|
||||||
|
border: 1px solid var(--border);
|
||||||
|
box-sizing: border-box;
|
||||||
|
display: block;
|
||||||
|
margin: 0 0 10px 0;
|
||||||
|
background-color: var(--lightGrey);
|
||||||
|
aspect-ratio: 2 / 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
@media screen and (max-width: 575px) {
|
||||||
|
.mapImg {
|
||||||
|
height: auto;
|
||||||
|
}
|
||||||
|
}
|
||||||
44
src/components/PredictionBlock.js
Normal file
|
|
@ -0,0 +1,44 @@
|
||||||
|
import { useContext } from 'react';
|
||||||
|
import DataContext from './Context';
|
||||||
|
import './PredictionBlock.css';
|
||||||
|
import Block from './Block';
|
||||||
|
import PredictionTableRow from './PredictionTableRow';
|
||||||
|
|
||||||
|
const PredictionBlock = () => {
|
||||||
|
const { prediction, mapUrl } = useContext(DataContext);
|
||||||
|
|
||||||
|
return (
|
||||||
|
<Block>
|
||||||
|
<h1>Location Prediction</h1>
|
||||||
|
<img
|
||||||
|
className="mapImg"
|
||||||
|
src={mapUrl}
|
||||||
|
alt="Map of location prediction"
|
||||||
|
/>
|
||||||
|
<div className="tableWrap">
|
||||||
|
<table>
|
||||||
|
<tbody>
|
||||||
|
<PredictionTableRow title="Country" value={prediction.country} percent={prediction.countryPercent} />
|
||||||
|
<PredictionTableRow title="City" value={prediction.city} percent={prediction.cityPercent} />
|
||||||
|
</tbody>
|
||||||
|
</table>
|
||||||
|
</div>
|
||||||
|
<p>
|
||||||
|
Accuracy of the location prediction is dependant on how much
|
||||||
|
authentic info you're exposing. To learn about how to hide your location visit the{' '}
|
||||||
|
<a
|
||||||
|
className="link"
|
||||||
|
target="_blank"
|
||||||
|
rel="noreferrer"
|
||||||
|
alt="Link to GitHub repo"
|
||||||
|
href="https://github.com/z0ccc/LocateJS#locatejs"
|
||||||
|
>
|
||||||
|
GitHub repo
|
||||||
|
</a>
|
||||||
|
.
|
||||||
|
</p>
|
||||||
|
</Block>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
export default PredictionBlock;
|
||||||
9
src/components/PredictionTableRow.js
Normal file
|
|
@ -0,0 +1,9 @@
|
||||||
|
const PredictionTableRow = ({ title, value, percent }) => (
|
||||||
|
<tr>
|
||||||
|
<td>{title}</td>
|
||||||
|
<td>{value || 'N/A'}</td>
|
||||||
|
<td>{percent}%</td>
|
||||||
|
</tr>
|
||||||
|
);
|
||||||
|
|
||||||
|
export default PredictionTableRow;
|
||||||
36
src/components/TableRow.css
Normal file
|
|
@ -0,0 +1,36 @@
|
||||||
|
.circleButton {
|
||||||
|
display: flex;
|
||||||
|
width: 20px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.modalHeader {
|
||||||
|
display: flex;
|
||||||
|
justify-content: space-between;
|
||||||
|
margin: 0 0 6px 0;
|
||||||
|
height: 20px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.modalTitle {
|
||||||
|
font-weight: 600;
|
||||||
|
}
|
||||||
|
|
||||||
|
.closeButton {
|
||||||
|
fill: var(--border);
|
||||||
|
display: flex;
|
||||||
|
width: 12px;
|
||||||
|
cursor: pointer;
|
||||||
|
margin: 0 0 0 12px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.closeButton:hover {
|
||||||
|
fill: var(--grey);
|
||||||
|
}
|
||||||
|
|
||||||
|
.issue {
|
||||||
|
cursor: pointer;
|
||||||
|
}
|
||||||
|
|
||||||
|
.issue:hover {
|
||||||
|
background-color: var(--issueBackground);
|
||||||
|
color: var(--issueText);
|
||||||
|
}
|
||||||
69
src/components/TableRow.js
Normal file
|
|
@ -0,0 +1,69 @@
|
||||||
|
import './TableRow.css';
|
||||||
|
import Modal from 'react-modal';
|
||||||
|
import { useState } from 'react';
|
||||||
|
import { ReactComponent as XCircle } from '../images/xCircle.svg';
|
||||||
|
import { ReactComponent as CheckCircle } from '../images/checkCircle.svg';
|
||||||
|
import { ReactComponent as X } from '../images/x.svg';
|
||||||
|
|
||||||
|
const modalStyles = {
|
||||||
|
content: {
|
||||||
|
top: '50%',
|
||||||
|
left: '50%',
|
||||||
|
right: 'auto',
|
||||||
|
bottom: 'auto',
|
||||||
|
marginRight: '-50%',
|
||||||
|
transform: 'translate(-50%, -50%)',
|
||||||
|
padding: '18px',
|
||||||
|
border: '1px solid var(--border)',
|
||||||
|
borderRadius: '6px',
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
Modal.setAppElement('#root');
|
||||||
|
|
||||||
|
const TableRow = ({ title, value, issues }) => {
|
||||||
|
const issuesExist = issues.length !== 0;
|
||||||
|
const [modalIsOpen, setIsOpen] = useState(false);
|
||||||
|
const dataValue = !value && value !== 0 ? 'null' : value;
|
||||||
|
|
||||||
|
const openModal = () => {
|
||||||
|
if (issuesExist) setIsOpen(true);
|
||||||
|
};
|
||||||
|
|
||||||
|
const closeModal = () => {
|
||||||
|
setIsOpen(false);
|
||||||
|
};
|
||||||
|
return (
|
||||||
|
<>
|
||||||
|
<tr className={issuesExist ? 'issue' : ''} onClick={openModal}>
|
||||||
|
<td>{title}</td>
|
||||||
|
<td>{Array.isArray(value) ? JSON.stringify(value) : dataValue}</td>
|
||||||
|
<td>
|
||||||
|
{issuesExist ? (
|
||||||
|
<XCircle className="circleButton" />
|
||||||
|
) : (
|
||||||
|
<CheckCircle className="circleButton" />
|
||||||
|
)}
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
<Modal
|
||||||
|
isOpen={modalIsOpen}
|
||||||
|
onRequestClose={closeModal}
|
||||||
|
style={modalStyles}
|
||||||
|
contentLabel="Issues Modal"
|
||||||
|
>
|
||||||
|
<div className="modalHeader">
|
||||||
|
<div className="modalTitle">{title} issues</div>
|
||||||
|
<X className="closeButton" onClick={closeModal} />
|
||||||
|
</div>
|
||||||
|
<ul>
|
||||||
|
{issues.filter(Boolean).map((ele, index) => (
|
||||||
|
<li key={index}>{ele}</li>
|
||||||
|
))}
|
||||||
|
</ul>
|
||||||
|
</Modal>
|
||||||
|
</>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
export default TableRow;
|
||||||
25
src/components/TorBlock.js
Normal file
|
|
@ -0,0 +1,25 @@
|
||||||
|
import Block from './Block';
|
||||||
|
import TableRow from './TableRow';
|
||||||
|
|
||||||
|
const TorBlock = ({ isTor }) => (
|
||||||
|
<Block>
|
||||||
|
<h1>Tor Browser</h1>
|
||||||
|
<div className="tableWrap">
|
||||||
|
<table>
|
||||||
|
<tbody>
|
||||||
|
<TableRow
|
||||||
|
title="Detected"
|
||||||
|
value={isTor ? 'True' : 'False'}
|
||||||
|
issues={[]}
|
||||||
|
/>
|
||||||
|
</tbody>
|
||||||
|
</table>
|
||||||
|
</div>
|
||||||
|
<p>
|
||||||
|
Tor Browser is a web browser that hides your location info,
|
||||||
|
causing the location prediction to be inaccurate.
|
||||||
|
</p>
|
||||||
|
</Block>
|
||||||
|
);
|
||||||
|
|
||||||
|
export default TorBlock;
|
||||||
41
src/components/WebRTCBlock.js
Normal file
|
|
@ -0,0 +1,41 @@
|
||||||
|
import Block from './Block';
|
||||||
|
import TableRow from './TableRow';
|
||||||
|
import { getConnection } from '../utils/connection';
|
||||||
|
|
||||||
|
const WebRTCBlock = ({ data }) => (
|
||||||
|
<Block>
|
||||||
|
<h1>WebRTC Leaks</h1>
|
||||||
|
{data.length === 0 ? (
|
||||||
|
<div className="boxWrap">
|
||||||
|
<div className="hash">No WebRTC leaks found</div>
|
||||||
|
</div>
|
||||||
|
)
|
||||||
|
: (
|
||||||
|
<div style={{ display: 'grid', gap: '24px' }}>
|
||||||
|
{data.map((ipData) => (
|
||||||
|
<div className="tableWrap" key={ipData.query}>
|
||||||
|
<table>
|
||||||
|
<tbody>
|
||||||
|
{getConnection(ipData).map((item) => (
|
||||||
|
<TableRow
|
||||||
|
key={item.key}
|
||||||
|
title={item.key}
|
||||||
|
value={item.value}
|
||||||
|
issues={item.issues}
|
||||||
|
/>
|
||||||
|
))}
|
||||||
|
</tbody>
|
||||||
|
</table>
|
||||||
|
</div>
|
||||||
|
))}
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
|
|
||||||
|
<p>
|
||||||
|
WebRTC leaks are a vulnerability that can expose your real IP address.
|
||||||
|
Using a quality VPN or disabling WebRTC will stop the leaks.
|
||||||
|
</p>
|
||||||
|
</Block>
|
||||||
|
);
|
||||||
|
|
||||||
|
export default WebRTCBlock;
|
||||||
5
src/images/checkCircle.svg
Normal file
|
|
@ -0,0 +1,5 @@
|
||||||
|
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512">
|
||||||
|
<g fill="#c3e6cb">
|
||||||
|
<path d="M504 256c0 136.967-111.033 248-248 248S8 392.967 8 256 119.033 8 256 8s248 111.033 248 248zM227.314 387.314l184-184c6.248-6.248 6.248-16.379 0-22.627l-22.627-22.627c-6.248-6.249-16.379-6.249-22.628 0L216 308.118l-70.059-70.059c-6.248-6.248-16.379-6.248-22.628 0l-22.627 22.627c-6.248 6.248-6.248 16.379 0 22.627l104 104c6.249 6.249 16.379 6.249 22.628.001z"/>
|
||||||
|
</g>
|
||||||
|
</svg>
|
||||||
|
After Width: | Height: | Size: 470 B |
1
src/images/connection.svg
Normal file
|
|
@ -0,0 +1 @@
|
||||||
|
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 640 512"><path d="M320 320c-44.18 0-80 35.82-80 80 0 44.19 35.83 80 80 80 44.19 0 80-35.84 80-80 0-44.18-35.82-80-80-80zm0 128c-26.47 0-48-21.53-48-48s21.53-48 48-48 48 21.53 48 48-21.53 48-48 48zm316.21-290.05C459.22-9.9 180.95-10.06 3.79 157.95c-4.94 4.69-5.08 12.51-.26 17.32l5.69 5.69c4.61 4.61 12.07 4.74 16.8.25 164.99-156.39 423.64-155.76 587.97 0 4.73 4.48 12.19 4.35 16.8-.25l5.69-5.69c4.81-4.81 4.67-12.63-.27-17.32zM526.02 270.31c-117.34-104.48-294.86-104.34-412.04 0-5.05 4.5-5.32 12.31-.65 17.2l5.53 5.79c4.46 4.67 11.82 4.96 16.66.67 105.17-93.38 264-93.21 368.98 0 4.83 4.29 12.19 4.01 16.66-.67l5.53-5.79c4.65-4.89 4.38-12.7-.67-17.2z"/></svg>
|
||||||
|
After Width: | Height: | Size: 712 B |
5
src/images/github.svg
Normal file
|
|
@ -0,0 +1,5 @@
|
||||||
|
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 496 512">
|
||||||
|
<g fill="#fff">
|
||||||
|
<path d="M165.9 397.4c0 2-2.3 3.6-5.2 3.6-3.3.3-5.6-1.3-5.6-3.6 0-2 2.3-3.6 5.2-3.6 3-.3 5.6 1.3 5.6 3.6zm-31.1-4.5c-.7 2 1.3 4.3 4.3 4.9 2.6 1 5.6 0 6.2-2s-1.3-4.3-4.3-5.2c-2.6-.7-5.5.3-6.2 2.3zm44.2-1.7c-2.9.7-4.9 2.6-4.6 4.9.3 2 2.9 3.3 5.9 2.6 2.9-.7 4.9-2.6 4.6-4.6-.3-1.9-3-3.2-5.9-2.9zM244.8 8C106.1 8 0 113.3 0 252c0 110.9 69.8 205.8 169.5 239.2 12.8 2.3 17.3-5.6 17.3-12.1 0-6.2-.3-40.4-.3-61.4 0 0-70 15-84.7-29.8 0 0-11.4-29.1-27.8-36.6 0 0-22.9-15.7 1.6-15.4 0 0 24.9 2 38.6 25.8 21.9 38.6 58.6 27.5 72.9 20.9 2.3-16 8.8-27.1 16-33.7-55.9-6.2-112.3-14.3-112.3-110.5 0-27.5 7.6-41.3 23.6-58.9-2.6-6.5-11.1-33.3 2.6-67.9 20.9-6.5 69 27 69 27 20-5.6 41.5-8.5 62.8-8.5s42.8 2.9 62.8 8.5c0 0 48.1-33.6 69-27 13.7 34.7 5.2 61.4 2.6 67.9 16 17.7 25.8 31.5 25.8 58.9 0 96.5-58.9 104.2-114.8 110.5 9.2 7.9 17 22.9 17 46.4 0 33.7-.3 75.4-.3 83.6 0 6.5 4.6 14.4 17.3 12.1C428.2 457.8 496 362.9 496 252 496 113.3 383.5 8 244.8 8zM97.2 352.9c-1.3 1-1 3.3.7 5.2 1.6 1.6 3.9 2.3 5.2 1 1.3-1 1-3.3-.7-5.2-1.6-1.6-3.9-2.3-5.2-1zm-10.8-8.1c-.7 1.3.3 2.9 2.3 3.9 1.6 1 3.6.7 4.3-.7.7-1.3-.3-2.9-2.3-3.9-2-.6-3.6-.3-4.3.7zm32.4 35.6c-1.6 1.3-1 4.3 1.3 6.2 2.3 2.3 5.2 2.6 6.5 1 1.3-1.3.7-4.3-1.3-6.2-2.2-2.3-5.2-2.6-6.5-1zm-11.4-14.7c-1.6 1-1.6 3.6 0 5.9 1.6 2.3 4.3 3.3 5.6 2.3 1.6-1.3 1.6-3.9 0-6.2-1.4-2.3-4-3.3-5.6-2z"/>
|
||||||
|
</g>
|
||||||
|
</svg>
|
||||||
|
After Width: | Height: | Size: 1.4 KiB |
48
src/images/logo.svg
Normal file
|
|
@ -0,0 +1,48 @@
|
||||||
|
<svg
|
||||||
|
version="1.0"
|
||||||
|
viewBox="0 0 2499.4541 511.75159"
|
||||||
|
preserveAspectRatio="xMidYMid meet">
|
||||||
|
<g
|
||||||
|
transform="matrix(0.1,0,0,-0.1,-5.0666667,511.8606)"
|
||||||
|
fill="#000000"
|
||||||
|
stroke="none"
|
||||||
|
id="g2722"
|
||||||
|
style="fill:#ffffff">
|
||||||
|
<path
|
||||||
|
d="M 1680,5105 C 1318,5059 988,4917 711,4685 387,4415 163,4035 80,3614 52,3477 42,3197 59,3065 139,2455 531,1669 1206,763 1435,456 1742,81 1806,31 c 56,-44 143,-39 201,12 48,42 315,363 467,560 757,988 1193,1837 1267,2471 17,142 7,403 -21,540 -85,430 -317,817 -654,1091 -389,315 -894,461 -1386,400 z m 373,-920 c 355,-62 640,-315 743,-660 36,-122 44,-313 19,-436 -58,-289 -241,-528 -502,-659 -229,-114 -515,-126 -753,-32 -152,61 -305,175 -399,298 -63,84 -133,223 -161,324 -28,96 -38,290 -21,396 82,505 572,857 1074,769 z"
|
||||||
|
id="path2704"
|
||||||
|
style="fill:#ffffff" />
|
||||||
|
<path
|
||||||
|
d="m 5156,4363 c -66,-17 -137,-96 -156,-173 -8,-32 -10,-516 -8,-1665 l 3,-1620 22,-40 c 25,-47 64,-85 113,-111 34,-18 79,-19 1071,-19 h 1036 l 48,30 c 75,47 109,101 113,185 2,38 -1,84 -8,105 -15,45 -59,96 -108,127 l -37,23 -885,5 -885,5 -5,1500 c -5,1375 -6,1503 -22,1530 -23,43 -77,91 -120,109 -35,15 -130,20 -172,9 z"
|
||||||
|
id="path2706"
|
||||||
|
style="fill:#ffffff" />
|
||||||
|
<path
|
||||||
|
d="m 16280,4359 c -45,-19 -89,-59 -117,-107 l -28,-47 -3,-462 -3,-461 -88,-4 c -77,-3 -94,-7 -137,-33 -136,-85 -152,-277 -31,-380 50,-43 90,-55 184,-55 h 72 l 4,-667 c 5,-720 5,-730 61,-895 91,-272 300,-440 619,-497 107,-19 263,-25 311,-11 99,27 170,121 169,225 0,69 -18,117 -62,167 -41,47 -95,68 -195,74 -102,8 -189,29 -242,59 -53,31 -121,105 -143,158 -39,90 -41,131 -41,767 v 618 l 183,4 182,3 46,33 c 125,90 138,263 27,369 -58,56 -89,63 -273,63 h -165 v 424 c 0,245 -4,446 -10,477 -13,65 -48,118 -103,155 -35,24 -54,29 -117,31 -41,2 -86,-2 -100,-8 z"
|
||||||
|
id="path2708"
|
||||||
|
style="fill:#ffffff" />
|
||||||
|
<path
|
||||||
|
d="m 22290,4354 c -42,-18 -95,-67 -118,-109 -16,-27 -17,-137 -22,-1260 -6,-1372 -1,-1270 -77,-1425 -49,-100 -169,-221 -268,-272 -106,-55 -174,-72 -295,-72 -178,-1 -328,60 -451,183 -115,115 -172,245 -188,426 -10,113 -21,142 -76,197 -108,107 -289,84 -370,-46 -26,-42 -30,-61 -33,-130 -8,-180 59,-414 166,-580 349,-543 1038,-697 1561,-349 223,149 392,379 465,635 45,155 47,253 44,1488 -3,1088 -4,1157 -21,1191 -25,50 -74,98 -122,120 -49,23 -146,24 -195,3 z"
|
||||||
|
id="path2710"
|
||||||
|
style="fill:#ffffff" />
|
||||||
|
<path
|
||||||
|
d="m 23855,4353 c -229,-49 -447,-200 -587,-406 -194,-286 -203,-695 -22,-999 67,-112 221,-267 329,-329 138,-80 216,-106 405,-134 242,-36 426,-163 520,-360 46,-97 60,-159 60,-273 0,-179 -63,-328 -189,-452 -80,-79 -152,-123 -261,-161 -84,-29 -241,-31 -345,-5 -147,38 -279,134 -391,287 -53,71 -107,99 -190,99 -165,0 -284,-159 -232,-311 45,-134 261,-346 453,-446 178,-92 325,-127 540,-126 141,0 172,3 260,27 116,30 293,115 379,180 279,212 424,469 456,808 32,337 -84,648 -334,893 -194,189 -385,281 -670,320 -215,30 -346,131 -416,322 -19,53 -22,77 -18,161 3,87 7,106 38,168 78,159 254,274 421,274 69,0 196,-36 251,-71 27,-18 70,-59 95,-93 114,-151 187,-197 286,-181 181,30 263,218 166,381 -19,31 -72,97 -118,146 -141,149 -307,244 -493,282 -97,20 -298,20 -393,-1 z"
|
||||||
|
id="path2712"
|
||||||
|
style="fill:#ffffff" />
|
||||||
|
<path
|
||||||
|
d="m 8660,3261 c -504,-93 -917,-501 -1016,-1006 -24,-123 -24,-377 -1,-498 49,-248 168,-466 353,-651 190,-190 419,-312 674,-357 97,-17 353,-17 450,0 316,56 605,236 803,500 89,119 136,208 182,344 123,368 71,787 -137,1104 -164,249 -411,440 -683,527 -170,54 -441,70 -625,37 z m 400,-473 c 244,-46 474,-237 574,-476 42,-99 58,-186 58,-307 0,-141 -19,-222 -83,-354 -45,-92 -62,-116 -148,-201 -161,-163 -309,-229 -531,-237 -237,-9 -407,53 -576,210 -127,119 -216,279 -245,442 -17,98 -7,282 21,370 47,152 144,295 264,391 192,154 420,209 666,162 z"
|
||||||
|
id="path2714"
|
||||||
|
style="fill:#ffffff" />
|
||||||
|
<path
|
||||||
|
d="m 11600,3266 c -366,-65 -661,-261 -868,-575 -250,-379 -264,-924 -35,-1316 120,-205 342,-415 548,-518 78,-39 216,-85 312,-104 121,-24 375,-24 496,0 214,43 421,142 566,273 67,60 90,109 91,190 0,129 -86,223 -211,232 -74,6 -123,-12 -204,-73 -83,-63 -227,-133 -310,-150 -89,-19 -260,-19 -347,0 -154,32 -268,96 -394,220 -102,101 -156,187 -201,320 -24,73 -27,94 -27,235 -1,136 2,164 22,230 100,318 360,533 688,570 198,22 399,-34 556,-155 31,-24 71,-51 90,-61 160,-84 348,28 348,205 -1,107 -51,174 -212,281 -138,93 -269,149 -433,185 -97,22 -378,28 -475,11 z"
|
||||||
|
id="path2716"
|
||||||
|
style="fill:#ffffff" />
|
||||||
|
<path
|
||||||
|
d="m 14175,3269 c -356,-52 -670,-255 -880,-569 -164,-245 -233,-549 -195,-860 65,-524 479,-976 991,-1080 203,-42 407,-36 608,16 110,29 287,111 385,178 75,52 86,50 86,-12 0,-116 125,-221 251,-209 65,6 104,24 149,69 72,72 70,46 70,708 0,633 -4,707 -51,862 -58,197 -165,375 -319,529 -200,201 -429,320 -700,364 -102,16 -297,18 -395,4 z m 373,-485 c 44,-10 113,-36 170,-65 165,-83 280,-199 361,-364 61,-123 83,-217 82,-355 0,-226 -86,-420 -254,-576 -116,-108 -233,-169 -380,-201 -109,-23 -298,-13 -402,21 -265,86 -468,303 -535,571 -26,101 -28,262 -4,366 34,156 146,341 262,435 204,166 438,222 700,168 z"
|
||||||
|
id="path2718"
|
||||||
|
style="fill:#ffffff" />
|
||||||
|
<path
|
||||||
|
d="m 18605,3274 c -300,-44 -517,-148 -722,-347 -209,-202 -332,-427 -378,-694 -20,-113 -19,-345 0,-459 63,-356 291,-679 621,-877 314,-188 767,-218 1116,-73 178,73 352,197 392,278 71,142 -6,306 -160,338 -74,16 -139,-4 -229,-71 -88,-65 -209,-122 -302,-143 -104,-23 -274,-21 -374,4 -192,49 -352,158 -465,317 -45,62 -104,180 -104,208 0,13 103,15 919,15 1014,0 971,-3 1039,63 70,67 83,120 71,286 -25,330 -169,614 -428,844 -163,145 -359,245 -571,292 -73,16 -359,29 -425,19 z m 372,-500 c 157,-45 261,-111 381,-239 71,-76 162,-231 162,-276 0,-18 -20,-19 -760,-19 -660,0 -760,2 -760,15 0,26 39,115 77,173 82,129 183,222 313,287 187,94 395,115 587,59 z"
|
||||||
|
id="path2720"
|
||||||
|
style="fill:#ffffff" />
|
||||||
|
</g>
|
||||||
|
</svg>
|
||||||
|
After Width: | Height: | Size: 5.7 KiB |
1
src/images/predict.svg
Normal file
|
|
@ -0,0 +1 @@
|
||||||
|
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 640 512"><!-- Font Awesome Pro 5.15.3 by @fontawesome - https://fontawesome.com License - https://fontawesome.com/license (Commercial License) --><path d="M266.34 277.65l11.31-11.31c3.12-3.12 3.12-8.19 0-11.31L230.62 208l47.03-47.03c3.12-3.12 3.12-8.19 0-11.31l-11.31-11.31c-3.12-3.12-8.19-3.12-11.31 0l-64 64c-3.12 3.12-3.12 8.19 0 11.31l64 64c3.13 3.12 8.19 3.12 11.31-.01zm96-11.31l11.31 11.31c3.12 3.12 8.19 3.12 11.31 0l64-64c3.12-3.12 3.12-8.19 0-11.31l-64-64c-3.12-3.12-8.19-3.12-11.31 0l-11.31 11.31c-3.12 3.12-3.12 8.19 0 11.31L409.38 208l-47.03 47.03a7.994 7.994 0 0 0-.01 11.31zM624 368h-48V96c0-35.3-28.72-64-64-64H128c-35.28 0-64 28.7-64 64v272H16c-8.84 0-16 7.16-16 16v48c0 44.11 35.88 80 80 80h480c44.12 0 80-35.89 80-80v-48c0-8.84-7.16-16-16-16zM96 96c0-17.67 14.33-32 32-32h384c17.67 0 32 14.33 32 32v272H391.13c-4.06 0-7.02 3.13-7.92 7.09C379.98 389.35 367.23 400 352 400h-64c-15.23 0-27.98-10.65-31.21-24.91-.9-3.96-3.86-7.09-7.92-7.09H96V96zm512 336c0 26.47-21.53 48-48 48H80c-26.47 0-48-21.53-48-48v-32h194.75c6.59 18.62 24.38 32 45.25 32h96c20.88 0 38.66-13.38 45.25-32H608v32z"/></svg>
|
||||||
|
After Width: | Height: | Size: 1.1 KiB |
1
src/images/system.svg
Normal file
|
|
@ -0,0 +1 @@
|
||||||
|
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512"><!-- Font Awesome Pro 5.15.3 by @fontawesome - https://fontawesome.com License - https://fontawesome.com/license (Commercial License) --><path d="M528 0H48C21.5 0 0 21.5 0 48v288c0 26.5 21.5 48 48 48h192l-24 96h-72c-8.8 0-16 7.2-16 16s7.2 16 16 16h288c8.8 0 16-7.2 16-16s-7.2-16-16-16h-72l-24-96h192c26.5 0 48-21.5 48-48V48c0-26.5-21.5-48-48-48zM249 480l16-64h46l16 64h-78zm295-144c0 8.8-7.2 16-16 16H48c-8.8 0-16-7.2-16-16V48c0-8.8 7.2-16 16-16h480c8.8 0 16 7.2 16 16v288z"/></svg>
|
||||||
|
After Width: | Height: | Size: 544 B |
1
src/images/x.svg
Normal file
|
|
@ -0,0 +1 @@
|
||||||
|
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 320 512"><path d="M207.6 256l107.72-107.72c6.23-6.23 6.23-16.34 0-22.58l-25.03-25.03c-6.23-6.23-16.34-6.23-22.58 0L160 208.4 52.28 100.68c-6.23-6.23-16.34-6.23-22.58 0L4.68 125.7c-6.23 6.23-6.23 16.34 0 22.58L112.4 256 4.68 363.72c-6.23 6.23-6.23 16.34 0 22.58l25.03 25.03c6.23 6.23 16.34 6.23 22.58 0L160 303.6l107.72 107.72c6.23 6.23 16.34 6.23 22.58 0l25.03-25.03c6.23-6.23 6.23-16.34 0-22.58L207.6 256z"/></svg>
|
||||||
|
After Width: | Height: | Size: 468 B |
5
src/images/xCircle.svg
Normal file
|
|
@ -0,0 +1,5 @@
|
||||||
|
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512">
|
||||||
|
<g fill="#f4c1c6">
|
||||||
|
<path d="M256 8C119 8 8 119 8 256s111 248 248 248 248-111 248-248S393 8 256 8zm121.6 313.1c4.7 4.7 4.7 12.3 0 17L338 377.6c-4.7 4.7-12.3 4.7-17 0L256 312l-65.1 65.6c-4.7 4.7-12.3 4.7-17 0L134.4 338c-4.7-4.7-4.7-12.3 0-17l65.6-65-65.6-65.1c-4.7-4.7-4.7-12.3 0-17l39.6-39.6c4.7-4.7 12.3-4.7 17 0l65 65.7 65.1-65.6c4.7-4.7 12.3-4.7 17 0l39.6 39.6c4.7 4.7 4.7 12.3 0 17L312 256l65.6 65.1z"/>
|
||||||
|
</g>
|
||||||
|
</svg>
|
||||||
|
After Width: | Height: | Size: 489 B |
16
src/index.js
Normal file
|
|
@ -0,0 +1,16 @@
|
||||||
|
import React from 'react';
|
||||||
|
import ReactDOM from 'react-dom';
|
||||||
|
import App from './components/App';
|
||||||
|
// import reportWebVitals from './reportWebVitals';
|
||||||
|
|
||||||
|
ReactDOM.render(
|
||||||
|
<React.StrictMode>
|
||||||
|
<App />
|
||||||
|
</React.StrictMode>,
|
||||||
|
document.getElementById('root')
|
||||||
|
);
|
||||||
|
|
||||||
|
// If you want to start measuring performance in your app, pass a function
|
||||||
|
// to log results (for example: reportWebVitals(console.log))
|
||||||
|
// or send to an analytics endpoint. Learn more: https://bit.ly/CRA-vitals
|
||||||
|
// reportWebVitals();
|
||||||
|
|
@ -1,20 +0,0 @@
|
||||||
{
|
|
||||||
"manifest_version": 3,
|
|
||||||
"name": "Vytal",
|
|
||||||
"description": "Vytal",
|
|
||||||
"permissions": ["storage", "debugger", "activeTab", "alarms"],
|
|
||||||
"background": { "service_worker": "background.bundle.js" },
|
|
||||||
"action": {
|
|
||||||
"default_popup": "popup.html",
|
|
||||||
"default_icon": "icon-32.png"
|
|
||||||
},
|
|
||||||
"icons": {
|
|
||||||
"128": "icon-128.png"
|
|
||||||
},
|
|
||||||
"web_accessible_resources": [
|
|
||||||
{
|
|
||||||
"resources": ["icon-128.png", "icon-32.png"],
|
|
||||||
"matches": []
|
|
||||||
}
|
|
||||||
]
|
|
||||||
}
|
|
||||||
|
|
@ -1,115 +0,0 @@
|
||||||
import userAgents from '../../utils/userAgents'
|
|
||||||
|
|
||||||
const attachTab = (tabId) => {
|
|
||||||
chrome.storage.sync.get(
|
|
||||||
[
|
|
||||||
'ipData',
|
|
||||||
'timezone',
|
|
||||||
'timezoneMatchIP',
|
|
||||||
'lat',
|
|
||||||
'latitudeMatchIP',
|
|
||||||
'lon',
|
|
||||||
'longitudeMatchIP',
|
|
||||||
'locale',
|
|
||||||
'localeMatchIP',
|
|
||||||
'userAgent',
|
|
||||||
],
|
|
||||||
(result) => {
|
|
||||||
if (
|
|
||||||
result.timezone ||
|
|
||||||
result.lat ||
|
|
||||||
result.lon ||
|
|
||||||
result.locale ||
|
|
||||||
result.userAgent
|
|
||||||
) {
|
|
||||||
chrome.debugger.attach({ tabId: tabId }, '1.3', () => {
|
|
||||||
if (!chrome.runtime.lastError) {
|
|
||||||
if (result.timezone) {
|
|
||||||
chrome.debugger.sendCommand(
|
|
||||||
{ tabId: tabId },
|
|
||||||
'Emulation.setTimezoneOverride',
|
|
||||||
{
|
|
||||||
timezoneId: result.timezone,
|
|
||||||
},
|
|
||||||
() => {
|
|
||||||
if (
|
|
||||||
chrome.runtime.lastError &&
|
|
||||||
chrome.runtime.lastError.message.includes(
|
|
||||||
'Timezone override is already in effect'
|
|
||||||
)
|
|
||||||
) {
|
|
||||||
chrome.debugger.detach({ tabId })
|
|
||||||
attachTab(tabId)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
if (result.locale) {
|
|
||||||
chrome.debugger.sendCommand(
|
|
||||||
{ tabId: tabId },
|
|
||||||
'Emulation.setLocaleOverride',
|
|
||||||
{
|
|
||||||
locale: result.locale,
|
|
||||||
}
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
if (result.lat || result.lon) {
|
|
||||||
chrome.debugger.sendCommand(
|
|
||||||
{ tabId: tabId },
|
|
||||||
'Emulation.setGeolocationOverride',
|
|
||||||
{
|
|
||||||
latitude: result.lat
|
|
||||||
? parseFloat(result.lat)
|
|
||||||
: result.ipData.lat,
|
|
||||||
longitude: result.lon
|
|
||||||
? parseFloat(result.lon)
|
|
||||||
: result.ipData.lon,
|
|
||||||
accuracy: 1,
|
|
||||||
}
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
if (result.userAgent) {
|
|
||||||
chrome.debugger.sendCommand(
|
|
||||||
{ tabId: tabId },
|
|
||||||
'Emulation.setUserAgentOverride',
|
|
||||||
{
|
|
||||||
userAgent: result.userAgent,
|
|
||||||
}
|
|
||||||
// { acceptLanguage: "en-CA" },
|
|
||||||
// { platform: "WebTV OS" }
|
|
||||||
)
|
|
||||||
// 'Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/99.0.4844.51 Safari/537.69',
|
|
||||||
}
|
|
||||||
}
|
|
||||||
})
|
|
||||||
}
|
|
||||||
}
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
chrome.tabs.onUpdated.addListener((tabId, change, tab) => {
|
|
||||||
chrome.debugger.getTargets((tabs) => {
|
|
||||||
const currentTab = tabs.find((obj) => obj.tabId === tabId)
|
|
||||||
if (!currentTab.attached) {
|
|
||||||
attachTab(tabId)
|
|
||||||
}
|
|
||||||
})
|
|
||||||
})
|
|
||||||
|
|
||||||
chrome.alarms.onAlarm.addListener((alarm) => {
|
|
||||||
if (alarm.name === 'userAgentAlarm') {
|
|
||||||
chrome.storage.sync.get(['randomUA'], (result) => {
|
|
||||||
if (result.randomUA) {
|
|
||||||
console.log('userAgentAlarm')
|
|
||||||
const randomUserAgent =
|
|
||||||
userAgents[Math.floor(Math.random() * userAgents.length)]
|
|
||||||
chrome.storage.sync.set({
|
|
||||||
userAgent: randomUserAgent,
|
|
||||||
})
|
|
||||||
}
|
|
||||||
})
|
|
||||||
}
|
|
||||||
})
|
|
||||||
|
|
@ -1,59 +0,0 @@
|
||||||
import React, { useState, useEffect } from 'react'
|
|
||||||
import profiles from '../../utils/profiles'
|
|
||||||
import countryLocales from '../../utils/countryLocales'
|
|
||||||
|
|
||||||
const DebugSettings = ({ type, title, ip, profile, setProfile }) => {
|
|
||||||
const [value, setValue] = useState('')
|
|
||||||
|
|
||||||
useEffect(() => {
|
|
||||||
if (profile === 'none') {
|
|
||||||
setValue('')
|
|
||||||
chrome.storage.sync.set({ [type]: '' })
|
|
||||||
} else if (profile === 'match') {
|
|
||||||
if (ip) {
|
|
||||||
const ipTypeValue =
|
|
||||||
type === 'locale' ? countryLocales[ip.countryCode].locale : ip[type]
|
|
||||||
setValue(ipTypeValue)
|
|
||||||
chrome.storage.sync.set({ [type]: ipTypeValue })
|
|
||||||
}
|
|
||||||
} else if (profile === 'custom') {
|
|
||||||
chrome.storage.sync.get([type], (result) => {
|
|
||||||
console.log(type)
|
|
||||||
console.log(result)
|
|
||||||
result[type] && setValue(result[type])
|
|
||||||
})
|
|
||||||
} else if (profile !== 'default') {
|
|
||||||
setValue(profiles[profile][type])
|
|
||||||
chrome.storage.sync.set({ [type]: profiles[profile][type] })
|
|
||||||
}
|
|
||||||
}, [ip, profile, type])
|
|
||||||
|
|
||||||
const changeTextValue = (e) => {
|
|
||||||
chrome.storage.sync.set({ timezone: e.target.value })
|
|
||||||
setValue(e.target.value)
|
|
||||||
chrome.storage.sync.set({ profile: 'custom' })
|
|
||||||
setProfile('custom')
|
|
||||||
}
|
|
||||||
|
|
||||||
return (
|
|
||||||
<div
|
|
||||||
style={{
|
|
||||||
display: 'flex',
|
|
||||||
justifyContent: 'space-between',
|
|
||||||
margin: '12px 0 0 0',
|
|
||||||
}}
|
|
||||||
>
|
|
||||||
<input
|
|
||||||
type="text"
|
|
||||||
value={value}
|
|
||||||
onChange={changeTextValue}
|
|
||||||
style={{
|
|
||||||
width: '206px',
|
|
||||||
}}
|
|
||||||
/>
|
|
||||||
<label>{title}</label>
|
|
||||||
</div>
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
export default DebugSettings
|
|
||||||
|
|
@ -1,31 +0,0 @@
|
||||||
import React from 'react'
|
|
||||||
import detachDebugger from '../../utils/detachDebugger'
|
|
||||||
|
|
||||||
const getFlagEmoji = (countryCode) => {
|
|
||||||
const codePoints = countryCode
|
|
||||||
.toUpperCase()
|
|
||||||
.split('')
|
|
||||||
.map((char) => 127397 + char.charCodeAt())
|
|
||||||
return String.fromCodePoint(...codePoints)
|
|
||||||
}
|
|
||||||
|
|
||||||
const IpSettings = ({ ip, getIP, setIP }) => {
|
|
||||||
return (
|
|
||||||
<div style={{ display: 'flex', justifyContent: 'space-between' }}>
|
|
||||||
<div>
|
|
||||||
Current IP: {ip && `${ip.query} ${getFlagEmoji(ip.countryCode)}`}
|
|
||||||
</div>
|
|
||||||
<button
|
|
||||||
type="button"
|
|
||||||
onClick={() => {
|
|
||||||
Promise.resolve(getIP()).then((ipData) => setIP(ipData))
|
|
||||||
detachDebugger()
|
|
||||||
}}
|
|
||||||
>
|
|
||||||
Reload
|
|
||||||
</button>
|
|
||||||
</div>
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
export default IpSettings
|
|
||||||
|
|
@ -1,47 +0,0 @@
|
||||||
import * as React from 'react'
|
|
||||||
// import { FontAwesomeIcon } from '@fortawesome/react-fontawesome';
|
|
||||||
// import { faExternalLinkAlt, faCog } from '@fortawesome/free-solid-svg-icons';
|
|
||||||
import Logo from '../../assets/img/logo.svg'
|
|
||||||
import Link from '../../assets/img/link.svg'
|
|
||||||
|
|
||||||
const Navbar = () => (
|
|
||||||
<div
|
|
||||||
style={{
|
|
||||||
width: '100%',
|
|
||||||
boxSizing: 'border-box',
|
|
||||||
display: 'flex',
|
|
||||||
justifyContent: 'space-between',
|
|
||||||
padding: '8px 10px',
|
|
||||||
backgroundColor: 'var(--navbar)',
|
|
||||||
alignItems: 'center',
|
|
||||||
}}
|
|
||||||
>
|
|
||||||
<img
|
|
||||||
src={Logo}
|
|
||||||
style={{
|
|
||||||
width: '100px',
|
|
||||||
height: '24px',
|
|
||||||
}}
|
|
||||||
alt="Vytal logo"
|
|
||||||
/>
|
|
||||||
<a
|
|
||||||
href="https://locatejs.com"
|
|
||||||
target="_blank"
|
|
||||||
rel="noreferrer"
|
|
||||||
style={{
|
|
||||||
height: '18px',
|
|
||||||
}}
|
|
||||||
>
|
|
||||||
<img
|
|
||||||
src={Link}
|
|
||||||
alt="Test Vytal"
|
|
||||||
style={{
|
|
||||||
width: '18px',
|
|
||||||
height: '18px',
|
|
||||||
}}
|
|
||||||
/>
|
|
||||||
</a>
|
|
||||||
</div>
|
|
||||||
)
|
|
||||||
|
|
||||||
export default Navbar
|
|
||||||
|
|
@ -1,84 +0,0 @@
|
||||||
import React, { useState, useEffect } from 'react'
|
|
||||||
import Navbar from './Navbar'
|
|
||||||
import IpSettings from './IpSettings'
|
|
||||||
import ProfileSelect from './ProfileSelect'
|
|
||||||
import DebugSettings from './DebugSettings'
|
|
||||||
import UserAgentSettings from './UserAgentSettings'
|
|
||||||
|
|
||||||
const getIP = () =>
|
|
||||||
fetch('http://ip-api.com/json/')
|
|
||||||
.then((response) => response.json())
|
|
||||||
.then((ipData) => {
|
|
||||||
chrome.storage.sync.set({ ipData })
|
|
||||||
return ipData
|
|
||||||
})
|
|
||||||
|
|
||||||
const Popup = () => {
|
|
||||||
const [ip, setIP] = useState(null)
|
|
||||||
const [profile, setProfile] = useState('default')
|
|
||||||
|
|
||||||
useEffect(() => {
|
|
||||||
chrome.storage.sync.get(['profile', 'ipData'], (result) => {
|
|
||||||
result.profile && setProfile(result.profile)
|
|
||||||
if (result.ipData) {
|
|
||||||
setIP(result.ipData)
|
|
||||||
} else {
|
|
||||||
Promise.resolve(getIP()).then((ipData) => setIP(ipData))
|
|
||||||
}
|
|
||||||
})
|
|
||||||
}, [])
|
|
||||||
|
|
||||||
return (
|
|
||||||
<div className="App">
|
|
||||||
<Navbar />
|
|
||||||
<div
|
|
||||||
style={{
|
|
||||||
padding: '12px',
|
|
||||||
}}
|
|
||||||
>
|
|
||||||
<IpSettings ip={ip} getIP={getIP} setIP={setIP} />
|
|
||||||
<ProfileSelect profile={profile} setProfile={setProfile} />
|
|
||||||
<DebugSettings
|
|
||||||
type="timezone"
|
|
||||||
title="Timezone"
|
|
||||||
ip={ip}
|
|
||||||
profile={profile}
|
|
||||||
setProfile={setProfile}
|
|
||||||
/>
|
|
||||||
<DebugSettings
|
|
||||||
type="locale"
|
|
||||||
title="Locale"
|
|
||||||
ip={ip}
|
|
||||||
profile={profile}
|
|
||||||
setProfile={setProfile}
|
|
||||||
/>
|
|
||||||
<DebugSettings
|
|
||||||
type="lat"
|
|
||||||
title="Latitude"
|
|
||||||
ip={ip}
|
|
||||||
profile={profile}
|
|
||||||
setProfile={setProfile}
|
|
||||||
/>
|
|
||||||
<DebugSettings
|
|
||||||
type="lon"
|
|
||||||
title="Longitude"
|
|
||||||
ip={ip}
|
|
||||||
profile={profile}
|
|
||||||
setProfile={setProfile}
|
|
||||||
/>
|
|
||||||
<UserAgentSettings ip={ip} type="lat" title="Latitude" />
|
|
||||||
<div
|
|
||||||
style={{
|
|
||||||
margin: '8px 0 0 0',
|
|
||||||
fontSize: '10px',
|
|
||||||
}}
|
|
||||||
>
|
|
||||||
Tabs need to be initialized for full protection.{' '}
|
|
||||||
<a href="/">Read more</a>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
export default Popup
|
|
||||||
|
|
@ -1,47 +0,0 @@
|
||||||
import React from 'react'
|
|
||||||
import profiles from '../../utils/profiles'
|
|
||||||
import detachDebugger from '../../utils/detachDebugger'
|
|
||||||
|
|
||||||
const ProfileSelect = ({ profile, setProfile }) => {
|
|
||||||
const changeProfile = (e) => {
|
|
||||||
detachDebugger()
|
|
||||||
chrome.storage.sync.set({
|
|
||||||
profile: e.target.value,
|
|
||||||
})
|
|
||||||
setProfile(e.target.value)
|
|
||||||
}
|
|
||||||
|
|
||||||
return (
|
|
||||||
<div
|
|
||||||
style={{
|
|
||||||
display: 'flex',
|
|
||||||
justifyContent: 'space-between',
|
|
||||||
margin: '12px 0 0 0',
|
|
||||||
}}
|
|
||||||
>
|
|
||||||
<select
|
|
||||||
name="profile"
|
|
||||||
id="profile"
|
|
||||||
value={profile}
|
|
||||||
onChange={changeProfile}
|
|
||||||
style={{
|
|
||||||
width: '214px',
|
|
||||||
}}
|
|
||||||
>
|
|
||||||
<option value="none">None</option>
|
|
||||||
<option value="match">Match IP</option>
|
|
||||||
<option value="custom">Custom</option>
|
|
||||||
<optgroup label="Locations">
|
|
||||||
{Object.keys(profiles).map((key) => (
|
|
||||||
<option value={key} key={key}>
|
|
||||||
{profiles[key].name}
|
|
||||||
</option>
|
|
||||||
))}
|
|
||||||
</optgroup>
|
|
||||||
</select>
|
|
||||||
<label>Profile</label>
|
|
||||||
</div>
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
export default ProfileSelect
|
|
||||||
|
|
@ -1,103 +0,0 @@
|
||||||
import React, { useState, useEffect } from 'react'
|
|
||||||
import userAgents from '../../utils/userAgents'
|
|
||||||
|
|
||||||
const UserAgentSettings = () => {
|
|
||||||
const [userAgent, setUserAgent] = useState('')
|
|
||||||
const [randomUA, setRandomUA] = useState(false)
|
|
||||||
const [interval, setInterval] = useState(60)
|
|
||||||
|
|
||||||
useEffect(() => {
|
|
||||||
chrome.storage.sync.get(['userAgent', 'randomUA', 'interval'], (result) => {
|
|
||||||
result.interval && setInterval(result.interval)
|
|
||||||
|
|
||||||
result.randomUA && setRandomUA(true)
|
|
||||||
|
|
||||||
if (result.userAgent) {
|
|
||||||
setUserAgent(result.userAgent)
|
|
||||||
}
|
|
||||||
})
|
|
||||||
}, [])
|
|
||||||
|
|
||||||
const changeUserAgent = (e) => {
|
|
||||||
chrome.storage.sync.set({ userAgent: e.target.value })
|
|
||||||
setUserAgent(e.target.value)
|
|
||||||
}
|
|
||||||
|
|
||||||
const randomize = (e) => {
|
|
||||||
const randomUserAgent =
|
|
||||||
userAgents[Math.floor(Math.random() * userAgents.length)]
|
|
||||||
chrome.storage.sync.set({
|
|
||||||
userAgent: randomUserAgent,
|
|
||||||
randomUA: e.target.checked,
|
|
||||||
})
|
|
||||||
e.target.checked ? setUserAgent(randomUserAgent) : setUserAgent('')
|
|
||||||
setRandomUA(e.target.checked)
|
|
||||||
if (parseInt(interval)) {
|
|
||||||
chrome.alarms.create('userAgentAlarm', {
|
|
||||||
delayInMinutes: parseInt(interval),
|
|
||||||
periodInMinutes: parseInt(interval),
|
|
||||||
})
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
const changeInterval = (e) => {
|
|
||||||
chrome.storage.sync.set({ interval: e.target.value })
|
|
||||||
setInterval(e.target.value)
|
|
||||||
if (parseInt(e.target.value)) {
|
|
||||||
chrome.alarms.create('userAgentAlarm', {
|
|
||||||
delayInMinutes: parseInt(e.target.value),
|
|
||||||
periodInMinutes: parseInt(e.target.value),
|
|
||||||
})
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return (
|
|
||||||
<>
|
|
||||||
<div
|
|
||||||
style={{
|
|
||||||
display: 'flex',
|
|
||||||
justifyContent: 'space-between',
|
|
||||||
margin: '12px 0 0 0',
|
|
||||||
}}
|
|
||||||
>
|
|
||||||
<input
|
|
||||||
type="text"
|
|
||||||
value={userAgent}
|
|
||||||
onChange={changeUserAgent}
|
|
||||||
style={{
|
|
||||||
width: '206px',
|
|
||||||
}}
|
|
||||||
/>
|
|
||||||
<label>User Agent</label>
|
|
||||||
</div>
|
|
||||||
<div
|
|
||||||
style={{
|
|
||||||
margin: '6px 0 0 0',
|
|
||||||
}}
|
|
||||||
>
|
|
||||||
<label
|
|
||||||
style={{
|
|
||||||
margin: '0 6px 0 0',
|
|
||||||
}}
|
|
||||||
>
|
|
||||||
<input type="checkbox" checked={randomUA} onChange={randomize} />
|
|
||||||
Randomize every
|
|
||||||
</label>
|
|
||||||
<label>
|
|
||||||
<input
|
|
||||||
type="number"
|
|
||||||
value={interval}
|
|
||||||
onChange={changeInterval}
|
|
||||||
style={{
|
|
||||||
width: '30px',
|
|
||||||
margin: '0 4px 0 0',
|
|
||||||
}}
|
|
||||||
/>
|
|
||||||
minutes
|
|
||||||
</label>
|
|
||||||
</div>
|
|
||||||
</>
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
export default UserAgentSettings
|
|
||||||
|
|
@ -1,26 +0,0 @@
|
||||||
:root {
|
|
||||||
--main: #943ec5;
|
|
||||||
--text: #212121;
|
|
||||||
--background: #fff;
|
|
||||||
--scrollbar: #ccc;
|
|
||||||
--navbar: #943ec5;
|
|
||||||
--icon: #aab7b8;
|
|
||||||
--border: #f0f3f4;
|
|
||||||
}
|
|
||||||
|
|
||||||
body {
|
|
||||||
color: var(--text);
|
|
||||||
background-color: var(--background);
|
|
||||||
font-size: 13px;
|
|
||||||
line-height: 22px;
|
|
||||||
width: 315px;
|
|
||||||
margin: 0;
|
|
||||||
font-family: 'Segoe UI', Tahoma, sans-serif;
|
|
||||||
}
|
|
||||||
|
|
||||||
input[type=checkbox] {
|
|
||||||
vertical-align: middle;
|
|
||||||
position: relative;
|
|
||||||
bottom: 1px;
|
|
||||||
margin: 0 4px 0 0
|
|
||||||
}
|
|
||||||
|
|
@ -1,11 +0,0 @@
|
||||||
<!DOCTYPE html>
|
|
||||||
<html lang="en">
|
|
||||||
<head>
|
|
||||||
<meta charset="UTF-8" />
|
|
||||||
<title></title>
|
|
||||||
</head>
|
|
||||||
|
|
||||||
<body>
|
|
||||||
<div id="app-container"></div>
|
|
||||||
</body>
|
|
||||||
</html>
|
|
||||||
|
|
@ -1,9 +0,0 @@
|
||||||
import React from 'react';
|
|
||||||
import { render } from 'react-dom';
|
|
||||||
|
|
||||||
import Popup from './Popup';
|
|
||||||
import './index.css';
|
|
||||||
|
|
||||||
render(<Popup />, window.document.querySelector('#app-container'));
|
|
||||||
|
|
||||||
if (module.hot) module.hot.accept();
|
|
||||||
13
src/reportWebVitals.js
Normal file
|
|
@ -0,0 +1,13 @@
|
||||||
|
// const reportWebVitals = (onPerfEntry) => {
|
||||||
|
// if (onPerfEntry && onPerfEntry instanceof Function) {
|
||||||
|
// import('web-vitals').then(({ getCLS, getFID, getFCP, getLCP, getTTFB }) => {
|
||||||
|
// getCLS(onPerfEntry);
|
||||||
|
// getFID(onPerfEntry);
|
||||||
|
// getFCP(onPerfEntry);
|
||||||
|
// getLCP(onPerfEntry);
|
||||||
|
// getTTFB(onPerfEntry);
|
||||||
|
// });
|
||||||
|
// }
|
||||||
|
// };
|
||||||
|
|
||||||
|
// export default reportWebVitals;
|
||||||
5
src/setupTests.js
Normal file
|
|
@ -0,0 +1,5 @@
|
||||||
|
// jest-dom adds custom jest matchers for asserting on DOM nodes.
|
||||||
|
// allows you to do things like:
|
||||||
|
// expect(element).toHaveTextContent(/react/i)
|
||||||
|
// learn more: https://github.com/testing-library/jest-dom
|
||||||
|
import '@testing-library/jest-dom';
|
||||||
87
src/utils/connection.js
Normal file
|
|
@ -0,0 +1,87 @@
|
||||||
|
export { fetchAPI, getConnection };
|
||||||
|
|
||||||
|
// Gets connection values
|
||||||
|
const fetchAPI = (setData) => {
|
||||||
|
fetch('https://api.locatejs.com/')
|
||||||
|
.then((response) => response.json())
|
||||||
|
.then((json) => {
|
||||||
|
setData(json);
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
// Returns object with connection data
|
||||||
|
const getConnection = (connectionData) => {
|
||||||
|
const isProxy = checkProxy(connectionData);
|
||||||
|
return [
|
||||||
|
{
|
||||||
|
key: 'IP address',
|
||||||
|
value: connectionData.query,
|
||||||
|
issues: isProxy,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
key: 'Country',
|
||||||
|
value: connectionData.country,
|
||||||
|
issues: isProxy,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
key: 'Region',
|
||||||
|
value: connectionData.regionName,
|
||||||
|
issues: isProxy,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
key: 'City',
|
||||||
|
value: connectionData.city,
|
||||||
|
issues: isProxy,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
key: 'Time zone',
|
||||||
|
value: connectionData.timezone,
|
||||||
|
issues: isProxy,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
key: 'Latitude',
|
||||||
|
value: connectionData.lat,
|
||||||
|
issues: isProxy,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
key: 'Longitude',
|
||||||
|
value: connectionData.lon,
|
||||||
|
issues: isProxy,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
key: 'ISP',
|
||||||
|
value: connectionData.isp,
|
||||||
|
issues: isProxy,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
key: 'Org',
|
||||||
|
value: connectionData.org,
|
||||||
|
issues: isProxy,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
key: 'ASN',
|
||||||
|
value: connectionData.as,
|
||||||
|
issues: isProxy,
|
||||||
|
},
|
||||||
|
];
|
||||||
|
};
|
||||||
|
|
||||||
|
const checkProxy = (data) => {
|
||||||
|
const issues = [];
|
||||||
|
if (data.security.vpn) {
|
||||||
|
issues.push('VPN has been detected');
|
||||||
|
}
|
||||||
|
if (data.security.proxy) {
|
||||||
|
issues.push('Proxy has been detected');
|
||||||
|
}
|
||||||
|
if (data.security.tor) {
|
||||||
|
issues.push('Tor node has been detected');
|
||||||
|
}
|
||||||
|
if (data.security.relay) {
|
||||||
|
issues.push('Private relay has been detected');
|
||||||
|
}
|
||||||
|
if (issues.length === 0 && data.proxy) {
|
||||||
|
issues.push('VPN/proxy has been detected');
|
||||||
|
}
|
||||||
|
return issues;
|
||||||
|
};
|
||||||
|
|
@ -1,207 +0,0 @@
|
||||||
const countryLocales = {
|
|
||||||
AD: { locale: 'ca-AD' },
|
|
||||||
AE: { locale: 'ar-AE' },
|
|
||||||
AF: { locale: 'fa-AF' },
|
|
||||||
AG: { locale: 'en-AG' },
|
|
||||||
AL: { locale: 'sq-AL' },
|
|
||||||
AM: { locale: 'hy-AM' },
|
|
||||||
AO: { locale: 'ln-AO' },
|
|
||||||
AQ: { locale: 'en-US' },
|
|
||||||
AR: { locale: 'es-AR' },
|
|
||||||
AT: { locale: 'de-AT' },
|
|
||||||
AU: { locale: 'en-AU' },
|
|
||||||
AZ: { locale: 'az-AZ' },
|
|
||||||
BA: { locale: 'bs-BA' },
|
|
||||||
BB: { locale: 'en-BB' },
|
|
||||||
BD: { locale: 'bn-BD' },
|
|
||||||
BE: { locale: 'nl-BE' },
|
|
||||||
BF: { locale: 'fr-BF' },
|
|
||||||
BG: { locale: 'bg-BG' },
|
|
||||||
BH: { locale: 'ar-BH' },
|
|
||||||
BJ: { locale: 'fr-BJ' },
|
|
||||||
BI: { locale: 'fr-BI' },
|
|
||||||
BM: { locale: 'en-BM' },
|
|
||||||
BN: { locale: 'ms-BN' },
|
|
||||||
BR: { locale: 'pt-BR' },
|
|
||||||
BO: { locale: 'es-BO' },
|
|
||||||
BS: { locale: 'en-BS' },
|
|
||||||
BT: { locale: 'dz-BT' },
|
|
||||||
BW: { locale: 'en-BW' },
|
|
||||||
BY: { locale: 'be-BY' },
|
|
||||||
BZ: { locale: 'en-BZ' },
|
|
||||||
CA: { locale: 'en-CA' },
|
|
||||||
CD: { locale: 'fr-CD' },
|
|
||||||
CF: { locale: 'fr-CF' },
|
|
||||||
CG: { locale: 'fr-CG' },
|
|
||||||
CH: { locale: 'de-CH' },
|
|
||||||
CI: { locale: 'en-CA' },
|
|
||||||
CL: { locale: 'es-CL' },
|
|
||||||
CM: { locale: 'fr-CM' },
|
|
||||||
CN: { locale: 'zh-CN' },
|
|
||||||
CO: { locale: 'es-CO' },
|
|
||||||
CR: { locale: 'es-CR' },
|
|
||||||
CU: { locale: 'es-CU' },
|
|
||||||
CV: { locale: 'pt-CV' },
|
|
||||||
CY: { locale: 'el-CY' },
|
|
||||||
CZ: { locale: 'cs-CZ' },
|
|
||||||
DE: { locale: 'de-DE' },
|
|
||||||
DJ: { locale: 'fr-DJ' },
|
|
||||||
DK: { locale: 'da-DK' },
|
|
||||||
DM: { locale: 'en-DM' },
|
|
||||||
DO: { locale: 'es-DO' },
|
|
||||||
DZ: { locale: 'ar-DZ' },
|
|
||||||
EC: { locale: 'es-EC' },
|
|
||||||
EE: { locale: 'et-EE' },
|
|
||||||
EG: { locale: 'ar-EG' },
|
|
||||||
ES: { locale: 'es-ES' },
|
|
||||||
ER: { locale: 'ti-ER' },
|
|
||||||
ET: { locale: 'om-ET' },
|
|
||||||
FI: { locale: 'fi-FI' },
|
|
||||||
FJ: { locale: 'en-FJ' },
|
|
||||||
FM: { locale: 'en-FM' },
|
|
||||||
FR: { locale: 'fr-FR' },
|
|
||||||
GA: { locale: 'fr-GA' },
|
|
||||||
GB: { locale: 'en-GB' },
|
|
||||||
GD: { locale: 'en-GD' },
|
|
||||||
GE: { locale: 'ka-GE' },
|
|
||||||
GH: { locale: 'ak-GH' },
|
|
||||||
GM: { locale: 'en-GM' },
|
|
||||||
GN: { locale: 'fr-GN' },
|
|
||||||
GQ: { locale: 'es-GQ' },
|
|
||||||
GR: { locale: 'el-GR' },
|
|
||||||
GT: { locale: 'es-GT' },
|
|
||||||
GU: { locale: 'en-GU' },
|
|
||||||
GW: { locale: 'pt-GW' },
|
|
||||||
GY: { locale: 'en-GY' },
|
|
||||||
HK: { locale: 'zh-HK' },
|
|
||||||
HN: { locale: 'es-HN' },
|
|
||||||
HR: { locale: 'hr-HR' },
|
|
||||||
HT: { locale: 'fr-HT' },
|
|
||||||
HU: { locale: 'hu-HU' },
|
|
||||||
ID: { locale: 'id-ID' },
|
|
||||||
IE: { locale: 'en-IE' },
|
|
||||||
IL: { locale: 'he-IL' },
|
|
||||||
IN: { locale: 'hi-IN' },
|
|
||||||
IQ: { locale: 'ar-IQ' },
|
|
||||||
IR: { locale: 'fa-IR' },
|
|
||||||
IS: { locale: 'is-IS' },
|
|
||||||
IT: { locale: 'it-IT' },
|
|
||||||
JM: { locale: 'en-JM' },
|
|
||||||
JO: { locale: 'ar-JO' },
|
|
||||||
JP: { locale: 'ja-JP' },
|
|
||||||
KE: { locale: 'en-KE' },
|
|
||||||
KG: { locale: 'ky-KG' },
|
|
||||||
KI: { locale: 'en-KI' },
|
|
||||||
KH: { locale: 'km-KH' },
|
|
||||||
KM: { locale: 'fr-KM' },
|
|
||||||
KN: { locale: 'en-KN' },
|
|
||||||
KP: { locale: 'ko-KP' },
|
|
||||||
KW: { locale: 'ar-KW' },
|
|
||||||
KY: { locale: 'en-KY' },
|
|
||||||
KR: { locale: 'ko-KR' },
|
|
||||||
KZ: { locale: 'kk-KZ' },
|
|
||||||
LA: { locale: 'lo-LA' },
|
|
||||||
LB: { locale: 'ar-LB' },
|
|
||||||
LC: { locale: 'en-LC' },
|
|
||||||
LI: { locale: 'de-LI' },
|
|
||||||
LK: { locale: 'si-LK' },
|
|
||||||
LR: { locale: 'en-LR' },
|
|
||||||
LS: { locale: 'en-LS' },
|
|
||||||
LT: { locale: 'lt-LT' },
|
|
||||||
LU: { locale: 'fr-LU' },
|
|
||||||
LV: { locale: 'lv-LV' },
|
|
||||||
LY: { locale: 'ar-LY' },
|
|
||||||
MA: { locale: 'ar-MA' },
|
|
||||||
MC: { locale: 'fr-MC' },
|
|
||||||
MD: { locale: 'ro-MD' },
|
|
||||||
ME: { locale: 'sr-Latn-ME' },
|
|
||||||
MF: { locale: 'fr-MF' },
|
|
||||||
MG: { locale: 'fr-MG' },
|
|
||||||
MH: { locale: 'en-MH' },
|
|
||||||
MK: { locale: 'mk-MK' },
|
|
||||||
ML: { locale: 'bm-ML' },
|
|
||||||
MM: { locale: 'my-MM' },
|
|
||||||
MN: { locale: 'mn-MN' },
|
|
||||||
MO: { locale: 'zh-MO' },
|
|
||||||
MP: { locale: 'en-MP' },
|
|
||||||
MR: { locale: 'fr-MR' },
|
|
||||||
MT: { locale: 'mt-MT' },
|
|
||||||
MU: { locale: 'en-MU' },
|
|
||||||
MV: { locale: 'dv-MV' },
|
|
||||||
MW: { locale: 'en-MW' },
|
|
||||||
MX: { locale: 'es-MX' },
|
|
||||||
MY: { locale: 'ms-MY' },
|
|
||||||
MZ: { locale: 'pt-MZ' },
|
|
||||||
NA: { locale: 'af-NA' },
|
|
||||||
NL: { locale: 'nl-NL' },
|
|
||||||
NE: { locale: 'fr-NE' },
|
|
||||||
NG: { locale: 'en-NG' },
|
|
||||||
NC: { locale: 'nl-NL' },
|
|
||||||
NI: { locale: 'es-NI' },
|
|
||||||
NO: { locale: 'nb-NO' },
|
|
||||||
NP: { locale: 'ne-NP' },
|
|
||||||
NR: { locale: 'en-NR' },
|
|
||||||
NZ: { locale: 'en-NZ' },
|
|
||||||
OM: { locale: 'ar-OM' },
|
|
||||||
PA: { locale: 'es-PA' },
|
|
||||||
PE: { locale: 'es-PE' },
|
|
||||||
PH: { locale: 'en-PH' },
|
|
||||||
PG: { locale: 'en-PG' },
|
|
||||||
PK: { locale: 'ur-PK' },
|
|
||||||
PS: { locale: 'ar-PS' },
|
|
||||||
PL: { locale: 'pl-PL' },
|
|
||||||
PT: { locale: 'pt-PT' },
|
|
||||||
PW: { locale: 'en-PW' },
|
|
||||||
PY: { locale: 'es-PY' },
|
|
||||||
QA: { locale: 'ar-QA' },
|
|
||||||
RO: { locale: 'ro-RO' },
|
|
||||||
RS: { locale: 'sr-RS' },
|
|
||||||
RU: { locale: 'ru-RU' },
|
|
||||||
RW: { locale: 'fr-RW' },
|
|
||||||
SA: { locale: 'ar-SA' },
|
|
||||||
SB: { locale: 'en-SB' },
|
|
||||||
SC: { locale: 'en-SC' },
|
|
||||||
SD: { locale: 'en-SD' },
|
|
||||||
SE: { locale: 'sv-SE' },
|
|
||||||
SG: { locale: 'zh-SG' },
|
|
||||||
SI: { locale: 'sl-SI' },
|
|
||||||
SL: { locale: 'en-SL' },
|
|
||||||
SK: { locale: 'sk-SK' },
|
|
||||||
SM: { locale: 'it-SM' },
|
|
||||||
SN: { locale: 'fr-SN' },
|
|
||||||
SO: { locale: 'en-SO' },
|
|
||||||
SR: { locale: 'nl-SR' },
|
|
||||||
SS: { locale: 'en-SS' },
|
|
||||||
ST: { locale: 'pt-ST' },
|
|
||||||
SV: { locale: 'es-SV' },
|
|
||||||
SY: { locale: 'ar-SY' },
|
|
||||||
SZ: { locale: 'en-SZ' },
|
|
||||||
TD: { locale: 'ar-TD' },
|
|
||||||
TG: { locale: 'fr-TG' },
|
|
||||||
TH: { locale: 'th-TH' },
|
|
||||||
TJ: { locale: 'ru-TJ' },
|
|
||||||
TL: { locale: 'pt-TL' },
|
|
||||||
TM: { locale: 'tk-TM' },
|
|
||||||
TN: { locale: 'ar-TN' },
|
|
||||||
TO: { locale: 'en-TO' },
|
|
||||||
TR: { locale: 'tr-TR' },
|
|
||||||
TT: { locale: 'en-TT' },
|
|
||||||
TV: { locale: 'en-TV' },
|
|
||||||
TZ: { locale: 'en-TZ' },
|
|
||||||
TW: { locale: 'zh-TW' },
|
|
||||||
UA: { locale: 'uk-UA' },
|
|
||||||
UG: { locale: 'en-UG' },
|
|
||||||
US: { locale: 'en-US' },
|
|
||||||
UY: { locale: 'es-UY' },
|
|
||||||
VA: { locale: 'it-VA' },
|
|
||||||
VC: { locale: 'en-VC' },
|
|
||||||
VN: { locale: 'vi-VN' },
|
|
||||||
VU: { locale: 'en-VU' },
|
|
||||||
WS: { locale: 'en-WS' },
|
|
||||||
YE: { locale: 'ar-YE' },
|
|
||||||
ZA: { locale: 'en-ZA' },
|
|
||||||
ZM: { locale: 'en-ZM' },
|
|
||||||
ZW: { locale: 'en-ZW' },
|
|
||||||
}
|
|
||||||
|
|
||||||
export default countryLocales
|
|
||||||
91
src/utils/data.js
Normal file
|
|
@ -0,0 +1,91 @@
|
||||||
|
const checkDatePrototype = () => {
|
||||||
|
if (!Date.prototype.setDate.toString().includes('[native code]')) {
|
||||||
|
return 'Failed Date.prototype.setDate.toString()';
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
};
|
||||||
|
|
||||||
|
const checkIntlConstructor = () => {
|
||||||
|
if (
|
||||||
|
!Object.getPrototypeOf(Intl.DateTimeFormat.prototype)
|
||||||
|
.constructor.toString()
|
||||||
|
.includes('Object')
|
||||||
|
) {
|
||||||
|
return 'Failed Object.getPrototypeOf(Intl.DateTimeFormat.prototype).constructor.toString()';
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
};
|
||||||
|
|
||||||
|
const checkIntlPrototype = () => {
|
||||||
|
if (
|
||||||
|
!Intl.DateTimeFormat.prototype.resolvedOptions
|
||||||
|
.toString()
|
||||||
|
.includes('[native code]')
|
||||||
|
) {
|
||||||
|
return 'Failed Intl.DateTimeFormat.prototype.resolvedOptions.toString()';
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
};
|
||||||
|
|
||||||
|
const checkNavigatorProperties = (key) => {
|
||||||
|
if (Object.getOwnPropertyDescriptor(navigator, key) !== undefined) {
|
||||||
|
return 'Failed Object.getOwnPropertyDescriptor(navigator, key)';
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
};
|
||||||
|
|
||||||
|
const checkNavigatorValue = (key) => {
|
||||||
|
if (
|
||||||
|
Object.getOwnPropertyDescriptor(Navigator.prototype, key).value !==
|
||||||
|
undefined
|
||||||
|
) {
|
||||||
|
return 'Failed object.getOwnPropertyDescriptor(Navigator.prototype, key).value';
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
};
|
||||||
|
|
||||||
|
const checkNavigatorPrototype = (key) => {
|
||||||
|
try {
|
||||||
|
// eslint-disable-next-line no-unused-vars
|
||||||
|
const check = Navigator.prototype[key];
|
||||||
|
return 'Failed Navigator.prototype[key]';
|
||||||
|
} catch (err) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
const getNavigatorValue = (type) =>
|
||||||
|
[
|
||||||
|
checkNavigatorProperties(type),
|
||||||
|
checkNavigatorValue(type),
|
||||||
|
checkNavigatorPrototype(type),
|
||||||
|
].filter(Boolean);
|
||||||
|
|
||||||
|
const delayedData = {
|
||||||
|
locale: {
|
||||||
|
value: Intl.DateTimeFormat().resolvedOptions().locale,
|
||||||
|
issues: [checkIntlPrototype(), checkIntlConstructor()].filter(Boolean),
|
||||||
|
},
|
||||||
|
timeZone: {
|
||||||
|
value: Intl.DateTimeFormat().resolvedOptions().timeZone,
|
||||||
|
issues: [checkIntlPrototype(), checkIntlConstructor()].filter(Boolean),
|
||||||
|
},
|
||||||
|
timezoneOffset: {
|
||||||
|
value: new Date().getTimezoneOffset(),
|
||||||
|
issues: [checkDatePrototype()].filter(Boolean),
|
||||||
|
},
|
||||||
|
dateString: {
|
||||||
|
value: new Date().toString(),
|
||||||
|
issues: [checkDatePrototype()].filter(Boolean),
|
||||||
|
},
|
||||||
|
dateLocale: {
|
||||||
|
value: new Date().toLocaleString(),
|
||||||
|
issues: [checkDatePrototype()].filter(Boolean),
|
||||||
|
},
|
||||||
|
userAgent: {
|
||||||
|
value: navigator.userAgent,
|
||||||
|
issues: getNavigatorValue('userAgent'),
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
export default delayedData;
|
||||||
|
|
@ -1,11 +0,0 @@
|
||||||
const detachDebugger = () => {
|
|
||||||
chrome.debugger.getTargets((tabs) => {
|
|
||||||
for (const tab in tabs) {
|
|
||||||
if (tabs[tab].attached && tabs[tab].tabId) {
|
|
||||||
chrome.debugger.detach({ tabId: tabs[tab].tabId })
|
|
||||||
}
|
|
||||||
}
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
export default detachDebugger
|
|
||||||
30
src/utils/geolocation.js
Normal file
|
|
@ -0,0 +1,30 @@
|
||||||
|
const getGeolocation = (setGeolocationData) => {
|
||||||
|
new Promise((showPosition, showError) => {
|
||||||
|
navigator.geolocation.getCurrentPosition(showPosition, showError, { enableHighAccuracy: true });
|
||||||
|
}).then(async (position) => {
|
||||||
|
const geocode = await getGeocode(position.coords.latitude, position.coords.longitude);
|
||||||
|
setGeolocationData([
|
||||||
|
getObj('Latitude', position.coords.latitude),
|
||||||
|
getObj('Longitude', position.coords.longitude),
|
||||||
|
getObj('Accuracy', position.coords.accuracy),
|
||||||
|
getObj('Altitude', position.coords.altitude),
|
||||||
|
getObj('Altitude accuracy', position.coords.altitudeAccuracy),
|
||||||
|
getObj('Heading', position.coords.heading),
|
||||||
|
getObj('Speed', position.coords.speed),
|
||||||
|
getObj('Reverse geocoding', geocode),
|
||||||
|
]);
|
||||||
|
})
|
||||||
|
.catch((e) => setGeolocationData(e.message));
|
||||||
|
};
|
||||||
|
|
||||||
|
const getGeocode = (lat, long) => fetch(`https://maps.googleapis.com/maps/api/geocode/json?latlng=${lat},${long}&key=AIzaSyByyRWGncal9iAq1-3Ek2WQ3ROLw9bCS-8`)
|
||||||
|
.then((response) => response.json())
|
||||||
|
.then((data) => data.results[0].formatted_address);
|
||||||
|
|
||||||
|
const getObj = (key, value) => ({
|
||||||
|
key,
|
||||||
|
value,
|
||||||
|
issues: [],
|
||||||
|
});
|
||||||
|
|
||||||
|
export default getGeolocation;
|
||||||
198
src/utils/predict.js
Normal file
|
|
@ -0,0 +1,198 @@
|
||||||
|
export { getMapUrl, getPrediction };
|
||||||
|
const ct = require('countries-and-timezones');
|
||||||
|
|
||||||
|
const getPrediction = (
|
||||||
|
initialData, delayedData, frameData, workerData, connectionData, webRTCData, isTor
|
||||||
|
) => {
|
||||||
|
let country, countryPercent, city, cityPercent, timeZone;
|
||||||
|
|
||||||
|
const getAccurateData = (type) => {
|
||||||
|
if (window.Worker.length && !workerData[type].issues.length) return workerData[type].value;
|
||||||
|
if (!frameData[type].issues.length) return frameData[type].value;
|
||||||
|
if (!delayedData[type].issues.length) return delayedData[type].value;
|
||||||
|
return initialData[type].value;
|
||||||
|
};
|
||||||
|
|
||||||
|
const accurateData = {
|
||||||
|
locale: getAccurateData('locale'),
|
||||||
|
timeZone: getAccurateData('timeZone'),
|
||||||
|
timezoneOffset: getAccurateData('timezoneOffset'),
|
||||||
|
dateString: getAccurateData('dateString'),
|
||||||
|
dateLocale: getAccurateData('dateLocale'),
|
||||||
|
language: getAccurateData('language'),
|
||||||
|
languages: getAccurateData('languages'),
|
||||||
|
};
|
||||||
|
|
||||||
|
const webRTCIP = checkWebRTC(webRTCData);
|
||||||
|
|
||||||
|
const systemCountry = checkCountry(accurateData);
|
||||||
|
|
||||||
|
const systemCity = checkCity(accurateData.timeZone, country);
|
||||||
|
|
||||||
|
if (!connectionData.isProxy) {
|
||||||
|
if (webRTCIP && !webRTCIP.isProxy) {
|
||||||
|
if (webRTCIP.query === connectionData.query) {
|
||||||
|
countryPercent = 90;
|
||||||
|
cityPercent = 90;
|
||||||
|
} else {
|
||||||
|
countryPercent = 80;
|
||||||
|
cityPercent = 80;
|
||||||
|
}
|
||||||
|
country = webRTCIP.countryCode;
|
||||||
|
city = webRTCIP.city;
|
||||||
|
timeZone = webRTCIP.timeZone;
|
||||||
|
} else {
|
||||||
|
country = connectionData.countryCode;
|
||||||
|
city = connectionData.city;
|
||||||
|
timeZone = connectionData.timeZone;
|
||||||
|
|
||||||
|
countryPercent = 80;
|
||||||
|
cityPercent = 80;
|
||||||
|
}
|
||||||
|
} else if (webRTCIP && !webRTCIP.isProxy) {
|
||||||
|
countryPercent = 85;
|
||||||
|
cityPercent = 85;
|
||||||
|
country = webRTCIP.countryCode;
|
||||||
|
city = webRTCIP.city;
|
||||||
|
timeZone = webRTCIP.timeZone;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (country && systemCountry === country) {
|
||||||
|
countryPercent = 95;
|
||||||
|
} else if (country && systemCountry !== country) {
|
||||||
|
countryPercent -= 40;
|
||||||
|
} else {
|
||||||
|
countryPercent = 40;
|
||||||
|
country = systemCountry;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (city && systemCity === city) {
|
||||||
|
cityPercent = 95;
|
||||||
|
} else if (city && systemCity !== city) {
|
||||||
|
cityPercent -= 20;
|
||||||
|
} else {
|
||||||
|
cityPercent = 20;
|
||||||
|
city = systemCity;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (timeZone && timeZone !== accurateData.timeZone) {
|
||||||
|
countryPercent -= 20;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (cityPercent > countryPercent) {
|
||||||
|
cityPercent = countryPercent - 5;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (isTor) {
|
||||||
|
countryPercent = 5;
|
||||||
|
cityPercent = 5;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!city) cityPercent = 0;
|
||||||
|
|
||||||
|
const regionNames = new Intl.DisplayNames(['en'], { type: 'region' });
|
||||||
|
country = regionNames.of(country);
|
||||||
|
|
||||||
|
return { country, countryPercent, city, cityPercent };
|
||||||
|
};
|
||||||
|
|
||||||
|
const checkWebRTC = (webRTCData) => {
|
||||||
|
let localIP, ipv6, publicIP;
|
||||||
|
for (let i = 0; i < webRTCData.length; i++) {
|
||||||
|
if (webRTCData[i].proxy === false) {
|
||||||
|
if (webRTCData[i].query.match(/^(192\.168\.|169\.254\.|10\.|172\.(1[6-9]|2\d|3[01]))/)) {
|
||||||
|
localIP = webRTCData[i];
|
||||||
|
} else if (webRTCData[i].query.match(/^[a-f0-9]{1,4}(:[a-f0-9]{1,4}){7}$/)) {
|
||||||
|
ipv6 = webRTCData[i];
|
||||||
|
} else {
|
||||||
|
publicIP = webRTCData[i];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (publicIP) return publicIP;
|
||||||
|
if (ipv6) return ipv6;
|
||||||
|
if (localIP) return localIP;
|
||||||
|
return null;
|
||||||
|
};
|
||||||
|
|
||||||
|
const checkCountry = (data) => {
|
||||||
|
const countryArr =
|
||||||
|
checkLocale(data.locale).concat(checkTimezone(data.timeZone), checkLanguages(data));
|
||||||
|
|
||||||
|
const countryObj = handleCountryArr(countryArr);
|
||||||
|
|
||||||
|
const topCountry =
|
||||||
|
Object.keys(countryObj).reduce((a, b) => (countryObj[a] > countryObj[b] ? a : b));
|
||||||
|
|
||||||
|
return topCountry;
|
||||||
|
};
|
||||||
|
|
||||||
|
// Get country from locale
|
||||||
|
const checkLocale = (locale) => {
|
||||||
|
const IntlLocale = new Intl.Locale(locale);
|
||||||
|
// const regionNames = new Intl.DisplayNames(['en'], { type: 'region' });
|
||||||
|
return [IntlLocale.region];
|
||||||
|
};
|
||||||
|
|
||||||
|
// Get countries of timezone
|
||||||
|
const checkTimezone = (timeZone) => {
|
||||||
|
if (timeZone) {
|
||||||
|
const countryArr = ct.getTimezone(timeZone).countries;
|
||||||
|
return countryArr.concat(countryArr, countryArr);
|
||||||
|
}
|
||||||
|
return [];
|
||||||
|
};
|
||||||
|
|
||||||
|
const checkLanguages = (data) => {
|
||||||
|
const countryArr = [];
|
||||||
|
// loop thru system data languages
|
||||||
|
data.languages.forEach((language) => {
|
||||||
|
if (language.length > 2) {
|
||||||
|
countryArr.push(language.slice(-2));
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
// Checks if main language has country code
|
||||||
|
// if (data.language.length > 2) {
|
||||||
|
// countryArr.push(data.language.slice(-2));
|
||||||
|
// }
|
||||||
|
|
||||||
|
return countryArr;
|
||||||
|
};
|
||||||
|
|
||||||
|
// converts array to object of value/frequency
|
||||||
|
const handleCountryArr = (countryArr) =>
|
||||||
|
countryArr.reduce((obj, val) => {
|
||||||
|
// eslint-disable-next-line no-param-reassign
|
||||||
|
obj[val] = (obj[val] || 0) + 1;
|
||||||
|
return obj;
|
||||||
|
}, {});
|
||||||
|
|
||||||
|
const checkCity = (timeZone) => {
|
||||||
|
let city = null;
|
||||||
|
|
||||||
|
// Check if timezone contains city info
|
||||||
|
if (
|
||||||
|
timeZone.includes('/') &&
|
||||||
|
timeZone.match(/universal|GMT|UCT|UTC/g) === null &&
|
||||||
|
!/\d/.test(timeZone)
|
||||||
|
) {
|
||||||
|
city = timeZone.split('/');
|
||||||
|
city = city[city.length - 1].replace('_', ' ');
|
||||||
|
}
|
||||||
|
|
||||||
|
return city;
|
||||||
|
};
|
||||||
|
|
||||||
|
// Return url for static map image
|
||||||
|
const getMapUrl = (prediction) => {
|
||||||
|
let location, zoom;
|
||||||
|
if (!prediction.city) {
|
||||||
|
location = prediction.country;
|
||||||
|
zoom = 3;
|
||||||
|
} else {
|
||||||
|
location = `${prediction.city},${prediction.country}`;
|
||||||
|
zoom = 7;
|
||||||
|
}
|
||||||
|
return `https://maps.googleapis.com/maps/api/staticmap?${location}&markers=color:red%7Clabel:%7C${location}&size=460x230&zoom=${zoom}&key=AIzaSyB-YN-X8PGBSPd7NOaQu4csVhgJUnF3ZGk`;
|
||||||
|
};
|
||||||
|
|
@ -1,236 +0,0 @@
|
||||||
const profiles = {
|
|
||||||
baghdad: {
|
|
||||||
name: 'Baghdad',
|
|
||||||
timezone: 'Asia/Baghdad',
|
|
||||||
locale: 'ar-IQ',
|
|
||||||
lat: 33.3152,
|
|
||||||
lon: 44.3661,
|
|
||||||
},
|
|
||||||
bangkok: {
|
|
||||||
name: 'Bangkok',
|
|
||||||
timezone: 'Asia/Bangkok',
|
|
||||||
locale: 'th-TH',
|
|
||||||
lat: 13.7563,
|
|
||||||
lon: 100.5018,
|
|
||||||
},
|
|
||||||
berlin: {
|
|
||||||
name: 'Berlin',
|
|
||||||
|
|
||||||
timezone: 'Europe/Berlin',
|
|
||||||
locale: 'de-DE',
|
|
||||||
lat: 52.520007,
|
|
||||||
lon: 13.404954,
|
|
||||||
},
|
|
||||||
cairo: {
|
|
||||||
name: 'Cairo',
|
|
||||||
timezone: 'Africa/Cairo',
|
|
||||||
locale: 'ar-EG',
|
|
||||||
lat: 30.0444,
|
|
||||||
lon: 31.2357,
|
|
||||||
},
|
|
||||||
delhi: {
|
|
||||||
name: 'Delhi',
|
|
||||||
timezone: 'Asia/Kolkata',
|
|
||||||
locale: 'hi-IN',
|
|
||||||
lat: 28.7041,
|
|
||||||
lon: 77.1025,
|
|
||||||
},
|
|
||||||
dubai: {
|
|
||||||
name: 'Dubai',
|
|
||||||
timezone: 'Asia/Dubai',
|
|
||||||
locale: 'ar-AE',
|
|
||||||
lat: 25.2048,
|
|
||||||
lon: 55.2708,
|
|
||||||
},
|
|
||||||
hongKong: {
|
|
||||||
name: 'Hong Kong',
|
|
||||||
timezone: 'Asia/Hong_Kong',
|
|
||||||
locale: 'zh-HK',
|
|
||||||
lat: 22.3193,
|
|
||||||
lon: 114.1694,
|
|
||||||
},
|
|
||||||
houston: {
|
|
||||||
name: 'Houston',
|
|
||||||
timezone: 'America/Chicago',
|
|
||||||
locale: 'en-US',
|
|
||||||
lat: 29.7627,
|
|
||||||
lon: -95.3863,
|
|
||||||
},
|
|
||||||
istanbul: {
|
|
||||||
name: 'Istanbul',
|
|
||||||
timezone: 'Europe/Istanbul',
|
|
||||||
locale: 'tr-TR',
|
|
||||||
lat: 41.0082,
|
|
||||||
lon: 28.9784,
|
|
||||||
},
|
|
||||||
jerusalem: {
|
|
||||||
name: 'Jerusalem',
|
|
||||||
timezone: 'Asia/Jerusalem',
|
|
||||||
locale: 'he-IL',
|
|
||||||
lat: 31.7683,
|
|
||||||
lon: 35.2137,
|
|
||||||
},
|
|
||||||
kyiv: {
|
|
||||||
name: 'Kyiv',
|
|
||||||
timezone: 'Europe/Kiev',
|
|
||||||
locale: 'uk-UA',
|
|
||||||
lat: 50.4501,
|
|
||||||
lon: 30.5234,
|
|
||||||
},
|
|
||||||
lagos: {
|
|
||||||
name: 'Lagos',
|
|
||||||
timezone: 'Africa/Lagos',
|
|
||||||
locale: 'en-NG',
|
|
||||||
lat: 6.5244,
|
|
||||||
lon: 3.379,
|
|
||||||
},
|
|
||||||
london: {
|
|
||||||
name: 'London',
|
|
||||||
timezone: 'Europe/London',
|
|
||||||
locale: 'en-GB',
|
|
||||||
lat: 51.5074,
|
|
||||||
lon: -0.1278,
|
|
||||||
},
|
|
||||||
losAngeles: {
|
|
||||||
name: 'Los Angeles',
|
|
||||||
timezone: 'America/Los_Angeles',
|
|
||||||
locale: 'en-US',
|
|
||||||
lat: 34.0522,
|
|
||||||
lon: -118.2437,
|
|
||||||
},
|
|
||||||
manila: {
|
|
||||||
name: 'Manila',
|
|
||||||
timezone: 'Asia/Manila',
|
|
||||||
locale: 'en-PH',
|
|
||||||
lat: 14.6042,
|
|
||||||
lon: 120.9822,
|
|
||||||
},
|
|
||||||
mecca: {
|
|
||||||
name: 'Mecca',
|
|
||||||
timezone: 'Asia/Riyadh',
|
|
||||||
locale: 'ar-SA',
|
|
||||||
lat: 21.4225,
|
|
||||||
lon: 39.8262,
|
|
||||||
},
|
|
||||||
mexicoCity: {
|
|
||||||
name: 'Mexico City',
|
|
||||||
timezone: 'America/Mexico_City',
|
|
||||||
locale: 'es-MX',
|
|
||||||
lat: 19.4271,
|
|
||||||
lon: -99.1276,
|
|
||||||
},
|
|
||||||
moscow: {
|
|
||||||
name: 'Moscow',
|
|
||||||
timezone: 'Europe/Moscow',
|
|
||||||
locale: 'ru-RU',
|
|
||||||
lat: 55.7558,
|
|
||||||
lon: 37.6176,
|
|
||||||
},
|
|
||||||
mumbai: {
|
|
||||||
name: 'Mumbai',
|
|
||||||
timezone: 'Asia/Kolkata',
|
|
||||||
locale: 'en-IN',
|
|
||||||
lat: 19.076,
|
|
||||||
lon: 72.8777,
|
|
||||||
},
|
|
||||||
newYork: {
|
|
||||||
name: 'New York',
|
|
||||||
timezone: 'America/New_York',
|
|
||||||
locale: 'en-US',
|
|
||||||
lat: 40.7127,
|
|
||||||
lon: -74.0059,
|
|
||||||
},
|
|
||||||
oslo: {
|
|
||||||
name: 'Oslo',
|
|
||||||
timezone: 'Europe/Oslo',
|
|
||||||
locale: 'no-NO',
|
|
||||||
lat: 59.9138,
|
|
||||||
lon: 10.7517,
|
|
||||||
},
|
|
||||||
paris: {
|
|
||||||
name: 'Paris',
|
|
||||||
timezone: 'Europe/Paris',
|
|
||||||
locale: 'fr-FR',
|
|
||||||
lat: 48.8566,
|
|
||||||
lon: 2.3522,
|
|
||||||
},
|
|
||||||
rome: {
|
|
||||||
name: 'Rome',
|
|
||||||
timezone: 'Europe/Rome',
|
|
||||||
locale: 'it-IT',
|
|
||||||
lat: 41.9028,
|
|
||||||
lon: 12.4964,
|
|
||||||
},
|
|
||||||
saoPaulo: {
|
|
||||||
name: 'São Paulo',
|
|
||||||
timezone: 'America/Sao_Paulo',
|
|
||||||
locale: 'pt-BR',
|
|
||||||
lat: -23.5505,
|
|
||||||
lon: -46.6333,
|
|
||||||
},
|
|
||||||
sanFrancisco: {
|
|
||||||
name: 'San Francisco',
|
|
||||||
timezone: 'America/Los_Angeles',
|
|
||||||
locale: 'en-US',
|
|
||||||
lat: 37.7749,
|
|
||||||
lon: -122.4194,
|
|
||||||
},
|
|
||||||
seoul: {
|
|
||||||
name: 'Seoul',
|
|
||||||
timezone: 'Asia/Seoul',
|
|
||||||
locale: 'ko-KR',
|
|
||||||
lat: 37.5665,
|
|
||||||
lon: 126.978,
|
|
||||||
},
|
|
||||||
shanghai: {
|
|
||||||
name: 'Shanghai',
|
|
||||||
timezone: 'Asia/Shanghai',
|
|
||||||
locale: 'zh-CN',
|
|
||||||
lat: 31.2304,
|
|
||||||
lon: 121.4737,
|
|
||||||
},
|
|
||||||
singapore: {
|
|
||||||
name: 'Singapore',
|
|
||||||
timezone: 'Asia/Singapore',
|
|
||||||
locale: 'en-SG',
|
|
||||||
lat: 1.3521,
|
|
||||||
lon: 103.8198,
|
|
||||||
},
|
|
||||||
sydney: {
|
|
||||||
name: 'Sydney',
|
|
||||||
timezone: 'Australia/Sydney',
|
|
||||||
locale: 'en-AU',
|
|
||||||
lat: -33.8688,
|
|
||||||
lon: 151.2195,
|
|
||||||
},
|
|
||||||
tokyo: {
|
|
||||||
name: 'Tokyo',
|
|
||||||
timezone: 'Asia/Tokyo',
|
|
||||||
locale: 'ja-JP',
|
|
||||||
lat: 35.6895,
|
|
||||||
lon: 139.6917,
|
|
||||||
},
|
|
||||||
vienna: {
|
|
||||||
name: 'Vienna',
|
|
||||||
timezone: 'Europe/Vienna',
|
|
||||||
locale: 'de-DE',
|
|
||||||
lat: 48.2082,
|
|
||||||
lon: 16.3738,
|
|
||||||
},
|
|
||||||
toronto: {
|
|
||||||
name: 'Toronto',
|
|
||||||
timezone: 'America/Toronto',
|
|
||||||
locale: 'en-CA',
|
|
||||||
lat: 43.6532,
|
|
||||||
lon: -79.3832,
|
|
||||||
},
|
|
||||||
washington: {
|
|
||||||
name: 'Washington',
|
|
||||||
timezone: 'America/New_York',
|
|
||||||
locale: 'en-US',
|
|
||||||
lat: 38.9072,
|
|
||||||
lon: -77.0369,
|
|
||||||
},
|
|
||||||
}
|
|
||||||
|
|
||||||
export default profiles
|
|
||||||
|
|
@ -1,18 +0,0 @@
|
||||||
const userAgents = [
|
|
||||||
'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/100.0.4896.127 Safari/537.36',
|
|
||||||
'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/100.0.4896.127 Safari/537.36',
|
|
||||||
'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/101.0.4951.54 Safari/537.36',
|
|
||||||
'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/100.0.4896.88 Safari/537.36',
|
|
||||||
'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/100.0.4896.75 Safari/537.36',
|
|
||||||
'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/100.0.4896.88 Safari/537.36',
|
|
||||||
'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/101.0.4951.54 Safari/537.36',
|
|
||||||
'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/101.0.4951.41 Safari/537.36',
|
|
||||||
'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/100.0.4896.75 Safari/537.36',
|
|
||||||
'Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/100.0.4896.127 Safari/537.36',
|
|
||||||
'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/100.0.4896.127 Safari/537.36 Edg/100.0.1185.50',
|
|
||||||
'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/100.0.4896.127 Safari/537.36 Edg/100.0.1185.44',
|
|
||||||
'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/101.0.4951.41 Safari/537.36 Edg/101.0.1210.32',
|
|
||||||
'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/101.0.4951.41 Safari/537.36',
|
|
||||||
]
|
|
||||||
|
|
||||||
export default userAgents
|
|
||||||
83
src/utils/webRTC.js
Normal file
|
|
@ -0,0 +1,83 @@
|
||||||
|
const getWebRTC = (setWebRTCData) => {
|
||||||
|
if (navigator.getUserMedia) {
|
||||||
|
const ipRegex = /([0-9]{1,3}(\.[0-9]{1,3}){3}|[a-f0-9]{1,4}(:[a-f0-9]{1,4}){7})/;
|
||||||
|
|
||||||
|
// compatibility for firefox and chrome
|
||||||
|
let RTCPeerConnection = window.RTCPeerConnection
|
||||||
|
|| window.mozRTCPeerConnection
|
||||||
|
|| window.webkitRTCPeerConnection;
|
||||||
|
|
||||||
|
// bypass naive webrtc blocking using an iframe
|
||||||
|
if (!RTCPeerConnection) {
|
||||||
|
const frame = document.createElement('iframe');
|
||||||
|
document.body.appendChild(frame);
|
||||||
|
frame.style.display = 'none';
|
||||||
|
const win = frame.contentWindow;
|
||||||
|
RTCPeerConnection = win.RTCPeerConnection
|
||||||
|
|| win.mozRTCPeerConnection
|
||||||
|
|| win.webkitRTCPeerConnection;
|
||||||
|
}
|
||||||
|
|
||||||
|
// minimal requirements for data connection
|
||||||
|
const mediaConstraints = {
|
||||||
|
optional: [{ RtpDataChannels: true }]
|
||||||
|
};
|
||||||
|
|
||||||
|
const servers = { iceServers: [{ urls: 'stun:stun.l.google.com:19302' }] };
|
||||||
|
|
||||||
|
// construct a new RTCPeerConnection
|
||||||
|
const pc = new RTCPeerConnection(servers, mediaConstraints);
|
||||||
|
|
||||||
|
let ips = [];
|
||||||
|
const ipsData = [];
|
||||||
|
|
||||||
|
// listen for candidate events
|
||||||
|
pc.onicecandidate = (ice) => {
|
||||||
|
// skip non-candidate events
|
||||||
|
if (ice.candidate) {
|
||||||
|
const ip = ipRegex.exec(ice.candidate.candidate);
|
||||||
|
if (ip !== null && ip.length > 1) {
|
||||||
|
ips.push(ip[1]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
// create a bogus data channel
|
||||||
|
pc.createDataChannel('');
|
||||||
|
|
||||||
|
// create an offer sdp
|
||||||
|
pc.createOffer((result) => {
|
||||||
|
// trigger the stun server request
|
||||||
|
pc.setLocalDescription(result, () => {}, () => {});
|
||||||
|
}, () => {});
|
||||||
|
|
||||||
|
const waitForElement = async () => {
|
||||||
|
if (pc.localDescription) {
|
||||||
|
const lines = pc.localDescription.sdp.split('\n');
|
||||||
|
lines.forEach((line) => {
|
||||||
|
if (line.indexOf('a=candidate:') === 0) {
|
||||||
|
const ip = ipRegex.exec(line);
|
||||||
|
if (ip !== null && ip.length > 1) {
|
||||||
|
ips.push(ip[1]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
ips = [...new Set(ips)];
|
||||||
|
for (let i = 0; i < ips.length; i++) {
|
||||||
|
ipsData.push(fetch(`https://api.locatejs.com/ip/${ips[i]}`)
|
||||||
|
.then((response) => response.json())
|
||||||
|
.then((json) => json));
|
||||||
|
}
|
||||||
|
setWebRTCData(await Promise.all(ipsData));
|
||||||
|
} else {
|
||||||
|
setTimeout(waitForElement, 1000);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
waitForElement();
|
||||||
|
} else {
|
||||||
|
setWebRTCData([]);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
export default getWebRTC;
|
||||||
|
|
@ -1,20 +0,0 @@
|
||||||
{
|
|
||||||
"compilerOptions": {
|
|
||||||
"target": "es5",
|
|
||||||
"lib": ["dom", "dom.iterable", "esnext"],
|
|
||||||
"allowJs": false,
|
|
||||||
"skipLibCheck": true,
|
|
||||||
"esModuleInterop": true,
|
|
||||||
"allowSyntheticDefaultImports": true,
|
|
||||||
"strict": true,
|
|
||||||
"forceConsistentCasingInFileNames": true,
|
|
||||||
"noFallthroughCasesInSwitch": true,
|
|
||||||
"module": "esnext",
|
|
||||||
"moduleResolution": "node",
|
|
||||||
"resolveJsonModule": true,
|
|
||||||
"noEmit": false,
|
|
||||||
"jsx": "react"
|
|
||||||
},
|
|
||||||
"include": ["src"],
|
|
||||||
"exclude": ["build", "node_modules"]
|
|
||||||
}
|
|
||||||
|
|
@ -1,15 +0,0 @@
|
||||||
// Do this as the first thing so that any code reading it knows the right env.
|
|
||||||
process.env.BABEL_ENV = 'production';
|
|
||||||
process.env.NODE_ENV = 'production';
|
|
||||||
process.env.ASSET_PATH = '/';
|
|
||||||
|
|
||||||
var webpack = require('webpack'),
|
|
||||||
config = require('../webpack.config');
|
|
||||||
|
|
||||||
delete config.chromeExtensionBoilerplate;
|
|
||||||
|
|
||||||
config.mode = 'production';
|
|
||||||
|
|
||||||
webpack(config, function (err) {
|
|
||||||
if (err) throw err;
|
|
||||||
});
|
|
||||||
|
|
@ -1,5 +0,0 @@
|
||||||
// tiny wrapper with default env vars
|
|
||||||
module.exports = {
|
|
||||||
NODE_ENV: process.env.NODE_ENV || 'development',
|
|
||||||
PORT: process.env.PORT || 3000,
|
|
||||||
};
|
|
||||||
|
|
@ -1,60 +0,0 @@
|
||||||
// Do this as the first thing so that any code reading it knows the right env.
|
|
||||||
process.env.BABEL_ENV = 'development';
|
|
||||||
process.env.NODE_ENV = 'development';
|
|
||||||
process.env.ASSET_PATH = '/';
|
|
||||||
|
|
||||||
var WebpackDevServer = require('webpack-dev-server'),
|
|
||||||
webpack = require('webpack'),
|
|
||||||
config = require('../webpack.config'),
|
|
||||||
env = require('./env'),
|
|
||||||
path = require('path');
|
|
||||||
|
|
||||||
var options = config.chromeExtensionBoilerplate || {};
|
|
||||||
var excludeEntriesToHotReload = options.notHotReload || [];
|
|
||||||
|
|
||||||
for (var entryName in config.entry) {
|
|
||||||
if (excludeEntriesToHotReload.indexOf(entryName) === -1) {
|
|
||||||
config.entry[entryName] = [
|
|
||||||
'webpack/hot/dev-server',
|
|
||||||
`webpack-dev-server/client?hot=true&hostname=localhost&port=${env.PORT}`,
|
|
||||||
].concat(config.entry[entryName]);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
config.plugins = [new webpack.HotModuleReplacementPlugin()].concat(
|
|
||||||
config.plugins || []
|
|
||||||
);
|
|
||||||
|
|
||||||
delete config.chromeExtensionBoilerplate;
|
|
||||||
|
|
||||||
var compiler = webpack(config);
|
|
||||||
|
|
||||||
var server = new WebpackDevServer(
|
|
||||||
{
|
|
||||||
https: false,
|
|
||||||
hot: false,
|
|
||||||
client: false,
|
|
||||||
host: 'localhost',
|
|
||||||
port: env.PORT,
|
|
||||||
static: {
|
|
||||||
directory: path.join(__dirname, '../build'),
|
|
||||||
},
|
|
||||||
devMiddleware: {
|
|
||||||
publicPath: `http://localhost:${env.PORT}/`,
|
|
||||||
writeToDisk: true,
|
|
||||||
},
|
|
||||||
headers: {
|
|
||||||
'Access-Control-Allow-Origin': '*',
|
|
||||||
},
|
|
||||||
allowedHosts: 'all',
|
|
||||||
},
|
|
||||||
compiler
|
|
||||||
);
|
|
||||||
|
|
||||||
if (process.env.NODE_ENV === 'development' && module.hot) {
|
|
||||||
module.hot.accept();
|
|
||||||
}
|
|
||||||
|
|
||||||
(async () => {
|
|
||||||
await server.start();
|
|
||||||
})();
|
|
||||||
|
|
@ -1,174 +0,0 @@
|
||||||
var webpack = require('webpack'),
|
|
||||||
path = require('path'),
|
|
||||||
fileSystem = require('fs-extra'),
|
|
||||||
env = require('./utils/env'),
|
|
||||||
CopyWebpackPlugin = require('copy-webpack-plugin'),
|
|
||||||
HtmlWebpackPlugin = require('html-webpack-plugin'),
|
|
||||||
TerserPlugin = require('terser-webpack-plugin')
|
|
||||||
var { CleanWebpackPlugin } = require('clean-webpack-plugin')
|
|
||||||
|
|
||||||
const ASSET_PATH = process.env.ASSET_PATH || '/'
|
|
||||||
|
|
||||||
var alias = {
|
|
||||||
'react-dom': '@hot-loader/react-dom',
|
|
||||||
}
|
|
||||||
|
|
||||||
// load the secrets
|
|
||||||
var secretsPath = path.join(__dirname, 'secrets.' + env.NODE_ENV + '.js')
|
|
||||||
|
|
||||||
var fileExtensions = [
|
|
||||||
'jpg',
|
|
||||||
'jpeg',
|
|
||||||
'png',
|
|
||||||
'gif',
|
|
||||||
'eot',
|
|
||||||
'otf',
|
|
||||||
'svg',
|
|
||||||
'ttf',
|
|
||||||
'woff',
|
|
||||||
'woff2',
|
|
||||||
]
|
|
||||||
|
|
||||||
if (fileSystem.existsSync(secretsPath)) {
|
|
||||||
alias['secrets'] = secretsPath
|
|
||||||
}
|
|
||||||
|
|
||||||
var options = {
|
|
||||||
mode: process.env.NODE_ENV || 'development',
|
|
||||||
entry: {
|
|
||||||
popup: path.join(__dirname, 'src', 'pages', 'Popup', 'index.jsx'),
|
|
||||||
background: path.join(__dirname, 'src', 'pages', 'Background', 'index.js'),
|
|
||||||
},
|
|
||||||
chromeExtensionBoilerplate: {
|
|
||||||
notHotReload: ['background'],
|
|
||||||
},
|
|
||||||
output: {
|
|
||||||
filename: '[name].bundle.js',
|
|
||||||
path: path.resolve(__dirname, 'build'),
|
|
||||||
clean: true,
|
|
||||||
publicPath: ASSET_PATH,
|
|
||||||
},
|
|
||||||
module: {
|
|
||||||
rules: [
|
|
||||||
{
|
|
||||||
// look for .css or .scss files
|
|
||||||
test: /\.(css|scss)$/,
|
|
||||||
// in the `src` directory
|
|
||||||
use: [
|
|
||||||
{
|
|
||||||
loader: 'style-loader',
|
|
||||||
},
|
|
||||||
{
|
|
||||||
loader: 'css-loader',
|
|
||||||
},
|
|
||||||
{
|
|
||||||
loader: 'sass-loader',
|
|
||||||
options: {
|
|
||||||
sourceMap: true,
|
|
||||||
},
|
|
||||||
},
|
|
||||||
],
|
|
||||||
},
|
|
||||||
{
|
|
||||||
test: new RegExp('.(' + fileExtensions.join('|') + ')$'),
|
|
||||||
type: 'asset/resource',
|
|
||||||
exclude: /node_modules/,
|
|
||||||
// loader: 'file-loader',
|
|
||||||
// options: {
|
|
||||||
// name: '[name].[ext]',
|
|
||||||
// },
|
|
||||||
},
|
|
||||||
{
|
|
||||||
test: /\.html$/,
|
|
||||||
loader: 'html-loader',
|
|
||||||
exclude: /node_modules/,
|
|
||||||
},
|
|
||||||
{ test: /\.(ts|tsx)$/, loader: 'ts-loader', exclude: /node_modules/ },
|
|
||||||
{
|
|
||||||
test: /\.(js|jsx)$/,
|
|
||||||
use: [
|
|
||||||
{
|
|
||||||
loader: 'source-map-loader',
|
|
||||||
},
|
|
||||||
{
|
|
||||||
loader: 'babel-loader',
|
|
||||||
},
|
|
||||||
],
|
|
||||||
exclude: /node_modules/,
|
|
||||||
},
|
|
||||||
],
|
|
||||||
},
|
|
||||||
resolve: {
|
|
||||||
alias: alias,
|
|
||||||
extensions: fileExtensions
|
|
||||||
.map((extension) => '.' + extension)
|
|
||||||
.concat(['.js', '.jsx', '.ts', '.tsx', '.css']),
|
|
||||||
},
|
|
||||||
plugins: [
|
|
||||||
new CleanWebpackPlugin({ verbose: false }),
|
|
||||||
new webpack.ProgressPlugin(),
|
|
||||||
// expose and write the allowed env vars on the compiled bundle
|
|
||||||
new webpack.EnvironmentPlugin(['NODE_ENV']),
|
|
||||||
new CopyWebpackPlugin({
|
|
||||||
patterns: [
|
|
||||||
{
|
|
||||||
from: 'src/manifest.json',
|
|
||||||
to: path.join(__dirname, 'build'),
|
|
||||||
force: true,
|
|
||||||
transform: function (content, path) {
|
|
||||||
// generates the manifest file using the package.json informations
|
|
||||||
return Buffer.from(
|
|
||||||
JSON.stringify({
|
|
||||||
description: process.env.npm_package_description,
|
|
||||||
version: process.env.npm_package_version,
|
|
||||||
...JSON.parse(content.toString()),
|
|
||||||
})
|
|
||||||
)
|
|
||||||
},
|
|
||||||
},
|
|
||||||
],
|
|
||||||
}),
|
|
||||||
new CopyWebpackPlugin({
|
|
||||||
patterns: [
|
|
||||||
{
|
|
||||||
from: 'src/assets/img/icon-128.png',
|
|
||||||
to: path.join(__dirname, 'build'),
|
|
||||||
force: true,
|
|
||||||
},
|
|
||||||
],
|
|
||||||
}),
|
|
||||||
new CopyWebpackPlugin({
|
|
||||||
patterns: [
|
|
||||||
{
|
|
||||||
from: 'src/assets/img/icon-32.png',
|
|
||||||
to: path.join(__dirname, 'build'),
|
|
||||||
force: true,
|
|
||||||
},
|
|
||||||
],
|
|
||||||
}),
|
|
||||||
new HtmlWebpackPlugin({
|
|
||||||
template: path.join(__dirname, 'src', 'pages', 'Popup', 'index.html'),
|
|
||||||
filename: 'popup.html',
|
|
||||||
chunks: ['popup'],
|
|
||||||
cache: false,
|
|
||||||
}),
|
|
||||||
],
|
|
||||||
infrastructureLogging: {
|
|
||||||
level: 'info',
|
|
||||||
},
|
|
||||||
}
|
|
||||||
|
|
||||||
if (env.NODE_ENV === 'development') {
|
|
||||||
options.devtool = 'cheap-module-source-map'
|
|
||||||
} else {
|
|
||||||
options.optimization = {
|
|
||||||
minimize: true,
|
|
||||||
minimizer: [
|
|
||||||
new TerserPlugin({
|
|
||||||
extractComments: false,
|
|
||||||
}),
|
|
||||||
],
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
module.exports = options
|
|
||||||