fixes #114
This commit is contained in:
parent
b9a23d8ce2
commit
ae8219dd9e
303 changed files with 321 additions and 24 deletions
147
extension/firefox/data/options/index.css
Normal file
147
extension/firefox/data/options/index.css
Normal file
|
@ -0,0 +1,147 @@
|
|||
:root {
|
||||
--color: #444;
|
||||
--bg-color: #fff;
|
||||
--color-admin: #444;
|
||||
--color-desc: #2f2f2f;
|
||||
--bg-desc: #e8e8e8;
|
||||
--color-note: #2f2f2f;
|
||||
--bg-note: #e8e8e8;
|
||||
--bg-admin: #ffffed;
|
||||
--border-admin: #e8ec3a;
|
||||
--color-light: #636363;
|
||||
--bg-color-light: #f5f5f5;
|
||||
--bg-focus: #eff6f9;
|
||||
}
|
||||
@media (prefers-color-scheme: dark) {
|
||||
:root {
|
||||
--color: #d1d2cc;
|
||||
--bg-color: #272d37;
|
||||
--color-light: #f5f5f5;
|
||||
--bg-color-light: #343946;
|
||||
--bg-focus: #454b5d;
|
||||
}
|
||||
}
|
||||
|
||||
body {
|
||||
min-width: 600px;
|
||||
color: var(--color);
|
||||
background-color: var(--bg-color);
|
||||
}
|
||||
@media (pointer: none), (pointer: coarse) {
|
||||
body {
|
||||
min-width: unset;
|
||||
}
|
||||
}
|
||||
|
||||
@supports (-moz-appearance:none) {
|
||||
body {
|
||||
font-size: 13px;
|
||||
font-family: Arial, "Helvetica Neue", Helvetica, sans-serif;
|
||||
margin: 10px;
|
||||
min-width: unset;
|
||||
}
|
||||
textarea {
|
||||
padding: 5px;
|
||||
}
|
||||
button,
|
||||
input[type=submit],
|
||||
input[type=button] {
|
||||
height: 24px;
|
||||
color: #444;
|
||||
background-image: linear-gradient(rgb(237, 237, 237), rgb(237, 237, 237) 38%, rgb(222, 222, 222));
|
||||
box-shadow: rgba(0, 0, 0, 0.08) 0 1px 0, rgba(255, 255, 255, 0.75) 0 1px 2px inset;
|
||||
text-shadow: rgb(240, 240, 240) 0 1px 0;
|
||||
border: solid 1px rgba(0, 0, 0, 0.25);
|
||||
}
|
||||
input[type=button]:disabled {
|
||||
opacity: 0.5;
|
||||
}
|
||||
}
|
||||
button,
|
||||
input[type=submit],
|
||||
input[type=button] {
|
||||
cursor: pointer;
|
||||
}
|
||||
textarea {
|
||||
color: var(--color-light);
|
||||
background-color: var(--bg-color-light);
|
||||
border: none;
|
||||
width: 100%;
|
||||
outline: none;
|
||||
}
|
||||
textarea:focus {
|
||||
background-color: var(--bg-focus);
|
||||
}
|
||||
h1 {
|
||||
font-size: 15px;
|
||||
font-weight: normal;
|
||||
}
|
||||
.mode-2,
|
||||
.mode {
|
||||
display: grid;
|
||||
white-space: nowrap;
|
||||
align-items: center;
|
||||
grid-gap: 5px;
|
||||
}
|
||||
.mode {
|
||||
grid-template-columns: min-content min-content min-content;
|
||||
}
|
||||
.mode-2 {
|
||||
grid-template-columns: min-content min-content;
|
||||
}
|
||||
#toggle-sibling-desc,
|
||||
#toggle-parser-desc,
|
||||
#toggle-protected-desc,
|
||||
#toggle-custom-desc,
|
||||
#toggle-whitelist-desc,
|
||||
#toggle-blacklist-desc {
|
||||
cursor: pointer;
|
||||
color: var(--color-desc);
|
||||
background-color: var(--bg-desc);
|
||||
padding: 1px 4px;
|
||||
}
|
||||
.note {
|
||||
background-color: var(--bg-note);
|
||||
color: var(--color-note);
|
||||
padding: 5px;
|
||||
margin: 0 0 10px 0;
|
||||
}
|
||||
.hidden {
|
||||
display: none;
|
||||
}
|
||||
.checked {
|
||||
display: grid;
|
||||
grid-template-columns: min-content 1fr;
|
||||
grid-gap: 5px;
|
||||
margin: 10px 0;
|
||||
}
|
||||
|
||||
#tools,
|
||||
#backup {
|
||||
display: grid;
|
||||
white-space: nowrap;
|
||||
grid-gap: 5px;
|
||||
}
|
||||
#backup {
|
||||
margin: 10px 0;
|
||||
grid-template-columns: min-content min-content;
|
||||
}
|
||||
#tools {
|
||||
grid-template-columns: repeat(4, min-content) 1fr;
|
||||
align-items: center;
|
||||
}
|
||||
@media screen and (max-width: 600px) {
|
||||
#backup {
|
||||
grid-template-columns: 1fr 1fr;
|
||||
}
|
||||
#tools {
|
||||
grid-template-columns: 1fr 1fr;
|
||||
}
|
||||
}
|
||||
.admin {
|
||||
color: var(--color-admin);
|
||||
background-color: var(--bg-admin);
|
||||
border: solid 1px var(--border-admin);
|
||||
padding: 10px;
|
||||
margin: 15px 0;
|
||||
}
|
81
extension/firefox/data/options/index.html
Normal file
81
extension/firefox/data/options/index.html
Normal file
|
@ -0,0 +1,81 @@
|
|||
<!DOCTYPE html>
|
||||
<html>
|
||||
<head>
|
||||
<title data-localize="userAgentSwitcherandManagerOptions">User-Agent Switcher and Manager :: Options</title>
|
||||
<link rel="stylesheet" type="text/css" href="index.css">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1">
|
||||
</head>
|
||||
|
||||
<body>
|
||||
<div class="mode">
|
||||
<input type="radio" name="mode" value="blacklist" id="mode-blacklist">
|
||||
<label for="mode-blacklist"><h1 data-localize="blackListMode">Black-List Mode</h1></label>
|
||||
<span id="toggle-blacklist-desc" data-localize="description">Description</span>
|
||||
</div>
|
||||
<p for="toggle-blacklist-desc" class="note hidden" data-localize="blackListModeDescription">Apply the custom user-agent string to all tabs except the tabs with the following top-level hostnames (comma-separated list of hostnames). Note that even if a window-based user-agent string is set from the toolbar popup, your browser's default user-agent string is used.</p>
|
||||
<textarea id="blacklist" rows="3" placeholder="e.g.: www.google.com, www.bing.com"></textarea>
|
||||
|
||||
<div class="mode">
|
||||
<input type="radio" name="mode" value="whitelist" id="mode-whitelist">
|
||||
<label for="mode-whitelist"><h1 data-localize="whiteListMode">White-List Mode</h1></label>
|
||||
<span id="toggle-whitelist-desc" data-localize="description">Description</span>
|
||||
</div>
|
||||
<p for="toggle-whitelist-desc" class="note hidden" data-localize="whiteListModeDescription">Only apply the custom user-agent string to the tabs with following top-level hostnames. Note that if a window-based user-agent string is set from the toolbar popup, this user-agent will overwrite the global one.</p>
|
||||
<textarea id="whitelist" rows="3" placeholder="e.g.: www.google.com, www.bing.com"></textarea>
|
||||
|
||||
<div class="mode">
|
||||
<input type="radio" name="mode" value="custom" id="mode-custom">
|
||||
<label for="mode-custom"><h1 data-localize="customMode">Custom Mode</h1></label>
|
||||
<span id="toggle-custom-desc" data-localize="description">Description</span>
|
||||
</div>
|
||||
<p for="toggle-custom-desc" class="note hidden"><span data-localize="customModeDescription">Try to resolve the user-agent string from a JSON object; otherwise either use the default user-agent string or use the one that the user is set from the popup interface. Use "*" as the hostname to match all domains. You can randomly select from multiple user-agent strings by providing an array instead of a fixed string. If there is a "_" key in your JSON object which refers to an array of hostnames, then the extension only randomly selects the user-agent string once for each hostname inside this list. This is useful if you don't want the random user-agent to change until this browser session is over.</span> <a href="#" id="sample" data-localize="insertSample">Insert a sample</a>.</p>
|
||||
<textarea id="custom" rows="8" wrap="off"></textarea>
|
||||
|
||||
<div class="checked">
|
||||
<input type="checkbox" id="cache">
|
||||
<label for="cache" data-localize="cache">Use caching to improve performance (recommended value is true). Uncheck this option only if you are using the custom mode and also you need the user-agent string to be altered from the provided list on every single request.</label>
|
||||
<input type="checkbox" id="exactMatch">
|
||||
<label for="exactMatch" data-localize="exactMatch">Use exact matching (if checked, you will need to insert all sub-domains in the white-list and black-list modes to be considered. If unchecked, all the sub-domains are passing the matching condition (e.g: www.google.com passes the matching if google.com is in the list))</label>
|
||||
<input type="checkbox" id="faqs">
|
||||
<label for="faqs" data-localize="faqs">Open FAQs page on updates</label>
|
||||
<input type="checkbox" id="log">
|
||||
<label for="log" data-localize="log">Display debugging info in the browser console</label>
|
||||
</div>
|
||||
|
||||
<div class="mode-2">
|
||||
<h1 data-localize="disableSpoofing">Disable Spoofing</h1>
|
||||
<span id="toggle-protected-desc" data-localize="description">Description</span>
|
||||
</div>
|
||||
<p for="toggle-protected-desc" class="note hidden" data-localize="disableSpoofingDescription">A comma-separated list of keywords that the extension should not spoof the user-agent header. Use this list to protect URLs that contain these protected keywords. Each keyword need to be at least 5 char long.</p>
|
||||
<textarea id="protected" rows="3" wrap="off"></textarea>
|
||||
|
||||
<div class="mode-2">
|
||||
<h1 data-localize="customUserAgentParsing">Custom User-Agent Parsing</h1>
|
||||
<span id="toggle-parser-desc" data-localize="description">Description</span>
|
||||
</div>
|
||||
<p for="toggle-parser-desc" class="note hidden"><span data-localize="customUserAgentParsingDescription">A JSON object to bypass the internal user-agent string parsing method. The keys are the actual user-agent strings and the value of each key is an object of the keys that need to be set for the "navigator" object. You can use the "[delete]" keyword if you want a key in the "navigator" object to get deleted.</span> <a href="#" id="sample-2" data-localize="insertSample">Insert a sample</a>.</p>
|
||||
<textarea id="parser" rows="5" wrap="off"></textarea>
|
||||
|
||||
<div class="mode-2">
|
||||
<h1 data-localize="siblingHostnames">Sibling Hostnames</h1>
|
||||
<span id="toggle-sibling-desc" data-localize="description">Description</span>
|
||||
</div>
|
||||
<p for="toggle-sibling-desc" class="note hidden"><span data-localize="siblingHostnamesDescription">A JSON array that contains one or more groups of hostnames to have a single user-agent string per group. For all hostnames in one group, the user-agent string calculation only occurs once, and all the other members use the same calculated string. This is useful to make sure a group of connected websites only access to the same user-agent string.</span> <a href="#" id="sample-3" data-localize="insertSample">Insert a sample</a>.</p>
|
||||
<textarea id="siblings" rows="5" wrap="off"></textarea>
|
||||
|
||||
<div class="admin" data-localize="managedStorage">This extension supports managed storage. All the preferences can be pre-configured by the domain administrator</div>
|
||||
<div id="backup">
|
||||
<button id="import" data-localize="importSettings">Import Settings</button>
|
||||
<button data-localized-title="exportSettingsTitle" title="To generate minified version, press Shift key while pressing this button" id="export" data-localize="exportSettings">Export Settings</button>
|
||||
</div>
|
||||
<div id="tools">
|
||||
<button id="help" data-localize="help">FAQs Page (Help)</button>
|
||||
<button id="donate" data-localize="donate">Support Development</button>
|
||||
<button id="reset" data-localize="reset">Reset</button>
|
||||
<button id="save" data-localize="save">Save</button>
|
||||
<span id="status"></span>
|
||||
</div>
|
||||
|
||||
<script src="index.js"></script>
|
||||
</body>
|
||||
</html>
|
277
extension/firefox/data/options/index.js
Normal file
277
extension/firefox/data/options/index.js
Normal file
|
@ -0,0 +1,277 @@
|
|||
'use strict';
|
||||
|
||||
// localization
|
||||
document.querySelectorAll('[data-localize]').forEach(e => {
|
||||
const ref = e.dataset.localize;
|
||||
const translated = chrome.i18n.getMessage(ref);
|
||||
if (translated) {
|
||||
e.textContent = translated;
|
||||
}
|
||||
});
|
||||
document.querySelectorAll('[data-localized-title]').forEach(e => {
|
||||
const ref = e.dataset.localizedTitle;
|
||||
const translated = chrome.i18n.getMessage(ref);
|
||||
if (translated) {
|
||||
e.title = translated;
|
||||
}
|
||||
});
|
||||
|
||||
function notify(msg, period = 750) {
|
||||
// Update status to let user know options were saved.
|
||||
const status = document.getElementById('status');
|
||||
status.textContent = msg;
|
||||
clearTimeout(notify.id);
|
||||
notify.id = setTimeout(() => status.textContent = '', period);
|
||||
}
|
||||
|
||||
function prepare(str) {
|
||||
return str.split(/\s*,\s*/)
|
||||
.map(s => s.replace('http://', '')
|
||||
.replace('https://', '').split('/')[0].trim())
|
||||
.filter((h, i, l) => h && l.indexOf(h) === i);
|
||||
}
|
||||
|
||||
function save() {
|
||||
let custom = {};
|
||||
const c = document.getElementById('custom').value;
|
||||
try {
|
||||
custom = JSON.parse(c);
|
||||
}
|
||||
catch (e) {
|
||||
window.setTimeout(() => {
|
||||
notify('Custom JSON error: ' + e.message, 5000);
|
||||
alert('Custom JSON error: ' + e.message);
|
||||
document.getElementById('custom').value = c;
|
||||
}, 1000);
|
||||
}
|
||||
|
||||
let parser = {};
|
||||
const p = document.getElementById('parser').value;
|
||||
try {
|
||||
parser = JSON.parse(p);
|
||||
}
|
||||
catch (e) {
|
||||
window.setTimeout(() => {
|
||||
notify('Parser JSON error: ' + e.message, 5000);
|
||||
alert('Parser JSON error: ' + e.message);
|
||||
document.getElementById('parser').value = p;
|
||||
}, 1000);
|
||||
}
|
||||
|
||||
let siblings = {};
|
||||
const s = document.getElementById('siblings').value;
|
||||
try {
|
||||
siblings = JSON.parse(s);
|
||||
siblings = siblings.reduce((p, c, i) => {
|
||||
c.forEach(hostname => p[hostname] = i);
|
||||
return p;
|
||||
}, {});
|
||||
}
|
||||
catch (e) {
|
||||
window.setTimeout(() => {
|
||||
notify('Sibling JSON error: ' + e.message, 5000);
|
||||
alert('Sibling JSON error: ' + e.message);
|
||||
document.getElementById('siblings').value = s;
|
||||
}, 1000);
|
||||
}
|
||||
|
||||
chrome.storage.local.set({
|
||||
exactMatch: document.getElementById('exactMatch').checked,
|
||||
faqs: document.getElementById('faqs').checked,
|
||||
log: document.getElementById('log').checked,
|
||||
cache: document.getElementById('cache').checked,
|
||||
blacklist: prepare(document.getElementById('blacklist').value),
|
||||
whitelist: prepare(document.getElementById('whitelist').value),
|
||||
custom,
|
||||
parser,
|
||||
siblings,
|
||||
mode: document.querySelector('[name="mode"]:checked').value,
|
||||
protected: document.getElementById('protected').value.split(/\s*,\s*/).filter(s => s.length > 4)
|
||||
}, () => {
|
||||
restore();
|
||||
notify(chrome.i18n.getMessage('optionsSaved'));
|
||||
|
||||
chrome.contextMenus.update(document.querySelector('[name="mode"]:checked').value, {
|
||||
checked: true
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
function restore() {
|
||||
chrome.storage.local.get({
|
||||
exactMatch: false,
|
||||
faqs: true,
|
||||
log: false,
|
||||
cache: true,
|
||||
mode: 'blacklist',
|
||||
whitelist: [],
|
||||
blacklist: [],
|
||||
custom: {},
|
||||
parser: {},
|
||||
siblings: {},
|
||||
protected: ['google.com/recaptcha', 'gstatic.com/recaptcha']
|
||||
}, prefs => {
|
||||
document.getElementById('exactMatch').checked = prefs.exactMatch;
|
||||
document.getElementById('faqs').checked = prefs.faqs;
|
||||
document.getElementById('log').checked = prefs.log;
|
||||
document.getElementById('cache').checked = prefs.cache;
|
||||
document.querySelector(`[name="mode"][value="${prefs.mode}"`).checked = true;
|
||||
document.getElementById('blacklist').value = prefs.blacklist.join(', ');
|
||||
document.getElementById('whitelist').value = prefs.whitelist.join(', ');
|
||||
document.getElementById('custom').value = JSON.stringify(prefs.custom, null, 2);
|
||||
document.getElementById('parser').value = JSON.stringify(prefs.parser, null, 2);
|
||||
document.getElementById('siblings').value = JSON.stringify(Object.entries(prefs.siblings).reduce((p, [hostname, index]) => {
|
||||
p[index] = p[index] || [];
|
||||
p[index].push(hostname);
|
||||
return p;
|
||||
}, []), null, 2);
|
||||
document.getElementById('protected').value = prefs.protected.join(', ');
|
||||
});
|
||||
}
|
||||
document.addEventListener('DOMContentLoaded', restore);
|
||||
document.getElementById('save').addEventListener('click', save);
|
||||
|
||||
document.getElementById('sample').addEventListener('click', e => {
|
||||
e.preventDefault();
|
||||
|
||||
document.getElementById('custom').value = JSON.stringify({
|
||||
'www.google.com': 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_13_1) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/63.0.3239.84 Safari/537.36',
|
||||
'www.bing.com, www.yahoo.com, www.wikipedia.org': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:57.0) Gecko/20100101 Firefox/57.0',
|
||||
'www.example.com': ['random-useragent-1', 'random-user-agent-2'],
|
||||
'*': 'useragent-for-all-hostnames'
|
||||
}, null, 2);
|
||||
});
|
||||
|
||||
document.getElementById('sample-2').addEventListener('click', e => {
|
||||
e.preventDefault();
|
||||
|
||||
document.getElementById('parser').value = JSON.stringify({
|
||||
'my-custom-useragent': {
|
||||
'appVersion': 'custom app version',
|
||||
'platform': 'custom platform',
|
||||
'vendor': '[delete]',
|
||||
'product': 'custom product',
|
||||
'oscpu': 'custom oscpu',
|
||||
'custom-variable': 'this is a custom variable'
|
||||
}
|
||||
}, null, 2);
|
||||
});
|
||||
|
||||
document.getElementById('sample-3').addEventListener('click', e => {
|
||||
e.preventDefault();
|
||||
|
||||
document.getElementById('siblings').value = JSON.stringify([[
|
||||
'www.google.com', 'www.youtube.com', 'www.youtube.be'
|
||||
], [
|
||||
'www.gmx.com', 'www.mail.com'
|
||||
]], null, 2);
|
||||
});
|
||||
|
||||
document.getElementById('donate').addEventListener('click', () => {
|
||||
chrome.tabs.create({
|
||||
url: chrome.runtime.getManifest().homepage_url + '?rd=donate'
|
||||
});
|
||||
});
|
||||
|
||||
document.getElementById('reset').addEventListener('click', e => {
|
||||
if (e.detail === 1) {
|
||||
notify(chrome.i18n.getMessage('dbReset'));
|
||||
}
|
||||
else {
|
||||
localStorage.clear();
|
||||
chrome.storage.local.clear(() => {
|
||||
chrome.runtime.reload();
|
||||
window.close();
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
document.getElementById('help').addEventListener('click', () => {
|
||||
chrome.tabs.create({
|
||||
url: chrome.runtime.getManifest().homepage_url
|
||||
});
|
||||
});
|
||||
|
||||
// export
|
||||
document.getElementById('export').addEventListener('click', e => {
|
||||
const guid = 'xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx'.replace(/[xy]/g, c => {
|
||||
const r = Math.random() * 16 | 0;
|
||||
const v = c == 'x' ? r : (r & 0x3 | 0x8);
|
||||
return v.toString(16);
|
||||
});
|
||||
|
||||
chrome.storage.local.get(null, prefs => {
|
||||
for (const key of Object.keys(prefs)) {
|
||||
if (key && key.startsWith('cache.')) {
|
||||
delete prefs[key];
|
||||
}
|
||||
}
|
||||
const text = JSON.stringify(Object.assign({}, prefs, {
|
||||
'json-guid': guid,
|
||||
'json-forced': false
|
||||
}), null, e.shiftKey ? '' : ' ');
|
||||
const blob = new Blob([text], {type: 'application/json'});
|
||||
const objectURL = URL.createObjectURL(blob);
|
||||
Object.assign(document.createElement('a'), {
|
||||
href: objectURL,
|
||||
type: 'application/json',
|
||||
download: 'useragent-switcher-preferences.json'
|
||||
}).dispatchEvent(new MouseEvent('click'));
|
||||
setTimeout(() => URL.revokeObjectURL(objectURL));
|
||||
});
|
||||
});
|
||||
// import
|
||||
document.getElementById('import').addEventListener('click', () => {
|
||||
const input = document.createElement('input');
|
||||
input.style.display = 'none';
|
||||
input.type = 'file';
|
||||
input.accept = '.json';
|
||||
input.acceptCharset = 'utf-8';
|
||||
|
||||
document.body.appendChild(input);
|
||||
input.initialValue = input.value;
|
||||
input.onchange = readFile;
|
||||
input.click();
|
||||
|
||||
function readFile() {
|
||||
if (input.value !== input.initialValue) {
|
||||
const file = input.files[0];
|
||||
if (file.size > 100e6) {
|
||||
console.warn('100MB backup? I don\'t believe you.');
|
||||
return;
|
||||
}
|
||||
const reader = new FileReader();
|
||||
reader.onloadend = event => {
|
||||
input.remove();
|
||||
const json = JSON.parse(event.target.result);
|
||||
chrome.storage.local.clear(() => {
|
||||
chrome.storage.local.set(json, () => {
|
||||
window.close();
|
||||
chrome.runtime.reload();
|
||||
});
|
||||
});
|
||||
};
|
||||
reader.readAsText(file, 'utf-8');
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
/* toggle */
|
||||
document.getElementById('toggle-blacklist-desc').addEventListener('click', () => {
|
||||
document.querySelector('[for="toggle-blacklist-desc"]').classList.toggle('hidden');
|
||||
});
|
||||
document.getElementById('toggle-whitelist-desc').addEventListener('click', () => {
|
||||
document.querySelector('[for="toggle-whitelist-desc"]').classList.toggle('hidden');
|
||||
});
|
||||
document.getElementById('toggle-custom-desc').addEventListener('click', () => {
|
||||
document.querySelector('[for="toggle-custom-desc"]').classList.toggle('hidden');
|
||||
});
|
||||
document.getElementById('toggle-protected-desc').addEventListener('click', () => {
|
||||
document.querySelector('[for="toggle-protected-desc"]').classList.toggle('hidden');
|
||||
});
|
||||
document.getElementById('toggle-parser-desc').addEventListener('click', () => {
|
||||
document.querySelector('[for="toggle-parser-desc"]').classList.toggle('hidden');
|
||||
});
|
||||
document.getElementById('toggle-sibling-desc').addEventListener('click', () => {
|
||||
document.querySelector('[for="toggle-sibling-desc"]').classList.toggle('hidden');
|
||||
});
|
Loading…
Add table
Add a link
Reference in a new issue