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

@ -1,152 +1,169 @@
'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? /*
By my observations it usually takes place near tabs.onUpdate of tab status to "loading". In what moment the title of the previous icon is cleared?
So if you set a title earlier it may be cleared by browser. By my observations it usually takes place near tabs.onUpdate of tab status
It pertains not only to page refesh but to newly opened pages too. to "loading".
Also on loosing title see: So if you set a title earlier it may be cleared by browser.
https://github.com/ilyaigpetrov/repository-for-chrome-bugs/blob/master/browserAction-title-lost-after-setting/background.js It pertains not only to page refesh but to newly opened pages too.
Crazy parallel Chrome. Also on loosing title see:
**/ https://github.com/ilyaigpetrov/repository-for-chrome-bugs/blob/master/browserAction-title-lost-after-setting/background.js
Crazy parallel Chrome.
window.chrome.browserAction.setBadgeBackgroundColor({ **/
color: '#db4b2f'
}); window.chrome.browserAction.setBadgeBackgroundColor({
color: '#db4b2f',
window.tabWithError2ip = {}; // For errors only: Error? -> Check this IP! });
{ window.tabWithError2ip = {}; // For errors only: Error? -> Check this IP!
const _tabCallbacks = {}; {
function afterTabUpdated(tabId, cb) { const _tabCallbacks = {};
if (_tabCallbacks[tabId])
_tabCallbacks[tabId].push(cb); function afterTabUpdated(tabId, cb) {
else _tabCallbacks[tabId] = [cb]; if (_tabCallbacks[tabId])
} _tabCallbacks[tabId].push(cb);
else _tabCallbacks[tabId] = [cb];
function onTabUpdate(tabId) { }
if (_tabCallbacks[tabId]) {
_tabCallbacks[tabId].map( f => f() ); function onTabUpdate(tabId) {
delete _tabCallbacks[tabId]; if (_tabCallbacks[tabId]) {
} _tabCallbacks[tabId].map( (f) => f() );
} delete _tabCallbacks[tabId];
}
chrome.tabs.onUpdated.addListener( onTabUpdate ); }
function isInsideTabWithIp(requestDetails) { chrome.tabs.onUpdated.addListener( onTabUpdate );
return requestDetails.tabId !== -1 && requestDetails.ip
} function isInsideTabWithIp(requestDetails) {
return requestDetails.tabId !== -1 && requestDetails.ip;
chrome.webRequest.onErrorOccurred.addListener( }
(requestDetails) =>
isInsideTabWithIp(requestDetails) && chrome.webRequest.onErrorOccurred.addListener(
( (requestDetails) =>
isProxiedAndInformed(requestDetails) || requestDetails.type === 'main_frame' && ( window.tabWithError2ip[requestDetails.tabId] = requestDetails.ip ) isInsideTabWithIp(requestDetails) &&
), (
{ urls: ['<all_urls>'] } isProxiedAndInformed(requestDetails)
); || requestDetails.type === 'main_frame'
&& (window.tabWithError2ip[requestDetails.tabId] = requestDetails.ip)
chrome.tabs.onRemoved.addListener( (tabId) => { onTabUpdate(tabId); delete window.tabWithError2ip[tabId] } ); ),
{urls: ['<all_urls>']}
function updateTitle(requestDetails, cb) { );
chrome.browserAction.getTitle( chrome.tabs.onRemoved.addListener( (tabId) => {
{ tabId: requestDetails.tabId }, onTabUpdate(tabId);
(title) => { delete window.tabWithError2ip[tabId];
});
const ifTitleSetAlready = /\n/.test(title);
const proxyHost = window.antiCensorRu.getPacProvider().proxyIps[ requestDetails.ip ]; function updateTitle(requestDetails, cb) {
const hostname = new URL( requestDetails.url ).hostname; chrome.browserAction.getTitle(
{tabId: requestDetails.tabId},
let ifShouldUpdateTitle = false; (title) => {
const indent = ' ';
const proxyTitle = 'Прокси:'; const ifTitleSetAlready = /\n/.test(title);
const proxyHost = window.antiCensorRu.getPacProvider()
if (!ifTitleSetAlready) { .proxyIps[requestDetails.ip];
title = 'Разблокированы:\n'+ indent + hostname +'\n'+ proxyTitle +'\n'+ indent + proxyHost;
ifShouldUpdateTitle = true; const hostname = new URL( requestDetails.url ).hostname;
chrome.browserAction.setBadgeText({ let ifShouldUpdateTitle = false;
tabId: requestDetails.tabId, const indent = ' ';
text: requestDetails.type === 'main_frame' ? '1' : '%1' const proxyTitle = 'Прокси:';
});
if (!ifTitleSetAlready) {
} title = 'Разблокированы:\n' + indent + hostname + '\n'
else { + proxyTitle + '\n' + indent + proxyHost;
const hostsProxiesPair = title.split(proxyTitle); ifShouldUpdateTitle = true;
if (hostsProxiesPair[1].indexOf(proxyHost) === -1) { chrome.browserAction.setBadgeText({
title = title.replace(hostsProxiesPair[1], hostsProxiesPair[1] +'\n'+ indent + proxyHost); tabId: requestDetails.tabId,
ifShouldUpdateTitle = true; text: requestDetails.type === 'main_frame' ? '1' : '%1',
} });
if (hostsProxiesPair[0].indexOf(hostname) === -1) { } else {
title = title.replace(proxyTitle, indent + hostname +'\n'+ proxyTitle); const hostsProxiesPair = title.split(proxyTitle);
ifShouldUpdateTitle = true;
if (hostsProxiesPair[1].indexOf(proxyHost) === -1) {
const _cb = cb; title = title.replace(
cb = () => chrome.browserAction.getBadgeText( hostsProxiesPair[1],
{tabId: requestDetails.tabId}, hostsProxiesPair[1] + '\n' + indent + proxyHost
(result) => { );
ifShouldUpdateTitle = true;
chrome.browserAction.setBadgeText( }
{
tabId: requestDetails.tabId, if (hostsProxiesPair[0].indexOf(hostname) === -1) {
text: ( isNaN( result.charAt(0) ) && result.charAt(0) || '' ) + (hostsProxiesPair[0].split('\n').length - 1) title = title.replace(
} proxyTitle,
); indent + hostname + '\n' + proxyTitle
return _cb(); );
ifShouldUpdateTitle = true;
}
); const _cb = cb;
cb = () => chrome.browserAction.getBadgeText(
} {tabId: requestDetails.tabId},
} (result) => {
if (ifShouldUpdateTitle) { chrome.browserAction.setBadgeText(
chrome.browserAction.setTitle({ {
title: title, tabId: requestDetails.tabId,
tabId: requestDetails.tabId text: (isNaN( result.charAt(0)) && result.charAt(0) || '')
}); + (hostsProxiesPair[0].split('\n').length - 1),
} }
);
return cb(); return _cb();
} }
); );
}
}
}
let previousUpdateTitleFinished = Promise.resolve();
if (ifShouldUpdateTitle) {
function isProxiedAndInformed(requestDetails) { chrome.browserAction.setTitle({
title: title,
if ( !(requestDetails.ip && antiCensorRu.isProxied( requestDetails.ip )) ) { tabId: requestDetails.tabId,
return false; });
} }
const ifMainFrame = requestDetails.type === 'main_frame'; return cb();
previousUpdateTitleFinished = previousUpdateTitleFinished.then( }
() => new Promise( );
(resolve) => { }
const cb = () => updateTitle( requestDetails, resolve );
return ifMainFrame ? afterTabUpdated(requestDetails.tabId, cb) : cb(); let previousUpdateTitleFinished = Promise.resolve();
}
) function isProxiedAndInformed(requestDetails) {
);
if ( !(requestDetails.ip
return true; && window.antiCensorRu.isProxied( requestDetails.ip )) ) {
} return false;
}
chrome.webRequest.onResponseStarted.addListener(
(requestDetails) => isInsideTabWithIp(requestDetails) && isProxiedAndInformed(requestDetails), const ifMainFrame = requestDetails.type === 'main_frame';
{ urls: ['<all_urls>'] }
); previousUpdateTitleFinished = previousUpdateTitleFinished.then(
() => new Promise(
} (resolve) => {
const cb = () => updateTitle( requestDetails, resolve );
return ifMainFrame
? afterTabUpdated(requestDetails.tabId, cb) : cb();
}
)
);
return true;
}
chrome.webRequest.onResponseStarted.addListener(
(requestDetails) => isInsideTabWithIp(requestDetails)
&& isProxiedAndInformed(requestDetails),
{urls: ['<all_urls>']}
);
}

View File

@ -1,23 +1,43 @@
'use strict'; 'use strict';
{ {
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( 'Сайт в реестре блокировок?', (tab) => 'https://antizapret.info/index.php?search=' + tab.url ); createMenuLinkEntry(
'Сайт доступен из-за границы? Is up?',
createMenuLinkEntry( 'Из архива archive.org', (tab) => 'https://web.archive.org/web/*/' + tab.url ); (tab) => 'http://isup.me/' + new URL(tab.url).hostname
);
createMenuLinkEntry( 'Открыть веб прокси (не наш)', (tab) => 'https://kproxy.com' );
createMenuLinkEntry(
createMenuLinkEntry( 'Другие варианты разблокировки', (tab) => 'https://rebrand.ly/unblock#' + tab.url ); 'Сайт в реестре блокировок?',
(tab) => 'https://antizapret.info/index.php?search=' + tab.url
createMenuLinkEntry( 'У меня проблемы с расширением!', (tab) => 'https://rebrand.ly/ac-support'); );
}; createMenuLinkEntry(
'Из архива archive.org',
(tab) => 'https://web.archive.org/web/*/' + tab.url
);
createMenuLinkEntry(
'Открыть веб прокси (не наш)',
(tab) => 'https://kproxy.com'
);
createMenuLinkEntry(
'Другие варианты разблокировки',
(tab) => 'https://rebrand.ly/unblock#' + tab.url
);
createMenuLinkEntry(
'У меня проблемы с расширением!',
(tab) => 'https://rebrand.ly/ac-support'
);
}

View File

@ -1,174 +1,189 @@
'use strict'; 'use strict';
chrome.runtime.getBackgroundPage( (backgroundPage) => { chrome.runtime.getBackgroundPage( (backgroundPage) => {
const getStatus = () => document.querySelector('#status'); const getStatus = () => document.querySelector('#status');
const setStatusTo = (msg) => { const setStatusTo = (msg) => {
const status = getStatus(); const status = getStatus();
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'); }
}
};
};
const antiCensorRu = backgroundPage.antiCensorRu;
const antiCensorRu = backgroundPage.antiCensorRu;
// SET DATE
// SET DATE
const setDate = () => {
const setDate = () => {
let dateForUser = 'никогда';
let dateForUser = 'никогда'; if( antiCensorRu.lastPacUpdateStamp ) {
if( antiCensorRu.lastPacUpdateStamp ) { let diff = Date.now() - antiCensorRu.lastPacUpdateStamp;
let diff = Date.now() - antiCensorRu.lastPacUpdateStamp; let units = ' мс';
let units = ' мс'; const gauges = [
const gauges = [ [1000, ' с'],
[1000, ' с'], [60, ' мин'],
[60, ' мин'], [60, ' ч'],
[60, ' ч'], [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]); if (!diffy)
if (!diffy) break;
break; diff = diffy;
diff = diffy; units = g[1];
units = g[1]; }
} dateForUser = diff + units + ' назад';
dateForUser = diff + units + ' назад'; }
}
const dateElement = document.querySelector('.update-date');
const dateElement = document.querySelector('.update-date'); dateElement.innerText = dateForUser;
dateElement.innerText = dateForUser; dateElement.title = new Date(antiCensorRu.lastPacUpdateStamp)
dateElement.title = new Date(antiCensorRu.lastPacUpdateStamp).toLocaleString('ru-RU'); .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 );
document.querySelector('.close-button').onclick = () => window.close(); // CLOSE BUTTON
// RADIOS document.querySelector('.close-button').onclick = () => window.close();
const currentProviderRadio = () => { // RADIOS
const id = antiCensorRu.currentPacProviderKey || 'none'; const currentProviderRadio = () => {
return document.querySelector('#'+id);
const id = antiCensorRu.currentPacProviderKey || 'none';
} return document.querySelector('#'+id);
const checkChosenProvider = () => currentProviderRadio().checked = true;
};
const showError = (err) => { const checkChosenProvider = () => currentProviderRadio().checked = true;
let clarification = err.clarification; const showError = (err) => {
const ifNotCritical = clarification && clarification.ifNotCritical;
let message = err.message || ''; let clarification = err.clarification;
const ifNotCritical = clarification && clarification.ifNotCritical;
while( clarification ) { let message = err.message || '';
message = (clarification && (clarification.message + ' ')) + message;
clarification = clarification.prev; while( clarification ) {
} message = (clarification && (clarification.message + ' ')) + message;
message = message.trim(); clarification = clarification.prev;
setStatusTo( }
`<span style="color:red">${ifNotCritical ? 'Некритичная ошибка.' : 'Ошибка!'}</span> message = message.trim();
<br/> setStatusTo(
<span style="font-size: 0.9em; color: darkred">${message}</span> `<span style="color:red">
<a href class="link-button">[Ещё&nbsp;подробнее]</a>` ${ifNotCritical ? 'Некритичная ошибка.' : 'Ошибка!'}
); </span>
getStatus().querySelector('.link-button').onclick = function() { <br/>
<span style="font-size: 0.9em; color: darkred">${message}</span>
const div = document.createElement('div'); <a href class="link-button">[Ещё&nbsp;подробнее]</a>`
div.innerHTML = ` );
Более подробную информацию можно узнать из логов фоновой страницы:<br/> getStatus().querySelector('.link-button').onclick = function() {
<a href="chrome://extensions?id=${chrome.runtime.id}" data-in-bg="true">chrome://extensions</a> Это расширение Отладка страниц: фоновая страница Console (DevTools)
<br> const div = document.createElement('div');
Ещё: ${JSON.stringify({err: err, stack: err.stack})} div.innerHTML = `
`; Более подробную информацию можно узнать из логов фоновой страницы:<br/>
getStatus().replaceChild(div, this); <a href="chrome://extensions?id=${chrome.runtime.id}" data-in-bg="true">
return false; chrome://extensions</a>
Это расширение Отладка страниц: фоновая страница Console (DevTools)
}; <br>
Ещё: ${JSON.stringify({err: err, stack: err.stack})}
}; `;
getStatus().replaceChild(div, this);
const enableDisableInputs = function () { return false;
const inputs = document.querySelectorAll('input'); };
for ( let i = 0; i < inputs.length; i++ ) {
inputs[i].disabled = !inputs[i].disabled; };
}
const enableDisableInputs = function() {
};
const inputs = document.querySelectorAll('input');
const conduct = (beforeStatus, operation, afterStatus, onSuccess) => { for ( let i = 0; i < inputs.length; i++ ) {
inputs[i].disabled = !inputs[i].disabled;
setStatusTo(beforeStatus); }
enableDisableInputs();
operation((err) => { };
if (err) {
showError(err); const conduct = (beforeStatus, operation, afterStatus, onSuccess) => {
}
else { setStatusTo(beforeStatus);
setStatusTo(afterStatus); enableDisableInputs();
onSuccess && onSuccess(); operation((err) => {
} if (err) {
enableDisableInputs(); showError(err);
}) } else {
setStatusTo(afterStatus);
}; onSuccess && onSuccess();
}
const ul = document.querySelector('#list-of-providers'); enableDisableInputs();
const _firstChild = ul.firstChild; });
for( const providerKey of Object.keys(antiCensorRu.pacProviders).sort() ) {
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.querySelector('.link-button').onclick = () => { conduct( 'Обновляем...', (cb) => antiCensorRu.syncWithPacProvider(cb), 'Обновлено.' ); return false; }; const ul = document.querySelector('#list-of-providers');
ul.insertBefore( li, _firstChild ); const _firstChild = ul.firstChild;
} for( const providerKey of Object.keys(antiCensorRu.pacProviders).sort() ) {
checkChosenProvider(); const li = document.createElement('li');
li.innerHTML = `<input type="radio" name="pacProvider" id="${providerKey}">
const radios = [].slice.apply( document.querySelectorAll('[name=pacProvider]') ); <label for="${providerKey}">${providerKey}</label>
for(const radio of radios) { <a href class="link-button checked-radio-panel">[обновить]</a>`;
radio.onclick = function(event) { li.querySelector('.link-button').onclick =
() => {
if (event.target.id === (antiCensorRu.currentPacProviderKey || 'none')) { conduct(
return false; 'Обновляем...', (cb) => antiCensorRu.syncWithPacProvider(cb),
} 'Обновлено.'
const pacKey = event.target.id; );
if (pacKey === 'none') { return false;
conduct( };
'Отключение...', ul.insertBefore( li, _firstChild );
(cb) => antiCensorRu.clearPac(cb), }
'Отключено.', checkChosenProvider();
checkChosenProvider
); const radios = [].slice.apply(
} document.querySelectorAll('[name=pacProvider]')
else { );
conduct( for(const radio of radios) {
'Установка...', radio.onclick = function(event) {
(cb) => antiCensorRu.installPac(pacKey, cb),
'PAC-скрипт установлен.', if (event.target.id === (antiCensorRu.currentPacProviderKey || 'none')) {
checkChosenProvider return false;
); }
} const pacKey = event.target.id;
return false; if (pacKey === 'none') {
}; conduct(
} 'Отключение...',
(cb) => antiCensorRu.clearPac(cb),
setStatusTo(''); 'Отключено.',
if (antiCensorRu.ifFirstInstall) { checkChosenProvider
currentProviderRadio().click(); );
} } else {
conduct(
}); 'Установка...',
(cb) => antiCensorRu.installPac(pacKey, cb),
'PAC-скрипт установлен.',
checkChosenProvider
);
}
return false;
};
}
setStatusTo('');
if (antiCensorRu.ifFirstInstall) {
currentProviderRadio().click();
}
});

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"
}
}