Add eslint w/ Google styles, restyle

This commit is contained in:
Ilya Ig. Petrov 2016-11-29 09:27:15 -08:00
parent 9ef8939127
commit 77a88e5778
9 changed files with 1138 additions and 975 deletions

1
.gitignore vendored
View File

@ -1,2 +1,3 @@
node_modules node_modules
npm-debug.log
.swp .swp

View File

@ -0,0 +1,27 @@
module.exports = {
"extends": ["eslint:recommended", "google"],
"plugins": [
//"hapi"
],
"env": {
"browser": true,
"webextensions": true,
"es6": true
},
"globals": {
"chrome": true
},
"parserOptions": {
"sourceType": "script",
"ecmaFeatures": {
"impliedStrict": false
}
},
"rules": {
"strict": ["error", "global"],
"no-console": "off",
"padded-blocks": "off",
"require-jsdoc": "off"
//"hapi/hapi-scope-start": ["warn"]
}
};

View File

@ -3,7 +3,8 @@
/* /*
Task 1. Gets IPs for proxies of antizapret/anticenz via dns over https. Task 1. Gets IPs for proxies of antizapret/anticenz via dns over https.
These IPs are used in block-informer to inform user when proxy is ON. These IPs are used in block-informer to inform user when proxy is ON.
Task 2. Downloads PAC proxy script from antizapret/anticenz/my Google Drive and sets it in Chromium settings. Task 2. Downloads PAC proxy script from antizapret/anticenz/
my Google Drive and sets it in Chromium settings.
Task 3. Schedules tasks 1 & 2 for every 4 hours. Task 3. Schedules tasks 1 & 2 for every 4 hours.
*/ */
@ -25,26 +26,26 @@
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',
'2a02:27ac::10': 'proxy.antizapret.prostovpn.org' '2a02:27ac::10': 'proxy.antizapret.prostovpn.org',
} },
}, },
Антиценз: { Антиценз: {
pacUrl: 'https://config.anticenz.org/proxy.pac', pacUrl: 'https://config.anticenz.org/proxy.pac',
proxyHosts: ['gw2.anticenz.org'], proxyHosts: ['gw2.anticenz.org'],
proxyIps: { proxyIps: {
'5.196.220.114': 'gw2.anticenz.org' '5.196.220.114': 'gw2.anticenz.org',
} },
}, },
Обаа_свитчах: { Обаа_свитчах: {
//pacUrl: 'https://drive.google.com/uc?export=download&id=0B-ZCVSvuNWf0akpCOURNS2VCTmc', // 0.14 // pacUrl: 'https://drive.google.com/uc?export=download&id=0B-ZCVSvuNWf0akpCOURNS2VCTmc', // 0.14
pacUrl: 'https://drive.google.com/uc?export=download&id=0B-ZCVSvuNWf0bzNUR2F4RF8wOU0', // 0.15 pacUrl: 'https://drive.google.com/uc?export=download&id=0B-ZCVSvuNWf0bzNUR2F4RF8wOU0', // 0.15
proxyHosts: ['proxy.antizapret.prostovpn.org', 'gw2.anticenz.org'], proxyHosts: ['proxy.antizapret.prostovpn.org', 'gw2.anticenz.org'],
proxyIps: { proxyIps: {
'195.123.209.38': 'proxy.antizapret.prostovpn.org', '195.123.209.38': 'proxy.antizapret.prostovpn.org',
'2a02:27ac::10': 'proxy.antizapret.prostovpn.org', '2a02:27ac::10': 'proxy.antizapret.prostovpn.org',
'5.196.220.114': 'gw2.anticenz.org' '5.196.220.114': 'gw2.anticenz.org',
} },
} },
}, },
_currentPacProviderKey: 'Оба_и_на_свитчах', _currentPacProviderKey: 'Оба_и_на_свитчах',
@ -52,20 +53,26 @@
isProxied(ip) { isProxied(ip) {
// Executed on each request with ip. Make it as fast as possible. // Executed on each request with ip. Make it as fast as possible.
return this._currentPacProviderKey && this.pacProviders[this._currentPacProviderKey].proxyIps.hasOwnProperty(ip); // Property lookups are cheap (I tested).
// The benefit of removing lookups is little, e.g. this._currentProxyIps && this._currentProxyIps.hasOwnProperty(ip); return this._currentPacProviderKey
&& this.pacProviders[this._currentPacProviderKey]
.proxyIps.hasOwnProperty(ip);
}, },
mustBeKey(key) { mustBeKey(key) {
if ( !(key === null || this.pacProviders[key]) ) { if ( !(key === null || this.pacProviders[key]) ) {
throw new IllegalArgumentException('No provider for key:' + key); throw new TypeError('No provider for key:' + key);
} }
}, },
get currentPacProviderKey() { return this._currentPacProviderKey }, get currentPacProviderKey() {
return this._currentPacProviderKey;
},
set currentPacProviderKey(newKey) { set currentPacProviderKey(newKey) {
this.mustBeKey(newKey); this.mustBeKey(newKey);
@ -77,16 +84,15 @@
if(key) { if(key) {
this.mustBeKey(key); this.mustBeKey(key);
} } else {
else {
key = this.currentPacProviderKey; key = this.currentPacProviderKey;
} }
return this.pacProviders[key]; return this.pacProviders[key];
}, },
/* /* 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.
*/ */
ifFirstInstall: false, ifFirstInstall: false,
lastPacUpdateStamp: 0, lastPacUpdateStamp: 0,
@ -100,7 +106,10 @@
// Copy only settable properties (except functions). // Copy only settable properties (except functions).
const onlySettable = {}; const onlySettable = {};
for(const key of Object.keys(this)) { for(const key of Object.keys(this)) {
if (Object.getOwnPropertyDescriptor(this, key).writable && typeof(this[key]) !== 'function') { if (
Object.getOwnPropertyDescriptor(this, key).writable
&& typeof(this[key]) !== 'function'
) {
onlySettable[key] = this[key]; onlySettable[key] = this[key];
} }
} }
@ -142,7 +151,11 @@
if (key === null) { if (key === null) {
// No pac provider set. // No pac provider set.
return cb({clarification:{message:'Сперва выберите PAC-провайдера.'}}); return cb({
clarification: {
message: 'Сперва выберите PAC-провайдера.',
},
});
} }
const pacProvider = this.getPacProvider(key); const pacProvider = this.getPacProvider(key);
@ -199,23 +212,28 @@
setAlarms() { setAlarms() {
let nextUpdateMoment = this.lastPacUpdateStamp + this._pacUpdatePeriodInMinutes*60*1000; let nextUpdateMoment = this.lastPacUpdateStamp
+ this._pacUpdatePeriodInMinutes*60*1000;
const now = Date.now(); const now = Date.now();
if (nextUpdateMoment < now) { if (nextUpdateMoment < now) {
nextUpdateMoment = now; nextUpdateMoment = now;
} }
console.log( 'Next PAC update is scheduled on', new Date(nextUpdateMoment).toLocaleString('ru-RU') ); console.log(
'Next PAC update is scheduled on',
new Date(nextUpdateMoment).toLocaleString('ru-RU')
);
chrome.alarms.create( chrome.alarms.create(
this._periodicUpdateAlarmReason, this._periodicUpdateAlarmReason,
{ {
when: nextUpdateMoment, when: nextUpdateMoment,
periodInMinutes: this._pacUpdatePeriodInMinutes periodInMinutes: this._pacUpdatePeriodInMinutes,
} }
); );
return nextUpdateMoment === now; // ifAlarmTriggered. May be changed in the future. // ifAlarmTriggered. May be changed in the future.
return nextUpdateMoment === now;
}, },
@ -251,7 +269,7 @@
} }
) )
); );
} },
}; };
@ -260,15 +278,20 @@
checkChromeError(); checkChromeError();
/* /*
Event handlers that ALWAYS work (even if installation is not done or failed). Event handlers that ALWAYS work (even if installation is not done
or failed).
E.g. install window may fail to open or be closed by user accidentally. E.g. install window may fail to open or be closed by user accidentally.
In such case extension _should_ try to work on default parameters. In such case extension _should_ try to work on default parameters.
*/ */
const antiCensorRu = window.antiCensorRu;
chrome.alarms.onAlarm.addListener( chrome.alarms.onAlarm.addListener(
(alarm) => { (alarm) => {
if (alarm.name === antiCensorRu._periodicUpdateAlarmReason) { if (alarm.name === antiCensorRu._periodicUpdateAlarmReason) {
console.log('Periodic PAC update triggered:', new Date().toLocaleString('ru-RU')); console.log(
'Periodic PAC update triggered:',
new Date().toLocaleString('ru-RU')
);
antiCensorRu.syncWithPacProvider(/* Swallows errors. */); antiCensorRu.syncWithPacProvider(/* Swallows errors. */);
} }
@ -276,7 +299,7 @@
); );
console.log('Alarm listener installed. We won\'t miss any PAC update.'); console.log('Alarm listener installed. We won\'t miss any PAC update.');
window.addEventListener('online', () => { window.addEventListener('online', () => {
console.log('We are online, checking periodic updates...'); console.log('We are online, checking periodic updates...');
antiCensorRu.setAlarms(); antiCensorRu.setAlarms();
@ -288,11 +311,16 @@
if (!antiCensorRu.ifFirstInstall) { if (!antiCensorRu.ifFirstInstall) {
// LAUNCH, RELOAD, UPDATE // LAUNCH, RELOAD, UPDATE
antiCensorRu._currentPacProviderKey = oldStorage._currentPacProviderKey || null; // Old or migrate. // Use old or migrate to default.
antiCensorRu.lastPacUpdateStamp = oldStorage.lastPacUpdateStamp || antiCensorRu.lastPacUpdateStamp; // Old or migrate to default. antiCensorRu._currentPacProviderKey =
console.log( 'Last PAC update was on', new Date(antiCensorRu.lastPacUpdateStamp).toLocaleString('ru-RU') ); oldStorage._currentPacProviderKey || null;
} antiCensorRu.lastPacUpdateStamp =
else { oldStorage.lastPacUpdateStamp || antiCensorRu.lastPacUpdateStamp;
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();
@ -308,7 +336,8 @@
} }
/* /*
1. There is no way to check that chrome.runtime.onInstalled wasn't fired except timeout. 1. There is no way to check that chrome.runtime.onInstalled wasn't fired
except timeout.
Otherwise we could put storage migration code only there. Otherwise we could put storage migration code only there.
2. We have to check storage for migration before using it. 2. We have to check storage for migration before using it.
Better on each launch then on each pull. Better on each launch then on each pull.
@ -343,26 +372,27 @@
function asyncLogGroup(...args) { function asyncLogGroup(...args) {
const cb = args.pop() || (() => {}); const cb = args.pop() || (() => {});
console.group.apply(console, args); console.group(...args);
return function(...cbArgs) { return function(...cbArgs) {
console.groupEnd(); console.groupEnd();
console.log('Group finished.'); console.log('Group finished.');
cb.apply(this, cbArgs); cb(...cbArgs);
} };
} }
function checkChromeError(betterStack) { function checkChromeError(betterStack) {
// Chrome API calls your cb in a context different from the point of API method invokation. // Chrome API calls your cb in a context different from the point of API
// method invokation.
const err = chrome.runtime.lastError || chrome.extension.lastError || null; const err = chrome.runtime.lastError || chrome.extension.lastError || null;
if (err) { if (err) {
const args = ['API returned error:', err]; const args = ['API returned error:', err];
if (betterStack) { if (betterStack) {
args.push('\n' + betterStack); args.push('\n' + betterStack);
} }
console.warn.apply(console, args); console.warn(...args);
} }
return err; return err;
@ -371,14 +401,14 @@
function chromified(cb, ...replaceArgs) { function chromified(cb, ...replaceArgs) {
const stack = (new Error()).stack; const stack = (new Error()).stack;
// Take error first callback and covert it to chrome api callback. // Take error first callback and convert it to chrome api callback.
return function(...args) { return function(...args) {
if (replaceArgs.length) { if (replaceArgs.length) {
args = replaceArgs; args = replaceArgs;
} }
const err = checkChromeError(stack); const err = checkChromeError(stack);
cb && cb.call(this, err, ...args); cb && cb.call(null, err, ...args);
}; };
@ -390,8 +420,8 @@
mode: 'pac_script', mode: 'pac_script',
pacScript: { pacScript: {
mandatory: false, mandatory: false,
data: pacData data: pacData,
} },
}; };
console.log('Setting chrome proxy settings...'); console.log('Setting chrome proxy settings...');
chrome.proxy.settings.set( {value: config}, () => { chrome.proxy.settings.set( {value: config}, () => {
@ -405,7 +435,7 @@
const ifThis = details.levelOfControl.startsWith('controlled_by_this'); const ifThis = details.levelOfControl.startsWith('controlled_by_this');
if (!ifThis) { if (!ifThis) {
console.warn('Failed, other extension is in control.'); console.warn('Failed, other extension is in control.');
return cb({clarification: {message:'Настройки прокси контролирует другое расширение. <a href="chrome://settings/search#proxy">Какое?</a>'}}); return cb({clarification: {message: 'Настройки прокси контролирует другое расширение. <a href="chrome://settings/search#proxy">Какое?</a>'}});
} }
console.log('Successfuly set PAC in proxy settings..'); console.log('Successfuly set PAC in proxy settings..');
cb(); cb();
@ -426,7 +456,9 @@
.then( (text) => cb(err, text), cb ); .then( (text) => cb(err, text), cb );
const status = res.status; const status = res.status;
if ( !( status >= 200 && status < 300 || status === 304 ) ) { if ( !( status >= 200 && status < 300 || status === 304 ) ) {
res.clarification = {message: 'Получен ответ с неудачным HTTP-кодом ' + status + '.'}; res.clarification = {
message: 'Получен ответ с неудачным HTTP-кодом ' + status + '.',
};
return textCb(res); return textCb(res);
} }
console.log('GETed with success:', url, Date.now() - start); console.log('GETed with success:', url, Date.now() - start);
@ -435,7 +467,9 @@
}, },
(err) => { (err) => {
err.clarification = {message: 'Что-то не так с сетью, проверьте соединение.'}; err.clarification = {
message: 'Что-то не так с сетью, проверьте соединение.',
};
cb && cb(err); cb && cb(err);
} }
@ -446,17 +480,17 @@
// args: { host:..., type: 'AAAA', filter: ['AAAA'] } // args: { host:..., type: 'AAAA', filter: ['AAAA'] }
if (!(args.host && args.type && cb)) { if (!(args.host && args.type && cb)) {
throw new Error('Wrong args:' + host + ',' + type); throw new Error('Wrong args:' + args.host + ',' + args.type);
} }
const type2str = { const type2str = {
// https://en.wikipedia.org/wiki/List_of_DNS_record_types // https://en.wikipedia.org/wiki/List_of_DNS_record_types
// A, AAAA may be localized (github, e.g.), but you may use ANY // A, AAAA may be localized (github, e.g.), but you may use ANY
1: 'A', // IPv4 1: 'A', // IPv4
28: 'AAAA', // IPv6 28: 'AAAA', // IPv6
2: 'NS', 2: 'NS',
5: 'CNAME', // Synonyms, returned by server together with A/AAAA. 5: 'CNAME', // Synonyms, returned by server together with A/AAAA.
255: 'ANY' // Deprecated on some servers, not recommended 255: 'ANY', // Deprecated on some servers, not recommended
}; };
httpGet( httpGet(
@ -468,25 +502,25 @@
console.log('Json parsed.'); console.log('Json parsed.');
if (err || res.Status) { if (err || res.Status) {
const msg = ['Answer', 'Comment', 'Status'] const msg = ['Answer', 'Comment', 'Status']
.filter( (prop) => res[ prop ] ) .filter( (prop) => res[prop] )
.map( (prop) => prop + ': ' + JSON.stringify( res[ prop ] ) ) .map( (prop) => prop + ': ' + JSON.stringify( res[prop] ) )
.join(', \n'); .join(', \n');
err.clarification.message += ' Сервер (json): ' + msg; err.clarification.message += ' Сервер (json): ' + msg;
err.data = err.data || res; err.data = err.data || res;
} } else {
else {
res = res.Answer || []; res = res.Answer || [];
for (const record of res) { for (const record of res) {
record.type = type2str[ record.type ]; record.type = type2str[record.type];
} }
if ( args.filter ) { if ( args.filter ) {
res = res.filter( (record) => args.filter.indexOf( record.type ) > -1 ); res = res.filter(
(record) => args.filter.indexOf( record.type ) > -1
);
} }
} }
} } catch(e) {
catch(e) { err = e || err || {clarification: {message: ''}};
err = e || err || {clarification:{message:''}}; err.clarification = err.clarification || {message: ''};
err.clarification = err.clarification || { message: '' };
err.clarification.message += ' Сервер (текст): '+ res; err.clarification.message += ' Сервер (текст): '+ res;
err.clarification.message.trim(); err.clarification.message.trim();
err.data = err.data || res; err.data = err.data || res;
@ -495,7 +529,7 @@
cb( err, res ); cb( err, res );
} }
); );
}; }
function getDnsRecords(args, cb) { function getDnsRecords(args, cb) {
@ -506,18 +540,22 @@
host: 'proxy.navalny.cia.gov', host: 'proxy.navalny.cia.gov',
// Optional // Optional
types: { types: {
string: ['A', 'AAAA'], // <- Default. Makes one request per each type. string: ['A', 'AAAA'],
filter: ['A', 'AAAA'], // <- Default. E.g., you want to get rid of CNAME type from response. // ^ Default. Makes one request per each type.
filter: ['A', 'AAAA'],
// ^ Default. E.g., you want to get rid of CNAME type from
// response.
} }
} }
Exmple of answer from google: Exmple of answer from google:
"Answer": "Answer":
[ [
{ {
"name": "apple.com.", // Always matches name in the Question section "name": "apple.com.", // Always matches name in the Question
"type": 1, // A - Standard DNS RR type // section.
"TTL": 3599, // Record's time-to-live in seconds "type": 1, // A - Standard DNS RR type.
"data": "17.178.96.59" // Data for A - IP address as text "TTL": 3599, // Record's time-to-live in seconds.
"data": "17.178.96.59" // Data for A - IP address as text.
}, },
... ...
Exmple of output: Exmple of output:
@ -529,25 +567,37 @@
} }
args.types = Object.assign({ args.types = Object.assign({
string: ['A', 'AAAA'], string: ['A', 'AAAA'],
filter: ['A', 'AAAA'] filter: ['A', 'AAAA'],
}, args.types); }, args.types);
const promises = args.types.string.map( const promises = args.types.string.map(
(type) => new Promise( (resolve, reject) => (type) => new Promise( (resolve, reject) =>
getOneDnsRecord({ host: args.host, type: type, filter: args.types.filter }, (err, res) => err ? reject(err) : resolve(res) ) getOneDnsRecord(
{host: args.host, type: type, filter: args.types.filter},
(err, res) => err ? reject(err) : resolve(res) )
) )
); );
Promise.all(promises).then( (answers) => cb( null, [].concat.apply([], answers) ), cb ); Promise.all(promises).then(
(answers) => cb( null, [].concat(...answers) ),
cb
);
} }
const getIpDnsRecords = (host, cb) => getDnsRecords({ host: host }, cb); const getIpDnsRecords = (host, cb) => getDnsRecords({host: host}, cb);
function updatePacProxyIps(provider, cb) { function updatePacProxyIps(provider, cb) {
cb = asyncLogGroup('Getting IP for '+ provider.proxyHosts.join(', ') +'...', cb); cb = asyncLogGroup(
'Getting IP for '+ provider.proxyHosts.join(', ') + '...',
cb
);
let failure = { let failure = {
clarification: {message:'Не удалось получить один или несколько IP адресов для прокси-серверов. Иконка для уведомления об обходе блокировок может не отображаться.'}, clarification: {
errors: {} message: 'Не удалось получить один или несколько IP адресов для' +
' прокси-серверов. Иконка для уведомления об обходе блокировок ' +
'может не отображаться.',
},
errors: {},
}; };
let i = 0; let i = 0;
provider.proxyHosts.map( provider.proxyHosts.map(
@ -557,9 +607,10 @@
if (!err) { if (!err) {
provider.proxyIps = provider.proxyIps || {}; provider.proxyIps = provider.proxyIps || {};
records.forEach( (ans) => provider.proxyIps[ ans.data ] = proxyHost ); records.forEach(
} (ans) => provider.proxyIps[ans.data] = proxyHost
else { );
} else {
failure.errors[proxyHost] = err; failure.errors[proxyHost] = err;
} }
@ -574,7 +625,10 @@
function setPacScriptFromProvider(provider, cb) { function setPacScriptFromProvider(provider, cb) {
cb = asyncLogGroup('Getting pac script from provider...', provider.pacUrl, cb); cb = asyncLogGroup(
'Getting pac script from provider...', provider.pacUrl,
cb
);
httpGet( httpGet(
provider.pacUrl, provider.pacUrl,
@ -582,8 +636,9 @@
if (err) { if (err) {
err.clarification = { err.clarification = {
message: 'Не удалось скачать PAC-скрипт с адреса: ' + provider.pacUrl + '.', message: 'Не удалось скачать PAC-скрипт с адреса: '
prev: err.clarification + provider.pacUrl + '.',
prev: err.clarification,
}; };
return cb(err); return cb(err);
} }
@ -612,6 +667,6 @@ window.addEventListener('unhandledrejection', (event) => {
chrome.proxy.settings.onChange.addListener((details) => { chrome.proxy.settings.onChange.addListener((details) => {
console.log('Proxy settings changed.', details); console.log('Proxy settings changed.', details);
const ifOther = details.levelOfControl.startsWith('controlled_by_other'); // const ifOther = details.levelOfControl.startsWith('controlled_by_other');
}); });

View File

@ -1,10 +1,12 @@
'use strict'; 'use strict';
// Shows user browserAction icon if any part of the current site is being blocked and proxied. // Shows user browserAction icon if any part of the current site is being
// blocked and proxied.
/* /*
In what moment the title of the previous icon is cleared? In what moment the title of the previous icon is cleared?
By my observations it usually takes place near tabs.onUpdate of tab status to "loading". By my observations it usually takes place near tabs.onUpdate of tab status
to "loading".
So if you set a title earlier it may be cleared by browser. So if you set a title earlier it may be cleared by browser.
It pertains not only to page refesh but to newly opened pages too. It pertains not only to page refesh but to newly opened pages too.
Also on loosing title see: Also on loosing title see:
@ -13,7 +15,7 @@
**/ **/
window.chrome.browserAction.setBadgeBackgroundColor({ window.chrome.browserAction.setBadgeBackgroundColor({
color: '#db4b2f' color: '#db4b2f',
}); });
window.tabWithError2ip = {}; // For errors only: Error? -> Check this IP! window.tabWithError2ip = {}; // For errors only: Error? -> Check this IP!
@ -30,7 +32,7 @@ window.tabWithError2ip = {}; // For errors only: Error? -> Check this IP!
function onTabUpdate(tabId) { function onTabUpdate(tabId) {
if (_tabCallbacks[tabId]) { if (_tabCallbacks[tabId]) {
_tabCallbacks[tabId].map( f => f() ); _tabCallbacks[tabId].map( (f) => f() );
delete _tabCallbacks[tabId]; delete _tabCallbacks[tabId];
} }
} }
@ -38,28 +40,34 @@ window.tabWithError2ip = {}; // For errors only: Error? -> Check this IP!
chrome.tabs.onUpdated.addListener( onTabUpdate ); chrome.tabs.onUpdated.addListener( onTabUpdate );
function isInsideTabWithIp(requestDetails) { function isInsideTabWithIp(requestDetails) {
return requestDetails.tabId !== -1 && requestDetails.ip return requestDetails.tabId !== -1 && requestDetails.ip;
} }
chrome.webRequest.onErrorOccurred.addListener( chrome.webRequest.onErrorOccurred.addListener(
(requestDetails) => (requestDetails) =>
isInsideTabWithIp(requestDetails) && isInsideTabWithIp(requestDetails) &&
( (
isProxiedAndInformed(requestDetails) || requestDetails.type === 'main_frame' && ( window.tabWithError2ip[requestDetails.tabId] = requestDetails.ip ) isProxiedAndInformed(requestDetails)
|| requestDetails.type === 'main_frame'
&& (window.tabWithError2ip[requestDetails.tabId] = requestDetails.ip)
), ),
{ urls: ['<all_urls>'] } {urls: ['<all_urls>']}
); );
chrome.tabs.onRemoved.addListener( (tabId) => { onTabUpdate(tabId); delete window.tabWithError2ip[tabId] } ); chrome.tabs.onRemoved.addListener( (tabId) => {
onTabUpdate(tabId);
delete window.tabWithError2ip[tabId];
});
function updateTitle(requestDetails, cb) { function updateTitle(requestDetails, cb) {
chrome.browserAction.getTitle( chrome.browserAction.getTitle(
{ tabId: requestDetails.tabId }, {tabId: requestDetails.tabId},
(title) => { (title) => {
const ifTitleSetAlready = /\n/.test(title); const ifTitleSetAlready = /\n/.test(title);
const proxyHost = window.antiCensorRu.getPacProvider().proxyIps[ requestDetails.ip ]; const proxyHost = window.antiCensorRu.getPacProvider()
.proxyIps[requestDetails.ip];
const hostname = new URL( requestDetails.url ).hostname; const hostname = new URL( requestDetails.url ).hostname;
@ -68,25 +76,31 @@ window.tabWithError2ip = {}; // For errors only: Error? -> Check this IP!
const proxyTitle = 'Прокси:'; const proxyTitle = 'Прокси:';
if (!ifTitleSetAlready) { if (!ifTitleSetAlready) {
title = 'Разблокированы:\n'+ indent + hostname +'\n'+ proxyTitle +'\n'+ indent + proxyHost; title = 'Разблокированы:\n' + indent + hostname + '\n'
+ proxyTitle + '\n' + indent + proxyHost;
ifShouldUpdateTitle = true; ifShouldUpdateTitle = true;
chrome.browserAction.setBadgeText({ chrome.browserAction.setBadgeText({
tabId: requestDetails.tabId, tabId: requestDetails.tabId,
text: requestDetails.type === 'main_frame' ? '1' : '%1' text: requestDetails.type === 'main_frame' ? '1' : '%1',
}); });
} } else {
else {
const hostsProxiesPair = title.split(proxyTitle); const hostsProxiesPair = title.split(proxyTitle);
if (hostsProxiesPair[1].indexOf(proxyHost) === -1) { if (hostsProxiesPair[1].indexOf(proxyHost) === -1) {
title = title.replace(hostsProxiesPair[1], hostsProxiesPair[1] +'\n'+ indent + proxyHost); title = title.replace(
hostsProxiesPair[1],
hostsProxiesPair[1] + '\n' + indent + proxyHost
);
ifShouldUpdateTitle = true; ifShouldUpdateTitle = true;
} }
if (hostsProxiesPair[0].indexOf(hostname) === -1) { if (hostsProxiesPair[0].indexOf(hostname) === -1) {
title = title.replace(proxyTitle, indent + hostname +'\n'+ proxyTitle); title = title.replace(
proxyTitle,
indent + hostname + '\n' + proxyTitle
);
ifShouldUpdateTitle = true; ifShouldUpdateTitle = true;
const _cb = cb; const _cb = cb;
@ -97,7 +111,8 @@ window.tabWithError2ip = {}; // For errors only: Error? -> Check this IP!
chrome.browserAction.setBadgeText( chrome.browserAction.setBadgeText(
{ {
tabId: requestDetails.tabId, tabId: requestDetails.tabId,
text: ( isNaN( result.charAt(0) ) && result.charAt(0) || '' ) + (hostsProxiesPair[0].split('\n').length - 1) text: (isNaN( result.charAt(0)) && result.charAt(0) || '')
+ (hostsProxiesPair[0].split('\n').length - 1),
} }
); );
return _cb(); return _cb();
@ -111,7 +126,7 @@ window.tabWithError2ip = {}; // For errors only: Error? -> Check this IP!
if (ifShouldUpdateTitle) { if (ifShouldUpdateTitle) {
chrome.browserAction.setTitle({ chrome.browserAction.setTitle({
title: title, title: title,
tabId: requestDetails.tabId tabId: requestDetails.tabId,
}); });
} }
@ -121,12 +136,12 @@ window.tabWithError2ip = {}; // For errors only: Error? -> Check this IP!
); );
} }
let previousUpdateTitleFinished = Promise.resolve(); let previousUpdateTitleFinished = Promise.resolve();
function isProxiedAndInformed(requestDetails) { function isProxiedAndInformed(requestDetails) {
if ( !(requestDetails.ip && antiCensorRu.isProxied( requestDetails.ip )) ) { if ( !(requestDetails.ip
&& window.antiCensorRu.isProxied( requestDetails.ip )) ) {
return false; return false;
} }
@ -136,7 +151,8 @@ window.tabWithError2ip = {}; // For errors only: Error? -> Check this IP!
() => new Promise( () => new Promise(
(resolve) => { (resolve) => {
const cb = () => updateTitle( requestDetails, resolve ); const cb = () => updateTitle( requestDetails, resolve );
return ifMainFrame ? afterTabUpdated(requestDetails.tabId, cb) : cb(); return ifMainFrame
? afterTabUpdated(requestDetails.tabId, cb) : cb();
} }
) )
); );
@ -145,8 +161,9 @@ window.tabWithError2ip = {}; // For errors only: Error? -> Check this IP!
} }
chrome.webRequest.onResponseStarted.addListener( chrome.webRequest.onResponseStarted.addListener(
(requestDetails) => isInsideTabWithIp(requestDetails) && isProxiedAndInformed(requestDetails), (requestDetails) => isInsideTabWithIp(requestDetails)
{ urls: ['<all_urls>'] } && isProxiedAndInformed(requestDetails),
{urls: ['<all_urls>']}
); );
} }

View File

@ -5,19 +5,39 @@
const createMenuLinkEntry = (title, tab2url) => chrome.contextMenus.create({ const createMenuLinkEntry = (title, tab2url) => chrome.contextMenus.create({
title: title, title: title,
contexts: ['browser_action'], contexts: ['browser_action'],
onclick: (menuInfo, tab) => Promise.resolve( tab2url( tab ) ).then( (url) => chrome.tabs.create({url: url}) ) onclick:
(menuInfo, tab) => Promise.resolve( tab2url( tab ) )
.then( (url) => chrome.tabs.create({url: url}) ),
}); });
createMenuLinkEntry( 'Сайт доступен из-за границы? Is up?', (tab) => 'http://isup.me/'+ new URL(tab.url).hostname ); createMenuLinkEntry(
'Сайт доступен из-за границы? Is up?',
(tab) => 'http://isup.me/' + new URL(tab.url).hostname
);
createMenuLinkEntry( 'Сайт в реестре блокировок?', (tab) => 'https://antizapret.info/index.php?search=' + tab.url ); createMenuLinkEntry(
'Сайт в реестре блокировок?',
(tab) => 'https://antizapret.info/index.php?search=' + tab.url
);
createMenuLinkEntry( 'Из архива archive.org', (tab) => 'https://web.archive.org/web/*/' + tab.url ); createMenuLinkEntry(
'Из архива archive.org',
(tab) => 'https://web.archive.org/web/*/' + tab.url
);
createMenuLinkEntry( 'Открыть веб прокси (не наш)', (tab) => 'https://kproxy.com' ); createMenuLinkEntry(
'Открыть веб прокси (не наш)',
(tab) => 'https://kproxy.com'
);
createMenuLinkEntry( 'Другие варианты разблокировки', (tab) => 'https://rebrand.ly/unblock#' + tab.url ); createMenuLinkEntry(
'Другие варианты разблокировки',
(tab) => 'https://rebrand.ly/unblock#' + tab.url
);
createMenuLinkEntry( 'У меня проблемы с расширением!', (tab) => 'https://rebrand.ly/ac-support'); createMenuLinkEntry(
'У меня проблемы с расширением!',
(tab) => 'https://rebrand.ly/ac-support'
);
}; }

View File

@ -10,8 +10,7 @@ chrome.runtime.getBackgroundPage( (backgroundPage) => {
if (msg) { if (msg) {
status.classList.remove('off'); status.classList.remove('off');
status.innerHTML = msg; status.innerHTML = msg;
} } else {
else {
status.classList.add('off'); status.classList.add('off');
} }
@ -34,7 +33,7 @@ chrome.runtime.getBackgroundPage( (backgroundPage) => {
[24, ' дн'], [24, ' дн'],
[7, ' недель'], [7, ' недель'],
[4, ' месяцев'], [4, ' месяцев'],
[12, ' г'] [12, ' г'],
]; ];
for(const g of gauges) { for(const g of gauges) {
const diffy = Math.floor(diff / g[0]); const diffy = Math.floor(diff / g[0]);
@ -48,12 +47,15 @@ chrome.runtime.getBackgroundPage( (backgroundPage) => {
const dateElement = document.querySelector('.update-date'); const dateElement = document.querySelector('.update-date');
dateElement.innerText = dateForUser; dateElement.innerText = dateForUser;
dateElement.title = new Date(antiCensorRu.lastPacUpdateStamp).toLocaleString('ru-RU'); dateElement.title = new Date(antiCensorRu.lastPacUpdateStamp)
.toLocaleString('ru-RU');
}; };
setDate(); setDate();
chrome.storage.onChanged.addListener( (changes) => changes.lastPacUpdateStamp.newValue && setDate() ); chrome.storage.onChanged.addListener(
(changes) => changes.lastPacUpdateStamp.newValue && setDate()
);
// CLOSE BUTTON // CLOSE BUTTON
@ -66,7 +68,7 @@ chrome.runtime.getBackgroundPage( (backgroundPage) => {
const id = antiCensorRu.currentPacProviderKey || 'none'; const id = antiCensorRu.currentPacProviderKey || 'none';
return document.querySelector('#'+id); return document.querySelector('#'+id);
} };
const checkChosenProvider = () => currentProviderRadio().checked = true; const checkChosenProvider = () => currentProviderRadio().checked = true;
const showError = (err) => { const showError = (err) => {
@ -81,7 +83,9 @@ chrome.runtime.getBackgroundPage( (backgroundPage) => {
} }
message = message.trim(); message = message.trim();
setStatusTo( setStatusTo(
`<span style="color:red">${ifNotCritical ? 'Некритичная ошибка.' : 'Ошибка!'}</span> `<span style="color:red">
${ifNotCritical ? 'Некритичная ошибка.' : 'Ошибка!'}
</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">[Ещё&nbsp;подробнее]</a>` <a href class="link-button">[Ещё&nbsp;подробнее]</a>`
@ -91,7 +95,9 @@ chrome.runtime.getBackgroundPage( (backgroundPage) => {
const div = document.createElement('div'); const div = document.createElement('div');
div.innerHTML = ` div.innerHTML = `
Более подробную информацию можно узнать из логов фоновой страницы:<br/> Более подробную информацию можно узнать из логов фоновой страницы:<br/>
<a href="chrome://extensions?id=${chrome.runtime.id}" data-in-bg="true">chrome://extensions</a> Это расширение Отладка страниц: фоновая страница Console (DevTools) <a href="chrome://extensions?id=${chrome.runtime.id}" data-in-bg="true">
chrome://extensions</a>
Это расширение Отладка страниц: фоновая страница Console (DevTools)
<br> <br>
Ещё: ${JSON.stringify({err: err, stack: err.stack})} Ещё: ${JSON.stringify({err: err, stack: err.stack})}
`; `;
@ -102,7 +108,7 @@ chrome.runtime.getBackgroundPage( (backgroundPage) => {
}; };
const enableDisableInputs = function () { const enableDisableInputs = function() {
const inputs = document.querySelectorAll('input'); const inputs = document.querySelectorAll('input');
for ( let i = 0; i < inputs.length; i++ ) { for ( let i = 0; i < inputs.length; i++ ) {
@ -118,13 +124,12 @@ chrome.runtime.getBackgroundPage( (backgroundPage) => {
operation((err) => { operation((err) => {
if (err) { if (err) {
showError(err); showError(err);
} } else {
else {
setStatusTo(afterStatus); setStatusTo(afterStatus);
onSuccess && onSuccess(); onSuccess && onSuccess();
} }
enableDisableInputs(); enableDisableInputs();
}) });
}; };
@ -132,13 +137,24 @@ chrome.runtime.getBackgroundPage( (backgroundPage) => {
const _firstChild = ul.firstChild; const _firstChild = ul.firstChild;
for( const providerKey of Object.keys(antiCensorRu.pacProviders).sort() ) { for( const providerKey of Object.keys(antiCensorRu.pacProviders).sort() ) {
const li = document.createElement('li'); const li = document.createElement('li');
li.innerHTML = `<input type="radio" name="pacProvider" id="${providerKey}"> <label for="${providerKey}">${providerKey}</label> <a href class="link-button checked-radio-panel">[обновить]</a>`; li.innerHTML = `<input type="radio" name="pacProvider" id="${providerKey}">
li.querySelector('.link-button').onclick = () => { conduct( 'Обновляем...', (cb) => antiCensorRu.syncWithPacProvider(cb), 'Обновлено.' ); return false; }; <label for="${providerKey}">${providerKey}</label>
<a href class="link-button checked-radio-panel">[обновить]</a>`;
li.querySelector('.link-button').onclick =
() => {
conduct(
'Обновляем...', (cb) => antiCensorRu.syncWithPacProvider(cb),
'Обновлено.'
);
return false;
};
ul.insertBefore( li, _firstChild ); ul.insertBefore( li, _firstChild );
} }
checkChosenProvider(); checkChosenProvider();
const radios = [].slice.apply( document.querySelectorAll('[name=pacProvider]') ); const radios = [].slice.apply(
document.querySelectorAll('[name=pacProvider]')
);
for(const radio of radios) { for(const radio of radios) {
radio.onclick = function(event) { radio.onclick = function(event) {
@ -153,8 +169,7 @@ chrome.runtime.getBackgroundPage( (backgroundPage) => {
'Отключено.', 'Отключено.',
checkChosenProvider checkChosenProvider
); );
} } else {
else {
conduct( conduct(
'Установка...', 'Установка...',
(cb) => antiCensorRu.installPac(pacKey, cb), (cb) => antiCensorRu.installPac(pacKey, cb),

View File

@ -13,9 +13,9 @@ const updateLinks = () => {
for (let i = 0; i < links.length; i++) { for (let i = 0; i < links.length; i++) {
const ln = links[i]; const ln = links[i];
const location = ln.href; const location = ln.href;
ln.onclick = function () { ln.onclick = function() {
chrome.tabs.create({ active: !this.dataset.inBg, url: location }); chrome.tabs.create({active: !this.dataset.inBg, url: location});
return false; return false;
}; };
@ -24,6 +24,11 @@ const updateLinks = () => {
}; };
new MutationObserver( updateLinks ) new MutationObserver( updateLinks )
.observe(target, { attributes: false, subtree: true, childList: true, characterData: false }); .observe(target, {
attributes: false,
subtree: true,
childList: true,
characterData: false,
});
document.addEventListener('DOMContentLoaded', updateLinks); document.addEventListener('DOMContentLoaded', updateLinks);

View File

@ -4,13 +4,15 @@ const setStatusTo = (msg) => document.getElementById('status').innerHTML = msg;
const red = (text) => '<span style="color: red">' + text + '</span>'; const red = (text) => '<span style="color: red">' + text + '</span>';
const editor = ace.edit('editor'); const editor = window.ace.edit('editor');
editor.getSession().setOptions({ editor.getSession().setOptions({
mode: "ace/mode/javascript", mode: 'ace/mode/javascript',
useSoftTabs: true useSoftTabs: true,
}); });
chrome.proxy.settings.onChange.addListener( (details) => setStatusTo(red( details.levelOfControl + '!') ) ); chrome.proxy.settings.onChange.addListener(
(details) => setStatusTo(red( details.levelOfControl + '!') )
);
document.querySelector('#read-button').onclick = () => { document.querySelector('#read-button').onclick = () => {
@ -36,8 +38,8 @@ document.querySelector('#save-button').onclick = () => {
mode: 'pac_script', mode: 'pac_script',
pacScript: { pacScript: {
mandatory: false, mandatory: false,
data: editor.getValue() data: editor.getValue(),
} },
}; };
chrome.proxy.settings.set( {value: config}, () => alert('Saved!') ); chrome.proxy.settings.set( {value: config}, () => alert('Saved!') );

View File

@ -0,0 +1,21 @@
{
"name": "russian-censorship-bypass",
"version": "0.0.15",
"description": "Development tools for chromium extension",
"main": "index.js",
"scripts": {
"test": "echo \"Error: no test specified\" && exit 1",
"lint": "./node_modules/.bin/eslint ./extension/**/*.js --ignore-pattern vendor"
},
"author": "Ilya Ig. Petrov",
"license": "GPLv3",
"devDependencies": {
"eslint": "^3.11.1",
"eslint-config-airbnb": "^13.0.0",
"eslint-config-google": "^0.7.1",
"eslint-plugin-hapi": "^4.0.0",
"eslint-plugin-import": "^2.2.0",
"eslint-plugin-jsx-a11y": "^2.2.3",
"eslint-plugin-react": "^6.7.1"
}
}