mirror of
https://github.com/anticensority/runet-censorship-bypass.git
synced 2024-11-27 20:03:45 +03:00
Remodulize, refactor, start adding exceptions
This commit is contained in:
parent
170b101a48
commit
ff5c51af11
|
@ -2,7 +2,7 @@
|
|||
|
||||
{
|
||||
|
||||
const IF_DEBUG = false;
|
||||
const IF_DEBUG = true;
|
||||
|
||||
if (!IF_DEBUG) {
|
||||
// I believe logging objects precludes them from being GCed.
|
||||
|
|
|
@ -0,0 +1,57 @@
|
|||
'use strict';
|
||||
/*
|
||||
* Error Library
|
||||
* PURPOSE 1:
|
||||
* Allow wrapping errors with clarifications when they bubble up.
|
||||
* Why:
|
||||
* Sometimes low level errors may bubble up through a chain of callbacks.
|
||||
* And when they reach top level of a user they loose context and convey
|
||||
* nonsense like "Error 404: Can't find file".
|
||||
* -- What file? WTH, I just hit update button?!
|
||||
* PURPOSE 2:
|
||||
* Supply separate class for warnings.
|
||||
* Why:
|
||||
* Some callbacks expect warnings which are like non-fatal errors.
|
||||
* I want Warnings and FooError to be distinctable by code readers,
|
||||
* so I create separate class for warnings.
|
||||
**/
|
||||
{
|
||||
|
||||
const mandatory = window.utils.mandatory;
|
||||
|
||||
const self = window.apis.errorsLib = {
|
||||
|
||||
// I don't use Error class, because we don't need stack here.
|
||||
Warning: class {
|
||||
|
||||
constructor(message = mandatory()) {
|
||||
|
||||
this.message = message;
|
||||
|
||||
}
|
||||
|
||||
},
|
||||
|
||||
clarify: function(err, message = mandatory(), {data} = {}) {
|
||||
|
||||
if (!err) {
|
||||
return err;
|
||||
}
|
||||
const warn = new self.Warning(message);
|
||||
warn.wrapped = err;
|
||||
if (data) {
|
||||
warn.data = data;
|
||||
}
|
||||
return warn;
|
||||
|
||||
},
|
||||
|
||||
clarifyThen: function(message, cb = mandatory()) {
|
||||
|
||||
return (err, ...args) => cb( clarify(err, message), ...args );
|
||||
|
||||
},
|
||||
|
||||
};
|
||||
|
||||
}
|
|
@ -0,0 +1,69 @@
|
|||
'use strict';
|
||||
|
||||
{
|
||||
|
||||
const mandatory = window.utils.mandatory;
|
||||
const errorsLib = window.apis.errorsLib;
|
||||
|
||||
const checkCon = 'Что-то не так с сетью, проверьте соединение.';
|
||||
|
||||
window.apis.httpLib = {
|
||||
|
||||
ifModifiedSince(
|
||||
url,
|
||||
lastModified,
|
||||
cb = mandatory()
|
||||
) {
|
||||
|
||||
const wasModified = new Date(0).toUTCString();
|
||||
const notModifiedCode = 304;
|
||||
fetch(url, {
|
||||
method: 'HEAD',
|
||||
headers: new Headers({
|
||||
'If-Modified-Since': lastModified,
|
||||
}),
|
||||
}).then(
|
||||
(res) => {
|
||||
cb(
|
||||
null,
|
||||
res.status === notModifiedCode ?
|
||||
false :
|
||||
(res.headers.get('Last-Modified') || wasModified)
|
||||
);
|
||||
},
|
||||
errorsLib.clarifyThen(checkCon, (err) => cb(err, wasModifed))
|
||||
);
|
||||
|
||||
},
|
||||
|
||||
get(url, cb = mandatory()) {
|
||||
|
||||
const start = Date.now();
|
||||
fetch(url, {cache: 'no-store'}).then(
|
||||
(res) => {
|
||||
|
||||
const textCb =
|
||||
(err) => res.text().then( (text) => cb(err, text), cb );
|
||||
|
||||
const status = res.status;
|
||||
if ( !( status >= 200 && status < 300 || status === 304 ) ) {
|
||||
return textCb(
|
||||
errorsLib.clarify(
|
||||
res,
|
||||
'Получен ответ с неудачным HTTP-кодом ' + status + '.'
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
console.log('GETed with success:', url, Date.now() - start);
|
||||
textCb();
|
||||
|
||||
},
|
||||
errorsLib.clarifyThen(checkCon, cb)
|
||||
);
|
||||
|
||||
},
|
||||
|
||||
};
|
||||
|
||||
}
|
|
@ -0,0 +1,300 @@
|
|||
'use strict';
|
||||
|
||||
{
|
||||
|
||||
const mandatory = window.utils.mandatory;
|
||||
const httpLib = window.apis.httpLib;
|
||||
const clarify = window.apis.errorsLib.clarify;
|
||||
|
||||
// IP REGEX starts.
|
||||
|
||||
const portOpt = '(:\\d+)?'; // The only capturing group, sic!
|
||||
const ipv4portOpt = '(?:[0-9]{1,3}\\.){3}[0-9]{1,3}' + portOpt;
|
||||
const ipv6nake = '(?:[0-9a-f]{0,4}:){2,7}[0-9a-f]{0,4}';
|
||||
const ipv6portOpt = '(?:' + ipv6nake + '|' + '\\[' + ipv6nake + '\\]' + portOpt + ')';
|
||||
|
||||
const ipv4Re = new RegExp('^' + ipv4portOpt + '$');
|
||||
const ipv6Re = new RegExp('^' + ipv6portOpt + '$');
|
||||
|
||||
const _match = function _match(ipRe, str) {
|
||||
|
||||
let m = (str.match(ipRe) || []).filter( (c) => c );
|
||||
const port = m.length > 1 ? m.pop() : false;
|
||||
return { ifMatched: m.length, port: port };
|
||||
|
||||
};
|
||||
|
||||
const _test = {
|
||||
|
||||
ipv4: _match.bind(null, ipv4Re),
|
||||
ipv6: _match.bind(null, ipv6Re),
|
||||
ipv4v6: function(str) {
|
||||
|
||||
let mr = this.ipv4(str);
|
||||
if (mr.ifMatched) {
|
||||
mr.ifv4 = true;
|
||||
mr.canonical = str.replace(mr.port, '');
|
||||
return mr;
|
||||
}
|
||||
mr = this.ipv6(str);
|
||||
if (mr.ifMatched) {
|
||||
mr.ifv6 = true;
|
||||
mr.canonical = str.replace(mr.port, '').replace(/[\[\]]/g, '');
|
||||
return mr;
|
||||
}
|
||||
return mr;
|
||||
|
||||
},
|
||||
|
||||
};
|
||||
|
||||
// IP REGEX ends.
|
||||
|
||||
const _state = window.utils.createStorage('ip-to-host');
|
||||
const ip2host = '';
|
||||
|
||||
const privates = {};
|
||||
|
||||
const _getHostObj = function _getHostObj(hostStr) {
|
||||
|
||||
let hostObj = privates._strToHostObj[hostStr];
|
||||
if (!hostObj) {
|
||||
hostObj = privates._strToHostObj[hostStr] = { host: hostStr };
|
||||
}
|
||||
return hostObj;
|
||||
|
||||
};
|
||||
|
||||
const init = function init() {
|
||||
|
||||
// Defaults.
|
||||
const _antizapret = {
|
||||
/* Don't use directly, please.
|
||||
Encoded to counter abuse. */
|
||||
host: '\x70\x72\x6f\x78\x79\x2e\x61\x6e\x74\x69\x7a\x61\x70\x72\x65\x74\x2e\x70\x72\x6f\x73\x74\x6f\x76\x70\x6e\x2e\x6f\x72\x67',
|
||||
};
|
||||
privates._strToHostObj = {
|
||||
[_antizapret.host]: _antizapret,
|
||||
};
|
||||
|
||||
privates._ipToHostObj = {};
|
||||
for( const ip of [
|
||||
// IPs of Antizapret.
|
||||
'195.123.209.38',
|
||||
'137.74.171.91',
|
||||
'51.15.39.201',
|
||||
'2001:bc8:4700:2300::1:d07',
|
||||
'2a02:27ac::10',
|
||||
] ) {
|
||||
privates._ipToHostObj[ip] = _antizapret;
|
||||
}
|
||||
|
||||
// Persisted.
|
||||
const ipToHost = _state(ip2host);
|
||||
if (ipToHost) {
|
||||
for( const ip of Object.keys(ipToHost) ) {
|
||||
const host = ipToHost[ip];
|
||||
privates._ipToHostObj[ip] = _getHostObj(host);
|
||||
}
|
||||
}
|
||||
|
||||
};
|
||||
|
||||
init();
|
||||
|
||||
const getIpsFor = function getIpsFor(host, cb = mandatory()) {
|
||||
|
||||
if (host.trim() === 'localhost') {
|
||||
return cb(null, ['127.0.0.1', '::1']);
|
||||
}
|
||||
const types = [1, 28];
|
||||
const promises = types.map(
|
||||
(type) => new Promise((resolve) =>
|
||||
httpLib.get(
|
||||
'https://dns.google.com/resolve?type=' + type + '&name=' + host,
|
||||
(err, res) => {
|
||||
|
||||
if (res) {
|
||||
try {
|
||||
res = JSON.parse(res);
|
||||
console.log('Json parsed.');
|
||||
if (err || res.Status) {
|
||||
const msg = ['Answer', 'Comment', 'Status']
|
||||
.filter( (prop) => res[prop] )
|
||||
.map( (prop) => prop + ': ' + JSON.stringify( res[prop] ) )
|
||||
.join(', \n');
|
||||
err = clarify(err || {}, 'Сервер (json): ' + msg, {data: res});
|
||||
} else {
|
||||
res = res.Answer || [];
|
||||
res = res.filter(
|
||||
(record) => types.includes(record.type)
|
||||
).map( (ans) => ans.data );
|
||||
}
|
||||
} catch(e) {
|
||||
err = clarify(
|
||||
e,
|
||||
'Сервер (текст): ' + res, err ? {data: err} : undefined
|
||||
);
|
||||
}
|
||||
}
|
||||
resolve([err, res]);
|
||||
|
||||
}
|
||||
)
|
||||
)
|
||||
);
|
||||
Promise.all(promises).then(
|
||||
([[v4err, v4res], [v6err, v6res]]) => {
|
||||
|
||||
if(v4err) {
|
||||
return cb(v4err, v4res);
|
||||
}
|
||||
const ips = v4res;
|
||||
let warns = [];
|
||||
if (!v6err) {
|
||||
ips.push(...v6res);
|
||||
} else {
|
||||
warns = [v6err];
|
||||
}
|
||||
cb(null, ips, ...warns);
|
||||
|
||||
}
|
||||
);
|
||||
|
||||
};
|
||||
|
||||
const _canonize = function canonize(addrArr) {
|
||||
|
||||
const ipSet = new Set();
|
||||
const hostSet = new Set();
|
||||
|
||||
for( const addr of addrArr ) {
|
||||
|
||||
const ipm = _test.ipv4v6(addr);
|
||||
if (ipm.ifMatched) {
|
||||
ipSet.add( ipm.canonical );
|
||||
} else {
|
||||
hostSet.add( addr.replace(/:\d+$/, '') );
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
console.log('Canonized hosts/ips:', hostSet.size + '/' + ipSet.size);
|
||||
return [ipSet, hostSet];
|
||||
|
||||
}
|
||||
|
||||
const self = window.apis.ipToHost = {
|
||||
|
||||
persistVoid() {
|
||||
|
||||
console.log('Persisting ipToHost...', privates);
|
||||
const ipToHost = {};
|
||||
for( const ip of Object.keys(privates._ipToHostObj) ) {
|
||||
ipToHost[ ip ] = privates._ipToHostObj[ ip ].host;
|
||||
}
|
||||
_state(ip2host, ipToHost);
|
||||
|
||||
},
|
||||
|
||||
resetToDefaultsVoid() {
|
||||
|
||||
_state(ip2host, null);
|
||||
init();
|
||||
|
||||
},
|
||||
|
||||
_addAsync(hostStr, cb = mandatory()) {
|
||||
|
||||
getIpsFor(hostStr, (err, ips, ...warns) => {
|
||||
|
||||
console.log('IPS', ips);
|
||||
if (!err) {
|
||||
// Object may be shared, string can't.
|
||||
const hostObj = _getHostObj(hostStr);
|
||||
for(const ip of ips) {
|
||||
privates._ipToHostObj[ip] = hostObj;
|
||||
}
|
||||
}
|
||||
return cb(err, null, ...warns);
|
||||
|
||||
});
|
||||
|
||||
},
|
||||
|
||||
_replaceAllAsync(hostArr = mandatory(), cb) {
|
||||
|
||||
if (typeof(hostArr) === 'function') {
|
||||
cb = hostArr;
|
||||
hostArr = Object.keys(privates._strToHostObj);
|
||||
}
|
||||
|
||||
this.resetToDefaultsVoid();
|
||||
|
||||
const promises = hostArr.map(
|
||||
(hostStr) => new Promise( (resolve) => this._addAsync(hostStr, (...args) => resolve(args) ) )
|
||||
);
|
||||
Promise.all( promises ).then( (cbsRes) => {
|
||||
|
||||
const errors = cbsRes.map( ([err]) => err ).filter( (err) => err );
|
||||
let newError;
|
||||
const ifAllErrors = cbsRes.length === errors.length;
|
||||
if (errors.length) {
|
||||
if (ifAllErrors) {
|
||||
newError = errors.shift();
|
||||
} else {
|
||||
newError = errors;
|
||||
}
|
||||
newError = clarify(
|
||||
newError,
|
||||
'Не удалось получить один или несколько IP адресов для' +
|
||||
' прокси-серверов. Иконка для уведомления об обходе' +
|
||||
' блокировок может не отображаться.'
|
||||
);
|
||||
if (ifAllErrors) {
|
||||
return cb(newError);
|
||||
}
|
||||
}
|
||||
cb(null, null, newError);
|
||||
|
||||
});
|
||||
|
||||
},
|
||||
|
||||
replaceAllAsync(addrArr, cb = mandatory()) {
|
||||
|
||||
console.log('Replacing...');
|
||||
const [ipSet, hostSet] = _canonize(addrArr);
|
||||
for( const ip of ipSet ) {
|
||||
const host = _getHostObj(ip);
|
||||
privates._ipToHostObj[ip] = host;
|
||||
}
|
||||
|
||||
const hostArr = Array.from(hostSet);
|
||||
this._replaceAllAsync(hostArr, (allErr, ...args) => {
|
||||
|
||||
if (!allErr) {
|
||||
this.persistVoid();
|
||||
}
|
||||
cb(allErr, ...args);
|
||||
|
||||
});
|
||||
|
||||
},
|
||||
|
||||
updateAllAsync(cb = mandatory()) {
|
||||
|
||||
this._replaceAllAsync(cb);
|
||||
|
||||
},
|
||||
|
||||
get(ip) {
|
||||
|
||||
const tmp = privates._ipToHostObj[ip];
|
||||
return tmp && tmp.host;
|
||||
|
||||
},
|
||||
|
||||
};
|
||||
|
||||
}
|
|
@ -99,7 +99,9 @@
|
|||
if (this.customProxyStringRaw) {
|
||||
customProxyArray = this.customProxyStringRaw
|
||||
.replace(/#.*$/mg, '') // Strip comments.
|
||||
.split( /(?:[^\S\r\n]*(?:;|\r?\n)+[^\S\r\n]*)+/g ).filter( (p) => p.trim() );
|
||||
.split( /(?:[^\S\r\n]*(?:;|\r?\n)+[^\S\r\n]*)+/g )
|
||||
.map( (p) => p.trim() )
|
||||
.filter( (p) => p && /\s+/g.test(p) );
|
||||
if (this.ifUseSecureProxiesOnly) {
|
||||
customProxyArray = customProxyArray.filter( (p) => !p.startsWith('HTTP ') );
|
||||
}
|
||||
|
@ -218,7 +220,7 @@
|
|||
}
|
||||
|
||||
kitchenState(ifIncontinence, true);
|
||||
cb(new TypeError(
|
||||
cb(null, null, new TypeError(
|
||||
'Не найдено активного PAC-скрипта! Изменения будут применены при возвращении контроля настроек прокси или установке нового PAC-скрипта.'
|
||||
));
|
||||
|
||||
|
@ -226,7 +228,7 @@
|
|||
|
||||
},
|
||||
|
||||
checkIncontinence(details) {
|
||||
checkIncontinenceVoid(details) {
|
||||
|
||||
if ( kitchenState(ifIncontinence) ) {
|
||||
this._tryNowAsync(details, () => {/* Swallow. */});
|
||||
|
@ -237,12 +239,10 @@
|
|||
|
||||
keepCookedNowAsync(pacMods = mandatory(), cb = throwIfError) {
|
||||
|
||||
console.log('Keep cooked now...');
|
||||
if (typeof(pacMods) === 'function') {
|
||||
cb = pacMods;
|
||||
const pacMods = getCurrentConfigs();
|
||||
if (!pacMods) {
|
||||
return cb(TypeError('PAC mods were never initialized and you haven\'t supplied any.'));
|
||||
}
|
||||
pacMods = this.getCurrentConfigs();
|
||||
} else {
|
||||
try {
|
||||
pacMods = new PacModifiers(pacMods);
|
||||
|
@ -251,7 +251,27 @@
|
|||
}
|
||||
kitchenState('mods', pacMods);
|
||||
}
|
||||
this._tryNowAsync( (err) => cb(null, null, err && [err]) );
|
||||
this._tryNowAsync(
|
||||
(err, res, ...warns) => {
|
||||
|
||||
console.log('Try now cb...', err);
|
||||
if (err) {
|
||||
return cb(err, res, ...warns);
|
||||
}
|
||||
|
||||
const par = pacMods.customProxyArray;
|
||||
if (!(par && par.length)) {
|
||||
return cb(null, res, ...warns);
|
||||
}
|
||||
|
||||
const hosts = par.map( (ps) => ps.split(/\s+/)[1] )
|
||||
window.apis.ipToHost.replaceAllAsync(
|
||||
hosts,
|
||||
(...args) => cb(...args, ...warns)
|
||||
);
|
||||
|
||||
}
|
||||
);
|
||||
|
||||
},
|
||||
|
||||
|
@ -288,7 +308,7 @@
|
|||
|
||||
};
|
||||
|
||||
pacKitchen.checkIncontinence();
|
||||
chrome.proxy.settings.onChange.addListener( pacKitchen.checkIncontinence.bind(pacKitchen) );
|
||||
pacKitchen.checkIncontinenceVoid();
|
||||
chrome.proxy.settings.onChange.addListener( pacKitchen.checkIncontinenceVoid.bind(pacKitchen) );
|
||||
|
||||
} // Private namespace ends.
|
|
@ -26,6 +26,12 @@
|
|||
const chromified = window.utils.chromified;
|
||||
const checkChromeError = window.utils.checkChromeError;
|
||||
|
||||
const clarify = window.apis.errorsLib.clarify;
|
||||
const clarifyThen = window.apis.errorsLib.clarifyThen;
|
||||
const Warning = window.apis.errorsLib.Warning;
|
||||
|
||||
const httpLib = window.apis.httpLib;
|
||||
|
||||
const asyncLogGroup = function asyncLogGroup(...args) {
|
||||
|
||||
const cb = args.pop();
|
||||
|
@ -43,43 +49,6 @@
|
|||
|
||||
};
|
||||
|
||||
class Clarification {
|
||||
|
||||
constructor(message = mandatory(), prevClarification) {
|
||||
|
||||
this.message = message;
|
||||
if (prevClarification) {
|
||||
this.prev = prevClarification;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
};
|
||||
|
||||
const clarify = function clarify(
|
||||
err = mandatory(),
|
||||
message = mandatory(),
|
||||
{data} = {}) {
|
||||
|
||||
err.clarification = new Clarification(message, err.clarification);
|
||||
if (data) {
|
||||
err.clarification.data = data;
|
||||
}
|
||||
return err;
|
||||
|
||||
};
|
||||
|
||||
class Warning {
|
||||
|
||||
constructor(message) {
|
||||
|
||||
clarify(this, message);
|
||||
|
||||
}
|
||||
|
||||
};
|
||||
|
||||
|
||||
const setPacAsync = function setPacAsync(
|
||||
pacData = mandatory(), cb = throwIfError
|
||||
) {
|
||||
|
@ -104,7 +73,7 @@
|
|||
|
||||
console.warn('Failed, other extension is in control.');
|
||||
return cb(
|
||||
new Warning( window.utils.messages.whichExtensionHtml() )
|
||||
new Error( window.utils.messages.whichExtensionHtml() )
|
||||
);
|
||||
|
||||
}
|
||||
|
@ -116,181 +85,20 @@
|
|||
|
||||
};
|
||||
|
||||
const clarifyErrorThen = function clarifyFetchErrorThen(message, cb) {
|
||||
|
||||
return (err, ...args) => cb( clarify(err || {}, message), ...args );
|
||||
|
||||
};
|
||||
|
||||
const clarifyFetchErrorThen = (cb) =>
|
||||
clarifyErrorThen('Что-то не так с сетью, проверьте соединение.', cb);
|
||||
|
||||
const ifModifiedSince = function ifModifiedSince(
|
||||
url = mandatory(),
|
||||
lastModified = mandatory(),
|
||||
cb = mandatory()
|
||||
) {
|
||||
|
||||
const wasModified = new Date(0).toUTCString();
|
||||
const notModifiedCode = 304;
|
||||
fetch(url, {
|
||||
method: 'HEAD',
|
||||
headers: new Headers({
|
||||
'If-Modified-Since': lastModified,
|
||||
}),
|
||||
}).then(
|
||||
(res) => {
|
||||
cb(
|
||||
null,
|
||||
res.status === notModifiedCode ?
|
||||
false :
|
||||
(res.headers.get('Last-Modified') || wasModified)
|
||||
);
|
||||
},
|
||||
clarifyFetchErrorThen((err) => cb(err, wasModified))
|
||||
);
|
||||
|
||||
};
|
||||
|
||||
const httpGet = function httpGet(url, cb = mandatory()) {
|
||||
|
||||
const start = Date.now();
|
||||
fetch(url, {cache: 'no-store'}).then(
|
||||
(res) => {
|
||||
|
||||
const textCb =
|
||||
(err) => res.text().then( (text) => cb(err, text), cb );
|
||||
|
||||
const status = res.status;
|
||||
if ( !( status >= 200 && status < 300 || status === 304 ) ) {
|
||||
return textCb(
|
||||
clarify(
|
||||
res,
|
||||
'Получен ответ с неудачным HTTP-кодом ' + status + '.'
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
console.log('GETed with success:', url, Date.now() - start);
|
||||
textCb();
|
||||
|
||||
},
|
||||
clarifyFetchErrorThen(cb)
|
||||
);
|
||||
|
||||
};
|
||||
|
||||
const getIpsFor = function getIpsFor(host = mandatory(), cb = mandatory()) {
|
||||
|
||||
const types = [1, 28];
|
||||
const promises = types.map(
|
||||
(type) => new Promise((resolve) =>
|
||||
httpGet(
|
||||
'https://dns.google.com/resolve?type=' + type + '&name=' + host,
|
||||
(err, res) => {
|
||||
|
||||
if (res) {
|
||||
try {
|
||||
res = JSON.parse(res);
|
||||
console.log('Json parsed.');
|
||||
if (err || res.Status) {
|
||||
const msg = ['Answer', 'Comment', 'Status']
|
||||
.filter( (prop) => res[prop] )
|
||||
.map( (prop) => prop + ': ' + JSON.stringify( res[prop] ) )
|
||||
.join(', \n');
|
||||
clarify(err, 'Сервер (json): ' + msg, {data: res});
|
||||
} else {
|
||||
res = res.Answer || [];
|
||||
res = res.filter(
|
||||
(record) => types.includes(record.type)
|
||||
);
|
||||
}
|
||||
} catch(e) {
|
||||
err = clarify(
|
||||
e,
|
||||
'Сервер (текст): ' + res, err ? {data: err} : null
|
||||
);
|
||||
}
|
||||
}
|
||||
resolve([err, res]);
|
||||
|
||||
}
|
||||
)
|
||||
)
|
||||
);
|
||||
Promise.all(promises).then(
|
||||
([[v4err, v4res], [v6err, v6res]]) => {
|
||||
|
||||
if(v4err) {
|
||||
return cb(v4err, v4res);
|
||||
}
|
||||
const ips = v4res;
|
||||
let warns = [];
|
||||
if (!v6err) {
|
||||
ips.push(...v6res);
|
||||
} else {
|
||||
warns = [v6err];
|
||||
}
|
||||
cb(null, ips, ...warns);
|
||||
|
||||
}
|
||||
);
|
||||
|
||||
};
|
||||
|
||||
const updatePacProxyIps = function updatePacProxyIps(
|
||||
provider,
|
||||
cb = throwIfError
|
||||
) {
|
||||
|
||||
cb = asyncLogGroup(
|
||||
'Getting IP for '+ provider.proxyHosts.join(', ') + '...',
|
||||
'Getting IPs for PAC hosts...',
|
||||
cb
|
||||
);
|
||||
let failure = {
|
||||
errors: {},
|
||||
};
|
||||
let hostsProcessed = 0;
|
||||
provider.proxyHosts.forEach(
|
||||
(proxyHost) => getIpsFor(
|
||||
proxyHost,
|
||||
(err, ips, ...warns) => {
|
||||
|
||||
if (!err) {
|
||||
provider.proxyIps = provider.proxyIps || {};
|
||||
ips.forEach(
|
||||
(ip) => provider.proxyIps[ip] = proxyHost
|
||||
);
|
||||
}
|
||||
if (err || warns.length) {
|
||||
failure.errors[proxyHost] = err || warns;
|
||||
}
|
||||
|
||||
if ( ++hostsProcessed < provider.proxyHosts.length ) {
|
||||
return;
|
||||
}
|
||||
// All hosts were processed.
|
||||
const errorsCount = Object.keys(failure.errors).length;
|
||||
if (!errorsCount) {
|
||||
return cb();
|
||||
}
|
||||
clarify(
|
||||
failure,
|
||||
'Не удалось получить один или несколько IP адресов для' +
|
||||
' прокси-серверов. Иконка для уведомления об обходе' +
|
||||
' блокировок может не отображаться.'
|
||||
);
|
||||
errorsCount === hostsProcessed
|
||||
? cb(failure)
|
||||
: cb(null, null, failure);
|
||||
}
|
||||
)
|
||||
);
|
||||
window.apis.ipToHost.updateAllAsync(cb);
|
||||
|
||||
};
|
||||
|
||||
const setPacScriptFromProviderAsync = function setPacScriptFromProviderAsync(
|
||||
provider = mandatory(), lastModified = mandatory(), cb = throwIfError
|
||||
provider, lastModified = mandatory(), cb = throwIfError
|
||||
) {
|
||||
|
||||
const pacUrl = provider.pacUrls[0];
|
||||
|
@ -299,7 +107,7 @@
|
|||
cb
|
||||
);
|
||||
|
||||
ifModifiedSince(pacUrl, lastModified, (err, newLastModified) => {
|
||||
httpLib.ifModifiedSince(pacUrl, lastModified, (err, newLastModified) => {
|
||||
|
||||
if (!newLastModified) {
|
||||
return cb(
|
||||
|
@ -318,7 +126,7 @@
|
|||
|
||||
pacDataPromise = pacDataPromise.catch(
|
||||
(err) => new Promise(
|
||||
(resolve, reject) => httpGet(
|
||||
(resolve, reject) => httpLib.get(
|
||||
url,
|
||||
(newErr, pacData) => newErr ? reject(newErr) : resolve(pacData)
|
||||
)
|
||||
|
@ -339,7 +147,7 @@
|
|||
);
|
||||
|
||||
},
|
||||
clarifyErrorThen(
|
||||
clarifyThen(
|
||||
'Не удалось скачать PAC-скрипт с адресов: [ '
|
||||
+ provider.pacUrls.join(' , ') + ' ].',
|
||||
cb
|
||||
|
@ -363,14 +171,6 @@
|
|||
' <br/> <a href="https://antizapret.prostovpn.org">Страница проекта</a>.',
|
||||
|
||||
pacUrls: ['https://antizapret.prostovpn.org/proxy.pac'],
|
||||
proxyHosts: ['proxy.antizapret.prostovpn.org'],
|
||||
proxyIps: {
|
||||
'195.123.209.38': 'proxy.antizapret.prostovpn.org',
|
||||
'137.74.171.91': 'proxy.antizapret.prostovpn.org',
|
||||
'51.15.39.201': 'proxy.antizapret.prostovpn.org',
|
||||
'2001:bc8:4700:2300::1:d07': 'proxy.antizapret.prostovpn.org',
|
||||
'2a02:27ac::10': 'proxy.antizapret.prostovpn.org',
|
||||
},
|
||||
},
|
||||
Антицензорити: {
|
||||
label: 'Антицензорити',
|
||||
|
@ -393,14 +193,6 @@
|
|||
'\x68\x74\x74\x70\x73\x3a\x2f\x2f\x72\x61\x77\x2e\x67\x69\x74\x68\x75\x62\x75\x73\x65\x72\x63\x6f\x6e\x74\x65\x6e\x74\x2e\x63\x6f\x6d\x2f\x61\x6e\x74\x69\x63\x65\x6e\x73\x6f\x72\x73\x68\x69\x70\x2d\x72\x75\x73\x73\x69\x61\x2f\x67\x65\x6e\x65\x72\x61\x74\x65\x64\x2d\x70\x61\x63\x2d\x73\x63\x72\x69\x70\x74\x73\x2f\x6d\x61\x73\x74\x65\x72\x2f\x61\x6e\x74\x69\x63\x65\x6e\x73\x6f\x72\x69\x74\x79\x2e\x70\x61\x63', // eslint-disable-line max-len
|
||||
// Google Drive (0.17):
|
||||
'\x68\x74\x74\x70\x73\x3a\x2f\x2f\x64\x72\x69\x76\x65\x2e\x67\x6f\x6f\x67\x6c\x65\x2e\x63\x6f\x6d\x2f\x75\x63\x3f\x65\x78\x70\x6f\x72\x74\x3d\x64\x6f\x77\x6e\x6c\x6f\x61\x64\x26\x69\x64\x3d\x30\x42\x2d\x5a\x43\x56\x53\x76\x75\x4e\x57\x66\x30\x54\x44\x46\x52\x4f\x47\x35\x46\x62\x55\x39\x4f\x64\x44\x67'], // eslint-disable-line max-len
|
||||
proxyHosts: ['proxy.antizapret.prostovpn.org'],
|
||||
proxyIps: {
|
||||
'195.123.209.38': 'proxy.antizapret.prostovpn.org',
|
||||
'137.74.171.91': 'proxy.antizapret.prostovpn.org',
|
||||
'51.15.39.201': 'proxy.antizapret.prostovpn.org',
|
||||
'2001:bc8:4700:2300::1:d07': 'proxy.antizapret.prostovpn.org',
|
||||
'2a02:27ac::10': 'proxy.antizapret.prostovpn.org',
|
||||
},
|
||||
},
|
||||
},
|
||||
|
||||
|
@ -422,22 +214,13 @@
|
|||
return new Date(0).toUTCString();
|
||||
|
||||
},
|
||||
|
||||
setLastModified(newValue = mandatory()) {
|
||||
|
||||
this._currentPacProviderLastModified = newValue;
|
||||
|
||||
},
|
||||
|
||||
isProxied(ip) {
|
||||
|
||||
// Executed on each request with ip. Make it as fast as possible.
|
||||
// Property lookups are cheap (I tested).
|
||||
return this._currentPacProviderKey
|
||||
&& this.pacProviders[this._currentPacProviderKey]
|
||||
.proxyIps.hasOwnProperty(ip);
|
||||
|
||||
},
|
||||
|
||||
mustBeKey(key = mandatory()) {
|
||||
|
||||
if ( !(key === null || this.pacProviders[key]) ) {
|
||||
|
@ -451,6 +234,7 @@
|
|||
return this._currentPacProviderKey;
|
||||
|
||||
},
|
||||
|
||||
setCurrentPacProviderKey(
|
||||
newKey = mandatory(),
|
||||
lastModified = new Date().toUTCString()
|
||||
|
@ -510,7 +294,7 @@
|
|||
|
||||
if (key === null) {
|
||||
// No pac provider set.
|
||||
return clarifyErrorThen('Сперва выберите PAC-провайдера.', cb);
|
||||
return clarifyThen('Сперва выберите PAC-провайдера.', cb);
|
||||
}
|
||||
|
||||
const pacProvider = this.getPacProvider(key);
|
||||
|
@ -536,7 +320,6 @@
|
|||
|
||||
const ipsErrorPromise = new Promise(
|
||||
(resolve, reject) => updatePacProxyIps(
|
||||
pacProvider,
|
||||
resolve
|
||||
)
|
||||
);
|
|
@ -43,17 +43,13 @@
|
|||
|
||||
chrome.tabs.onUpdated.addListener( onTabUpdate );
|
||||
|
||||
const antiCensorRu = window.apis.antiCensorRu;
|
||||
|
||||
const updateTitle = function updateTitle(requestDetails, cb) {
|
||||
const updateTitle = function updateTitle(requestDetails, proxyHost, cb) {
|
||||
|
||||
chrome.browserAction.getTitle(
|
||||
{tabId: requestDetails.tabId},
|
||||
(title) => {
|
||||
|
||||
const ifTitleSetAlready = /\n/.test(title);
|
||||
const proxyHost = antiCensorRu.getPacProvider()
|
||||
.proxyIps[requestDetails.ip];
|
||||
|
||||
const hostname = new URL( requestDetails.url ).hostname;
|
||||
|
||||
|
@ -131,8 +127,8 @@
|
|||
|
||||
const isProxiedAndInformed = function isProxiedAndInformed(requestDetails) {
|
||||
|
||||
if ( !(requestDetails.ip
|
||||
&& antiCensorRu.isProxied( requestDetails.ip )) ) {
|
||||
const host = window.apis.ipToHost.get( requestDetails.ip );
|
||||
if (!host) {
|
||||
return false;
|
||||
}
|
||||
|
||||
|
@ -141,7 +137,7 @@
|
|||
previousUpdateTitleFinished = previousUpdateTitleFinished.then(
|
||||
() => new Promise(
|
||||
(resolve) => {
|
||||
const cb = () => updateTitle( requestDetails, resolve );
|
||||
const cb = () => updateTitle( requestDetails, host, resolve );
|
||||
return ifMainFrame
|
||||
? afterTabUpdated(requestDetails.tabId, cb) : cb();
|
||||
}
|
||||
|
|
|
@ -25,9 +25,12 @@
|
|||
"background": {
|
||||
"scripts": [
|
||||
"00-init-apis.js",
|
||||
"11-api-error-handlers.js",
|
||||
"13-pac-kitchen.js",
|
||||
"15-api-sync-pac-script-with-pac-provider.js",
|
||||
"11-error-handlers-api.js",
|
||||
"12-errors-lib.js",
|
||||
"13-http-lib.js",
|
||||
"14-ip-to-host-api.js",
|
||||
"15-pac-kitchen-api.js",
|
||||
"17-sync-pac-script-with-pac-provider-api.js",
|
||||
"20-api-fixes.js",
|
||||
"30-block-informer.js",
|
||||
"40-context-menus.js"
|
||||
|
|
|
@ -6,14 +6,29 @@
|
|||
:root {
|
||||
--ribbon-color: #4169e1; /* #1a6cc8 */
|
||||
}
|
||||
div {
|
||||
div, section {
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
}
|
||||
header {
|
||||
margin: 0 0 0.4em;
|
||||
}
|
||||
|
||||
section header {
|
||||
display: block;
|
||||
border-bottom: 1px solid #558abb;
|
||||
border-left: 5px solid #1048ac;
|
||||
margin: 0;
|
||||
position: relative;
|
||||
height: 2em;
|
||||
background-color: DodgerBlue;
|
||||
color: white;
|
||||
}
|
||||
|
||||
label {
|
||||
user-select: none;
|
||||
}
|
||||
ul {
|
||||
ul, ol {
|
||||
list-style-type: none;
|
||||
padding: 0;
|
||||
margin: 0;
|
||||
|
@ -53,9 +68,6 @@
|
|||
#none:checked + label {
|
||||
color: red;
|
||||
}
|
||||
#configs-panel > header {
|
||||
margin: 0 0 0.4em;
|
||||
}
|
||||
.if-not-controlled {
|
||||
display: none;
|
||||
color: red;
|
||||
|
@ -147,6 +159,39 @@
|
|||
margin-left: 1em;
|
||||
text-align: right;
|
||||
}
|
||||
|
||||
select#exceptions-select {
|
||||
color: black;
|
||||
background: transparent;
|
||||
border-radius: 0;
|
||||
box-shadow: none;
|
||||
text-shadow: none;
|
||||
padding: 0;
|
||||
}
|
||||
#flex-right {
|
||||
flex-grow: 99;
|
||||
}
|
||||
#flex-right > * {
|
||||
width: 100%;
|
||||
}
|
||||
#except-editor {
|
||||
border-radius: 0 !important;
|
||||
border-bottom: 0;
|
||||
height: 1.6em !important;
|
||||
min-height: 1.6em !important;
|
||||
}
|
||||
|
||||
#flex-container {
|
||||
position: relative;
|
||||
padding: 0;
|
||||
margin: 0;
|
||||
display: flex;
|
||||
}
|
||||
#flex-container > * {
|
||||
flex-grow: 1;
|
||||
padding: 0;
|
||||
margin: 0;
|
||||
}
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
|
@ -154,27 +199,61 @@
|
|||
<span id="which-extension"></span>
|
||||
<hr style="border-color: red; border-style: solid;"/>
|
||||
</span>
|
||||
<section>
|
||||
<header>PAC-скрипт</header>
|
||||
<ul id="list-of-providers">
|
||||
<li><input type="radio" name="pacProvider" id="none" checked> <label for="none">Отключить</label></li>
|
||||
</ul>
|
||||
<div style="white-space: nowrap">
|
||||
Обновлялись: <span class="update-date">...</span>
|
||||
</div>
|
||||
<span id="status" style="will-change: contents">Загрузка...</span>
|
||||
</section>
|
||||
<section id="status" style="will-change: contents">Загрузка...</section>
|
||||
<hr/>
|
||||
<div id="exceptions">
|
||||
<header>Проксировать этот сайт?</header>
|
||||
<div id="flex-container">
|
||||
<ul style="padding-right: 1em">
|
||||
<li><input id="this-auto" type="radio" checked name="if-proxy-this-site"/> <label for="this-auto">🔄︎ авто</label></li>
|
||||
<li><input id="this-yes" type="radio" name="if-proxy-this-site"/> <label for="this-yes">✔ да</label></li>
|
||||
<li><input id="this-no" type="radio" name="if-proxy-this-site"/> <label for="this-no">✘ нет</label></li>
|
||||
<li><a href>Весь список</a></li>
|
||||
</ul>
|
||||
<div id="flex-right">
|
||||
<input type="text" value="google.com" id="except-editor"/>
|
||||
<select multiple id="exceptions-select">
|
||||
<option class="sel">google.com</option>
|
||||
<option class="sel">yandex.ru</option>
|
||||
<option>yandex.ru</option>
|
||||
<option>yandex.ru</option>
|
||||
<option>yandex.ru</option>
|
||||
<option>yandex.ru</option>
|
||||
<option>yandex.ru</option>
|
||||
<option>yandex.ru</option>
|
||||
<option>yandex.ru</option>
|
||||
<option>yandex.ru</option>
|
||||
<option>yandex.ru</option>
|
||||
<option>yandex.ru</option>
|
||||
<option>yandex.ru</option>
|
||||
<option>yandex.ru</option>
|
||||
<option>yandex.ru</option>
|
||||
<option>yandex.ru</option>
|
||||
<option>yandex.ru</option>
|
||||
</select>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<hr/>
|
||||
<div>
|
||||
<ul id="pac-mods">
|
||||
<li class="control-row">
|
||||
<input type="button" value="Применить" id="apply-mods" disabled/>
|
||||
<a href id="reset-mods" class="link-button">К изначальным!</a>
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
<hr/>
|
||||
<div id="configs-panel">
|
||||
<header>Я ❤️ уведомления:</header>
|
||||
<ul id="list-of-handlers">
|
||||
</ul>
|
||||
<ul id="list-of-handlers"></ul>
|
||||
</div>
|
||||
<footer class="control-row">
|
||||
<input type="button" value="Готово" class="close-button">
|
||||
|
|
|
@ -80,8 +80,7 @@ chrome.runtime.getBackgroundPage( (backgroundPage) =>
|
|||
|
||||
const warning = warns
|
||||
.map(
|
||||
(w) =>
|
||||
(w && (w.clarification && w.clarification.message || w.message) || '')
|
||||
(w) => w && w.message || ''
|
||||
)
|
||||
.filter( (m) => m )
|
||||
.map( (m) => '✘ ' + m )
|
||||
|
@ -89,18 +88,20 @@ chrome.runtime.getBackgroundPage( (backgroundPage) =>
|
|||
|
||||
let message = '';
|
||||
if (err) {
|
||||
let clarification = err.clarification;
|
||||
let wrapped = err.wrapped;
|
||||
message = err.message || '';
|
||||
|
||||
while( clarification ) {
|
||||
message = (clarification && (clarification.message + ' ')) +
|
||||
message;
|
||||
clarification = clarification.prev;
|
||||
while( wrapped ) {
|
||||
const deeperMsg = wrapped && wrapped.message;
|
||||
if (deeperMsg) {
|
||||
message = message + ' > ' + deeperMsg;
|
||||
}
|
||||
wrapped = wrapped.wrapped;
|
||||
}
|
||||
}
|
||||
message = message.trim();
|
||||
if (warning) {
|
||||
message += ' ' + warning;
|
||||
message = message ? message + '<br/>' + warning : warning;
|
||||
}
|
||||
setStatusTo(
|
||||
`<span style="color:red">
|
||||
|
@ -121,11 +122,11 @@ chrome.runtime.getBackgroundPage( (backgroundPage) =>
|
|||
|
||||
};
|
||||
|
||||
const enableDisableInputs = function() {
|
||||
const switchInputs = function(val) {
|
||||
|
||||
const inputs = document.querySelectorAll('input');
|
||||
for ( let i = 0; i < inputs.length; i++ ) {
|
||||
inputs[i].disabled = !inputs[i].disabled;
|
||||
inputs[i].disabled = val === 'on' ? false : true;
|
||||
}
|
||||
|
||||
};
|
||||
|
@ -133,14 +134,17 @@ chrome.runtime.getBackgroundPage( (backgroundPage) =>
|
|||
const conduct = (beforeStatus, operation, afterStatus, onSuccess) => {
|
||||
|
||||
setStatusTo(beforeStatus);
|
||||
enableDisableInputs();
|
||||
switchInputs('off');
|
||||
operation((err, res, ...warns) => {
|
||||
|
||||
warns = warns.filter( (w) => w );
|
||||
if (err || warns.length) {
|
||||
backgroundPage.console.log('ERR', err, 'W', warns.length, 'w', warns);
|
||||
showErrors(err, ...warns);
|
||||
} else {
|
||||
setStatusTo(afterStatus);
|
||||
}
|
||||
enableDisableInputs();
|
||||
switchInputs('on');
|
||||
if (!err) {
|
||||
onSuccess && onSuccess(res);
|
||||
}
|
||||
|
@ -148,6 +152,15 @@ chrome.runtime.getBackgroundPage( (backgroundPage) =>
|
|||
|
||||
};
|
||||
|
||||
const infoSign = function infoSign(tooltip) {
|
||||
|
||||
return `<div class="desc">
|
||||
<span class="info-sign">🛈</span>
|
||||
<div class="tooltip">${tooltip}</div>
|
||||
</div>`;
|
||||
|
||||
};
|
||||
|
||||
{
|
||||
const ul = document.querySelector('#list-of-providers');
|
||||
const _firstChild = ul.firstChild;
|
||||
|
@ -161,11 +174,8 @@ chrome.runtime.getBackgroundPage( (backgroundPage) =>
|
|||
<input type="radio" name="pacProvider" id="${providerKey}">
|
||||
<label for="${providerKey}"> ${provider.label}</label>
|
||||
<a href class="link-button checked-radio-panel"
|
||||
id="update-${providerKey}">[обновить]</a>
|
||||
<div class="desc">
|
||||
<span class="info-sign">🛈</span>
|
||||
<div class="tooltip">${provider.desc}</div>
|
||||
</div>`;
|
||||
id="update-${providerKey}">[обновить]</a> ` +
|
||||
infoSign(provider.desc);
|
||||
li.querySelector('.link-button').onclick =
|
||||
() => {
|
||||
conduct(
|
||||
|
@ -212,6 +222,26 @@ chrome.runtime.getBackgroundPage( (backgroundPage) =>
|
|||
};
|
||||
}
|
||||
|
||||
// EXCEPTIONS PANEL
|
||||
|
||||
chrome.tabs.query({ active: true, currentWindow: true }, ([tab]) => {
|
||||
|
||||
/*
|
||||
console.log(tab);
|
||||
const opt = document.createElement('option');
|
||||
opt.text =
|
||||
opt.selected = true;
|
||||
opt.style.backgroundColor = 'green !important';
|
||||
opt.style.background = 'green !important';
|
||||
|
||||
const sl = document.getElementById('exceptions-select');
|
||||
sl.insertBefore( opt, sl.firstChild );
|
||||
*/
|
||||
|
||||
document.getElementById('except-editor').value = new URL(tab.url).hostname;
|
||||
|
||||
});
|
||||
|
||||
// PAC MODS PANEL
|
||||
|
||||
{
|
||||
|
@ -222,6 +252,7 @@ chrome.runtime.getBackgroundPage( (backgroundPage) =>
|
|||
const _firstChild = modPanel.firstChild;
|
||||
const keyToLi = {};
|
||||
const customProxyStringKey = 'customProxyStringRaw';
|
||||
const uiRaw = 'ui-proxy-string-raw';
|
||||
pacKitchen.getConfigs().forEach( (conf) => {
|
||||
|
||||
const key = conf.key;
|
||||
|
@ -229,16 +260,12 @@ chrome.runtime.getBackgroundPage( (backgroundPage) =>
|
|||
const li = document.createElement('li');
|
||||
li.className = 'info-row';
|
||||
keyToLi[key] = li;
|
||||
console.log(key, conf.value);
|
||||
li.innerHTML = `
|
||||
<input type="checkbox" id="${iddy}" ${ conf.value ? 'checked' : '' }/>
|
||||
<label for="${iddy}"> ${ conf.label }</label>`;
|
||||
|
||||
if (key !== customProxyStringKey) {
|
||||
li.innerHTML += `<div class="desc">
|
||||
<span class="info-sign">🛈</span>
|
||||
<div class="tooltip">${conf.desc}</div>
|
||||
</div>`;
|
||||
li.innerHTML += infoSign(conf.desc);
|
||||
} else {
|
||||
li.innerHTML += `<a href="${conf.url}" class="info-sign info-url">🛈</a><br/>
|
||||
<textarea
|
||||
|
@ -246,7 +273,7 @@ chrome.runtime.getBackgroundPage( (backgroundPage) =>
|
|||
placeholder="SOCKS5 localhost:9050; # TOR Expert
|
||||
SOCKS5 localhost:9150; # TOR Browser
|
||||
HTTPS foobar.com:3143;
|
||||
HTTPS 11.22.33.44:8080;">${conf.value || ''}</textarea>`;
|
||||
HTTPS 11.22.33.44:8080;">${conf.value || localStorage.getItem(uiRaw) || ''}</textarea>`;
|
||||
li.querySelector('textarea').onkeyup = function() {
|
||||
|
||||
this.dispatchEvent(new Event('change', { 'bubbles': true }));
|
||||
|
@ -270,12 +297,28 @@ HTTPS 11.22.33.44:8080;">${conf.value || ''}</textarea>`;
|
|||
return configs;
|
||||
|
||||
}, {});
|
||||
const taVal = keyToLi[customProxyStringKey].querySelector('textarea').value;
|
||||
if (configs[customProxyStringKey]) {
|
||||
configs[customProxyStringKey] = keyToLi[customProxyStringKey].querySelector('textarea').value;
|
||||
const ifValid = taVal
|
||||
.replace(/#.*$/mg)
|
||||
.split(/\s*[;\n\r]+\s*/g)
|
||||
.every(
|
||||
(str) =>
|
||||
/^(?:DIRECT|(?:(?:HTTPS?|PROXY|SOCKS(?:4|5))\s+\S+))$/g
|
||||
.test(str)
|
||||
)
|
||||
if (!ifValid) {
|
||||
return showErrors(new TypeError(
|
||||
'Неверный формат своих прокси. Свертесь с <a href="https://rebrand.ly/ac-own-proxy" data-in-bg="true">документацией</a>.'
|
||||
))
|
||||
}
|
||||
configs[customProxyStringKey] = taVal;
|
||||
} else {
|
||||
localStorage.setItem(uiRaw, taVal);
|
||||
}
|
||||
conduct(
|
||||
'Применяем настройки...',
|
||||
(cb) => pacKitchen.keepCookedNow(configs, cb),
|
||||
(cb) => pacKitchen.keepCookedNowAsync(configs, cb),
|
||||
'Настройки применены.',
|
||||
() => { document.getElementById('apply-mods').disabled = true; }
|
||||
);
|
||||
|
@ -284,7 +327,12 @@ HTTPS 11.22.33.44:8080;">${conf.value || ''}</textarea>`;
|
|||
|
||||
document.getElementById('reset-mods').onclick = () => {
|
||||
|
||||
const ifSure = backgroundPage.confirm('Сбросить все модификации PAC-скрипта?');
|
||||
if (!ifSure) {
|
||||
return;
|
||||
}
|
||||
pacKitchen.resetToDefaultsVoid();
|
||||
backgroundPage.apis.ipToHost.resetToDefaultsVoid();
|
||||
window.close();
|
||||
|
||||
};
|
||||
|
|
Loading…
Reference in New Issue
Block a user