├── Example.jpeg
├── logo_128.png
├── Chrome
├── logo.png
├── fonts
│ ├── Poppins
│ │ ├── Poppins-Bold.ttf
│ │ ├── Poppins-Italic.ttf
│ │ ├── Poppins-Light.ttf
│ │ ├── Poppins-Medium.ttf
│ │ ├── Poppins-Regular.ttf
│ │ ├── Poppins-ExtraBold.ttf
│ │ ├── Poppins-SemiBold.ttf
│ │ ├── Poppins-BoldItalic.ttf
│ │ ├── Poppins-LightItalic.ttf
│ │ ├── Poppins-MediumItalic.ttf
│ │ ├── Poppins-SemiBoldItalic.ttf
│ │ └── Poppins-ExtraBoldItalic.ttf
│ ├── Open_Sans
│ │ ├── OpenSans-Bold.ttf
│ │ ├── OpenSans-Italic.ttf
│ │ ├── OpenSans-Light.ttf
│ │ ├── OpenSans-Medium.ttf
│ │ ├── OpenSans-ExtraBold.ttf
│ │ ├── OpenSans-Regular.ttf
│ │ ├── OpenSans-SemiBold.ttf
│ │ ├── OpenSans-BoldItalic.ttf
│ │ ├── OpenSans-LightItalic.ttf
│ │ ├── OpenSans-MediumItalic.ttf
│ │ ├── OpenSans-SemiBoldItalic.ttf
│ │ └── OpenSans-ExtraBoldItalic.ttf
│ └── Plus_Jakarta_Sans
│ │ ├── PlusJakartaSans-Bold.ttf
│ │ ├── PlusJakartaSans-Italic.ttf
│ │ ├── PlusJakartaSans-Light.ttf
│ │ ├── PlusJakartaSans-Medium.ttf
│ │ ├── PlusJakartaSans-ExtraBold.ttf
│ │ ├── PlusJakartaSans-Regular.ttf
│ │ ├── PlusJakartaSans-SemiBold.ttf
│ │ ├── PlusJakartaSans-BoldItalic.ttf
│ │ ├── PlusJakartaSans-LightItalic.ttf
│ │ ├── PlusJakartaSans-MediumItalic.ttf
│ │ ├── PlusJakartaSans-SemiBoldItalic.ttf
│ │ └── PlusJakartaSans-ExtraBoldItalic.ttf
├── popup.js
├── options.js
├── background.js
├── popup.html
├── options.html
├── popup.css
├── manifest.json
├── visualizer.js
├── options.css
├── fonts.css
├── visualizer.html
└── content-inject.js
├── Firefox
├── logo.png
├── fonts
│ ├── Poppins
│ │ ├── Poppins-Bold.ttf
│ │ ├── Poppins-Italic.ttf
│ │ ├── Poppins-Light.ttf
│ │ ├── Poppins-Medium.ttf
│ │ ├── Poppins-Regular.ttf
│ │ ├── Poppins-SemiBold.ttf
│ │ ├── Poppins-BoldItalic.ttf
│ │ ├── Poppins-ExtraBold.ttf
│ │ ├── Poppins-LightItalic.ttf
│ │ ├── Poppins-MediumItalic.ttf
│ │ ├── Poppins-ExtraBoldItalic.ttf
│ │ └── Poppins-SemiBoldItalic.ttf
│ ├── Open_Sans
│ │ ├── OpenSans-Bold.ttf
│ │ ├── OpenSans-Light.ttf
│ │ ├── OpenSans-Italic.ttf
│ │ ├── OpenSans-Medium.ttf
│ │ ├── OpenSans-Regular.ttf
│ │ ├── OpenSans-SemiBold.ttf
│ │ ├── OpenSans-BoldItalic.ttf
│ │ ├── OpenSans-ExtraBold.ttf
│ │ ├── OpenSans-LightItalic.ttf
│ │ ├── OpenSans-MediumItalic.ttf
│ │ ├── OpenSans-ExtraBoldItalic.ttf
│ │ └── OpenSans-SemiBoldItalic.ttf
│ └── Plus_Jakarta_Sans
│ │ ├── PlusJakartaSans-Bold.ttf
│ │ ├── PlusJakartaSans-Light.ttf
│ │ ├── PlusJakartaSans-Italic.ttf
│ │ ├── PlusJakartaSans-Medium.ttf
│ │ ├── PlusJakartaSans-Regular.ttf
│ │ ├── PlusJakartaSans-SemiBold.ttf
│ │ ├── PlusJakartaSans-BoldItalic.ttf
│ │ ├── PlusJakartaSans-ExtraBold.ttf
│ │ ├── PlusJakartaSans-LightItalic.ttf
│ │ ├── PlusJakartaSans-MediumItalic.ttf
│ │ ├── PlusJakartaSans-ExtraBoldItalic.ttf
│ │ └── PlusJakartaSans-SemiBoldItalic.ttf
├── popup.js
├── options.js
├── background.js
├── popup.html
├── popup.css
├── options.html
├── manifest.json
├── visualizer.js
├── options.css
├── fonts.css
├── visualizer.html
└── content-inject.js
├── package.json
├── README.md
└── LICENSE
/Example.jpeg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/unreleased/TorpedoRead/HEAD/Example.jpeg
--------------------------------------------------------------------------------
/logo_128.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/unreleased/TorpedoRead/HEAD/logo_128.png
--------------------------------------------------------------------------------
/Chrome/logo.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/unreleased/TorpedoRead/HEAD/Chrome/logo.png
--------------------------------------------------------------------------------
/Firefox/logo.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/unreleased/TorpedoRead/HEAD/Firefox/logo.png
--------------------------------------------------------------------------------
/Chrome/fonts/Poppins/Poppins-Bold.ttf:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/unreleased/TorpedoRead/HEAD/Chrome/fonts/Poppins/Poppins-Bold.ttf
--------------------------------------------------------------------------------
/Chrome/fonts/Open_Sans/OpenSans-Bold.ttf:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/unreleased/TorpedoRead/HEAD/Chrome/fonts/Open_Sans/OpenSans-Bold.ttf
--------------------------------------------------------------------------------
/Chrome/fonts/Poppins/Poppins-Italic.ttf:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/unreleased/TorpedoRead/HEAD/Chrome/fonts/Poppins/Poppins-Italic.ttf
--------------------------------------------------------------------------------
/Chrome/fonts/Poppins/Poppins-Light.ttf:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/unreleased/TorpedoRead/HEAD/Chrome/fonts/Poppins/Poppins-Light.ttf
--------------------------------------------------------------------------------
/Chrome/fonts/Poppins/Poppins-Medium.ttf:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/unreleased/TorpedoRead/HEAD/Chrome/fonts/Poppins/Poppins-Medium.ttf
--------------------------------------------------------------------------------
/Chrome/fonts/Poppins/Poppins-Regular.ttf:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/unreleased/TorpedoRead/HEAD/Chrome/fonts/Poppins/Poppins-Regular.ttf
--------------------------------------------------------------------------------
/Firefox/fonts/Poppins/Poppins-Bold.ttf:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/unreleased/TorpedoRead/HEAD/Firefox/fonts/Poppins/Poppins-Bold.ttf
--------------------------------------------------------------------------------
/Firefox/fonts/Poppins/Poppins-Italic.ttf:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/unreleased/TorpedoRead/HEAD/Firefox/fonts/Poppins/Poppins-Italic.ttf
--------------------------------------------------------------------------------
/Firefox/fonts/Poppins/Poppins-Light.ttf:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/unreleased/TorpedoRead/HEAD/Firefox/fonts/Poppins/Poppins-Light.ttf
--------------------------------------------------------------------------------
/Firefox/fonts/Poppins/Poppins-Medium.ttf:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/unreleased/TorpedoRead/HEAD/Firefox/fonts/Poppins/Poppins-Medium.ttf
--------------------------------------------------------------------------------
/Chrome/fonts/Open_Sans/OpenSans-Italic.ttf:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/unreleased/TorpedoRead/HEAD/Chrome/fonts/Open_Sans/OpenSans-Italic.ttf
--------------------------------------------------------------------------------
/Chrome/fonts/Open_Sans/OpenSans-Light.ttf:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/unreleased/TorpedoRead/HEAD/Chrome/fonts/Open_Sans/OpenSans-Light.ttf
--------------------------------------------------------------------------------
/Chrome/fonts/Open_Sans/OpenSans-Medium.ttf:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/unreleased/TorpedoRead/HEAD/Chrome/fonts/Open_Sans/OpenSans-Medium.ttf
--------------------------------------------------------------------------------
/Chrome/fonts/Poppins/Poppins-ExtraBold.ttf:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/unreleased/TorpedoRead/HEAD/Chrome/fonts/Poppins/Poppins-ExtraBold.ttf
--------------------------------------------------------------------------------
/Chrome/fonts/Poppins/Poppins-SemiBold.ttf:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/unreleased/TorpedoRead/HEAD/Chrome/fonts/Poppins/Poppins-SemiBold.ttf
--------------------------------------------------------------------------------
/Firefox/fonts/Open_Sans/OpenSans-Bold.ttf:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/unreleased/TorpedoRead/HEAD/Firefox/fonts/Open_Sans/OpenSans-Bold.ttf
--------------------------------------------------------------------------------
/Firefox/fonts/Open_Sans/OpenSans-Light.ttf:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/unreleased/TorpedoRead/HEAD/Firefox/fonts/Open_Sans/OpenSans-Light.ttf
--------------------------------------------------------------------------------
/Firefox/fonts/Poppins/Poppins-Regular.ttf:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/unreleased/TorpedoRead/HEAD/Firefox/fonts/Poppins/Poppins-Regular.ttf
--------------------------------------------------------------------------------
/Firefox/fonts/Poppins/Poppins-SemiBold.ttf:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/unreleased/TorpedoRead/HEAD/Firefox/fonts/Poppins/Poppins-SemiBold.ttf
--------------------------------------------------------------------------------
/Chrome/fonts/Open_Sans/OpenSans-ExtraBold.ttf:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/unreleased/TorpedoRead/HEAD/Chrome/fonts/Open_Sans/OpenSans-ExtraBold.ttf
--------------------------------------------------------------------------------
/Chrome/fonts/Open_Sans/OpenSans-Regular.ttf:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/unreleased/TorpedoRead/HEAD/Chrome/fonts/Open_Sans/OpenSans-Regular.ttf
--------------------------------------------------------------------------------
/Chrome/fonts/Open_Sans/OpenSans-SemiBold.ttf:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/unreleased/TorpedoRead/HEAD/Chrome/fonts/Open_Sans/OpenSans-SemiBold.ttf
--------------------------------------------------------------------------------
/Chrome/fonts/Poppins/Poppins-BoldItalic.ttf:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/unreleased/TorpedoRead/HEAD/Chrome/fonts/Poppins/Poppins-BoldItalic.ttf
--------------------------------------------------------------------------------
/Chrome/fonts/Poppins/Poppins-LightItalic.ttf:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/unreleased/TorpedoRead/HEAD/Chrome/fonts/Poppins/Poppins-LightItalic.ttf
--------------------------------------------------------------------------------
/Chrome/fonts/Poppins/Poppins-MediumItalic.ttf:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/unreleased/TorpedoRead/HEAD/Chrome/fonts/Poppins/Poppins-MediumItalic.ttf
--------------------------------------------------------------------------------
/Firefox/fonts/Open_Sans/OpenSans-Italic.ttf:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/unreleased/TorpedoRead/HEAD/Firefox/fonts/Open_Sans/OpenSans-Italic.ttf
--------------------------------------------------------------------------------
/Firefox/fonts/Open_Sans/OpenSans-Medium.ttf:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/unreleased/TorpedoRead/HEAD/Firefox/fonts/Open_Sans/OpenSans-Medium.ttf
--------------------------------------------------------------------------------
/Firefox/fonts/Open_Sans/OpenSans-Regular.ttf:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/unreleased/TorpedoRead/HEAD/Firefox/fonts/Open_Sans/OpenSans-Regular.ttf
--------------------------------------------------------------------------------
/Firefox/fonts/Open_Sans/OpenSans-SemiBold.ttf:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/unreleased/TorpedoRead/HEAD/Firefox/fonts/Open_Sans/OpenSans-SemiBold.ttf
--------------------------------------------------------------------------------
/Firefox/fonts/Poppins/Poppins-BoldItalic.ttf:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/unreleased/TorpedoRead/HEAD/Firefox/fonts/Poppins/Poppins-BoldItalic.ttf
--------------------------------------------------------------------------------
/Firefox/fonts/Poppins/Poppins-ExtraBold.ttf:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/unreleased/TorpedoRead/HEAD/Firefox/fonts/Poppins/Poppins-ExtraBold.ttf
--------------------------------------------------------------------------------
/Firefox/fonts/Poppins/Poppins-LightItalic.ttf:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/unreleased/TorpedoRead/HEAD/Firefox/fonts/Poppins/Poppins-LightItalic.ttf
--------------------------------------------------------------------------------
/Chrome/fonts/Open_Sans/OpenSans-BoldItalic.ttf:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/unreleased/TorpedoRead/HEAD/Chrome/fonts/Open_Sans/OpenSans-BoldItalic.ttf
--------------------------------------------------------------------------------
/Chrome/fonts/Open_Sans/OpenSans-LightItalic.ttf:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/unreleased/TorpedoRead/HEAD/Chrome/fonts/Open_Sans/OpenSans-LightItalic.ttf
--------------------------------------------------------------------------------
/Chrome/fonts/Poppins/Poppins-SemiBoldItalic.ttf:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/unreleased/TorpedoRead/HEAD/Chrome/fonts/Poppins/Poppins-SemiBoldItalic.ttf
--------------------------------------------------------------------------------
/Firefox/fonts/Open_Sans/OpenSans-BoldItalic.ttf:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/unreleased/TorpedoRead/HEAD/Firefox/fonts/Open_Sans/OpenSans-BoldItalic.ttf
--------------------------------------------------------------------------------
/Firefox/fonts/Open_Sans/OpenSans-ExtraBold.ttf:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/unreleased/TorpedoRead/HEAD/Firefox/fonts/Open_Sans/OpenSans-ExtraBold.ttf
--------------------------------------------------------------------------------
/Firefox/fonts/Poppins/Poppins-MediumItalic.ttf:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/unreleased/TorpedoRead/HEAD/Firefox/fonts/Poppins/Poppins-MediumItalic.ttf
--------------------------------------------------------------------------------
/Chrome/fonts/Open_Sans/OpenSans-MediumItalic.ttf:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/unreleased/TorpedoRead/HEAD/Chrome/fonts/Open_Sans/OpenSans-MediumItalic.ttf
--------------------------------------------------------------------------------
/Chrome/fonts/Open_Sans/OpenSans-SemiBoldItalic.ttf:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/unreleased/TorpedoRead/HEAD/Chrome/fonts/Open_Sans/OpenSans-SemiBoldItalic.ttf
--------------------------------------------------------------------------------
/Chrome/fonts/Poppins/Poppins-ExtraBoldItalic.ttf:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/unreleased/TorpedoRead/HEAD/Chrome/fonts/Poppins/Poppins-ExtraBoldItalic.ttf
--------------------------------------------------------------------------------
/Firefox/fonts/Open_Sans/OpenSans-LightItalic.ttf:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/unreleased/TorpedoRead/HEAD/Firefox/fonts/Open_Sans/OpenSans-LightItalic.ttf
--------------------------------------------------------------------------------
/Firefox/fonts/Open_Sans/OpenSans-MediumItalic.ttf:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/unreleased/TorpedoRead/HEAD/Firefox/fonts/Open_Sans/OpenSans-MediumItalic.ttf
--------------------------------------------------------------------------------
/Firefox/fonts/Poppins/Poppins-ExtraBoldItalic.ttf:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/unreleased/TorpedoRead/HEAD/Firefox/fonts/Poppins/Poppins-ExtraBoldItalic.ttf
--------------------------------------------------------------------------------
/Firefox/fonts/Poppins/Poppins-SemiBoldItalic.ttf:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/unreleased/TorpedoRead/HEAD/Firefox/fonts/Poppins/Poppins-SemiBoldItalic.ttf
--------------------------------------------------------------------------------
/Chrome/fonts/Open_Sans/OpenSans-ExtraBoldItalic.ttf:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/unreleased/TorpedoRead/HEAD/Chrome/fonts/Open_Sans/OpenSans-ExtraBoldItalic.ttf
--------------------------------------------------------------------------------
/Firefox/fonts/Open_Sans/OpenSans-ExtraBoldItalic.ttf:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/unreleased/TorpedoRead/HEAD/Firefox/fonts/Open_Sans/OpenSans-ExtraBoldItalic.ttf
--------------------------------------------------------------------------------
/Firefox/fonts/Open_Sans/OpenSans-SemiBoldItalic.ttf:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/unreleased/TorpedoRead/HEAD/Firefox/fonts/Open_Sans/OpenSans-SemiBoldItalic.ttf
--------------------------------------------------------------------------------
/Chrome/fonts/Plus_Jakarta_Sans/PlusJakartaSans-Bold.ttf:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/unreleased/TorpedoRead/HEAD/Chrome/fonts/Plus_Jakarta_Sans/PlusJakartaSans-Bold.ttf
--------------------------------------------------------------------------------
/Chrome/fonts/Plus_Jakarta_Sans/PlusJakartaSans-Italic.ttf:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/unreleased/TorpedoRead/HEAD/Chrome/fonts/Plus_Jakarta_Sans/PlusJakartaSans-Italic.ttf
--------------------------------------------------------------------------------
/Chrome/fonts/Plus_Jakarta_Sans/PlusJakartaSans-Light.ttf:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/unreleased/TorpedoRead/HEAD/Chrome/fonts/Plus_Jakarta_Sans/PlusJakartaSans-Light.ttf
--------------------------------------------------------------------------------
/Chrome/fonts/Plus_Jakarta_Sans/PlusJakartaSans-Medium.ttf:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/unreleased/TorpedoRead/HEAD/Chrome/fonts/Plus_Jakarta_Sans/PlusJakartaSans-Medium.ttf
--------------------------------------------------------------------------------
/Firefox/fonts/Plus_Jakarta_Sans/PlusJakartaSans-Bold.ttf:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/unreleased/TorpedoRead/HEAD/Firefox/fonts/Plus_Jakarta_Sans/PlusJakartaSans-Bold.ttf
--------------------------------------------------------------------------------
/Firefox/fonts/Plus_Jakarta_Sans/PlusJakartaSans-Light.ttf:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/unreleased/TorpedoRead/HEAD/Firefox/fonts/Plus_Jakarta_Sans/PlusJakartaSans-Light.ttf
--------------------------------------------------------------------------------
/Chrome/fonts/Plus_Jakarta_Sans/PlusJakartaSans-ExtraBold.ttf:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/unreleased/TorpedoRead/HEAD/Chrome/fonts/Plus_Jakarta_Sans/PlusJakartaSans-ExtraBold.ttf
--------------------------------------------------------------------------------
/Chrome/fonts/Plus_Jakarta_Sans/PlusJakartaSans-Regular.ttf:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/unreleased/TorpedoRead/HEAD/Chrome/fonts/Plus_Jakarta_Sans/PlusJakartaSans-Regular.ttf
--------------------------------------------------------------------------------
/Chrome/fonts/Plus_Jakarta_Sans/PlusJakartaSans-SemiBold.ttf:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/unreleased/TorpedoRead/HEAD/Chrome/fonts/Plus_Jakarta_Sans/PlusJakartaSans-SemiBold.ttf
--------------------------------------------------------------------------------
/Firefox/fonts/Plus_Jakarta_Sans/PlusJakartaSans-Italic.ttf:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/unreleased/TorpedoRead/HEAD/Firefox/fonts/Plus_Jakarta_Sans/PlusJakartaSans-Italic.ttf
--------------------------------------------------------------------------------
/Firefox/fonts/Plus_Jakarta_Sans/PlusJakartaSans-Medium.ttf:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/unreleased/TorpedoRead/HEAD/Firefox/fonts/Plus_Jakarta_Sans/PlusJakartaSans-Medium.ttf
--------------------------------------------------------------------------------
/Firefox/fonts/Plus_Jakarta_Sans/PlusJakartaSans-Regular.ttf:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/unreleased/TorpedoRead/HEAD/Firefox/fonts/Plus_Jakarta_Sans/PlusJakartaSans-Regular.ttf
--------------------------------------------------------------------------------
/Firefox/fonts/Plus_Jakarta_Sans/PlusJakartaSans-SemiBold.ttf:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/unreleased/TorpedoRead/HEAD/Firefox/fonts/Plus_Jakarta_Sans/PlusJakartaSans-SemiBold.ttf
--------------------------------------------------------------------------------
/Chrome/fonts/Plus_Jakarta_Sans/PlusJakartaSans-BoldItalic.ttf:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/unreleased/TorpedoRead/HEAD/Chrome/fonts/Plus_Jakarta_Sans/PlusJakartaSans-BoldItalic.ttf
--------------------------------------------------------------------------------
/Chrome/fonts/Plus_Jakarta_Sans/PlusJakartaSans-LightItalic.ttf:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/unreleased/TorpedoRead/HEAD/Chrome/fonts/Plus_Jakarta_Sans/PlusJakartaSans-LightItalic.ttf
--------------------------------------------------------------------------------
/Firefox/fonts/Plus_Jakarta_Sans/PlusJakartaSans-BoldItalic.ttf:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/unreleased/TorpedoRead/HEAD/Firefox/fonts/Plus_Jakarta_Sans/PlusJakartaSans-BoldItalic.ttf
--------------------------------------------------------------------------------
/Firefox/fonts/Plus_Jakarta_Sans/PlusJakartaSans-ExtraBold.ttf:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/unreleased/TorpedoRead/HEAD/Firefox/fonts/Plus_Jakarta_Sans/PlusJakartaSans-ExtraBold.ttf
--------------------------------------------------------------------------------
/Chrome/fonts/Plus_Jakarta_Sans/PlusJakartaSans-MediumItalic.ttf:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/unreleased/TorpedoRead/HEAD/Chrome/fonts/Plus_Jakarta_Sans/PlusJakartaSans-MediumItalic.ttf
--------------------------------------------------------------------------------
/Chrome/fonts/Plus_Jakarta_Sans/PlusJakartaSans-SemiBoldItalic.ttf:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/unreleased/TorpedoRead/HEAD/Chrome/fonts/Plus_Jakarta_Sans/PlusJakartaSans-SemiBoldItalic.ttf
--------------------------------------------------------------------------------
/Firefox/fonts/Plus_Jakarta_Sans/PlusJakartaSans-LightItalic.ttf:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/unreleased/TorpedoRead/HEAD/Firefox/fonts/Plus_Jakarta_Sans/PlusJakartaSans-LightItalic.ttf
--------------------------------------------------------------------------------
/Firefox/fonts/Plus_Jakarta_Sans/PlusJakartaSans-MediumItalic.ttf:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/unreleased/TorpedoRead/HEAD/Firefox/fonts/Plus_Jakarta_Sans/PlusJakartaSans-MediumItalic.ttf
--------------------------------------------------------------------------------
/Chrome/fonts/Plus_Jakarta_Sans/PlusJakartaSans-ExtraBoldItalic.ttf:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/unreleased/TorpedoRead/HEAD/Chrome/fonts/Plus_Jakarta_Sans/PlusJakartaSans-ExtraBoldItalic.ttf
--------------------------------------------------------------------------------
/Firefox/fonts/Plus_Jakarta_Sans/PlusJakartaSans-ExtraBoldItalic.ttf:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/unreleased/TorpedoRead/HEAD/Firefox/fonts/Plus_Jakarta_Sans/PlusJakartaSans-ExtraBoldItalic.ttf
--------------------------------------------------------------------------------
/Firefox/fonts/Plus_Jakarta_Sans/PlusJakartaSans-SemiBoldItalic.ttf:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/unreleased/TorpedoRead/HEAD/Firefox/fonts/Plus_Jakarta_Sans/PlusJakartaSans-SemiBoldItalic.ttf
--------------------------------------------------------------------------------
/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "dependencies": {
3 | "@pdftron/pdfnet-node": "^9.2.0-1",
4 | "cheerio": "^1.0.0-rc.11",
5 | "epub": "^1.2.1",
6 | "glob": "^8.0.3",
7 | "multer": "^1.4.4",
8 | "pdfcrowd": "^5.6.0",
9 | "pdfjs-dist": "^2.14.305",
10 | "request": "^2.88.2",
11 | "request-promise": "^4.2.6",
12 | "uuid": "^8.3.2"
13 | }
14 | }
15 |
--------------------------------------------------------------------------------
/Firefox/popup.js:
--------------------------------------------------------------------------------
1 | const inject = document.getElementById("inject");
2 |
3 | const clicked = [];
4 |
5 | inject.addEventListener("click", async () => {
6 | // Get tab + options (visuals, runObserver)
7 | const tab = await browser.tabs.query({ active: true, currentWindow: true });
8 | const options = await browser.storage.sync.get({
9 | visuals: false,
10 | runObserver: false,
11 | })
12 |
13 | // Send those options to Chrome
14 | // Make sure autoapply is true since they want to manually inject the content.
15 | chrome.tabs.sendMessage(tab[0].id, {
16 | ...options,
17 | autoapply: true
18 | })
19 | });
20 |
--------------------------------------------------------------------------------
/Chrome/popup.js:
--------------------------------------------------------------------------------
1 | const inject = document.getElementById("inject");
2 |
3 | const clicked = [];
4 |
5 | inject.addEventListener("click", async () => {
6 | // Get tab + options (visuals, runObserver)
7 | const tab = await chrome.tabs.query({ active: true, currentWindow: true });
8 | const options = await chrome.storage.sync.get({
9 | visuals: false,
10 | runObserver: false,
11 | })
12 |
13 | // Send those options to Chrome
14 | // Make sure autoapply is true since they want to manually inject the content.
15 | chrome.tabs.sendMessage(tab[0].id, {
16 | ...options,
17 | autoapply: true
18 | })
19 | });
20 |
21 | window.onload = async function() {
22 | // Get URL for appending to text.
23 | const tab = await chrome.tabs.query({ active: true, currentWindow: true });
24 | console.log("onload" + Date(), tab)
25 | }
--------------------------------------------------------------------------------
/Chrome/options.js:
--------------------------------------------------------------------------------
1 | const save = document.getElementById("save")
2 | const autoapply = document.getElementById("autoapply")
3 | const runObserver = document.getElementById("runObserver")
4 |
5 |
6 | save.addEventListener("click", (e) => {
7 | // Save to chrome storage:
8 | chrome.storage.sync.set({
9 | autoapply: autoapply.checked,
10 | runObserver: runObserver.checked,
11 | }, function() {
12 | // Update status to let user know options were saved.
13 | save.innerText = 'Saved!'
14 | setTimeout(function() {
15 | save.innerText = 'Save';
16 | }, 450);
17 | });
18 | })
19 |
20 |
21 | document.addEventListener('DOMContentLoaded', (e) => {
22 | // Use default value color = 'red' and likesColor = true.
23 | chrome.storage.sync.get({
24 | autoapply: false,
25 | runObserver: false,
26 | }, function(options) {
27 | if (options.autoapply) {
28 | autoapply.checked = true
29 | }
30 |
31 | if (options.runObserver) {
32 | runObserver.checked = true
33 | }
34 | });
35 | });
36 |
37 |
38 |
39 |
--------------------------------------------------------------------------------
/Firefox/options.js:
--------------------------------------------------------------------------------
1 | const save = document.getElementById("save")
2 | const autoapply = document.getElementById("autoapply")
3 | const runObserver = document.getElementById("runObserver")
4 |
5 |
6 | save.addEventListener("click", (e) => {
7 | // Save to chrome storage:
8 | chrome.storage.sync.set({
9 | autoapply: autoapply.checked,
10 | runObserver: runObserver.checked,
11 | }, function() {
12 | // Update status to let user know options were saved.
13 | save.innerText = 'Saved!'
14 | setTimeout(function() {
15 | save.innerText = 'Save';
16 | }, 450);
17 | });
18 | })
19 |
20 |
21 | document.addEventListener('DOMContentLoaded', (e) => {
22 | // Use default value color = 'red' and likesColor = true.
23 | chrome.storage.sync.get({
24 | autoapply: false,
25 | runObserver: false,
26 | }, function(options) {
27 | if (options.autoapply) {
28 | autoapply.checked = true
29 | }
30 |
31 | if (options.runObserver) {
32 | runObserver.checked = true
33 | }
34 | });
35 | });
36 |
37 |
38 |
39 |
--------------------------------------------------------------------------------
/Chrome/background.js:
--------------------------------------------------------------------------------
1 | let injected = false
2 |
3 | chrome.commands.onCommand.addListener(async (command) => {
4 | if (!injected) {
5 | injected = true
6 | const tab = await chrome.tabs.query({ active: true, lastFocusedWindow: true });
7 | const options = await chrome.storage.sync.get({
8 | runObserver: false,
9 | visuals: false,
10 | })
11 |
12 | console.log(`[TorpedoRead] Sending script message due to user input.`)
13 | chrome.tabs.sendMessage(tab[0].id, {
14 | ...options,
15 | startCommand: true,
16 | });
17 | }
18 | });
19 |
20 | chrome.tabs.onUpdated.addListener(async function (id, change, tab) {
21 | if (change.status === "complete") {
22 | injected = false
23 | try {
24 | const url = new URL(tab.url);
25 | if (["file:", "http:", "https:"].includes(url.protocol)) {
26 | // Get options
27 | const options = await chrome.storage.sync.get({
28 | runObserver: false,
29 | autoapply: false,
30 | visuals: false
31 | })
32 |
33 | if (options.autoapply) {
34 | injected = true
35 | const tab = await chrome.tabs.query({ active: true, lastFocusedWindow: true });
36 |
37 | console.log(`[TorpedoRead] Sending script message due to AutoApply`)
38 | chrome.tabs.sendMessage(tab[0].id, options)
39 | }
40 | }
41 | } catch (err) {
42 | console.log(`err: ${err.message}`);
43 | }
44 | }
45 | });
46 |
--------------------------------------------------------------------------------
/Firefox/background.js:
--------------------------------------------------------------------------------
1 | let injected = false
2 |
3 | chrome.commands.onCommand.addListener(async (command) => {
4 | console.log("COMMAND!")
5 | if (!injected) {
6 | injected = true
7 | const tab = await browser.tabs.query({ active: true, lastFocusedWindow: true });
8 | const options = await browser.storage.sync.get({
9 | runObserver: false,
10 | visuals: false,
11 | })
12 |
13 | console.log(`[TorpedoRead] Sending script message due to user input.`)
14 | chrome.tabs.sendMessage(tab[0].id, {
15 | ...options,
16 | startCommand: true,
17 | });
18 | }
19 | });
20 |
21 | chrome.tabs.onUpdated.addListener(async function (id, change, tab) {
22 | if (change.status === "complete") {
23 | injected = false
24 | try {
25 | const url = new URL(tab.url);
26 | if (["file:", "http:", "https:"].includes(url.protocol)) {
27 | // Get options
28 | const options = await browser.storage.sync.get({
29 | runObserver: false,
30 | autoapply: false,
31 | visuals: false
32 | })
33 |
34 | if (options.autoapply) {
35 | injected = true
36 | const tab = await browser.tabs.query({ active: true, lastFocusedWindow: true });
37 |
38 | console.log(`[TorpedoRead] Sending script message due to AutoApply`)
39 | chrome.tabs.sendMessage(tab[0].id, options)
40 | }
41 | }
42 | } catch (err) {
43 | console.log(`err: ${err.message}`);
44 | }
45 | }
46 | });
47 |
--------------------------------------------------------------------------------
/Firefox/popup.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
24 |
TorpedoRead
25 |
BETA
26 |
Apply accelerated reading to any website at the click of a button! You can customize how you read by using our visualizer
27 |
Torpedify
28 |
Or Ctrl + Shift + 0
31 |
32 |
33 |
39 |
40 |
41 |
42 |
43 |
44 |
--------------------------------------------------------------------------------
/Chrome/popup.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
24 |
TorpedoRead
25 |
BETA
26 |
Apply accelerated reading to any website at the click of a button! You can customize how you read by using our visualizer
27 |
Torpedify
28 |
Or Ctrl + Shift + 0
31 |
32 |
35 |
36 |
42 |
43 |
44 |
45 |
46 |
47 |
48 |
49 |
--------------------------------------------------------------------------------
/Firefox/popup.css:
--------------------------------------------------------------------------------
1 | body {
2 | width: 300px;
3 | padding: 20px;
4 | background-color: #ff5722;
5 | overflow: hidden;
6 | }
7 |
8 | * {
9 | font-family: "Open Sans";
10 | font-weight: 300;
11 | }
12 |
13 | h3 {
14 | font-size: 22px;
15 | margin-bottom: 0;
16 | }
17 |
18 | .beta {
19 | font-weight: 700;
20 | color: #ff5722;
21 | }
22 |
23 | h3 span {
24 | font-weight: 500;
25 | }
26 |
27 | p,
28 | a {
29 | font-size: 15px;
30 | font-weight: 400;
31 | line-height: 1.5;
32 | }
33 |
34 | a {
35 | display: inline;
36 | text-decoration: none;
37 | font-size: 14px;
38 | color: deepskyblue;
39 | padding-bottom: 2px;
40 | }
41 |
42 | a:hover {
43 | border-bottom: 1px solid deepskyblue;
44 | }
45 |
46 | .contact {
47 | margin-top: 20px;
48 | }
49 |
50 | .shortcut {
51 | margin-left: 5px;
52 | }
53 |
54 | .shortcut > span {
55 | font-weight: 500;
56 | }
57 |
58 | #inject {
59 | color: white;
60 | background-color: #ff5722;
61 | font-weight: 600;
62 | padding: 5px 10px;
63 | border-radius: 3px;
64 | opacity: 0.85;
65 | transition: all 0.3s;
66 | border: 0;
67 | }
68 |
69 | #inject:hover {
70 | cursor: pointer;
71 | opacity: 1;
72 | }
73 |
74 | .circle {
75 | width: 530px;
76 | height: 600px;
77 | position: absolute;
78 | z-index: 1;
79 | background-color: white;
80 | border-radius: 50%;
81 | top: 10px;
82 | left: -130px;
83 | box-shadow: inset 0px -2px 9px 6px #3f16084a;
84 | }
85 |
86 | .wrapper {
87 | position: relative;
88 | z-index: 2;
89 | }
90 |
91 | .contact {
92 | display: flex;
93 | align-items: center;
94 | justify-content: space-between;
95 | width: 100%;
96 | }
97 |
98 | .contact .settings {
99 | color: #ff5722;
100 | font-weight: 600;
101 | }
102 |
103 | .contact .settings:hover {
104 | border-bottom: 1px solid #ff5722;
105 | }
--------------------------------------------------------------------------------
/Chrome/options.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | TorpedoRead | Options
5 |
6 |
7 |
8 |
9 |
41 |
42 |
43 |
--------------------------------------------------------------------------------
/Firefox/options.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | TorpedoRead | Options
5 |
6 |
7 |
8 |
9 |
41 |
42 |
43 |
--------------------------------------------------------------------------------
/Chrome/popup.css:
--------------------------------------------------------------------------------
1 | body {
2 | width: 300px;
3 | padding: 20px;
4 | background-color: #ff5722;
5 | overflow: hidden;
6 | }
7 |
8 | * {
9 | font-family: "Open Sans";
10 | font-weight: 300;
11 | }
12 |
13 | h3 {
14 | font-size: 22px;
15 | margin-bottom: 0;
16 | }
17 |
18 | .beta {
19 | font-weight: 700;
20 | color: #ff5722;
21 | }
22 |
23 | h3 span {
24 | font-weight: 500;
25 | }
26 |
27 | p,
28 | a {
29 | font-size: 15px;
30 | font-weight: 400;
31 | line-height: 1.5;
32 | }
33 |
34 | a {
35 | display: inline;
36 | text-decoration: none;
37 | font-size: 14px;
38 | color: deepskyblue;
39 | padding-bottom: 2px;
40 | border-bottom: 1px solid transparent;
41 | }
42 |
43 | a:hover {
44 | border-bottom: 1px solid deepskyblue;
45 | }
46 |
47 | .contact {
48 | margin-top: 20px;
49 | }
50 |
51 | .shortcut {
52 | margin-left: 5px;
53 | }
54 |
55 | .shortcut > span {
56 | font-weight: 500;
57 | }
58 |
59 | #inject {
60 | color: white;
61 | background-color: #ff5722;
62 | font-weight: 600;
63 | padding: 5px 10px;
64 | border-radius: 3px;
65 | opacity: 0.85;
66 | transition: all 0.3s;
67 | border: 0;
68 | }
69 |
70 | #inject:hover {
71 | cursor: pointer;
72 | opacity: 1;
73 | }
74 |
75 | .circle {
76 | width: 530px;
77 | height: 600px;
78 | position: absolute;
79 | z-index: 1;
80 | background-color: white;
81 | border-radius: 50%;
82 | top: 10px;
83 | left: -130px;
84 | box-shadow: inset 0px -2px 9px 6px #3f16084a;
85 | }
86 |
87 | .wrapper {
88 | position: relative;
89 | z-index: 2;
90 | }
91 |
92 | .contact {
93 | display: flex;
94 | align-items: center;
95 | justify-content: space-between;
96 | width: 100%;
97 | }
98 |
99 | .contact .settings {
100 | color: #ff5722;
101 | font-weight: 600;
102 | border-bottom: 1px solid transparent;
103 | }
104 |
105 | .contact .settings:hover {
106 | border-bottom: 1px solid #ff5722;
107 | }
108 |
109 | .blacklist-wrapper {
110 | margin-top: 12px;
111 | border-bottom: 1px solid transparent;
112 | padding-bottom: 3px;
113 | }
114 |
115 | .blacklist-wrapper:hover {
116 | border-bottom: 1px solid #ff5722;
117 | }
118 |
119 |
120 | .blacklist-wrapper #blacklist {
121 | font-weight: 500;
122 | color: #ff5722;
123 | }
124 |
125 | .blacklist-wrapper span {
126 | font-size: 14px;
127 | }
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # Welcome to TorpedoRead 👋
2 | TorpedoRead is an innovative browser extension that biohacks your brain into reading quicker with a deeper focus.
3 |
4 | 
5 |
6 |
7 | ## Open Source Initiative.
8 |
9 | As of the 12th September 2022, TorpedoRead has been made open source 🎉. We will slowly start open-sourcing all our features including our PDF and Ebook converter. We welcome anyone who wishes to contribute to the improvement of TorpedoRead by creating bug reports, and/or contributing their own code.
10 |
11 |
12 | #### Personal Note:
13 |
14 | Although I have openly shared the code for my projects in the past, I've never actively maintained and updated open-source software, I've only ever worked in small teams of developers under constant communication. Along with this, I also work on other software full-time and don't have an unlimited amount of spare time. I will be improving the documentation and contributing guidelines as I learn more. You can also contact me on Twitter [@unreleased](https://twitter.com/unreleased) or on Discord unreleased#0001 if you feel this is a project that you are able to better lead than I.
15 |
16 |
17 | ## Contributing
18 |
19 | You can contribute by opening an issue or pull request. I will review & test the code and push a new version of the extension to Chrome and Firefox for each iteration.
20 |
21 |
22 | #### What features can I work on?
23 |
24 | You can work on anything you think would be a useful feature, currently some of the key features we're missing are:
25 |
26 | - Adding the option to disable TorpedoRead on certain websites along with a page to view those/modify those options.
27 | - Improve the overall design of the extension pages
28 | - Add additional fonts you feel would be beneficial (Such as open dyslexia)
29 |
30 |
31 | ## Firefox & Chrome
32 |
33 | When I created TorpedoRead, Firefox did not support Manifest V3, I had to change the code around to work for Manifest V2. Once Firefox starts supporting Manifest V3 we should be able to use a single directory for the whole extension.
34 |
35 |
36 | ## Downloading and Installing TorpedoRead locally.
37 |
38 | TODO. Currently, you can use these guides
39 |
40 | - [Chrome](https://dev.to/ben/how-to-install-chrome-extensions-manually-from-github-1612)
41 | - [Firefox](https://www.makeuseof.com/tag/how-to-install-firefox-add-ons-manually-even-from-github/)
--------------------------------------------------------------------------------
/Chrome/manifest.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "TorpedoRead",
3 | "description": "Enhanced text readability through font-weights and brightness adjustment.",
4 | "version": "0.0.2",
5 | "manifest_version": 3,
6 | "permissions": ["storage", "tabs"],
7 | "background": {
8 | "service_worker": "background.js"
9 | },
10 | "options_page": "options.html",
11 | "host_permissions": ["http://*/", "https://*/", "file://*"],
12 | "action": {
13 | "default_title": "Ok",
14 | "default_popup": "./popup.html"
15 | },
16 | "icons": {
17 | "16": "logo.png",
18 | "32": "logo.png",
19 | "48": "logo.png",
20 | "128": "logo.png"
21 | },
22 | "web_accessible_resources": [{
23 | "resources": [
24 | "/fonts/Open_Sans/OpenSans-Light.ttf",
25 | "/fonts/Open_Sans/OpenSans-Regular.ttf",
26 | "/fonts/Open_Sans/OpenSans-Medium.ttf",
27 | "/fonts/Open_Sans/OpenSans-SemiBold.ttf",
28 | "/fonts/Open_Sans/OpenSans-Bold.ttf",
29 | "/fonts/Open_Sans/OpenSans-ExtraBold.ttf",
30 | "/fonts/Open_Sans/OpenSans-LightItalic.ttf",
31 | "/fonts/Open_Sans/OpenSans-Italic.ttf",
32 | "/fonts/Open_Sans/OpenSans-MediumItalic.ttf",
33 | "/fonts/Open_Sans/OpenSans-SemiBoldItalic.ttf",
34 | "/fonts/Open_Sans/OpenSans-BoldItalic.ttf",
35 | "/fonts/Open_Sans/OpenSans-ExtraBoldItalic.ttf",
36 | "/fonts/Plus_Jakarta_Sans/PlusJakartaSans-Light.ttf",
37 | "/fonts/Plus_Jakarta_Sans/PlusJakartaSans-Regular.ttf",
38 | "/fonts/Plus_Jakarta_Sans/PlusJakartaSans-Medium.ttf",
39 | "/fonts/Plus_Jakarta_Sans/PlusJakartaSans-SemiBold.ttf",
40 | "/fonts/Plus_Jakarta_Sans/PlusJakartaSans-Bold.ttf",
41 | "/fonts/Plus_Jakarta_Sans/PlusJakartaSans-ExtraBold.ttf",
42 | "/fonts/Plus_Jakarta_Sans/PlusJakartaSans-LightItalic.ttf",
43 | "/fonts/Plus_Jakarta_Sans/PlusJakartaSans-Italic.ttf",
44 | "/fonts/Plus_Jakarta_Sans/PlusJakartaSans-MediumItalic.ttf",
45 | "/fonts/Plus_Jakarta_Sans/PlusJakartaSans-SemiBoldItalic.ttf",
46 | "/fonts/Plus_Jakarta_Sans/PlusJakartaSans-BoldItalic.ttf",
47 | "/fonts/Plus_Jakarta_Sans/PlusJakartaSans-ExtraBoldItalic.ttf",
48 | "/fonts/Poppins/Poppins-Light.ttf",
49 | "/fonts/Poppins/Poppins-Regular.ttf",
50 | "/fonts/Poppins/Poppins-Medium.ttf",
51 | "/fonts/Poppins/Poppins-SemiBold.ttf",
52 | "/fonts/Poppins/Poppins-Bold.ttf",
53 | "/fonts/Poppins/Poppins-ExtraBold.ttf",
54 | "/fonts/Poppins/Poppins-LightItalic.ttf",
55 | "/fonts/Poppins/Poppins-Italic.ttf",
56 | "/fonts/Poppins/Poppins-MediumItalic.ttf",
57 | "/fonts/Poppins/Poppins-SemiBoldItalic.ttf",
58 | "/fonts/Poppins/Poppins-BoldItalic.ttf",
59 | "/fonts/Poppins/Poppins-ExtraBoldItalic.ttf",
60 | "/fonts.css"
61 | ],
62 | "matches": [""]
63 | }],
64 | "content_scripts": [
65 | {
66 | "matches": [""],
67 | "js": ["content-inject.js"]
68 | }
69 | ],
70 | "commands": {
71 | "torpedify": {
72 | "suggested_key": {
73 | "default": "Ctrl+Shift+0",
74 | "mac": "Command+Shift+0"
75 | },
76 | "description": "Torpedify the page"
77 | }
78 | }
79 | }
--------------------------------------------------------------------------------
/Firefox/manifest.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "TorpedoRead",
3 | "description": "Enhanced text readability for neurodivergents using font-weight and brightness adjustment.",
4 | "version": "0.0.3",
5 | "manifest_version": 2,
6 | "permissions": ["unlimitedStorage", "storage", "tabs", "*://*/*"],
7 | "background": {
8 | "scripts": ["background.js"]
9 | },
10 | "browser_specific_settings": {
11 | "gecko": {
12 | "id": "hello@conorre.id"
13 | }
14 | },
15 | "options_ui": {
16 | "page": "options.html",
17 | "open_in_tab": true
18 | },
19 | "browser_action": {
20 | "default_icon": "./logo.png",
21 | "default_title": "TorpedoRead",
22 | "default_popup": "./popup.html"
23 | },
24 | "icons": {
25 | "16": "logo.png",
26 | "32": "logo.png",
27 | "48": "logo.png",
28 | "128": "logo.png"
29 | },
30 | "web_accessible_resources": [
31 | "/fonts/Open_Sans/OpenSans-Light.ttf",
32 | "/fonts/Open_Sans/OpenSans-Regular.ttf",
33 | "/fonts/Open_Sans/OpenSans-Medium.ttf",
34 | "/fonts/Open_Sans/OpenSans-SemiBold.ttf",
35 | "/fonts/Open_Sans/OpenSans-Bold.ttf",
36 | "/fonts/Open_Sans/OpenSans-ExtraBold.ttf",
37 | "/fonts/Open_Sans/OpenSans-LightItalic.ttf",
38 | "/fonts/Open_Sans/OpenSans-Italic.ttf",
39 | "/fonts/Open_Sans/OpenSans-MediumItalic.ttf",
40 | "/fonts/Open_Sans/OpenSans-SemiBoldItalic.ttf",
41 | "/fonts/Open_Sans/OpenSans-BoldItalic.ttf",
42 | "/fonts/Open_Sans/OpenSans-ExtraBoldItalic.ttf",
43 | "/fonts/Plus_Jakarta_Sans/PlusJakartaSans-Light.ttf",
44 | "/fonts/Plus_Jakarta_Sans/PlusJakartaSans-Regular.ttf",
45 | "/fonts/Plus_Jakarta_Sans/PlusJakartaSans-Medium.ttf",
46 | "/fonts/Plus_Jakarta_Sans/PlusJakartaSans-SemiBold.ttf",
47 | "/fonts/Plus_Jakarta_Sans/PlusJakartaSans-Bold.ttf",
48 | "/fonts/Plus_Jakarta_Sans/PlusJakartaSans-ExtraBold.ttf",
49 | "/fonts/Plus_Jakarta_Sans/PlusJakartaSans-LightItalic.ttf",
50 | "/fonts/Plus_Jakarta_Sans/PlusJakartaSans-Italic.ttf",
51 | "/fonts/Plus_Jakarta_Sans/PlusJakartaSans-MediumItalic.ttf",
52 | "/fonts/Plus_Jakarta_Sans/PlusJakartaSans-SemiBoldItalic.ttf",
53 | "/fonts/Plus_Jakarta_Sans/PlusJakartaSans-BoldItalic.ttf",
54 | "/fonts/Plus_Jakarta_Sans/PlusJakartaSans-ExtraBoldItalic.ttf",
55 | "/fonts/Poppins/Poppins-Light.ttf",
56 | "/fonts/Poppins/Poppins-Regular.ttf",
57 | "/fonts/Poppins/Poppins-Medium.ttf",
58 | "/fonts/Poppins/Poppins-SemiBold.ttf",
59 | "/fonts/Poppins/Poppins-Bold.ttf",
60 | "/fonts/Poppins/Poppins-ExtraBold.ttf",
61 | "/fonts/Poppins/Poppins-LightItalic.ttf",
62 | "/fonts/Poppins/Poppins-Italic.ttf",
63 | "/fonts/Poppins/Poppins-MediumItalic.ttf",
64 | "/fonts/Poppins/Poppins-SemiBoldItalic.ttf",
65 | "/fonts/Poppins/Poppins-BoldItalic.ttf",
66 | "/fonts/Poppins/Poppins-ExtraBoldItalic.ttf",
67 | "/fonts.css"
68 | ],
69 | "content_scripts": [
70 | {
71 | "matches": [""],
72 | "js": ["content-inject.js"]
73 | }
74 | ],
75 | "commands": {
76 | "torpedify": {
77 | "suggested_key": {
78 | "default": "Ctrl+Shift+0",
79 | "mac": "Command+Shift+0"
80 | },
81 | "description": "Torpedify the page"
82 | }
83 | }
84 | }
85 |
--------------------------------------------------------------------------------
/Chrome/visualizer.js:
--------------------------------------------------------------------------------
1 | /**
2 | * Visualizer codebase
3 | */
4 |
5 | const visualizer = {
6 | contrast: {
7 | bold: document.getElementById("boldContrast"),
8 | regular: document.getElementById("regularContrast"),
9 | thickness: document.getElementById("thickness")
10 | },
11 | baseThickness: document.getElementById("baseThickness"),
12 | spacing: document.getElementById("spacing"),
13 | font: document.getElementById("font"),
14 | }
15 |
16 | const saveContrastBold = () => {
17 | const brightness = parseFloat(visualizer.contrast.bold.value)
18 | const elements = document.getElementsByClassName("torpedo__bold")
19 | for (const el of elements) {
20 | el.style['filter'] = `brightness(${brightness})`
21 | }
22 | }
23 |
24 | const saveContrastRegular = () => {
25 | const brightness = parseFloat(visualizer.contrast.regular.value)
26 | const elements = document.getElementsByClassName("torpedo__regular")
27 | for (const el of elements) {
28 | el.style['filter'] = `brightness(${brightness})`
29 | }
30 | }
31 |
32 | const saveSpacing = () => {
33 | const spacing = parseFloat(visualizer.spacing.value)
34 | const elements = document.querySelectorAll("[class^=torpedo__]")
35 | for (const el of elements) {
36 | el.style['letter-spacing'] = `${spacing}px`
37 | }
38 | }
39 |
40 | const saveFont = () => {
41 | const font = visualizer.font.value
42 | const elements = document.querySelectorAll("[class^=torpedo__]")
43 | for (const el of elements) {
44 | el.style['font-family'] = font
45 | }
46 | }
47 |
48 | const saveThickness = () => {
49 | const baseThickness = parseInt(visualizer.baseThickness.value)
50 | const contrastThickness = parseInt(visualizer.contrast.thickness.value)
51 |
52 | for (const el of document.getElementsByClassName("torpedo__bold")) {
53 | el.style['font-weight'] = baseThickness + contrastThickness
54 | }
55 |
56 | for (const el of document.getElementsByClassName("torpedo__regular")) {
57 | el.style['font-weight'] = baseThickness
58 | }
59 | }
60 |
61 | visualizer.baseThickness.addEventListener("change", saveThickness);
62 | visualizer.contrast.thickness.addEventListener("change", saveThickness);
63 | visualizer.font.addEventListener("change", saveFont);
64 | visualizer.spacing.addEventListener("input", saveSpacing);
65 | visualizer.contrast.regular.addEventListener("input", saveContrastRegular);
66 | visualizer.contrast.bold.addEventListener("input", saveContrastBold);
67 |
68 | /**
69 | * Code for saving to Chrome Storage
70 | */
71 |
72 | const save = document.getElementById("saveVisuals")
73 |
74 | save.addEventListener("click", () => {
75 | const visuals = {
76 | contrast: {
77 | bold: visualizer.contrast.bold.value,
78 | regular: visualizer.contrast.regular.value,
79 | thickness: parseInt(visualizer.contrast.thickness.value),
80 | },
81 | baseThickness: parseInt(visualizer.baseThickness.value),
82 | spacing: visualizer.spacing.value,
83 | font: visualizer.font.value,
84 | }
85 |
86 | chrome.storage.sync.set({
87 | visuals: visuals
88 | }, function() {
89 | // Update status to let user know options were saved.
90 | save.innerText = 'Saved!'
91 | setTimeout(function() {
92 | save.innerText = 'Save';
93 | }, 450);
94 | });
95 | })
96 |
97 |
98 | document.addEventListener('DOMContentLoaded', (e) => {
99 | // Use default value color = 'red' and likesColor = true.
100 | chrome.storage.sync.get({
101 | visuals: false
102 | }, function(options) {
103 | if (options.visuals) {
104 | visualizer.contrast.bold.value = options.visuals.contrast.bold
105 | visualizer.contrast.regular.value = options.visuals.contrast.regular
106 | visualizer.contrast.thickness.value = options.visuals.contrast.thickness
107 | visualizer.baseThickness.value = options.visuals.baseThickness
108 | visualizer.spacing.value = options.visuals.spacing
109 | visualizer.font.value = options.visuals.font
110 | }
111 |
112 | saveThickness();
113 | saveFont();
114 | saveSpacing();
115 | saveContrastRegular();
116 | saveContrastBold();
117 | });
118 | });
119 |
120 |
--------------------------------------------------------------------------------
/Firefox/visualizer.js:
--------------------------------------------------------------------------------
1 | /**
2 | * Visualizer codebase
3 | */
4 |
5 | const visualizer = {
6 | contrast: {
7 | bold: document.getElementById("boldContrast"),
8 | regular: document.getElementById("regularContrast"),
9 | thickness: document.getElementById("thickness")
10 | },
11 | baseThickness: document.getElementById("baseThickness"),
12 | spacing: document.getElementById("spacing"),
13 | font: document.getElementById("font"),
14 | }
15 |
16 | const saveContrastBold = () => {
17 | const brightness = parseFloat(visualizer.contrast.bold.value)
18 | const elements = document.getElementsByClassName("torpedo__bold")
19 | for (const el of elements) {
20 | el.style['filter'] = `brightness(${brightness})`
21 | }
22 | }
23 |
24 | const saveContrastRegular = () => {
25 | const brightness = parseFloat(visualizer.contrast.regular.value)
26 | const elements = document.getElementsByClassName("torpedo__regular")
27 | for (const el of elements) {
28 | el.style['filter'] = `brightness(${brightness})`
29 | }
30 | }
31 |
32 | const saveSpacing = () => {
33 | const spacing = parseFloat(visualizer.spacing.value)
34 | const elements = document.querySelectorAll("[class^=torpedo__]")
35 | for (const el of elements) {
36 | el.style['letter-spacing'] = `${spacing}px`
37 | }
38 | }
39 |
40 | const saveFont = () => {
41 | const font = visualizer.font.value
42 | const elements = document.querySelectorAll("[class^=torpedo__]")
43 | for (const el of elements) {
44 | el.style['font-family'] = font
45 | }
46 | }
47 |
48 | const saveThickness = () => {
49 | const baseThickness = parseInt(visualizer.baseThickness.value)
50 | const contrastThickness = parseInt(visualizer.contrast.thickness.value)
51 |
52 | for (const el of document.getElementsByClassName("torpedo__bold")) {
53 | el.style['font-weight'] = baseThickness + contrastThickness
54 | }
55 |
56 | for (const el of document.getElementsByClassName("torpedo__regular")) {
57 | el.style['font-weight'] = baseThickness
58 | }
59 | }
60 |
61 | visualizer.baseThickness.addEventListener("change", saveThickness);
62 | visualizer.contrast.thickness.addEventListener("change", saveThickness);
63 | visualizer.font.addEventListener("change", saveFont);
64 | visualizer.spacing.addEventListener("input", saveSpacing);
65 | visualizer.contrast.regular.addEventListener("input", saveContrastRegular);
66 | visualizer.contrast.bold.addEventListener("input", saveContrastBold);
67 |
68 | /**
69 | * Code for saving to Chrome Storage
70 | */
71 |
72 | const save = document.getElementById("saveVisuals")
73 |
74 | save.addEventListener("click", () => {
75 | const visuals = {
76 | contrast: {
77 | bold: visualizer.contrast.bold.value,
78 | regular: visualizer.contrast.regular.value,
79 | thickness: parseInt(visualizer.contrast.thickness.value),
80 | },
81 | baseThickness: parseInt(visualizer.baseThickness.value),
82 | spacing: visualizer.spacing.value,
83 | font: visualizer.font.value,
84 | }
85 |
86 | chrome.storage.sync.set({
87 | visuals: visuals
88 | }, function() {
89 | // Update status to let user know options were saved.
90 | save.innerText = 'Saved!'
91 | setTimeout(function() {
92 | save.innerText = 'Save';
93 | }, 450);
94 | });
95 | })
96 |
97 |
98 | document.addEventListener('DOMContentLoaded', (e) => {
99 | // Use default value color = 'red' and likesColor = true.
100 | chrome.storage.sync.get({
101 | visuals: false
102 | }, function(options) {
103 | if (options.visuals) {
104 | visualizer.contrast.bold.value = options.visuals.contrast.bold
105 | visualizer.contrast.regular.value = options.visuals.contrast.regular
106 | visualizer.contrast.thickness.value = options.visuals.contrast.thickness
107 | visualizer.baseThickness.value = options.visuals.baseThickness
108 | visualizer.spacing.value = options.visuals.spacing
109 | visualizer.font.value = options.visuals.font
110 | }
111 |
112 | saveThickness();
113 | saveFont();
114 | saveSpacing();
115 | saveContrastRegular();
116 | saveContrastBold();
117 | });
118 | });
119 |
120 |
--------------------------------------------------------------------------------
/Chrome/options.css:
--------------------------------------------------------------------------------
1 | body {
2 | margin: 0;
3 | padding: 0 0 50px 0;
4 | font-family: "Open Sans", "Arial";
5 | }
6 |
7 | header {
8 | background-color: #ff5722;
9 | padding: 10px 20px;
10 | box-sizing: border-box;
11 | display: flex;
12 | align-items: center;
13 | justify-content: space-between;
14 | box-shadow: 0px -15px 19px 20px #0000004a;
15 | }
16 |
17 | .brand {
18 | display: flex;
19 | text-decoration: none;
20 | color: inherit;
21 | }
22 |
23 | .brand img {
24 | width: 34px;
25 | height: 34px;
26 | }
27 |
28 | .brand h1 {
29 | margin: 0 0 0 10px;
30 | font-size: 20px;
31 | font-weight: 500;
32 | color: white;
33 | line-height: 1.5;
34 | }
35 |
36 | .brand h1 span {
37 | font-weight: 300;
38 | }
39 |
40 | header .button {
41 | background-color: white;
42 | box-shadow: 0px 0px 20px 4px #6a240e40;
43 | color: #ff5722;
44 | border: 0;
45 | font-weight: 500;
46 | padding: 8px 25px;
47 | font-size: 14px;
48 | border-radius: 6px;
49 | cursor: pointer;
50 | display: block;
51 | transition: opacity .3s;
52 | text-decoration: none;
53 | }
54 |
55 | button#save, button#saveVisuals {
56 | border: 0;
57 | background-color: #ff5722;
58 | box-shadow: 0px 0px 20px 4px #6a240e40;
59 | color: white;
60 | font-weight: 600;
61 | padding: 8px 25px;
62 | font-size: 14px;
63 | border-radius: 6px;
64 | cursor: pointer;
65 | display: block;
66 | transition: opacity .3s;
67 | text-decoration: none;
68 | }
69 |
70 | button {
71 | margin-top: 30px;
72 | }
73 |
74 | button:hover {
75 | opacity: 0.85;
76 | }
77 |
78 |
79 | main {
80 | margin: 30px;
81 | }
82 |
83 | main h2 {
84 | font-size: 30px;
85 | font-weight: 400;
86 | color: #343434;
87 | margin-bottom: 0;
88 | }
89 |
90 | main p {
91 | font-size: 14px;
92 | color: #505050;
93 | }
94 |
95 | main h3 {
96 | font-size: 22px;
97 | color: #343434;
98 | margin: 0 0 15px 0;
99 | }
100 |
101 |
102 | main > div {
103 | margin-top: 35px;
104 | }
105 |
106 | #options label {
107 | margin-top: 5px;
108 | display: flex;
109 | align-items: center;
110 | padding: 10px 5px 5px 5px;
111 | }
112 |
113 | #options label > span {
114 | font-size: 14px;
115 | margin-left: 10px;
116 | }
117 |
118 | #options label .experimental {
119 | color: #ff5722;
120 | font-weight: 600;
121 | }
122 |
123 | #options label input {
124 | accent-color: #ff5721;
125 | }
126 |
127 | #options label input:disabled {
128 | cursor: not-allowed;
129 | }
130 |
131 | #options label input:disabled ~ span {
132 | color: #bebebe;
133 | cursor: not-allowed;
134 | }
135 |
136 |
137 | .quote {
138 | max-width: 600px;
139 | width: 100%;
140 | padding-left: 10px;
141 | margin-left: 10px;
142 | border-left: 4px solid #ff5722;
143 | }
144 |
145 | .quote p {
146 | line-height: 1.7;
147 | margin: 0;
148 | }
149 |
150 | .quote span {
151 | font-weight: bold;
152 | }
153 |
154 |
155 | .quote a {
156 | color: deepskyblue;
157 | text-decoration: none;
158 | border-bottom: 1px solid transparent;
159 | padding-bottom: 1px;
160 | }
161 |
162 | .quote a:hover {
163 | border-bottom: 1px solid deepskyblue;
164 | }
165 |
166 |
167 |
168 |
169 |
170 |
171 |
172 | /**
173 | * CSS for Visualizer
174 | */
175 |
176 | .visualizer .textVisualizer {
177 | padding: 25px;
178 | max-width: 400px;
179 | line-height: 1.7;
180 | margin: 0 0 20px 0;
181 | }
182 |
183 | .visualizer .v1 {
184 | background-color: #121212;
185 | border: 1px solid #121212;
186 | color: white;
187 | }
188 |
189 | .visualizer .v2 {
190 | border: 1px solid #121212;
191 | }
192 |
193 | .visualizer .v3 {
194 | background-color: #22303C;
195 | border: 1px solid #22303C;
196 | color: #8899A6;
197 | }
198 |
199 | .wrapper {
200 | max-width: 700px;
201 | display: flex;
202 | }
203 |
204 | .visualizer .settings {
205 | margin-left: 40px;
206 | }
207 |
208 | .visualizer input, .visualizer select {
209 | accent-color: #ff5721;
210 | display: block;
211 | }
212 |
213 | .visualizer label {
214 | display: block;
215 | }
216 |
217 | .visualizer label:not(:first-child) {
218 | margin-top: 20px;
219 | }
220 |
221 | .visualizer label input, .visualizer label select {
222 | padding: 6px 15px;
223 | width: 180px;
224 | box-sizing: border-box;
225 | }
226 |
227 | .visualizer label span {
228 | font-weight: 600;
229 | display: block;
230 | margin-bottom: 3px;
231 | }
232 |
233 |
234 | /**
235 | * Torpedo styles
236 | */
237 |
238 | .torpedo__regular {
239 | font-family: 'Open Sans';
240 | font-weight: inherit;
241 | font-style: normal;
242 | font-weight: 300;
243 | filter: brightness(0.9);
244 | }
245 |
246 | .torpedo__bold {
247 | font-family: 'Open Sans';
248 | font-weight: 500;
249 | font-style: normal;
250 | filter: brightness(1.1);
251 | }
--------------------------------------------------------------------------------
/Firefox/options.css:
--------------------------------------------------------------------------------
1 | body {
2 | margin: 0;
3 | padding: 0 0 50px 0;
4 | font-family: "Open Sans", "Arial";
5 | }
6 |
7 | header {
8 | background-color: #ff5722;
9 | padding: 10px 20px;
10 | box-sizing: border-box;
11 | display: flex;
12 | align-items: center;
13 | justify-content: space-between;
14 | box-shadow: 0px -15px 19px 20px #0000004a;
15 | }
16 |
17 | .brand {
18 | display: flex;
19 | text-decoration: none;
20 | color: inherit;
21 | }
22 |
23 | .brand img {
24 | width: 34px;
25 | height: 34px;
26 | }
27 |
28 | .brand h1 {
29 | margin: 0 0 0 10px;
30 | font-size: 20px;
31 | font-weight: 500;
32 | color: white;
33 | line-height: 1.5;
34 | }
35 |
36 | .brand h1 span {
37 | font-weight: 300;
38 | }
39 |
40 | header .button {
41 | background-color: white;
42 | box-shadow: 0px 0px 20px 4px #6a240e40;
43 | color: #ff5722;
44 | border: 0;
45 | font-weight: 500;
46 | padding: 8px 25px;
47 | font-size: 14px;
48 | border-radius: 6px;
49 | cursor: pointer;
50 | display: block;
51 | transition: opacity .3s;
52 | text-decoration: none;
53 | }
54 |
55 | button#save, button#saveVisuals {
56 | border: 0;
57 | background-color: #ff5722;
58 | box-shadow: 0px 0px 20px 4px #6a240e40;
59 | color: white;
60 | font-weight: 600;
61 | padding: 8px 25px;
62 | font-size: 14px;
63 | border-radius: 6px;
64 | cursor: pointer;
65 | display: block;
66 | transition: opacity .3s;
67 | text-decoration: none;
68 | }
69 |
70 | button {
71 | margin-top: 30px;
72 | }
73 |
74 | button:hover {
75 | opacity: 0.85;
76 | }
77 |
78 |
79 | main {
80 | margin: 30px;
81 | }
82 |
83 | main h2 {
84 | font-size: 30px;
85 | font-weight: 400;
86 | color: #343434;
87 | margin-bottom: 0;
88 | }
89 |
90 | main p {
91 | font-size: 14px;
92 | color: #505050;
93 | }
94 |
95 | main h3 {
96 | font-size: 22px;
97 | color: #343434;
98 | margin: 0 0 15px 0;
99 | }
100 |
101 |
102 | main > div {
103 | margin-top: 35px;
104 | }
105 |
106 | #options label {
107 | margin-top: 5px;
108 | display: flex;
109 | align-items: center;
110 | padding: 10px 5px 5px 5px;
111 | }
112 |
113 | #options label > span {
114 | font-size: 14px;
115 | margin-left: 10px;
116 | }
117 |
118 | #options label .experimental {
119 | color: #ff5722;
120 | font-weight: 600;
121 | }
122 |
123 | #options label input {
124 | accent-color: #ff5721;
125 | }
126 |
127 | #options label input:disabled {
128 | cursor: not-allowed;
129 | }
130 |
131 | #options label input:disabled ~ span {
132 | color: #bebebe;
133 | cursor: not-allowed;
134 | }
135 |
136 |
137 | .quote {
138 | max-width: 600px;
139 | width: 100%;
140 | padding-left: 10px;
141 | margin-left: 10px;
142 | border-left: 4px solid #ff5722;
143 | }
144 |
145 | .quote p {
146 | line-height: 1.7;
147 | margin: 0;
148 | }
149 |
150 | .quote span {
151 | font-weight: bold;
152 | }
153 |
154 |
155 | .quote a {
156 | color: deepskyblue;
157 | text-decoration: none;
158 | border-bottom: 1px solid transparent;
159 | padding-bottom: 1px;
160 | }
161 |
162 | .quote a:hover {
163 | border-bottom: 1px solid deepskyblue;
164 | }
165 |
166 |
167 |
168 |
169 |
170 |
171 |
172 | /**
173 | * CSS for Visualizer
174 | */
175 |
176 | .visualizer .textVisualizer {
177 | padding: 25px;
178 | max-width: 400px;
179 | line-height: 1.7;
180 | margin: 0 0 20px 0;
181 | }
182 |
183 | .visualizer .v1 {
184 | background-color: #121212;
185 | border: 1px solid #121212;
186 | color: white;
187 | }
188 |
189 | .visualizer .v2 {
190 | border: 1px solid #121212;
191 | }
192 |
193 | .visualizer .v3 {
194 | background-color: #22303C;
195 | border: 1px solid #22303C;
196 | color: #8899A6;
197 | }
198 |
199 | .wrapper {
200 | max-width: 700px;
201 | display: flex;
202 | }
203 |
204 | .visualizer .settings {
205 | margin-left: 40px;
206 | }
207 |
208 | .visualizer input, .visualizer select {
209 | accent-color: #ff5721;
210 | display: block;
211 | }
212 |
213 | .visualizer label {
214 | display: block;
215 | }
216 |
217 | .visualizer label:not(:first-child) {
218 | margin-top: 20px;
219 | }
220 |
221 | .visualizer label input, .visualizer label select {
222 | padding: 6px 15px;
223 | width: 180px;
224 | box-sizing: border-box;
225 | }
226 |
227 | .visualizer label span {
228 | font-weight: 600;
229 | display: block;
230 | margin-bottom: 3px;
231 | }
232 |
233 |
234 | /**
235 | * Torpedo styles
236 | */
237 |
238 | .torpedo__regular {
239 | font-family: 'Open Sans';
240 | font-weight: inherit;
241 | font-style: normal;
242 | font-weight: 300;
243 | filter: brightness(0.9);
244 | }
245 |
246 | .torpedo__bold {
247 | font-family: 'Open Sans';
248 | font-weight: 500;
249 | font-style: normal;
250 | filter: brightness(1.1);
251 | }
--------------------------------------------------------------------------------
/Chrome/fonts.css:
--------------------------------------------------------------------------------
1 | /**
2 | * Open Sans (Regular)
3 | */
4 |
5 | @font-face {
6 | font-family: 'Open Sans';
7 | font-weight: 300;
8 | font-style: normal;
9 | src: url('/fonts/Open_Sans/OpenSans-Light.ttf');
10 | }
11 |
12 | @font-face {
13 | font-family: 'Open Sans';
14 | font-weight: 400;
15 | font-style: normal;
16 | src: url('/fonts/Open_Sans/OpenSans-Regular.ttf');
17 | }
18 |
19 | @font-face {
20 | font-family: 'Open Sans';
21 | font-weight: 500;
22 | font-style: normal;
23 | src: url('/fonts/Open_Sans/OpenSans-Medium.ttf');
24 | }
25 |
26 | @font-face {
27 | font-family: 'Open Sans';
28 | font-weight: 600;
29 | font-style: normal;
30 | src: url('/fonts/Open_Sans/OpenSans-SemiBold.ttf');
31 | }
32 |
33 | @font-face {
34 | font-family: 'Open Sans';
35 | font-weight: 700;
36 | font-style: normal;
37 | src: url('/fonts/Open_Sans/OpenSans-Bold.ttf');
38 | }
39 |
40 | @font-face {
41 | font-family: 'Open Sans';
42 | font-weight: 800;
43 | font-style: normal;
44 | src: url('/fonts/Open_Sans/OpenSans-ExtraBold.ttf');
45 | }
46 |
47 |
48 | /**
49 | * Open Sans (Italic)
50 | */
51 |
52 |
53 | @font-face {
54 | font-family: 'Open Sans';
55 | font-weight: 300;
56 | font-style: italic;
57 | src: url('/fonts/Open_Sans/OpenSans-LightItalic.ttf');
58 | }
59 |
60 | @font-face {
61 | font-family: 'Open Sans';
62 | font-weight: 400;
63 | font-style: italic;
64 | src: url('/fonts/Open_Sans/OpenSans-Italic.ttf');
65 | }
66 |
67 | @font-face {
68 | font-family: 'Open Sans';
69 | font-weight: 500;
70 | font-style: italic;
71 | src: url('/fonts/Open_Sans/OpenSans-MediumItalic.ttf');
72 | }
73 |
74 | @font-face {
75 | font-family: 'Open Sans';
76 | font-weight: 600;
77 | font-style: italic;
78 | src: url('/fonts/Open_Sans/OpenSans-SemiBoldItalic.ttf');
79 | }
80 |
81 | @font-face {
82 | font-family: 'Open Sans';
83 | font-weight: 700;
84 | font-style: italic;
85 | src: url('/fonts/Open_Sans/OpenSans-BoldItalic.ttf');
86 | }
87 |
88 | @font-face {
89 | font-family: 'Open Sans';
90 | font-weight: 800;
91 | font-style: italic;
92 | src: url('/fonts/Open_Sans/OpenSans-ExtraBoldItalic.ttf');
93 | }
94 |
95 |
96 | /**
97 | * Plus Jakarta Sans (Regular)
98 | */
99 |
100 | @font-face {
101 | font-family: 'Plus Jakarta Sans';
102 | font-weight: 300;
103 | font-style: normal;
104 | src: url('/fonts/Plus_Jakarta_Sans/PlusJakartaSans-Light.ttf');
105 | }
106 |
107 | @font-face {
108 | font-family: 'Plus Jakarta Sans';
109 | font-weight: 400;
110 | font-style: normal;
111 | src: url('/fonts/Plus_Jakarta_Sans/PlusJakartaSans-Regular.ttf');
112 | }
113 |
114 | @font-face {
115 | font-family: 'Plus Jakarta Sans';
116 | font-weight: 500;
117 | font-style: normal;
118 | src: url('/fonts/Plus_Jakarta_Sans/PlusJakartaSans-Medium.ttf');
119 | }
120 |
121 | @font-face {
122 | font-family: 'Plus Jakarta Sans';
123 | font-weight: 600;
124 | font-style: normal;
125 | src: url('/fonts/Plus_Jakarta_Sans/PlusJakartaSans-SemiBold.ttf');
126 | }
127 |
128 | @font-face {
129 | font-family: 'Plus Jakarta Sans';
130 | font-weight: 700;
131 | font-style: normal;
132 | src: url('/fonts/Plus_Jakarta_Sans/PlusJakartaSans-Bold.ttf');
133 | }
134 |
135 | @font-face {
136 | font-family: 'Plus Jakarta Sans';
137 | font-weight: 800;
138 | font-style: normal;
139 | src: url('/fonts/Plus_Jakarta_Sans/PlusJakartaSans-ExtraBold.ttf');
140 | }
141 |
142 | /**
143 | * Plus Jakarta Sans (Italic)
144 | */
145 |
146 | @font-face {
147 | font-family: 'Plus Jakarta Sans';
148 | font-weight: 300;
149 | font-style: italic;
150 | src: url('/fonts/Plus_Jakarta_Sans/PlusJakartaSans-LightItalic.ttf');
151 | }
152 |
153 | @font-face {
154 | font-family: 'Plus Jakarta Sans';
155 | font-weight: 400;
156 | font-style: italic;
157 | src: url('/fonts/Plus_Jakarta_Sans/PlusJakartaSans-Italic.ttf');
158 | }
159 |
160 | @font-face {
161 | font-family: 'Plus Jakarta Sans';
162 | font-weight: 500;
163 | font-style: italic;
164 | src: url('/fonts/Plus_Jakarta_Sans/PlusJakartaSans-MediumItalic.ttf');
165 | }
166 |
167 | @font-face {
168 | font-family: 'Plus Jakarta Sans';
169 | font-weight: 600;
170 | font-style: italic;
171 | src: url('/fonts/Plus_Jakarta_Sans/PlusJakartaSans-SemiBoldItalic.ttf');
172 | }
173 |
174 | @font-face {
175 | font-family: 'Plus Jakarta Sans';
176 | font-weight: 700;
177 | font-style: italic;
178 | src: url('/fonts/Plus_Jakarta_Sans/PlusJakartaSans-BoldItalic.ttf');
179 | }
180 |
181 | @font-face {
182 | font-family: 'Plus Jakarta Sans';
183 | font-weight: 800;
184 | font-style: italic;
185 | src: url('/fonts/Plus_Jakarta_Sans/PlusJakartaSans-ExtraBoldItalic.ttf');
186 | }
187 |
188 | /**
189 | * Poppins (Regular)
190 | */
191 |
192 | @font-face {
193 | font-family: 'Poppins';
194 | font-weight: 300;
195 | font-style: normal;
196 | src: url('/fonts/Poppins/Poppins-Light.ttf');
197 | }
198 |
199 | @font-face {
200 | font-family: 'Poppins';
201 | font-weight: 400;
202 | font-style: normal;
203 | src: url('/fonts/Poppins/Poppins-Regular.ttf');
204 | }
205 |
206 | @font-face {
207 | font-family: 'Poppins';
208 | font-weight: 500;
209 | font-style: normal;
210 | src: url('/fonts/Poppins/Poppins-Medium.ttf');
211 | }
212 |
213 | @font-face {
214 | font-family: 'Poppins';
215 | font-weight: 600;
216 | font-style: normal;
217 | src: url('/fonts/Poppins/Poppins-SemiBold.ttf');
218 | }
219 |
220 | @font-face {
221 | font-family: 'Poppins';
222 | font-weight: 700;
223 | font-style: normal;
224 | src: url('/fonts/Poppins/Poppins-Bold.ttf');
225 | }
226 |
227 | @font-face {
228 | font-family: 'Poppins';
229 | font-weight: 800;
230 | font-style: normal;
231 | src: url('/fonts/Poppins/Poppins-ExtraBold.ttf');
232 | }
233 |
234 |
235 | /**
236 | * Poppins (City)
237 | */
238 |
239 |
240 | @font-face {
241 | font-family: 'Poppins';
242 | font-weight: 300;
243 | font-style: italic;
244 | src: url('/fonts/Poppins/Poppins-LightItalic.ttf');
245 | }
246 |
247 | @font-face {
248 | font-family: 'Poppins';
249 | font-weight: 400;
250 | font-style: italic;
251 | src: url('/fonts/Poppins/Poppins-Italic.ttf');
252 | }
253 |
254 | @font-face {
255 | font-family: 'Poppins';
256 | font-weight: 500;
257 | font-style: italic;
258 | src: url('/fonts/Poppins/Poppins-MediumItalic.ttf');
259 | }
260 |
261 | @font-face {
262 | font-family: 'Poppins';
263 | font-weight: 600;
264 | font-style: italic;
265 | src: url('/fonts/Poppins/Poppins-SemiBoldItalic.ttf');
266 | }
267 |
268 | @font-face {
269 | font-family: 'Poppins';
270 | font-weight: 700;
271 | font-style: italic;
272 | src: url('/fonts/Poppins/Poppins-BoldItalic.ttf');
273 | }
274 |
275 | @font-face {
276 | font-family: 'Poppins';
277 | font-weight: 800;
278 | font-style: italic;
279 | src: url('/fonts/Poppins/Poppins-ExtraBoldItalic.ttf');
280 | }
--------------------------------------------------------------------------------
/Firefox/fonts.css:
--------------------------------------------------------------------------------
1 | /**
2 | * Open Sans (Regular)
3 | */
4 |
5 | @font-face {
6 | font-family: 'Open Sans';
7 | font-weight: 300;
8 | font-style: normal;
9 | src: url('/fonts/Open_Sans/OpenSans-Light.ttf');
10 | }
11 |
12 | @font-face {
13 | font-family: 'Open Sans';
14 | font-weight: 400;
15 | font-style: normal;
16 | src: url('/fonts/Open_Sans/OpenSans-Regular.ttf');
17 | }
18 |
19 | @font-face {
20 | font-family: 'Open Sans';
21 | font-weight: 500;
22 | font-style: normal;
23 | src: url('/fonts/Open_Sans/OpenSans-Medium.ttf');
24 | }
25 |
26 | @font-face {
27 | font-family: 'Open Sans';
28 | font-weight: 600;
29 | font-style: normal;
30 | src: url('/fonts/Open_Sans/OpenSans-SemiBold.ttf');
31 | }
32 |
33 | @font-face {
34 | font-family: 'Open Sans';
35 | font-weight: 700;
36 | font-style: normal;
37 | src: url('/fonts/Open_Sans/OpenSans-Bold.ttf');
38 | }
39 |
40 | @font-face {
41 | font-family: 'Open Sans';
42 | font-weight: 800;
43 | font-style: normal;
44 | src: url('/fonts/Open_Sans/OpenSans-ExtraBold.ttf');
45 | }
46 |
47 |
48 | /**
49 | * Open Sans (Italic)
50 | */
51 |
52 |
53 | @font-face {
54 | font-family: 'Open Sans';
55 | font-weight: 300;
56 | font-style: italic;
57 | src: url('/fonts/Open_Sans/OpenSans-LightItalic.ttf');
58 | }
59 |
60 | @font-face {
61 | font-family: 'Open Sans';
62 | font-weight: 400;
63 | font-style: italic;
64 | src: url('/fonts/Open_Sans/OpenSans-Italic.ttf');
65 | }
66 |
67 | @font-face {
68 | font-family: 'Open Sans';
69 | font-weight: 500;
70 | font-style: italic;
71 | src: url('/fonts/Open_Sans/OpenSans-MediumItalic.ttf');
72 | }
73 |
74 | @font-face {
75 | font-family: 'Open Sans';
76 | font-weight: 600;
77 | font-style: italic;
78 | src: url('/fonts/Open_Sans/OpenSans-SemiBoldItalic.ttf');
79 | }
80 |
81 | @font-face {
82 | font-family: 'Open Sans';
83 | font-weight: 700;
84 | font-style: italic;
85 | src: url('/fonts/Open_Sans/OpenSans-BoldItalic.ttf');
86 | }
87 |
88 | @font-face {
89 | font-family: 'Open Sans';
90 | font-weight: 800;
91 | font-style: italic;
92 | src: url('/fonts/Open_Sans/OpenSans-ExtraBoldItalic.ttf');
93 | }
94 |
95 |
96 | /**
97 | * Plus Jakarta Sans (Regular)
98 | */
99 |
100 | @font-face {
101 | font-family: 'Plus Jakarta Sans';
102 | font-weight: 300;
103 | font-style: normal;
104 | src: url('/fonts/Plus_Jakarta_Sans/PlusJakartaSans-Light.ttf');
105 | }
106 |
107 | @font-face {
108 | font-family: 'Plus Jakarta Sans';
109 | font-weight: 400;
110 | font-style: normal;
111 | src: url('/fonts/Plus_Jakarta_Sans/PlusJakartaSans-Regular.ttf');
112 | }
113 |
114 | @font-face {
115 | font-family: 'Plus Jakarta Sans';
116 | font-weight: 500;
117 | font-style: normal;
118 | src: url('/fonts/Plus_Jakarta_Sans/PlusJakartaSans-Medium.ttf');
119 | }
120 |
121 | @font-face {
122 | font-family: 'Plus Jakarta Sans';
123 | font-weight: 600;
124 | font-style: normal;
125 | src: url('/fonts/Plus_Jakarta_Sans/PlusJakartaSans-SemiBold.ttf');
126 | }
127 |
128 | @font-face {
129 | font-family: 'Plus Jakarta Sans';
130 | font-weight: 700;
131 | font-style: normal;
132 | src: url('/fonts/Plus_Jakarta_Sans/PlusJakartaSans-Bold.ttf');
133 | }
134 |
135 | @font-face {
136 | font-family: 'Plus Jakarta Sans';
137 | font-weight: 800;
138 | font-style: normal;
139 | src: url('/fonts/Plus_Jakarta_Sans/PlusJakartaSans-ExtraBold.ttf');
140 | }
141 |
142 | /**
143 | * Plus Jakarta Sans (Italic)
144 | */
145 |
146 | @font-face {
147 | font-family: 'Plus Jakarta Sans';
148 | font-weight: 300;
149 | font-style: italic;
150 | src: url('/fonts/Plus_Jakarta_Sans/PlusJakartaSans-LightItalic.ttf');
151 | }
152 |
153 | @font-face {
154 | font-family: 'Plus Jakarta Sans';
155 | font-weight: 400;
156 | font-style: italic;
157 | src: url('/fonts/Plus_Jakarta_Sans/PlusJakartaSans-Italic.ttf');
158 | }
159 |
160 | @font-face {
161 | font-family: 'Plus Jakarta Sans';
162 | font-weight: 500;
163 | font-style: italic;
164 | src: url('/fonts/Plus_Jakarta_Sans/PlusJakartaSans-MediumItalic.ttf');
165 | }
166 |
167 | @font-face {
168 | font-family: 'Plus Jakarta Sans';
169 | font-weight: 600;
170 | font-style: italic;
171 | src: url('/fonts/Plus_Jakarta_Sans/PlusJakartaSans-SemiBoldItalic.ttf');
172 | }
173 |
174 | @font-face {
175 | font-family: 'Plus Jakarta Sans';
176 | font-weight: 700;
177 | font-style: italic;
178 | src: url('/fonts/Plus_Jakarta_Sans/PlusJakartaSans-BoldItalic.ttf');
179 | }
180 |
181 | @font-face {
182 | font-family: 'Plus Jakarta Sans';
183 | font-weight: 800;
184 | font-style: italic;
185 | src: url('/fonts/Plus_Jakarta_Sans/PlusJakartaSans-ExtraBoldItalic.ttf');
186 | }
187 |
188 | /**
189 | * Poppins (Regular)
190 | */
191 |
192 | @font-face {
193 | font-family: 'Poppins';
194 | font-weight: 300;
195 | font-style: normal;
196 | src: url('/fonts/Poppins/Poppins-Light.ttf');
197 | }
198 |
199 | @font-face {
200 | font-family: 'Poppins';
201 | font-weight: 400;
202 | font-style: normal;
203 | src: url('/fonts/Poppins/Poppins-Regular.ttf');
204 | }
205 |
206 | @font-face {
207 | font-family: 'Poppins';
208 | font-weight: 500;
209 | font-style: normal;
210 | src: url('/fonts/Poppins/Poppins-Medium.ttf');
211 | }
212 |
213 | @font-face {
214 | font-family: 'Poppins';
215 | font-weight: 600;
216 | font-style: normal;
217 | src: url('/fonts/Poppins/Poppins-SemiBold.ttf');
218 | }
219 |
220 | @font-face {
221 | font-family: 'Poppins';
222 | font-weight: 700;
223 | font-style: normal;
224 | src: url('/fonts/Poppins/Poppins-Bold.ttf');
225 | }
226 |
227 | @font-face {
228 | font-family: 'Poppins';
229 | font-weight: 800;
230 | font-style: normal;
231 | src: url('/fonts/Poppins/Poppins-ExtraBold.ttf');
232 | }
233 |
234 |
235 | /**
236 | * Poppins (City)
237 | */
238 |
239 |
240 | @font-face {
241 | font-family: 'Poppins';
242 | font-weight: 300;
243 | font-style: italic;
244 | src: url('/fonts/Poppins/Poppins-LightItalic.ttf');
245 | }
246 |
247 | @font-face {
248 | font-family: 'Poppins';
249 | font-weight: 400;
250 | font-style: italic;
251 | src: url('/fonts/Poppins/Poppins-Italic.ttf');
252 | }
253 |
254 | @font-face {
255 | font-family: 'Poppins';
256 | font-weight: 500;
257 | font-style: italic;
258 | src: url('/fonts/Poppins/Poppins-MediumItalic.ttf');
259 | }
260 |
261 | @font-face {
262 | font-family: 'Poppins';
263 | font-weight: 600;
264 | font-style: italic;
265 | src: url('/fonts/Poppins/Poppins-SemiBoldItalic.ttf');
266 | }
267 |
268 | @font-face {
269 | font-family: 'Poppins';
270 | font-weight: 700;
271 | font-style: italic;
272 | src: url('/fonts/Poppins/Poppins-BoldItalic.ttf');
273 | }
274 |
275 | @font-face {
276 | font-family: 'Poppins';
277 | font-weight: 800;
278 | font-style: italic;
279 | src: url('/fonts/Poppins/Poppins-ExtraBoldItalic.ttf');
280 | }
--------------------------------------------------------------------------------
/Chrome/visualizer.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | TorpedoRead | Visualizer
5 |
6 |
7 |
8 |
9 |
10 |
17 |
18 |
19 | Visualizer
20 | Visualize and customize your Accelerated Reading settings.
21 |
22 |
23 |
24 |
25 |
26 |
27 | Torpedo Read's facili tates t he eye 's jour ney thro ugh te xt b y guid ing i t wi th artif icial fixa tion poi nts th at foc us on ly o n init ial lett ers inst ead o f holi stic read ings. Th is remo ves e ye reloc ation a nd encou rages dee per compreh ension th an shal low for ms o f med ia usua lly all ow.
28 |
29 |
30 | Torpedo Read's facili tates t he eye 's jour ney thro ugh te xt b y guid ing i t wi th artif icial fixa tion poi nts th at foc us on ly o n init ial lett ers inst ead o f holi stic read ings. Th is remo ves e ye reloc ation a nd encou rages dee per compreh ension th an shal low for ms o f med ia usua lly all ow.
31 |
32 |
33 | Torpedo Read's facili tates t he eye 's jour ney thro ugh te xt b y guid ing i t wi th artif icial fixa tion poi nts th at foc us on ly o n init ial lett ers inst ead o f holi stic read ings. Th is remo ves e ye reloc ation a nd encou rages dee per compreh ension th an shal low for ms o f med ia usua lly all ow.
34 |
35 |
36 |
37 |
38 | Bold Contrast
39 |
40 |
41 |
42 | Regular Contrast
43 |
44 |
45 |
46 | Base Thickness
47 |
48 | 300
49 | 400
50 | 500
51 |
52 |
53 |
54 | Thickness Contrast
55 |
56 | 100 (Thin)
57 | 200 (Thinner)
58 | 300 (Default)
59 |
60 |
61 |
62 | Font
63 |
64 | Open Sans
65 | Plus Jakarta Sans
66 | Poppins
67 |
68 |
69 |
70 | Letter Spacing
71 |
72 |
73 | Save
74 |
75 |
76 |
77 |
78 |
79 |
80 |
81 |
82 |
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | Apache License
2 | Version 2.0, January 2004
3 | http://www.apache.org/licenses/
4 |
5 | TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
6 |
7 | 1. Definitions.
8 |
9 | "License" shall mean the terms and conditions for use, reproduction,
10 | and distribution as defined by Sections 1 through 9 of this document.
11 |
12 | "Licensor" shall mean the copyright owner or entity authorized by
13 | the copyright owner that is granting the License.
14 |
15 | "Legal Entity" shall mean the union of the acting entity and all
16 | other entities that control, are controlled by, or are under common
17 | control with that entity. For the purposes of this definition,
18 | "control" means (i) the power, direct or indirect, to cause the
19 | direction or management of such entity, whether by contract or
20 | otherwise, or (ii) ownership of fifty percent (50%) or more of the
21 | outstanding shares, or (iii) beneficial ownership of such entity.
22 |
23 | "You" (or "Your") shall mean an individual or Legal Entity
24 | exercising permissions granted by this License.
25 |
26 | "Source" form shall mean the preferred form for making modifications,
27 | including but not limited to software source code, documentation
28 | source, and configuration files.
29 |
30 | "Object" form shall mean any form resulting from mechanical
31 | transformation or translation of a Source form, including but
32 | not limited to compiled object code, generated documentation,
33 | and conversions to other media types.
34 |
35 | "Work" shall mean the work of authorship, whether in Source or
36 | Object form, made available under the License, as indicated by a
37 | copyright notice that is included in or attached to the work
38 | (an example is provided in the Appendix below).
39 |
40 | "Derivative Works" shall mean any work, whether in Source or Object
41 | form, that is based on (or derived from) the Work and for which the
42 | editorial revisions, annotations, elaborations, or other modifications
43 | represent, as a whole, an original work of authorship. For the purposes
44 | of this License, Derivative Works shall not include works that remain
45 | separable from, or merely link (or bind by name) to the interfaces of,
46 | the Work and Derivative Works thereof.
47 |
48 | "Contribution" shall mean any work of authorship, including
49 | the original version of the Work and any modifications or additions
50 | to that Work or Derivative Works thereof, that is intentionally
51 | submitted to Licensor for inclusion in the Work by the copyright owner
52 | or by an individual or Legal Entity authorized to submit on behalf of
53 | the copyright owner. For the purposes of this definition, "submitted"
54 | means any form of electronic, verbal, or written communication sent
55 | to the Licensor or its representatives, including but not limited to
56 | communication on electronic mailing lists, source code control systems,
57 | and issue tracking systems that are managed by, or on behalf of, the
58 | Licensor for the purpose of discussing and improving the Work, but
59 | excluding communication that is conspicuously marked or otherwise
60 | designated in writing by the copyright owner as "Not a Contribution."
61 |
62 | "Contributor" shall mean Licensor and any individual or Legal Entity
63 | on behalf of whom a Contribution has been received by Licensor and
64 | subsequently incorporated within the Work.
65 |
66 | 2. Grant of Copyright License. Subject to the terms and conditions of
67 | this License, each Contributor hereby grants to You a perpetual,
68 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable
69 | copyright license to reproduce, prepare Derivative Works of,
70 | publicly display, publicly perform, sublicense, and distribute the
71 | Work and such Derivative Works in Source or Object form.
72 |
73 | 3. Grant of Patent License. Subject to the terms and conditions of
74 | this License, each Contributor hereby grants to You a perpetual,
75 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable
76 | (except as stated in this section) patent license to make, have made,
77 | use, offer to sell, sell, import, and otherwise transfer the Work,
78 | where such license applies only to those patent claims licensable
79 | by such Contributor that are necessarily infringed by their
80 | Contribution(s) alone or by combination of their Contribution(s)
81 | with the Work to which such Contribution(s) was submitted. If You
82 | institute patent litigation against any entity (including a
83 | cross-claim or counterclaim in a lawsuit) alleging that the Work
84 | or a Contribution incorporated within the Work constitutes direct
85 | or contributory patent infringement, then any patent licenses
86 | granted to You under this License for that Work shall terminate
87 | as of the date such litigation is filed.
88 |
89 | 4. Redistribution. You may reproduce and distribute copies of the
90 | Work or Derivative Works thereof in any medium, with or without
91 | modifications, and in Source or Object form, provided that You
92 | meet the following conditions:
93 |
94 | (a) You must give any other recipients of the Work or
95 | Derivative Works a copy of this License; and
96 |
97 | (b) You must cause any modified files to carry prominent notices
98 | stating that You changed the files; and
99 |
100 | (c) You must retain, in the Source form of any Derivative Works
101 | that You distribute, all copyright, patent, trademark, and
102 | attribution notices from the Source form of the Work,
103 | excluding those notices that do not pertain to any part of
104 | the Derivative Works; and
105 |
106 | (d) If the Work includes a "NOTICE" text file as part of its
107 | distribution, then any Derivative Works that You distribute must
108 | include a readable copy of the attribution notices contained
109 | within such NOTICE file, excluding those notices that do not
110 | pertain to any part of the Derivative Works, in at least one
111 | of the following places: within a NOTICE text file distributed
112 | as part of the Derivative Works; within the Source form or
113 | documentation, if provided along with the Derivative Works; or,
114 | within a display generated by the Derivative Works, if and
115 | wherever such third-party notices normally appear. The contents
116 | of the NOTICE file are for informational purposes only and
117 | do not modify the License. You may add Your own attribution
118 | notices within Derivative Works that You distribute, alongside
119 | or as an addendum to the NOTICE text from the Work, provided
120 | that such additional attribution notices cannot be construed
121 | as modifying the License.
122 |
123 | You may add Your own copyright statement to Your modifications and
124 | may provide additional or different license terms and conditions
125 | for use, reproduction, or distribution of Your modifications, or
126 | for any such Derivative Works as a whole, provided Your use,
127 | reproduction, and distribution of the Work otherwise complies with
128 | the conditions stated in this License.
129 |
130 | 5. Submission of Contributions. Unless You explicitly state otherwise,
131 | any Contribution intentionally submitted for inclusion in the Work
132 | by You to the Licensor shall be under the terms and conditions of
133 | this License, without any additional terms or conditions.
134 | Notwithstanding the above, nothing herein shall supersede or modify
135 | the terms of any separate license agreement you may have executed
136 | with Licensor regarding such Contributions.
137 |
138 | 6. Trademarks. This License does not grant permission to use the trade
139 | names, trademarks, service marks, or product names of the Licensor,
140 | except as required for reasonable and customary use in describing the
141 | origin of the Work and reproducing the content of the NOTICE file.
142 |
143 | 7. Disclaimer of Warranty. Unless required by applicable law or
144 | agreed to in writing, Licensor provides the Work (and each
145 | Contributor provides its Contributions) on an "AS IS" BASIS,
146 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
147 | implied, including, without limitation, any warranties or conditions
148 | of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
149 | PARTICULAR PURPOSE. You are solely responsible for determining the
150 | appropriateness of using or redistributing the Work and assume any
151 | risks associated with Your exercise of permissions under this License.
152 |
153 | 8. Limitation of Liability. In no event and under no legal theory,
154 | whether in tort (including negligence), contract, or otherwise,
155 | unless required by applicable law (such as deliberate and grossly
156 | negligent acts) or agreed to in writing, shall any Contributor be
157 | liable to You for damages, including any direct, indirect, special,
158 | incidental, or consequential damages of any character arising as a
159 | result of this License or out of the use or inability to use the
160 | Work (including but not limited to damages for loss of goodwill,
161 | work stoppage, computer failure or malfunction, or any and all
162 | other commercial damages or losses), even if such Contributor
163 | has been advised of the possibility of such damages.
164 |
165 | 9. Accepting Warranty or Additional Liability. While redistributing
166 | the Work or Derivative Works thereof, You may choose to offer,
167 | and charge a fee for, acceptance of support, warranty, indemnity,
168 | or other liability obligations and/or rights consistent with this
169 | License. However, in accepting such obligations, You may act only
170 | on Your own behalf and on Your sole responsibility, not on behalf
171 | of any other Contributor, and only if You agree to indemnify,
172 | defend, and hold each Contributor harmless for any liability
173 | incurred by, or claims asserted against, such Contributor by reason
174 | of your accepting any such warranty or additional liability.
175 |
176 | END OF TERMS AND CONDITIONS
177 |
178 | APPENDIX: How to apply the Apache License to your work.
179 |
180 | To apply the Apache License to your work, attach the following
181 | boilerplate notice, with the fields enclosed by brackets "[]"
182 | replaced with your own identifying information. (Don't include
183 | the brackets!) The text should be enclosed in the appropriate
184 | comment syntax for the file format. We also recommend that a
185 | file or class name and description of purpose be included on the
186 | same "printed page" as the copyright notice for easier
187 | identification within third-party archives.
188 |
189 | Copyright [yyyy] [name of copyright owner]
190 |
191 | Licensed under the Apache License, Version 2.0 (the "License");
192 | you may not use this file except in compliance with the License.
193 | You may obtain a copy of the License at
194 |
195 | http://www.apache.org/licenses/LICENSE-2.0
196 |
197 | Unless required by applicable law or agreed to in writing, software
198 | distributed under the License is distributed on an "AS IS" BASIS,
199 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
200 | See the License for the specific language governing permissions and
201 | limitations under the License.
202 |
--------------------------------------------------------------------------------
/Firefox/visualizer.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | TorpedoRead | Visualizer
5 |
6 |
7 |
8 |
9 |
10 |
19 |
20 |
21 | Visualizer
22 | Visualize and customize your Accelerated Reading settings.
23 |
24 |
25 |
26 |
27 |
28 |
29 | Torpedo Read's facili tates t he eye 's jour ney thro ugh te xt b y guid ing i t wi th artif icial fixa tion poi nts th at foc us on ly o n init ial lett ers inst ead o f holi stic read ings. Th is remo ves e ye reloc ation a nd encou rages dee per compreh ension th an shal low for ms o f med ia usua lly all ow.
30 |
31 |
32 | Torpedo Read's facili tates t he eye 's jour ney thro ugh te xt b y guid ing i t wi th artif icial fixa tion poi nts th at foc us on ly o n init ial lett ers inst ead o f holi stic read ings. Th is remo ves e ye reloc ation a nd encou rages dee per compreh ension th an shal low for ms o f med ia usua lly all ow.
33 |
34 |
35 | Torpedo Read's facili tates t he eye 's jour ney thro ugh te xt b y guid ing i t wi th artif icial fixa tion poi nts th at foc us on ly o n init ial lett ers inst ead o f holi stic read ings. Th is remo ves e ye reloc ation a nd encou rages dee per compreh ension th an shal low for ms o f med ia usua lly all ow.
36 |
37 |
38 |
39 |
40 | Bold Contrast
41 |
42 |
43 |
44 | Regular Contrast
45 |
46 |
47 |
48 | Base Thickness
49 |
50 | 300
51 | 400
52 | 500
53 |
54 |
55 |
56 | Thickness Contrast
57 |
58 | 100 (Thin)
59 | 200 (Thinner)
60 | 300 (Default)
61 |
62 |
63 |
64 | Font
65 |
66 | Open Sans
67 | Plus Jakarta Sans
68 | Poppins
69 |
70 |
71 |
72 | Letter Spacing
73 |
74 |
75 | Save
76 |
77 |
78 |
79 |
80 |
81 |
82 |
83 |
84 |
--------------------------------------------------------------------------------
/Chrome/content-inject.js:
--------------------------------------------------------------------------------
1 | console.log("[TorpedoRead] Script Injected.")
2 |
3 | const validateEmail = (email) => {
4 | return email.match(
5 | /^(([^<>()[\]\\.,;:\s@\"]+(\.[^<>()[\]\\.,;:\s@\"]+)*)|(\".+\"))@((\[[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\])|(([a-zA-Z\-0-9]+\.)+[a-zA-Z]{2,}))$/
6 | );
7 | };
8 |
9 | function textNodesUnder(el) {
10 | if (el.nodeName === '#text') {
11 | if (el.parentElement.closest("pre, code, style, script, noscript, torpedo, input, textarea, br, img, hr, [contenteditable]")) {
12 | return []
13 | }
14 |
15 | return [ el ]
16 | }
17 |
18 | let n,
19 | a = [],
20 | walk = document.createTreeWalker(el, NodeFilter.SHOW_TEXT, (node) => {
21 | // Check if element is some part of a code/pre element or part of a style or script.
22 | if (node.parentElement.closest("pre, code, style, script, noscript, torpedo, input, textarea, br, img, hr, [contenteditable]")) {
23 | return false
24 | }
25 |
26 | // Check string isn't empty
27 | // Usually found as part of webpage indenting
28 | if (node.textContent.trim().length === 0) {
29 | return false
30 | }
31 |
32 | return true
33 | });
34 |
35 | while ((n = walk.nextNode())) a.push(n);
36 | return a;
37 | }
38 |
39 | const check = (startEl = false, baseWeight = 300, weightContrast = 200) => {
40 | // Remove all existing Torpedo nodes and replace with their text.
41 | const body = startEl ? startEl : document.querySelector("body");
42 | if (!startEl) {
43 | console.log(`[TorpedoRead] Running Full Check.`)
44 | }
45 |
46 | // Use the preferred element if defined otherwise whole page.
47 | // Used in MutationObserver
48 | const nodes = textNodesUnder(body);
49 |
50 | // Loop through all the nodes.
51 | for (const node of nodes) {
52 | checkNode(node, baseWeight, weightContrast)
53 | }
54 | }
55 |
56 | const checkNode = (node = false, baseWeight = 300, weightContrast = 200) => {
57 | const words = node.textContent
58 | .split(/([\s-]+)/)
59 | .map((word, index) => {
60 | if (index % 2 === 0) {
61 | // Get style details (weight and italics)
62 | const styles = getComputedStyle(node.parentElement);
63 | const _weight = styles["font-weight"];
64 | const italic = styles["font-style"] === "italic";
65 |
66 | // Get weight, round to 100, take away 400 for the "boldness differentiator"
67 | const weight = (Math.round(_weight / 100) * 100) - baseWeight;
68 | const boldWeight = weight + baseWeight + weightContrast >= 800 ? 800 : weight + baseWeight + weightContrast
69 | const regularWeight = boldWeight - weightContrast
70 |
71 | const el = document.createElement("torpedo");
72 | el.innerHTML = Torpedify(word, boldWeight, regularWeight, italic);
73 |
74 | return el
75 | } else {
76 | return word
77 | }
78 | })
79 |
80 | node.replaceWith(...words);
81 | }
82 |
83 | const base = [0, 1, 1, 1, 2, 3, 3, 4, 4, 5, 5, 6, 6];
84 | const emojiRegex = /[\u{1f300}-\u{1f5ff}\u{1f900}-\u{1f9ff}\u{1f600}-\u{1f64f}\u{1f680}-\u{1f6ff}\u{2600}-\u{26ff}\u{2700}-\u{27bf}\u{1f1e6}-\u{1f1ff}\u{1f191}-\u{1f251}\u{1f004}\u{1f0cf}\u{1f170}-\u{1f171}\u{1f17e}-\u{1f17f}\u{1f18e}\u{3030}\u{2b50}\u{2b55}\u{2934}-\u{2935}\u{2b05}-\u{2b07}\u{2b1b}-\u{2b1c}\u{3297}\u{3299}\u{303d}\u{00a9}\u{00ae}\u{2122}\u{23f3}\u{24c2}\u{23e9}-\u{23ef}\u{25b6}\u{23f8}-\u{23fa}]/gu;
85 |
86 |
87 | const Torpedify = (str, boldWeight, regularWeight, italic = false) => {
88 | // If emoji return the emoji
89 | if (emojiRegex.test(str)) {
90 | return `${str} `;
91 | }
92 |
93 | let prefix = ''
94 | let suffix = ''
95 | let content = str
96 | const letters = str.split("");
97 |
98 | // Get prefix + content strip
99 | for (const l of letters) {
100 | if ([",", "(", ")", "!", "?", ";", "[", "]", ".", ":", "'"].includes(l)) {
101 | prefix += l
102 | content = content.substring(1);
103 | } else {
104 | // String started, stop search.
105 | break;
106 | }
107 | }
108 |
109 | // Get suffix, content strip + stop cloning w/ reverse()
110 | // Don't run if content is empty because we already have punctuation
111 | if (content !== '') {
112 | for (const l of str.split("").reverse()) {
113 | if ([",", "(", ")", "!", "?", ";", "[", "]", ".", ":", "'"].includes(l)) {
114 | suffix = l + suffix
115 | content = content.slice(0, -1)
116 | } else {
117 | // String started, stop search.
118 | break;
119 | }
120 | }
121 | }
122 |
123 | const __italic = italic ? '__italic' : ''
124 |
125 | // Do not highlight any email addresses
126 | if (validateEmail(content)) {
127 | return `${content} `;
128 | }
129 |
130 | // Do not highlight any website URLs
131 | try {
132 | const protocols = ["file:", "http:", "https:"];
133 | const url = new URL(content);
134 | if (protocols.includes(url.protocol)) {
135 | return `${content} `;
136 | }
137 | } catch (e) {}
138 |
139 | // Recreate the initial and remaining text.
140 | const index = base[content.length] ? base[content.length] : 7;
141 | const contentSplit = content.split('')
142 |
143 | const initial = contentSplit.splice(0, index).join("");
144 | const remaining = contentSplit.join("");
145 |
146 | // Return as string
147 | const prefixEl = prefix.length > 0 ? `${prefix} ` : ''
148 | const suffixEl = suffix.length > 0 ? `${suffix} ` : ''
149 | const initialEl = initial.length > 0 ? `${initial} ` : ''
150 | const remainingEl = remaining.length > 0 ? `${remaining} ` : ''
151 |
152 | return prefixEl + initialEl + remainingEl + suffixEl;
153 | };
154 |
155 | const init = (visuals = false) => {
156 | /**
157 | * Init will inject the CSS required for our program to function.
158 | * Everyone else will be triggered using the Chrome runtime message receiver.
159 | */
160 |
161 | const exists = document.getElementById("torpedo__injected");
162 | if (exists) {
163 | // Already injected Torpedo.
164 | return;
165 | } else {
166 | if (!visuals) {
167 | visuals = {
168 | contrast: {
169 | bold: 1.2,
170 | regular: 1,
171 | thickness: 200
172 | },
173 | baseThickness: 300,
174 | spacing: 0,
175 | font: 'Open Sans',
176 | }
177 | }
178 |
179 | const extensionId = chrome.i18n.getMessage("@@extension_id")
180 | const css = `
181 | [class^="torpedo__regular"] {
182 | font-family: '${visuals.font}' !important;
183 | filter: brightness(${visuals.contrast.regular});
184 | font-weight: ${visuals.baseThickness};
185 | letter-spacing: ${visuals.spacing}px;
186 | }
187 |
188 | [class^="torpedo__bold"] {
189 | font-family: '${visuals.font}' !important;
190 | filter: brightness(${visuals.contrast.bold});
191 | font-weight: ${visuals.baseThickness + visuals.contrast.thickness};
192 | letter-spacing: ${visuals.spacing}px;
193 | }
194 |
195 | .torpedo__bold__300 {
196 | font-weight: 300 !important;;
197 | }
198 |
199 | .torpedo__bold__italic__300 {
200 | font-weight: 300 !important;;;
201 | font-style: italic;
202 | }
203 |
204 | .torpedo__bold__400 {
205 | font-weight: 400 !important;
206 | font-style: normal;
207 | }
208 |
209 | .torpedo__bold__italic__400 {
210 | font-weight: 400 !important;
211 | font-style: italic;
212 | }
213 |
214 | .torpedo__bold__500 {
215 | font-weight: 500 !important;
216 | font-style: normal;
217 | }
218 |
219 | .torpedo__bold__italic__500 {
220 | font-weight: 500 !important;
221 | font-style: italic;
222 | }
223 |
224 | .torpedo__bold__600 {
225 | font-weight: 600 !important;
226 | font-style: normal;
227 | }
228 |
229 | .torpedo__bold__italic__600 {
230 | font-weight: 600 !important;
231 | font-style: italic;
232 | }
233 |
234 | .torpedo__bold__700 {
235 | font-weight: 700 !important;
236 | font-style: normal;
237 | }
238 |
239 | .torpedo__bold__italic__700 {
240 | font-weight: 700 !important;
241 | font-style: italic;
242 | }
243 |
244 | .torpedo__bold__800 {
245 | font-weight: 800 !important;
246 | font-style: normal;
247 | }
248 |
249 | .torpedo__bold__italic__800 {
250 | font-weight: 800 !important;
251 | font-style: italic;
252 | }
253 |
254 | .torpedo__regular__300 {
255 | font-weight: 300 !important;;
256 | }
257 |
258 | .torpedo__regular__italic__300 {
259 | font-weight: 300 !important;;;
260 | font-style: italic;
261 | }
262 |
263 | .torpedo__regular__400 {
264 | font-weight: 400 !important;
265 | font-style: normal;
266 | }
267 |
268 | .torpedo__regular__italic__400 {
269 | font-weight: 400 !important;
270 | font-style: italic;
271 | }
272 |
273 | .torpedo__regular__500 {
274 | font-weight: 500 !important;
275 | font-style: normal;
276 | }
277 |
278 | .torpedo__regular__italic__500 {
279 | font-weight: 500 !important;
280 | font-style: italic;
281 | }
282 |
283 | .torpedo__regular__600 {
284 | font-weight: 600 !important;
285 | font-style: normal;
286 | }
287 |
288 | .torpedo__regular__italic__600 {
289 | font-weight: 600 !important;
290 | font-style: italic;
291 | }
292 |
293 | .torpedo__regular__700 {
294 | font-weight: 700 !important;
295 | font-style: normal;
296 | }
297 |
298 | .torpedo__regular__italic__700 {
299 | font-weight: 700 !important;
300 | font-style: italic;
301 | }
302 |
303 | .torpedo__regular__800 {
304 | font-weight: 800 !important;
305 | font-style: normal;
306 | }
307 |
308 | .torpedo__regular__italic__800 {
309 | font-weight: 800 !important;
310 | font-style: italic;
311 | }
312 |
313 | `;
314 |
315 | const style = document.createElement("style");
316 | style.id = "torpedo__injected";
317 | style.appendChild(document.createTextNode(css));
318 |
319 | // Inject fonts files without cramming the CSS inside the JS
320 | const fontLink = document.createElement("link")
321 | fontLink.setAttribute('href', `chrome-extension://${extensionId}/fonts.css`)
322 | fontLink.setAttribute('type', 'text/css')
323 | fontLink.setAttribute('rel', 'stylesheet')
324 |
325 | const head = document.querySelector("head")
326 | head.appendChild(fontLink)
327 | head.appendChild(style)
328 | }
329 | };
330 |
331 |
332 | // We **do not** want to inject this twice.
333 | chrome.runtime.onMessage.addListener(function(options, sender, sendResponse) {
334 | // Only start the observer once the script has been started
335 | // The script can be started with the shortcut or autoapply setting
336 | if (options.runObserver && (options.startCommand || options.autoapply)) {
337 | console.log(`[TorpedoRead] Experimental observer launched.`)
338 | const weightContrast = options?.visuals?.contrast?.thickness || 200
339 | const baseWeight = options?.visuals?.baseThickness || 300
340 |
341 | /**
342 | * Create the MutationObservers
343 | */
344 |
345 | let NewNodeTimer;
346 | const NewNodeObserver = new MutationObserver(function(mutations) {
347 | // if (NewNodeTimer) clearTimeout(NewNodeTimer)
348 |
349 | // NewNodeTimer = setTimeout(() => {
350 | mutations.forEach((mutation) => {
351 | mutation.addedNodes.forEach((node) => {
352 | // Get every new text nodes and append it to the TextChangeObserver.
353 | const textNodes = textNodesUnder(node)
354 | for (const node of textNodes) {
355 | const replaced = node.textContent.replaceAll(/([\s-]+)/gi, '')
356 | if (replaced.length > 0) {
357 | TextChangeObserver.observe(node, {
358 | attributes: false,
359 | childList: true,
360 | characterData: true,
361 | subtree: true
362 | })
363 |
364 | // Torpedify!
365 | checkNode(node, baseWeight, weightContrast)
366 | }
367 | }
368 | })
369 | })
370 | // }, 25)
371 | });
372 |
373 | let TextChangeTimer;
374 | const TextChangeObserver = new MutationObserver(function(mutations) {
375 | if (TextChangeTimer) clearTimeout(TextChangeTimer)
376 |
377 | TextChangeTimer = setTimeout(() => {
378 | mutations.forEach((mutation) => {
379 | if (mutation.type === 'characterData') {
380 | check(mutation.target, baseWeight, weightContrast)
381 | }
382 | })
383 | }, 25)
384 | });
385 |
386 | NewNodeObserver.observe(document.querySelector("body"), {
387 | attributes: false,
388 | childList: true,
389 | characterData: false,
390 | subtree: true
391 | });
392 |
393 | // Start the observer.
394 | const nodes = textNodesUnder(document.querySelector("body"))
395 | console.log(`[TorpedoRead] Adding ${nodes.length} nodes to the TextChangeObserver.`)
396 | for (const node of nodes) {
397 | TextChangeObserver.observe(node, {
398 | attributes: false,
399 | childList: true,
400 | characterData: true,
401 | subtree: true
402 | });
403 | }
404 | }
405 |
406 | // Init!
407 | init(options.visuals)
408 |
409 | if (options.autoapply) {
410 | // Autoapply and start the program!
411 | console.log("[TorpedoRead] Autoapply preference injected.")
412 | const weightContrast = options?.visuals?.contrast?.thickness || 200
413 | const baseWeight = options?.visuals?.baseThickness || 300
414 |
415 | check(false, baseWeight, weightContrast);
416 | }
417 |
418 | if (options.startCommand) {
419 | // Start the script!
420 | console.log("[TorpedoRead] Starting due to user input command.")
421 | const weightContrast = options?.visuals?.contrast?.thickness || 200
422 | const baseWeight = options?.visuals?.baseThickness || 300
423 | check(false, baseWeight, weightContrast);
424 | }
425 | });
--------------------------------------------------------------------------------
/Firefox/content-inject.js:
--------------------------------------------------------------------------------
1 | console.log("[TorpedoRead] Script Injected.")
2 |
3 | const validateEmail = (email) => {
4 | return email.match(
5 | /^(([^<>()[\]\\.,;:\s@\"]+(\.[^<>()[\]\\.,;:\s@\"]+)*)|(\".+\"))@((\[[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\])|(([a-zA-Z\-0-9]+\.)+[a-zA-Z]{2,}))$/
6 | );
7 | };
8 |
9 | function textNodesUnder(el) {
10 | if (el.nodeName === '#text') {
11 | if (el.parentElement.closest("pre, code, style, script, noscript, torpedo, input, textarea, br, img, hr, [contenteditable]")) {
12 | return []
13 | }
14 |
15 | return [ el ]
16 | }
17 |
18 | let n,
19 | a = [],
20 | walk = document.createTreeWalker(el, NodeFilter.SHOW_TEXT, (node) => {
21 | // Check if element is some part of a code/pre element or part of a style or script.
22 | if (node.parentElement.closest("pre, code, style, script, noscript, torpedo, input, textarea, br, img, hr, [contenteditable]")) {
23 | return false
24 | }
25 |
26 | // Check string isn't empty
27 | // Usually found as part of webpage indenting
28 | if (node.textContent.trim().length === 0) {
29 | return false
30 | }
31 |
32 | return true
33 | });
34 |
35 | while ((n = walk.nextNode())) a.push(n);
36 | return a;
37 | }
38 |
39 | const check = (startEl = false, baseWeight = 300, weightContrast = 200) => {
40 | // Remove all existing Torpedo nodes and replace with their text.
41 | const body = startEl ? startEl : document.querySelector("body");
42 | if (!startEl) {
43 | console.log(`[TorpedoRead] Running Full Check.`)
44 | }
45 |
46 | // Use the preferred element if defined otherwise whole page.
47 | // Used in MutationObserver
48 | const nodes = textNodesUnder(body);
49 |
50 | // Loop through all the nodes.
51 | for (const node of nodes) {
52 | checkNode(node, baseWeight, weightContrast)
53 | }
54 | }
55 |
56 | const checkNode = (node = false, baseWeight = 300, weightContrast = 200) => {
57 | const words = node.textContent
58 | .split(/([\s-]+)/)
59 | .map((word, index) => {
60 | if (index % 2 === 0) {
61 | // Get style details (weight and italics)
62 | const styles = getComputedStyle(node.parentElement);
63 | const _weight = styles["font-weight"];
64 | const italic = styles["font-style"] === "italic";
65 |
66 | // Get weight, round to 100, take away 400 for the "boldness differentiator"
67 | const weight = (Math.round(_weight / 100) * 100) - baseWeight;
68 | const boldWeight = weight + baseWeight + weightContrast >= 800 ? 800 : weight + baseWeight + weightContrast
69 | const regularWeight = boldWeight - weightContrast
70 |
71 | const el = document.createElement("torpedo");
72 | el.innerHTML = Torpedify(word, boldWeight, regularWeight, italic);
73 |
74 | return el
75 | } else {
76 | return word
77 | }
78 | })
79 |
80 | node.replaceWith(...words);
81 | }
82 |
83 | const base = [0, 1, 1, 1, 2, 3, 3, 4, 4, 5, 5, 6, 6];
84 | const emojiRegex = /[\u{1f300}-\u{1f5ff}\u{1f900}-\u{1f9ff}\u{1f600}-\u{1f64f}\u{1f680}-\u{1f6ff}\u{2600}-\u{26ff}\u{2700}-\u{27bf}\u{1f1e6}-\u{1f1ff}\u{1f191}-\u{1f251}\u{1f004}\u{1f0cf}\u{1f170}-\u{1f171}\u{1f17e}-\u{1f17f}\u{1f18e}\u{3030}\u{2b50}\u{2b55}\u{2934}-\u{2935}\u{2b05}-\u{2b07}\u{2b1b}-\u{2b1c}\u{3297}\u{3299}\u{303d}\u{00a9}\u{00ae}\u{2122}\u{23f3}\u{24c2}\u{23e9}-\u{23ef}\u{25b6}\u{23f8}-\u{23fa}]/gu;
85 |
86 |
87 | const Torpedify = (str, boldWeight, regularWeight, italic = false) => {
88 | // If emoji return the emoji
89 | if (emojiRegex.test(str)) {
90 | return `${str} `;
91 | }
92 |
93 | let prefix = ''
94 | let suffix = ''
95 | let content = str
96 | const letters = str.split("");
97 |
98 | // Get prefix + content strip
99 | for (const l of letters) {
100 | if ([",", "(", ")", "!", "?", ";", "[", "]", ".", ":", "'"].includes(l)) {
101 | prefix += l
102 | content = content.substring(1);
103 | } else {
104 | // String started, stop search.
105 | break;
106 | }
107 | }
108 |
109 | // Get suffix, content strip + stop cloning w/ reverse()
110 | // Don't run if content is empty because we already have punctuation
111 | if (content !== '') {
112 | for (const l of str.split("").reverse()) {
113 | if ([",", "(", ")", "!", "?", ";", "[", "]", ".", ":", "'"].includes(l)) {
114 | suffix = l + suffix
115 | content = content.slice(0, -1)
116 | } else {
117 | // String started, stop search.
118 | break;
119 | }
120 | }
121 | }
122 |
123 | const __italic = italic ? '__italic' : ''
124 |
125 | // Do not highlight any email addresses
126 | if (validateEmail(content)) {
127 | return `${content} `;
128 | }
129 |
130 | // Do not highlight any website URLs
131 | try {
132 | const protocols = ["file:", "http:", "https:"];
133 | const url = new URL(content);
134 | if (protocols.includes(url.protocol)) {
135 | return `${content} `;
136 | }
137 | } catch (e) {}
138 |
139 | // Recreate the initial and remaining text.
140 | const index = base[content.length] ? base[content.length] : 7;
141 | const contentSplit = content.split('')
142 |
143 | const initial = contentSplit.splice(0, index).join("");
144 | const remaining = contentSplit.join("");
145 |
146 | // Return as string
147 | const prefixEl = prefix.length > 0 ? `${prefix} ` : ''
148 | const suffixEl = suffix.length > 0 ? `${suffix} ` : ''
149 | const initialEl = initial.length > 0 ? `${initial} ` : ''
150 | const remainingEl = remaining.length > 0 ? `${remaining} ` : ''
151 |
152 | return prefixEl + initialEl + remainingEl + suffixEl;
153 | };
154 |
155 | const init = (visuals = false) => {
156 | /**
157 | * Init will inject the CSS required for our program to function.
158 | * Everyone else will be triggered using the Chrome runtime message receiver.
159 | */
160 |
161 | const exists = document.getElementById("torpedo__injected");
162 | if (exists) {
163 | // Already injected Torpedo.
164 | return;
165 | } else {
166 | if (!visuals) {
167 | visuals = {
168 | contrast: {
169 | bold: 1.2,
170 | regular: 1,
171 | thickness: 200
172 | },
173 | baseThickness: 300,
174 | spacing: 0,
175 | font: 'Open Sans',
176 | }
177 | }
178 |
179 | const extensionId = chrome.i18n.getMessage("@@extension_id")
180 | const css = `
181 | [class^="torpedo__regular"] {
182 | font-family: '${visuals.font}' !important;
183 | filter: brightness(${visuals.contrast.regular});
184 | font-weight: ${visuals.baseThickness};
185 | letter-spacing: ${visuals.spacing}px;
186 | }
187 |
188 | [class^="torpedo__bold"] {
189 | font-family: '${visuals.font}' !important;
190 | filter: brightness(${visuals.contrast.bold});
191 | font-weight: ${visuals.baseThickness + visuals.contrast.thickness};
192 | letter-spacing: ${visuals.spacing}px;
193 | }
194 |
195 | .torpedo__bold__300 {
196 | font-weight: 300 !important;;
197 | }
198 |
199 | .torpedo__bold__italic__300 {
200 | font-weight: 300 !important;;;
201 | font-style: italic;
202 | }
203 |
204 | .torpedo__bold__400 {
205 | font-weight: 400 !important;
206 | font-style: normal;
207 | }
208 |
209 | .torpedo__bold__italic__400 {
210 | font-weight: 400 !important;
211 | font-style: italic;
212 | }
213 |
214 | .torpedo__bold__500 {
215 | font-weight: 500 !important;
216 | font-style: normal;
217 | }
218 |
219 | .torpedo__bold__italic__500 {
220 | font-weight: 500 !important;
221 | font-style: italic;
222 | }
223 |
224 | .torpedo__bold__600 {
225 | font-weight: 600 !important;
226 | font-style: normal;
227 | }
228 |
229 | .torpedo__bold__italic__600 {
230 | font-weight: 600 !important;
231 | font-style: italic;
232 | }
233 |
234 | .torpedo__bold__700 {
235 | font-weight: 700 !important;
236 | font-style: normal;
237 | }
238 |
239 | .torpedo__bold__italic__700 {
240 | font-weight: 700 !important;
241 | font-style: italic;
242 | }
243 |
244 | .torpedo__bold__800 {
245 | font-weight: 800 !important;
246 | font-style: normal;
247 | }
248 |
249 | .torpedo__bold__italic__800 {
250 | font-weight: 800 !important;
251 | font-style: italic;
252 | }
253 |
254 | .torpedo__regular__300 {
255 | font-weight: 300 !important;;
256 | }
257 |
258 | .torpedo__regular__italic__300 {
259 | font-weight: 300 !important;;;
260 | font-style: italic;
261 | }
262 |
263 | .torpedo__regular__400 {
264 | font-weight: 400 !important;
265 | font-style: normal;
266 | }
267 |
268 | .torpedo__regular__italic__400 {
269 | font-weight: 400 !important;
270 | font-style: italic;
271 | }
272 |
273 | .torpedo__regular__500 {
274 | font-weight: 500 !important;
275 | font-style: normal;
276 | }
277 |
278 | .torpedo__regular__italic__500 {
279 | font-weight: 500 !important;
280 | font-style: italic;
281 | }
282 |
283 | .torpedo__regular__600 {
284 | font-weight: 600 !important;
285 | font-style: normal;
286 | }
287 |
288 | .torpedo__regular__italic__600 {
289 | font-weight: 600 !important;
290 | font-style: italic;
291 | }
292 |
293 | .torpedo__regular__700 {
294 | font-weight: 700 !important;
295 | font-style: normal;
296 | }
297 |
298 | .torpedo__regular__italic__700 {
299 | font-weight: 700 !important;
300 | font-style: italic;
301 | }
302 |
303 | .torpedo__regular__800 {
304 | font-weight: 800 !important;
305 | font-style: normal;
306 | }
307 |
308 | .torpedo__regular__italic__800 {
309 | font-weight: 800 !important;
310 | font-style: italic;
311 | }
312 |
313 | `;
314 |
315 | const style = document.createElement("style");
316 | style.id = "torpedo__injected";
317 | style.appendChild(document.createTextNode(css));
318 |
319 | // Inject fonts files without cramming the CSS inside the JS
320 | const fontLink = document.createElement("link")
321 | fontLink.setAttribute('href', `moz-extension://${extensionId}/fonts.css`)
322 | fontLink.setAttribute('type', 'text/css')
323 | fontLink.setAttribute('rel', 'stylesheet')
324 |
325 | const head = document.querySelector("head")
326 | head.appendChild(fontLink)
327 | head.appendChild(style)
328 | }
329 | };
330 |
331 |
332 | // We **do not** want to inject this twice.
333 | chrome.runtime.onMessage.addListener(function(options, sender, sendResponse) {
334 | // Only start the observer once the script has been started
335 | // The script can be started with the shortcut or autoapply setting
336 | if (options.runObserver && (options.startCommand || options.autoapply)) {
337 | console.log(`[TorpedoRead] Experimental observer launched.`)
338 | const weightContrast = options?.visuals?.contrast?.thickness || 200
339 | const baseWeight = options?.visuals?.baseThickness || 300
340 |
341 | /**
342 | * Create the MutationObservers
343 | */
344 |
345 | let NewNodeTimer;
346 | const NewNodeObserver = new MutationObserver(function(mutations) {
347 | // if (NewNodeTimer) clearTimeout(NewNodeTimer)
348 |
349 | // NewNodeTimer = setTimeout(() => {
350 | mutations.forEach((mutation) => {
351 | mutation.addedNodes.forEach((node) => {
352 | // Get every new text nodes and append it to the TextChangeObserver.
353 | const textNodes = textNodesUnder(node)
354 | for (const node of textNodes) {
355 | const replaced = node.textContent.replaceAll(/([\s-]+)/gi, '')
356 | if (replaced.length > 0) {
357 | TextChangeObserver.observe(node, {
358 | attributes: false,
359 | childList: true,
360 | characterData: true,
361 | subtree: true
362 | })
363 |
364 | // Torpedify!
365 | checkNode(node, baseWeight, weightContrast)
366 | }
367 | }
368 | })
369 | })
370 | // }, 25)
371 | });
372 |
373 | let TextChangeTimer;
374 | const TextChangeObserver = new MutationObserver(function(mutations) {
375 | if (TextChangeTimer) clearTimeout(TextChangeTimer)
376 |
377 | TextChangeTimer = setTimeout(() => {
378 | mutations.forEach((mutation) => {
379 | if (mutation.type === 'characterData') {
380 | check(mutation.target, baseWeight, weightContrast)
381 | }
382 | })
383 | }, 25)
384 | });
385 |
386 | NewNodeObserver.observe(document.querySelector("body"), {
387 | attributes: false,
388 | childList: true,
389 | characterData: false,
390 | subtree: true
391 | });
392 |
393 | // Start the observer.
394 | const nodes = textNodesUnder(document.querySelector("body"))
395 | console.log(`[TorpedoRead] Adding ${nodes.length} nodes to the TextChangeObserver.`)
396 | for (const node of nodes) {
397 | TextChangeObserver.observe(node, {
398 | attributes: false,
399 | childList: true,
400 | characterData: true,
401 | subtree: true
402 | });
403 | }
404 | }
405 |
406 | // Init!
407 | init(options.visuals)
408 |
409 | if (options.autoapply) {
410 | // Autoapply and start the program!
411 | console.log("[TorpedoRead] Autoapply preference injected.")
412 | const weightContrast = options?.visuals?.contrast?.thickness || 200
413 | const baseWeight = options?.visuals?.baseThickness || 300
414 |
415 | check(false, baseWeight, weightContrast);
416 | }
417 |
418 | if (options.startCommand) {
419 | // Start the script!
420 | console.log("[TorpedoRead] Starting due to user input command.")
421 | const weightContrast = options?.visuals?.contrast?.thickness || 200
422 | const baseWeight = options?.visuals?.baseThickness || 300
423 | check(false, baseWeight, weightContrast);
424 | }
425 | });
--------------------------------------------------------------------------------