mirror of
https://github.com/anticensority/runet-censorship-bypass.git
synced 2025-10-26 21:51:17 +03:00
Add help hints, warnings, restyle
This commit is contained in:
parent
797ef68397
commit
fc1b0fcae2
|
|
@ -1,5 +1,18 @@
|
||||||
'use strict';
|
'use strict';
|
||||||
|
|
||||||
|
const IF_DEBUG = true;
|
||||||
|
|
||||||
|
if (!IF_DEBUG) {
|
||||||
|
// I believe logging objects precludes them from being GCed.
|
||||||
|
// I also don't remove logs for sake of client-side troubleshooting
|
||||||
|
// (though no one sent me logs so far).
|
||||||
|
['log', 'warn', 'error'].forEach( (meth) => {
|
||||||
|
const _meth = window.console[meth].bind(console);
|
||||||
|
window.console[meth] = function(...args) { _meth(...args.map((a) => '' + a)) }
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
window.utils = {
|
window.utils = {
|
||||||
|
|
||||||
areSettingsNotControlledFor(details) {
|
areSettingsNotControlledFor(details) {
|
||||||
|
|
|
||||||
|
|
@ -82,8 +82,7 @@
|
||||||
|
|
||||||
const json = JSON.stringify(err, errorJsonReplacer, 0);
|
const json = JSON.stringify(err, errorJsonReplacer, 0);
|
||||||
openAndFocus(
|
openAndFocus(
|
||||||
//'https://rebrand.ly/ac-error/?' + btoa(encodeURIComponent(json))
|
'http://rebrand.ly/ac-error/?json=' + encodeURIComponent(json) + '&version=' + chrome.runtime.getManifest().version
|
||||||
'https://anticensorship-russia.tk/error/?' + json
|
|
||||||
);
|
);
|
||||||
|
|
||||||
},
|
},
|
||||||
|
|
@ -137,8 +136,11 @@
|
||||||
|
|
||||||
mayNotifyVoid(
|
mayNotifyVoid(
|
||||||
id, title, errOrMessage,
|
id, title, errOrMessage,
|
||||||
icon = 'default-128.png',
|
{
|
||||||
context = extName
|
icon = 'default-128.png',
|
||||||
|
context = extName,
|
||||||
|
ifSticky = true
|
||||||
|
}
|
||||||
) {
|
) {
|
||||||
|
|
||||||
if ( !this.isOn(id) ) {
|
if ( !this.isOn(id) ) {
|
||||||
|
|
@ -152,7 +154,7 @@
|
||||||
title: title,
|
title: title,
|
||||||
message: message,
|
message: message,
|
||||||
contextMessage: context,
|
contextMessage: context,
|
||||||
requireInteraction: true,
|
requireInteraction: ifSticky,
|
||||||
type: 'basic',
|
type: 'basic',
|
||||||
iconUrl: './icons/' + icon,
|
iconUrl: './icons/' + icon,
|
||||||
appIconMaskUrl: './icons/default-mask-128.png',
|
appIconMaskUrl: './icons/default-mask-128.png',
|
||||||
|
|
@ -168,7 +170,7 @@
|
||||||
|
|
||||||
console.warn(name + ':GLOBAL ERROR', errEvent);
|
console.warn(name + ':GLOBAL ERROR', errEvent);
|
||||||
this.mayNotifyVoid('ext-error', 'Ошибка расширения', errEvent,
|
this.mayNotifyVoid('ext-error', 'Ошибка расширения', errEvent,
|
||||||
'ext-error-128.png');
|
{icon: 'ext-error-128.png'});
|
||||||
|
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
@ -230,7 +232,7 @@
|
||||||
// TOOD: add "view pac script at this line" button.
|
// TOOD: add "view pac script at this line" button.
|
||||||
handlers.mayNotifyVoid('pac-error', 'Ошибка PAC!',
|
handlers.mayNotifyVoid('pac-error', 'Ошибка PAC!',
|
||||||
details.error + '\n' + details.details,
|
details.error + '\n' + details.details,
|
||||||
'pac-error-128.png'
|
{icon: 'pac-error-128.png'}
|
||||||
);
|
);
|
||||||
|
|
||||||
});
|
});
|
||||||
|
|
@ -244,7 +246,7 @@
|
||||||
noCon,
|
noCon,
|
||||||
chrome.i18n.getMessage('noControl'),
|
chrome.i18n.getMessage('noControl'),
|
||||||
chrome.i18n.getMessage('which'),
|
chrome.i18n.getMessage('which'),
|
||||||
'no-control-128.png'
|
{icon:'no-control-128.png', ifSticky: false}
|
||||||
);
|
);
|
||||||
} else {
|
} else {
|
||||||
chrome.notifications.clear( noCon );
|
chrome.notifications.clear( noCon );
|
||||||
|
|
|
||||||
|
|
@ -21,22 +21,22 @@
|
||||||
|
|
||||||
{ // Private namespace starts.
|
{ // Private namespace starts.
|
||||||
|
|
||||||
function mandatory() {
|
const mandatory = function mandatory() {
|
||||||
|
|
||||||
throw new TypeError('Missing required argument. ' +
|
throw new TypeError('Missing required argument. ' +
|
||||||
'Be explicit if you swallow errors.');
|
'Be explicit if you swallow errors.');
|
||||||
|
|
||||||
}
|
};
|
||||||
|
|
||||||
function throwIfError(err) {
|
const throwIfError = function throwIfError(err) {
|
||||||
|
|
||||||
if(err) {
|
if(err) {
|
||||||
throw err;
|
throw err;
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
};
|
||||||
|
|
||||||
function asyncLogGroup(...args) {
|
const asyncLogGroup = function asyncLogGroup(...args) {
|
||||||
|
|
||||||
const cb = args.pop();
|
const cb = args.pop();
|
||||||
if(!(cb && typeof(cb) === 'function')) {
|
if(!(cb && typeof(cb) === 'function')) {
|
||||||
|
|
@ -51,13 +51,13 @@
|
||||||
|
|
||||||
};
|
};
|
||||||
|
|
||||||
}
|
};
|
||||||
|
|
||||||
function checkChromeError(betterStack) {
|
const checkChromeError = function checkChromeError(betterStack) {
|
||||||
|
|
||||||
// Chrome API calls your cb in a context different from the point of API
|
// Chrome API calls your cb in a context different from the point of API
|
||||||
// method invokation.
|
// method invokation.
|
||||||
const err = chrome.runtime.lastError || chrome.extension.lastError || null;
|
const err = chrome.runtime.lastError || chrome.extension.lastError;
|
||||||
if (err) {
|
if (err) {
|
||||||
const args = ['API returned error:', err];
|
const args = ['API returned error:', err];
|
||||||
if (betterStack) {
|
if (betterStack) {
|
||||||
|
|
@ -67,9 +67,9 @@
|
||||||
}
|
}
|
||||||
return err;
|
return err;
|
||||||
|
|
||||||
}
|
};
|
||||||
|
|
||||||
function chromified(cb = mandatory(), ...replaceArgs) {
|
const chromified = function chromified(cb = mandatory(), ...replaceArgs) {
|
||||||
|
|
||||||
const stack = (new Error()).stack;
|
const stack = (new Error()).stack;
|
||||||
// Take error first callback and convert it to chrome api callback.
|
// Take error first callback and convert it to chrome api callback.
|
||||||
|
|
@ -84,57 +84,355 @@
|
||||||
|
|
||||||
};
|
};
|
||||||
|
|
||||||
}
|
};
|
||||||
|
|
||||||
|
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
|
||||||
|
) {
|
||||||
|
|
||||||
|
const config = {
|
||||||
|
mode: 'pac_script',
|
||||||
|
pacScript: {
|
||||||
|
mandatory: false,
|
||||||
|
data: pacData,
|
||||||
|
},
|
||||||
|
};
|
||||||
|
console.log('Setting chrome proxy settings...');
|
||||||
|
chrome.proxy.settings.set( {value: config}, () => {
|
||||||
|
|
||||||
|
const err = checkChromeError();
|
||||||
|
if (err) {
|
||||||
|
return cb(err);
|
||||||
|
}
|
||||||
|
chrome.proxy.settings.get({}, (details) => {
|
||||||
|
|
||||||
|
if ( window.utils.areSettingsNotControlledFor( details ) ) {
|
||||||
|
|
||||||
|
console.warn('Failed, other extension is in control.');
|
||||||
|
return cb(
|
||||||
|
null, null,
|
||||||
|
[new Warning( window.utils.messages.whichExtensionHtml() )]
|
||||||
|
);
|
||||||
|
|
||||||
|
}
|
||||||
|
console.log('Successfuly set PAC in proxy settings..');
|
||||||
|
cb();
|
||||||
|
});
|
||||||
|
|
||||||
|
});
|
||||||
|
|
||||||
|
};
|
||||||
|
|
||||||
|
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 = null;
|
||||||
|
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(', ') + '...',
|
||||||
|
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) {
|
||||||
|
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])
|
||||||
|
}
|
||||||
|
)
|
||||||
|
);
|
||||||
|
|
||||||
|
};
|
||||||
|
|
||||||
|
const setPacScriptFromProviderAsync = function setPacScriptFromProviderAsync(
|
||||||
|
provider = mandatory(), lastModified = mandatory(), cb = throwIfError
|
||||||
|
) {
|
||||||
|
|
||||||
|
const pacUrl = provider.pacUrls[0];
|
||||||
|
cb = asyncLogGroup(
|
||||||
|
'Getting PAC script from provider...', pacUrl,
|
||||||
|
cb
|
||||||
|
);
|
||||||
|
|
||||||
|
ifModifiedSince(pacUrl, lastModified, (err, newLastModified) => {
|
||||||
|
|
||||||
|
if (!newLastModified) {
|
||||||
|
return cb(
|
||||||
|
null,
|
||||||
|
{lastModified},
|
||||||
|
[new Warning('Ваш PAC-скрипт не нуждается в обновлении. Его дата: ' + lastModified)]
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Employ all urls, the latter are fallbacks for the former.
|
||||||
|
let pacDataPromise = Promise.reject();
|
||||||
|
for(const url of provider.pacUrls) {
|
||||||
|
|
||||||
|
pacDataPromise = pacDataPromise.catch(
|
||||||
|
(err) => new Promise(
|
||||||
|
(resolve, reject) => httpGet(
|
||||||
|
url,
|
||||||
|
(newErr, pacData) => newErr ? reject(newErr) : resolve(pacData)
|
||||||
|
)
|
||||||
|
)
|
||||||
|
);
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
pacDataPromise.then(
|
||||||
|
(pacData) => {
|
||||||
|
|
||||||
|
setPacAsync(
|
||||||
|
pacData,
|
||||||
|
(err, res) => cb(
|
||||||
|
err,
|
||||||
|
Object.assign(res || {}, {lastModified: newLastModified})
|
||||||
|
)
|
||||||
|
);
|
||||||
|
|
||||||
|
},
|
||||||
|
clarifyErrorThen(
|
||||||
|
'Не удалось скачать PAC-скрипт с адресов: [ '
|
||||||
|
+ provider.pacUrls.join(' , ') + ' ].',
|
||||||
|
cb
|
||||||
|
)
|
||||||
|
);
|
||||||
|
|
||||||
|
});
|
||||||
|
|
||||||
|
};
|
||||||
|
|
||||||
window.apis.antiCensorRu = {
|
window.apis.antiCensorRu = {
|
||||||
|
|
||||||
version: chrome.runtime.getManifest().version,
|
version: chrome.runtime.getManifest().version,
|
||||||
|
|
||||||
|
throwAsync() { throw new Error('ABC') }, // TODO: delete
|
||||||
|
|
||||||
pacProviders: {
|
pacProviders: {
|
||||||
Антизапрет: {
|
Антизапрет: {
|
||||||
|
label: 'Антизапрет',
|
||||||
|
desc: 'Альтернативный PAC-скрипт от стороннего разработчика.' +
|
||||||
|
' Блокировка определяется по доменному имени,' +
|
||||||
|
' для некоторых провайдеров есть автоопредление.' +
|
||||||
|
' <br/> <a href="https://antizapret.prostovpn.org">Страница проекта</a>.',
|
||||||
|
|
||||||
pacUrls: ['https://antizapret.prostovpn.org/proxy.pac'],
|
pacUrls: ['https://antizapret.prostovpn.org/proxy.pac'],
|
||||||
proxyHosts: ['proxy.antizapret.prostovpn.org'],
|
proxyHosts: ['proxy.antizapret.prostovpn.org'],
|
||||||
proxyIps: {
|
proxyIps: {
|
||||||
'195.123.209.38': 'proxy.antizapret.prostovpn.org',
|
'195.123.209.38': 'proxy.antizapret.prostovpn.org',
|
||||||
'137.74.171.91': 'proxy.antizapret.prostovpn.org',
|
'137.74.171.91': 'proxy.antizapret.prostovpn.org',
|
||||||
'51.15.39.201': 'proxy.antizapret.prostovpn.org',
|
'51.15.39.201': 'proxy.antizapret.prostovpn.org',
|
||||||
'2a02:27ac::10': 'proxy.antizapret.prostovpn.org',
|
|
||||||
'2001:bc8:4700:2300::1:d07': 'proxy.antizapret.prostovpn.org',
|
'2001:bc8:4700:2300::1:d07': 'proxy.antizapret.prostovpn.org',
|
||||||
|
'2a02:27ac::10': 'proxy.antizapret.prostovpn.org',
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
Антиценз: {
|
Антицензорити: {
|
||||||
pacUrls: ['https://config.anticenz.org/proxy.pac'],
|
label: 'Антицензорити',
|
||||||
proxyHosts: ['gw2.anticenz.org'],
|
desc: 'Основной PAC-скрипт от автора расширения.' +
|
||||||
proxyIps: {
|
' Блокировка определятся по доменному имени или IP адресу. Работает на switch-ах.' +
|
||||||
'5.196.220.114': 'gw2.anticenz.org',
|
' <br/><a href="https://rebrand.ly/ac-wiki">Страница проекта</a>.',
|
||||||
},
|
|
||||||
},
|
|
||||||
Оба_и_на_свитчах: {
|
|
||||||
/*
|
/*
|
||||||
Don't use in system configs! Because Win does poor caching.
|
Don't use in system configs! Because Windows does poor caching.
|
||||||
Url is encoded to counter abuse.
|
Some urls are encoded to counter abuse.
|
||||||
Version: 0.17
|
Version: 0.17
|
||||||
*/
|
*/
|
||||||
pacUrls: [
|
pacUrls: [
|
||||||
// Cloud Flare
|
// Official, Cloud Flare with caching:
|
||||||
'\x68\x74\x74\x70\x73\x3a\x2f\x2f\x61\x6e\x74\x69\x63\x65\x6e\x73\x6f\x72\x73\x68\x69\x70\x2d\x72\x75\x73\x73\x69\x61\x2e\x74\x6b\x2f\x67\x65\x6e\x65\x72\x61\x74\x65\x64\x2d\x70\x61\x63\x2d\x73\x63\x72\x69\x70\x74\x73\x2f\x6f\x6e\x2d\x73\x77\x69\x74\x63\x68\x65\x73\x2d\x30\x2e\x31\x37\x2e\x70\x61\x63',
|
'https://anticensorship-russia.tk/generated-pac-scripts/anticensority.pac',
|
||||||
// GitHub
|
// GitHub.io:
|
||||||
'\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\x6f\x6e\x2d\x73\x77\x69\x74\x63\x68\x65\x73\x2d\x30\x2e\x31\x37\x2e\x70\x61\x63',
|
'\x68\x74\x74\x70\x73\x3a\x2f\x2f\x61\x6e\x74\x69\x63\x65\x6e\x73\x6f\x72\x73\x68\x69\x70\x2d\x72\x75\x73\x73\x69\x61\x2e\x67\x69\x74\x68\x75\x62\x2e\x69\x6f\x2f\x67\x65\x6e\x65\x72\x61\x74\x65\x64\x2d\x70\x61\x63\x2d\x73\x63\x72\x69\x70\x74\x73\x2f\x61\x6e\x74\x69\x63\x65\x6e\x73\x6f\x72\x69\x74\x79\x2e\x70\x61\x63',
|
||||||
// Google Drive
|
// GitHub repo:
|
||||||
|
'\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',
|
||||||
|
// 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'],
|
'\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'],
|
||||||
proxyHosts: ['proxy.antizapret.prostovpn.org', 'gw2.anticenz.org'],
|
proxyHosts: ['proxy.antizapret.prostovpn.org'],
|
||||||
proxyIps: {
|
proxyIps: {
|
||||||
'195.123.209.38': 'proxy.antizapret.prostovpn.org',
|
'195.123.209.38': 'proxy.antizapret.prostovpn.org',
|
||||||
'137.74.171.91': 'proxy.antizapret.prostovpn.org',
|
'137.74.171.91': 'proxy.antizapret.prostovpn.org',
|
||||||
'51.15.39.201': 'proxy.antizapret.prostovpn.org',
|
'51.15.39.201': 'proxy.antizapret.prostovpn.org',
|
||||||
'2a02:27ac::10': 'proxy.antizapret.prostovpn.org',
|
|
||||||
'2001:bc8:4700:2300::1:d07': 'proxy.antizapret.prostovpn.org',
|
'2001:bc8:4700:2300::1:d07': 'proxy.antizapret.prostovpn.org',
|
||||||
'5.196.220.114': 'gw2.anticenz.org',
|
'2a02:27ac::10': 'proxy.antizapret.prostovpn.org',
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
|
||||||
_currentPacProviderKey: 'Оба_и_на_свитчах',
|
_currentPacProviderKey: 'Антицензорити',
|
||||||
|
|
||||||
/* Is it the first time extension installed?
|
/* Is it the first time extension installed?
|
||||||
Do something, e.g. initiate PAC sync.
|
Do something, e.g. initiate PAC sync.
|
||||||
|
|
@ -181,7 +479,7 @@
|
||||||
return this._currentPacProviderKey;
|
return this._currentPacProviderKey;
|
||||||
|
|
||||||
},
|
},
|
||||||
setCurrentPacProviderKey(newKey, lastModified = new Date().toUTCString()) {
|
setCurrentPacProviderKey(newKey = mandatory(), lastModified = new Date().toUTCString()) {
|
||||||
|
|
||||||
this.mustBeKey(newKey);
|
this.mustBeKey(newKey);
|
||||||
this._currentPacProviderKey = newKey;
|
this._currentPacProviderKey = newKey;
|
||||||
|
|
@ -237,11 +535,7 @@
|
||||||
|
|
||||||
if (key === null) {
|
if (key === null) {
|
||||||
// No pac provider set.
|
// No pac provider set.
|
||||||
return cb({
|
return clarifyErrorThen('Сперва выберите PAC-провайдера.', cb);
|
||||||
clarification: {
|
|
||||||
message: 'Сперва выберите PAC-провайдера.',
|
|
||||||
},
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
|
|
||||||
const pacProvider = this.getPacProvider(key);
|
const pacProvider = this.getPacProvider(key);
|
||||||
|
|
@ -250,43 +544,40 @@
|
||||||
(resolve, reject) => setPacScriptFromProviderAsync(
|
(resolve, reject) => setPacScriptFromProviderAsync(
|
||||||
pacProvider,
|
pacProvider,
|
||||||
this.getLastModifiedForKey(key),
|
this.getLastModifiedForKey(key),
|
||||||
(err, res) => {
|
(err, res, warns) => {
|
||||||
|
|
||||||
if (res && res.ifPacSet) {
|
if (!err) {
|
||||||
this.setCurrentPacProviderKey(key, res.lastModified);
|
this.setCurrentPacProviderKey(key, res.lastModified);
|
||||||
this.lastPacUpdateStamp = Date.now();
|
this.lastPacUpdateStamp = Date.now();
|
||||||
this.ifFirstInstall = false;
|
this.ifFirstInstall = false;
|
||||||
this.setAlarms();
|
this.setAlarms();
|
||||||
}
|
}
|
||||||
|
|
||||||
resolve([err, res]);
|
resolve([err, null, warns]);
|
||||||
|
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
);
|
);
|
||||||
|
|
||||||
const ipsPromise = new Promise(
|
const ipsErrorPromise = new Promise(
|
||||||
(resolve, reject) => updatePacProxyIps(
|
(resolve, reject) => updatePacProxyIps(
|
||||||
pacProvider,
|
pacProvider,
|
||||||
(ipsError) => {
|
resolve
|
||||||
|
|
||||||
if (ipsError && ipsError.clarification) {
|
|
||||||
ipsError.clarification.ifNotCritical = true;
|
|
||||||
}
|
|
||||||
resolve([ipsError]);
|
|
||||||
|
|
||||||
}
|
|
||||||
)
|
)
|
||||||
);
|
);
|
||||||
|
|
||||||
Promise.all([pacSetPromise, ipsPromise]).then(
|
Promise.all([pacSetPromise, ipsErrorPromise]).then(
|
||||||
([[pacErr, pacRes], [ipsErr]]) => {
|
([[pacErr, pacRes, pacWarns], ipsErr]) => {
|
||||||
|
|
||||||
if (pacErr && ipsErr) {
|
if (pacErr && ipsErr) {
|
||||||
return cb(pacErr, pacRes);
|
return cb(pacErr, pacRes);
|
||||||
}
|
}
|
||||||
|
let warns = [...(pacWarns || []), ipsErr].filter( (_) => _ );
|
||||||
|
if (!warns.length) {
|
||||||
|
warns = null;
|
||||||
|
}
|
||||||
this.pushToStorageAsync(
|
this.pushToStorageAsync(
|
||||||
(pushErr) => cb(pacErr || ipsErr || pushErr, pacRes)
|
(pushErr) => cb(pacErr || pushErr, null, warns)
|
||||||
);
|
);
|
||||||
|
|
||||||
},
|
},
|
||||||
|
|
@ -407,33 +698,55 @@
|
||||||
console.log('Storage on init:', oldStorage);
|
console.log('Storage on init:', oldStorage);
|
||||||
antiCensorRu.ifFirstInstall = Object.keys(oldStorage).length === 0;
|
antiCensorRu.ifFirstInstall = Object.keys(oldStorage).length === 0;
|
||||||
|
|
||||||
if (!antiCensorRu.ifFirstInstall) {
|
if (antiCensorRu.ifFirstInstall) {
|
||||||
// LAUNCH, RELOAD, UPDATE
|
|
||||||
// Use old or migrate to default.
|
|
||||||
antiCensorRu._currentPacProviderKey =
|
|
||||||
oldStorage._currentPacProviderKey || null;
|
|
||||||
antiCensorRu.lastPacUpdateStamp =
|
|
||||||
oldStorage.lastPacUpdateStamp || antiCensorRu.lastPacUpdateStamp;
|
|
||||||
antiCensorRu._currentPacProviderLastModified =
|
|
||||||
oldStorage._currentPacProviderLastModified
|
|
||||||
|| antiCensorRu._currentPacProviderLastModified;
|
|
||||||
console.log(
|
|
||||||
'Last PAC update was on',
|
|
||||||
new Date(antiCensorRu.lastPacUpdateStamp).toLocaleString('ru-RU')
|
|
||||||
);
|
|
||||||
} else {
|
|
||||||
// INSTALL
|
// INSTALL
|
||||||
console.log('Installing...');
|
console.log('Installing...');
|
||||||
return chrome.runtime.openOptionsPage();
|
return chrome.runtime.openOptionsPage();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// LAUNCH, RELOAD, UPDATE
|
||||||
|
// Use old or migrate to default.
|
||||||
|
antiCensorRu._currentPacProviderKey =
|
||||||
|
oldStorage._currentPacProviderKey || null;
|
||||||
|
antiCensorRu.lastPacUpdateStamp =
|
||||||
|
oldStorage.lastPacUpdateStamp || antiCensorRu.lastPacUpdateStamp;
|
||||||
|
antiCensorRu._currentPacProviderLastModified =
|
||||||
|
oldStorage._currentPacProviderLastModified
|
||||||
|
|| antiCensorRu._currentPacProviderLastModified;
|
||||||
|
console.log(
|
||||||
|
'Last PAC update was on',
|
||||||
|
new Date(antiCensorRu.lastPacUpdateStamp).toLocaleString('ru-RU')
|
||||||
|
);
|
||||||
|
|
||||||
|
const ifUpdating = antiCensorRu.version !== oldStorage.version;
|
||||||
|
console.log('IF_UPD?', ifUpdating, antiCensorRu.version, 'vs', oldStorage.version);
|
||||||
|
|
||||||
|
const pushOnUpdate = () => ifUpdating ? antiCensorRu.pushToStorageAsync() : null;
|
||||||
|
|
||||||
|
if (!ifUpdating) {
|
||||||
|
// LAUNCH, RELOAD, ENABLE
|
||||||
|
antiCensorRu.pacProviders = oldStorage.pacProviders;
|
||||||
|
console.log('Extension launched, reloaded or enabled.');
|
||||||
|
} else {
|
||||||
|
// UPDATE & MIGRATION
|
||||||
|
const key = antiCensorRu._currentPacProviderKey;
|
||||||
|
if (
|
||||||
|
key !== null &&
|
||||||
|
!Object.keys(antiCensorRu.pacProviders).includes(key)
|
||||||
|
) {
|
||||||
|
antiCensorRu._currentPacProviderKey = 'Антицензорити'
|
||||||
|
}
|
||||||
|
console.log('Extension updated.');
|
||||||
|
}
|
||||||
|
|
||||||
if (!antiCensorRu.getPacProvider()) {
|
if (!antiCensorRu.getPacProvider()) {
|
||||||
/*
|
/*
|
||||||
In case of UPDATE:
|
In case of UPDATE:
|
||||||
1. new providers will still be shown.
|
1. new providers will still be shown.
|
||||||
2. new version won't be pushed to storage
|
2. new version won't be pushed to storage
|
||||||
*/
|
*/
|
||||||
return console.log('No PAC provider set. Do nothing.');
|
console.log('No PAC provider set. Do nothing.');
|
||||||
|
return pushOnUpdate();
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
|
|
@ -446,306 +759,24 @@
|
||||||
|
|
||||||
const ifAlarmTriggered = antiCensorRu.setAlarms();
|
const ifAlarmTriggered = antiCensorRu.setAlarms();
|
||||||
|
|
||||||
if (antiCensorRu.version === oldStorage.version) {
|
|
||||||
// LAUNCH, RELOAD, ENABLE
|
|
||||||
antiCensorRu.pacProviders = oldStorage.pacProviders;
|
|
||||||
return console.log('Extension launched, reloaded or enabled.');
|
|
||||||
}
|
|
||||||
|
|
||||||
// UPDATE & MIGRATION
|
|
||||||
console.log('Extension updated.');
|
|
||||||
if (!ifAlarmTriggered) {
|
if (!ifAlarmTriggered) {
|
||||||
antiCensorRu.pushToStorageAsync();
|
return pushOnUpdate();
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
History of Changes to Storage (Migration Guide)
|
History of Changes to Storage (Migration Guide)
|
||||||
-----------------------------------------------
|
-----------------------------------------------
|
||||||
Version 0.0.0.10
|
Version 0.0.0.17:
|
||||||
* Added this.version
|
* "Антиценз" removed.
|
||||||
* PacProvider.proxyIps changed from {ip -> Boolean} to {ip -> hostname}
|
* "Оба_и_на_свитчах" renamed to "Антицензорити".
|
||||||
Version 0.0.0.8-9
|
Version 0.0.0.10:
|
||||||
* Changed storage.ifNotInstalled to storage.ifFirstInstall
|
* Added this.version.
|
||||||
* Added storage.lastPacUpdateStamp
|
* PacProvider.proxyIps changed from {ip -> Boolean} to {ip -> hostname}.
|
||||||
|
Version 0.0.0.8-9:
|
||||||
|
* Changed storage.ifNotInstalled to storage.ifFirstInstall.
|
||||||
|
* Added storage.lastPacUpdateStamp.
|
||||||
**/
|
**/
|
||||||
|
|
||||||
});
|
});
|
||||||
|
|
||||||
/*
|
|
||||||
* result.ifPacSet is true if PAC was set (maybe with non-critical errors).
|
|
||||||
* */
|
|
||||||
function setPacAsync(
|
|
||||||
{pacData = mandatory(), pacUrl = mandatory()},
|
|
||||||
cb = throwIfError
|
|
||||||
) {
|
|
||||||
|
|
||||||
const config = {
|
|
||||||
mode: 'pac_script',
|
|
||||||
pacScript: {
|
|
||||||
mandatory: false,
|
|
||||||
data: pacData,
|
|
||||||
},
|
|
||||||
};
|
|
||||||
console.log('Setting chrome proxy settings...');
|
|
||||||
chrome.proxy.settings.set( {value: config}, async () => {
|
|
||||||
|
|
||||||
let err = checkChromeError();
|
|
||||||
let asciiErr;
|
|
||||||
if (err) {
|
|
||||||
if (err.message.startsWith('\'pacScript.data\' supports only ASCII')) {
|
|
||||||
asciiErr = err;
|
|
||||||
asciiErr.clarification = {ifNotCritical: true};
|
|
||||||
err = await new Promise((resolve) => {
|
|
||||||
|
|
||||||
chrome.proxy.settings.set({
|
|
||||||
value: {
|
|
||||||
mode: 'pac_script',
|
|
||||||
pacScript: {
|
|
||||||
url: pacUrl,
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
() => resolve( checkChromeError() )
|
|
||||||
);
|
|
||||||
|
|
||||||
});
|
|
||||||
|
|
||||||
}
|
|
||||||
if (err) {
|
|
||||||
return cb(err);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
chrome.proxy.settings.get({}, (details) => {
|
|
||||||
|
|
||||||
if ( window.utils.areSettingsNotControlledFor( details ) ) {
|
|
||||||
console.warn('Failed, other extension is in control.');
|
|
||||||
return cb({clarification: {
|
|
||||||
message: window.utils.messages.whichExtensionHtml(),
|
|
||||||
}});
|
|
||||||
}
|
|
||||||
console.log('Successfuly set PAC in proxy settings..');
|
|
||||||
cb(asciiErr, {ifPacSet: true});
|
|
||||||
});
|
|
||||||
|
|
||||||
});
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
function clarifyFetchErrorThen(cb) {
|
|
||||||
|
|
||||||
return (err) => {
|
|
||||||
|
|
||||||
err.clarification = {
|
|
||||||
message: 'Что-то не так с сетью, проверьте соединение.',
|
|
||||||
};
|
|
||||||
return cb(err);
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
function ifModifiedSince(url, lastModified = mandatory(), cb = mandatory()) {
|
|
||||||
|
|
||||||
const nowModified = new Date(0).toUTCString();
|
|
||||||
fetch(url, {
|
|
||||||
method: 'HEAD',
|
|
||||||
headers: new Headers({
|
|
||||||
'If-Modified-Since': lastModified
|
|
||||||
})
|
|
||||||
}).then(
|
|
||||||
(res) => {
|
|
||||||
cb(null, res.status === 304 ? false : (res.headers.get('Last-Modified') || nowModified) );
|
|
||||||
},
|
|
||||||
clarifyFetchErrorThen((err) => cb(err, nowModified))
|
|
||||||
);
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
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 ) ) {
|
|
||||||
res.clarification = {
|
|
||||||
message: 'Получен ответ с неудачным HTTP-кодом ' + status + '.',
|
|
||||||
};
|
|
||||||
return textCb(res);
|
|
||||||
}
|
|
||||||
console.log('GETed with success:', url, Date.now() - start);
|
|
||||||
textCb();
|
|
||||||
|
|
||||||
},
|
|
||||||
clarifyFetchErrorThen(cb)
|
|
||||||
);
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
function getIpsFor(host, 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');
|
|
||||||
err.clarification.message += ' Сервер (json): ' + msg;
|
|
||||||
err.data = err.data || res;
|
|
||||||
} else {
|
|
||||||
res = res.Answer || [];
|
|
||||||
res = res.filter(
|
|
||||||
(record) => types.includes(record.type)
|
|
||||||
);
|
|
||||||
}
|
|
||||||
} catch(e) {
|
|
||||||
err = e || err || {clarification: {message: ''}};
|
|
||||||
err.clarification = err.clarification || {message: ''};
|
|
||||||
err.clarification.message = (
|
|
||||||
err.clarification.message
|
|
||||||
+ ' Сервер (текст): '+ res
|
|
||||||
).trim();
|
|
||||||
err.data = err.data || res;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
resolve([err, res]);
|
|
||||||
|
|
||||||
}
|
|
||||||
)
|
|
||||||
)
|
|
||||||
);
|
|
||||||
Promise.all(promises).then(
|
|
||||||
([[v4err, v4res], [v6err, v6res]]) => {
|
|
||||||
|
|
||||||
if(v4err) {
|
|
||||||
return cb(v4err, v4res);
|
|
||||||
}
|
|
||||||
const ips = v4res;
|
|
||||||
if (!v6err) {
|
|
||||||
ips.push(...v6res);
|
|
||||||
} else {
|
|
||||||
v6err.clarification.ifNotCritical = true;
|
|
||||||
console.warn(v6err);
|
|
||||||
}
|
|
||||||
cb(v6err, ips);
|
|
||||||
|
|
||||||
}
|
|
||||||
);
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
function updatePacProxyIps(provider, cb = throwIfError) {
|
|
||||||
|
|
||||||
cb = asyncLogGroup(
|
|
||||||
'Getting IP for '+ provider.proxyHosts.join(', ') + '...',
|
|
||||||
cb
|
|
||||||
);
|
|
||||||
let failure = {
|
|
||||||
clarification: {
|
|
||||||
message: 'Не удалось получить один или несколько IP адресов для' +
|
|
||||||
' прокси-серверов. Иконка для уведомления об обходе блокировок ' +
|
|
||||||
'может не отображаться.',
|
|
||||||
},
|
|
||||||
errors: {},
|
|
||||||
};
|
|
||||||
let i = 0;
|
|
||||||
provider.proxyHosts.forEach(
|
|
||||||
(proxyHost) => getIpsFor(
|
|
||||||
proxyHost,
|
|
||||||
(err, ips) => {
|
|
||||||
|
|
||||||
if (!err || err.clarification.ifNotCritical) {
|
|
||||||
provider.proxyIps = provider.proxyIps || {};
|
|
||||||
ips.forEach(
|
|
||||||
(ip) => provider.proxyIps[ip] = proxyHost
|
|
||||||
);
|
|
||||||
} else {
|
|
||||||
failure.errors[proxyHost] = err;
|
|
||||||
}
|
|
||||||
|
|
||||||
if ( ++i === provider.proxyHosts.length ) {
|
|
||||||
failure = Object.keys(failure.errors).length ? failure : null;
|
|
||||||
cb(failure, provider.proxyIps);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
)
|
|
||||||
);
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
/*
|
|
||||||
* result.ifPacSet is true if PAC was set.
|
|
||||||
**/
|
|
||||||
function setPacScriptFromProviderAsync(provider = mandatory(), lastModified = mandatory(), cb = throwIfError) {
|
|
||||||
|
|
||||||
const pacUrl = provider.pacUrls[0];
|
|
||||||
cb = asyncLogGroup(
|
|
||||||
'Getting PAC script from provider...', pacUrl,
|
|
||||||
cb
|
|
||||||
);
|
|
||||||
|
|
||||||
ifModifiedSince(pacUrl, lastModified, (err, newLastModified) => {
|
|
||||||
|
|
||||||
if (!newLastModified) {
|
|
||||||
return cb(
|
|
||||||
{clarification: {
|
|
||||||
message: 'Ваш PAC-скрипт не нуждается в обновлении. Его дата: ' + lastModified,
|
|
||||||
ifNotCritical: true,
|
|
||||||
}}
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Employ all urls, the latter are fallbacks for the former.
|
|
||||||
let pacDataPromise = Promise.reject();
|
|
||||||
for(const url of provider.pacUrls) {
|
|
||||||
|
|
||||||
pacDataPromise = pacDataPromise.catch(
|
|
||||||
(err) => new Promise(
|
|
||||||
(resolve, reject) => httpGet(
|
|
||||||
url,
|
|
||||||
(newErr, pacData) => newErr ? reject(newErr) : resolve(pacData)
|
|
||||||
)
|
|
||||||
)
|
|
||||||
);
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
pacDataPromise.then(
|
|
||||||
(pacData) => {
|
|
||||||
|
|
||||||
setPacAsync(
|
|
||||||
{pacData, pacUrl},
|
|
||||||
(err, res) => cb( err, Object.assign(res || {}, {lastModified: newLastModified}) )
|
|
||||||
);
|
|
||||||
|
|
||||||
},
|
|
||||||
(err) => {
|
|
||||||
|
|
||||||
err.clarification = {
|
|
||||||
message: 'Не удалось скачать PAC-скрипт с адресов: [ '
|
|
||||||
+ provider.pacUrls.join(' , ') + ' ].',
|
|
||||||
prev: err.clarification,
|
|
||||||
};
|
|
||||||
return cb(err);
|
|
||||||
|
|
||||||
}
|
|
||||||
);
|
|
||||||
|
|
||||||
});
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -11,7 +11,7 @@
|
||||||
});
|
});
|
||||||
|
|
||||||
createMenuLinkEntry(
|
createMenuLinkEntry(
|
||||||
'Сайт доступен из-за границы? Is up?',
|
'Сайт доступен из-за границы?',
|
||||||
(tab) => `data:text/html;charset=utf8,<title>Запрашиваю...</title>
|
(tab) => `data:text/html;charset=utf8,<title>Запрашиваю...</title>
|
||||||
<form class='tracker-form' method='POST'
|
<form class='tracker-form' method='POST'
|
||||||
action='https://www.host-tracker.com/ru/InstantCheck/Create'>
|
action='https://www.host-tracker.com/ru/InstantCheck/Create'>
|
||||||
|
|
@ -42,8 +42,8 @@
|
||||||
);
|
);
|
||||||
|
|
||||||
createMenuLinkEntry(
|
createMenuLinkEntry(
|
||||||
'У меня проблемы с расширением!',
|
'Руководство / Помощь / Ссылки',
|
||||||
(tab) => 'https://rebrand.ly/ac-support'
|
(tab) => 'https://rebrand.ly/ac-wiki'
|
||||||
);
|
);
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,7 +1,7 @@
|
||||||
{
|
{
|
||||||
"manifest_version": 2,
|
"manifest_version": 2,
|
||||||
|
|
||||||
"name": "__MSG_extName__ 0.17",
|
"name": "__MSG_extName__ 0.16",
|
||||||
"default_locale": "ru",
|
"default_locale": "ru",
|
||||||
"description": "__MSG_extDesc__",
|
"description": "__MSG_extDesc__",
|
||||||
"version": "0.0.0.17",
|
"version": "0.0.0.17",
|
||||||
|
|
|
||||||
File diff suppressed because one or more lines are too long
Binary file not shown.
Binary file not shown.
File diff suppressed because it is too large
Load Diff
|
After Width: | Height: | Size: 434 KiB |
Binary file not shown.
Binary file not shown.
Binary file not shown.
|
|
@ -2,7 +2,11 @@
|
||||||
<html style="display: none">
|
<html style="display: none">
|
||||||
<head>
|
<head>
|
||||||
<title>Выбор провайдера PAC</title>
|
<title>Выбор провайдера PAC</title>
|
||||||
|
<link rel="stylesheet" href="./font-awesome/css/font-awesome.min.css">
|
||||||
<style>
|
<style>
|
||||||
|
:root {
|
||||||
|
--ribbon-color: #4169e1; /* #1a6cc8 */
|
||||||
|
}
|
||||||
div {
|
div {
|
||||||
margin: 0;
|
margin: 0;
|
||||||
padding: 0;
|
padding: 0;
|
||||||
|
|
@ -27,9 +31,6 @@
|
||||||
input[type="radio"], label {
|
input[type="radio"], label {
|
||||||
cursor: pointer;
|
cursor: pointer;
|
||||||
}
|
}
|
||||||
.off {
|
|
||||||
display: none;
|
|
||||||
}
|
|
||||||
.link-button, .link-button:visited {
|
.link-button, .link-button:visited {
|
||||||
color: #0000EE;
|
color: #0000EE;
|
||||||
text-decoration: none;
|
text-decoration: none;
|
||||||
|
|
@ -58,6 +59,112 @@
|
||||||
display: none;
|
display: none;
|
||||||
color: red;
|
color: red;
|
||||||
}
|
}
|
||||||
|
li.provider {
|
||||||
|
display: table;
|
||||||
|
width: 100%;
|
||||||
|
}
|
||||||
|
|
||||||
|
.desc i {
|
||||||
|
vertical-align: bottom;
|
||||||
|
font-size: 1.1em;
|
||||||
|
}
|
||||||
|
/* Source: https://jsfiddle.net/greypants/zgCb7/ */
|
||||||
|
.tooltip {
|
||||||
|
display: none;
|
||||||
|
white-space: initial;
|
||||||
|
padding: 1em;
|
||||||
|
}
|
||||||
|
.desc {
|
||||||
|
display: table-cell;
|
||||||
|
text-align: right;
|
||||||
|
color: var(--ribbon-color);
|
||||||
|
cursor: help;
|
||||||
|
}
|
||||||
|
.desc:hover br .tooltip {
|
||||||
|
display: block;
|
||||||
|
text-align: left;
|
||||||
|
position: absolute;
|
||||||
|
left: 0;
|
||||||
|
top: 0;
|
||||||
|
bottom: 0;
|
||||||
|
right: 2.6em;
|
||||||
|
background-color: var(--ribbon-color);
|
||||||
|
color: white;
|
||||||
|
z-index: 1;
|
||||||
|
}
|
||||||
|
.tooltip a {
|
||||||
|
color: white;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* CSS Triangles - courtesy of Trevor */
|
||||||
|
.desc .tooltip:after {
|
||||||
|
border-top: solid transparent 7px;
|
||||||
|
border-bottom: solid transparent 7px;
|
||||||
|
border-left: solid var(--ribbon-color) 7px;
|
||||||
|
right: -7px;
|
||||||
|
content: "";
|
||||||
|
width: 0;
|
||||||
|
top: 7px;
|
||||||
|
margin-top: -13px;
|
||||||
|
position: absolute;
|
||||||
|
height: 0;
|
||||||
|
}
|
||||||
|
/* This bridges the gap so you can mouse into the tooltip without it disappearing
|
||||||
|
.desc .tooltip:before {
|
||||||
|
position: absolute;
|
||||||
|
right: -14px;
|
||||||
|
content: " ";
|
||||||
|
display: block;
|
||||||
|
width: 14px;
|
||||||
|
top: 0;
|
||||||
|
height: 100%;
|
||||||
|
}
|
||||||
|
*/
|
||||||
|
|
||||||
|
|
||||||
|
li.provider {
|
||||||
|
position: relative;
|
||||||
|
}
|
||||||
|
.xyz {
|
||||||
|
display: none;
|
||||||
|
position: absolute;
|
||||||
|
white-space: initial;
|
||||||
|
word-break: initial;
|
||||||
|
top: 100%;
|
||||||
|
left: 0;
|
||||||
|
right: 1em;
|
||||||
|
z-index: 1;
|
||||||
|
background-color: var(--ribbon-color);
|
||||||
|
padding: 1em;
|
||||||
|
color: white;
|
||||||
|
text-align: initial;
|
||||||
|
}
|
||||||
|
.desc:hover .xyz {
|
||||||
|
display: block;
|
||||||
|
}
|
||||||
|
.xyz a {
|
||||||
|
color: white;
|
||||||
|
}
|
||||||
|
.desc .xyz:after {
|
||||||
|
border-left: solid transparent 7px;
|
||||||
|
border-bottom: solid var(--ribbon-color) 7px;
|
||||||
|
position: absolute;
|
||||||
|
top: -7px;
|
||||||
|
content: "";
|
||||||
|
width: 0;
|
||||||
|
right: 0;
|
||||||
|
height: 0;
|
||||||
|
}
|
||||||
|
/* This bridges the gap so you can mouse into the tooltip without it disappearing */
|
||||||
|
.desc .xyz:before {
|
||||||
|
position: absolute;
|
||||||
|
top: -14px;
|
||||||
|
content: "";
|
||||||
|
display: block;
|
||||||
|
height: 22px;
|
||||||
|
left: 0;
|
||||||
|
width: calc(100% + 0.6em);
|
||||||
|
}
|
||||||
</style>
|
</style>
|
||||||
</head>
|
</head>
|
||||||
<body>
|
<body>
|
||||||
|
|
|
||||||
|
|
@ -68,33 +68,43 @@ chrome.runtime.getBackgroundPage( (backgroundPage) =>
|
||||||
};
|
};
|
||||||
const checkChosenProvider = () => currentProviderRadio().checked = true;
|
const checkChosenProvider = () => currentProviderRadio().checked = true;
|
||||||
|
|
||||||
const showError = (err) => {
|
const showErrors = (err, warns) => {
|
||||||
|
|
||||||
let clarification = err.clarification;
|
warns = warns || [];
|
||||||
const ifNotCritical = clarification && clarification.ifNotCritical;
|
const warning = warns
|
||||||
let message = err.message || '';
|
.map( (w) => '✘ ' + (w.clarification && w.clarification.message || w.message || '') )
|
||||||
|
.join('<br/>');
|
||||||
|
|
||||||
while( clarification ) {
|
let message = '';
|
||||||
message = (clarification && (clarification.message + ' ')) + message;
|
if (err) {
|
||||||
clarification = clarification.prev;
|
let clarification = err.clarification;
|
||||||
|
message = err.message || '';
|
||||||
|
|
||||||
|
while( clarification ) {
|
||||||
|
message = (clarification && (clarification.message + ' ')) + message;
|
||||||
|
clarification = clarification.prev;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
message = message.trim();
|
message = message.trim();
|
||||||
|
if (warning) {
|
||||||
|
message += ' ' + warning;
|
||||||
|
}
|
||||||
setStatusTo(
|
setStatusTo(
|
||||||
`<span style="color:red">
|
`<span style="color:red">
|
||||||
${ifNotCritical ? 'Некритичная ошибка.' : 'Ошибка!'}
|
${err ? '🔥 Ошибка!' : 'Некритичная ошибка.'}
|
||||||
</span>
|
</span>
|
||||||
<br/>
|
<br/>
|
||||||
<span style="font-size: 0.9em; color: darkred">${message}</span>
|
<span style="font-size: 0.9em; color: darkred">${message}</span>
|
||||||
<a href class="link-button">
|
${err ? '<a href class="link-button">[Ещё подробнее]</a>' : ''}`
|
||||||
[Ещё подробнее]
|
|
||||||
</a>`
|
|
||||||
);
|
);
|
||||||
getStatus().querySelector('.link-button').onclick = function() {
|
if (err) {
|
||||||
|
getStatus().querySelector('.link-button').onclick = function() {
|
||||||
|
|
||||||
errorHandlers.viewErrorVoid(err);
|
errorHandlers.viewErrorVoid(err);
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
};
|
};
|
||||||
|
}
|
||||||
|
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
@ -111,14 +121,13 @@ chrome.runtime.getBackgroundPage( (backgroundPage) =>
|
||||||
|
|
||||||
setStatusTo(beforeStatus);
|
setStatusTo(beforeStatus);
|
||||||
enableDisableInputs();
|
enableDisableInputs();
|
||||||
operation((err) => {
|
operation((err, res, warns) => {
|
||||||
if (err) {
|
if (err || warns) {
|
||||||
showError(err);
|
showErrors(err, warns);
|
||||||
if (err.clarification && err.clarification.ifNotCritical) {
|
|
||||||
onSuccess && onSuccess();
|
|
||||||
}
|
|
||||||
} else {
|
} else {
|
||||||
setStatusTo(afterStatus);
|
setStatusTo(afterStatus);
|
||||||
|
}
|
||||||
|
if (!err) {
|
||||||
onSuccess && onSuccess();
|
onSuccess && onSuccess();
|
||||||
}
|
}
|
||||||
enableDisableInputs();
|
enableDisableInputs();
|
||||||
|
|
@ -131,12 +140,20 @@ chrome.runtime.getBackgroundPage( (backgroundPage) =>
|
||||||
for(
|
for(
|
||||||
const providerKey of Object.keys(antiCensorRu.pacProviders).sort()
|
const providerKey of Object.keys(antiCensorRu.pacProviders).sort()
|
||||||
) {
|
) {
|
||||||
|
const provider = antiCensorRu.getPacProvider(providerKey);
|
||||||
const li = document.createElement('li');
|
const li = document.createElement('li');
|
||||||
|
li.className = 'provider';
|
||||||
li.innerHTML = `
|
li.innerHTML = `
|
||||||
<input type="radio" name="pacProvider" id="${providerKey}">
|
<input type="radio" name="pacProvider" id="${providerKey}">
|
||||||
<label for="${providerKey}">${providerKey}</label>
|
<label for="${providerKey}"> ${provider.label}</label>
|
||||||
<a href class="link-button checked-radio-panel"
|
<a href class="link-button checked-radio-panel"
|
||||||
id="update-${providerKey}">[обновить]</a>`;
|
id="update-${providerKey}"> [обновить]</a>
|
||||||
|
<div class="desc">
|
||||||
|
<i class="fa fa-question-circle" aria-hidden="true"></i>
|
||||||
|
<div class="tooltip">${provider.desc}</div>
|
||||||
|
<div class="xyz">${provider.desc}</div>
|
||||||
|
</div>
|
||||||
|
`
|
||||||
li.querySelector('.link-button').onclick =
|
li.querySelector('.link-button').onclick =
|
||||||
() => {
|
() => {
|
||||||
conduct(
|
conduct(
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue
Block a user