Merge branch 'build/1.x' into development

This commit is contained in:
Ilya Ig. Petrov 2017-06-09 07:50:08 +05:00
commit 46f2a00248
20 changed files with 159 additions and 1489 deletions

View File

@ -214,7 +214,7 @@
}); });
if (self.included.length && !self.filteredCustomsString) { if (self.included.length && !self.filteredCustomsString) {
return [null, self, new TypeError( return [null, self, new TypeError(
'Имеются сайты, добавленные вручную. Они проксироваться не будут, т.к. нет СВОИХ проски, удовлетворяющих вашим запросам!' 'Имеются сайты, добавленные вручную. Они проксироваться не будут, т.к. нет СВОИХ проски, удовлетворяющих вашим требованиям! Если прокси всё же имеются, то проверьте требования (модификаторы).'
)]; )];
} }
} }

View File

@ -193,13 +193,16 @@
// First official, shortened: // First official, shortened:
'https://rebrand.ly/ac-chrome-anticensority-pac', 'https://rebrand.ly/ac-chrome-anticensority-pac',
// Second official, Cloud Flare with caching: // Second official, Cloud Flare with caching:
'https://anticensority.tk/generated-pac-scripts/anticensority.pac',
// GitHub.io (anticensority):
'\x68\x74\x74\x70\x73\x3a\x2f\x2f\x61\x6e\x74\x69\x63\x65\x6e\x73\x6f\x72\x69\x74\x79\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',
// GitHub repo (anticensority):
'\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\x69\x74\x79\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',
// Old, deprecated:
'https://anticensorship-russia.tk/generated-pac-scripts/anticensority.pac', 'https://anticensorship-russia.tk/generated-pac-scripts/anticensority.pac',
// GitHub.io: // Google Drive (0.17, anticensority):
'\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', // eslint-disable-line max-len '\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\x32\x6d\x68\x42\x67\x46\x6e\x66\x34\x70\x45\x4c\x56\x6c\x47\x4e\x54\x42\x45\x4d\x58\x4e\x6d\x52\x58\x63',
// 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', // eslint-disable-line max-len
// Google Drive (0.17):
'\x68\x74\x74\x70\x73\x3a\x2f\x2f\x64\x72\x69\x76\x65\x2e\x67\x6f\x6f\x67\x6c\x65\x2e\x63\x6f\x6d\x2f\x75\x63\x3f\x65\x78\x70\x6f\x72\x74\x3d\x64\x6f\x77\x6e\x6c\x6f\x61\x64\x26\x69\x64\x3d\x30\x42\x2d\x5a\x43\x56\x53\x76\x75\x4e\x57\x66\x30\x54\x44\x46\x52\x4f\x47\x35\x46\x62\x55\x39\x4f\x64\x44\x67'], // eslint-disable-line max-len
}, },
onlyOwnSites: { onlyOwnSites: {
label: 'Только свои сайты и свои прокси', label: 'Только свои сайты и свои прокси',
@ -538,12 +541,7 @@
const key = antiCensorRu._currentPacProviderKey; const key = antiCensorRu._currentPacProviderKey;
if (key !== null) { if (key !== null) {
const ifVeryOld = !Object.keys(antiCensorRu.pacProviders).includes(key); const ifVeryOld = !Object.keys(antiCensorRu.pacProviders).includes(key);
const ifNeedsForcing = (oldStorage.version < '0.0.0.2') && !localStorage.getItem('provider-backup'); if (ifVeryOld) {
if ( ifVeryOld || ifNeedsForcing ) {
if (ifNeedsForcing) {
console.log('Update forces antizapret...')
localStorage.setItem('provider-backup', antiCensorRu._currentPacProviderKey);
}
antiCensorRu._currentPacProviderKey = 'Антизапрет'; antiCensorRu._currentPacProviderKey = 'Антизапрет';
} }
} }

View File

@ -39,7 +39,7 @@
support: { support: {
title: 'Документация / Помощь / Поддержка', title: 'Документация / Помощь / Поддержка',
getUrl: (blockedUrl) => 'https://rebrand.ly/ac-support', getUrl: (blockedUrl) => 'https://rebrand.ly/ac-wiki',
order: 99, order: 99,
}, },

View File

@ -1,780 +0,0 @@
'use strict';
const START = Date.now();
{
const enableButton = function enablebutton() {
document.getElementById('apply-mods').disabled = false;
};
const onModChanged = function onModChanged(ev) {
enableButton();
};
['pac-mods', 'own-mods'].forEach((id) => {
document.getElementById(id).onchange = onModChanged;
});
}
chrome.runtime.getBackgroundPage( (backgroundPage) =>
backgroundPage.apis.errorHandlers.installListenersOn(
window, 'PUP', async() => {
const getStatus = () => document.querySelector('#status');
const setStatusTo = (msg) => {
getStatus().innerHTML = msg || 'Хорошего настроения Вам!';
};
const antiCensorRu = backgroundPage.apis.antiCensorRu;
const errorHandlers = backgroundPage.apis.errorHandlers;
// SET DATE
const setDate = () => {
let dateForUser = 'никогда';
if( antiCensorRu.lastPacUpdateStamp ) {
let diff = Date.now() - antiCensorRu.lastPacUpdateStamp;
let units = 'мс';
const gauges = [
[1000, 'с'],
[60, 'мин'],
[60, 'ч'],
[24, 'дн'],
[7, ' недель'],
[4, ' месяцев'],
];
for(const g of gauges) {
const diffy = Math.floor(diff / g[0]);
if (!diffy)
break;
diff = diffy;
units = g[1];
}
dateForUser = diff + units + ' назад';
}
const dateElement = document.querySelector('.update-date');
dateElement.innerText = dateForUser + ' / ' +
(antiCensorRu.pacUpdatePeriodInMinutes/60) + 'ч';
dateElement.title = new Date(antiCensorRu.lastPacUpdateStamp)
.toLocaleString('ru-RU');
};
setDate();
chrome.storage.onChanged.addListener(
(changes) => changes.lastPacUpdateStamp.newValue && setDate()
);
// CLOSE BUTTON
document.querySelector('.close-button').onclick = () => window.close();
// RADIOS FOR PROVIDERS
const currentProviderRadio = () => {
const iddy = antiCensorRu.getCurrentPacProviderKey() || 'none';
return document.getElementById(iddy);
};
const checkChosenProvider = () => {
currentProviderRadio().checked = true;
};
const showErrors = (err, ...warns) => {
const warning = warns
.map(
(w) => w && w.message || ''
)
.filter( (m) => m )
.map( (m) => '✘ ' + m )
.join('<br/>');
let message = '';
if (err) {
let wrapped = err.wrapped;
message = err.message || '';
while( wrapped ) {
const deeperMsg = wrapped && wrapped.message;
if (deeperMsg) {
message = message + ' &gt; ' + deeperMsg;
}
wrapped = wrapped.wrapped;
}
}
message = message.trim();
if (warning) {
message = message ? message + '<br/>' + warning : warning;
}
setStatusTo(
`<span style="color:red">
${err ? '<span class="emoji">🔥</span> Ошибка!' : 'Некритичная ошибка.'}
</span>
<br/>
<span style="font-size: 0.9em; color: darkred">${message}</span>
${err ? '<a href class="link-button">[Техн.детали]</a>' : ''}`
);
if (err) {
getStatus().querySelector('.link-button').onclick = function() {
errorHandlers.viewError('pup-ext-err', err);
return false;
};
}
};
const switchInputs = function(val) {
const inputs = document.querySelectorAll('input');
for ( let i = 0; i < inputs.length; i++ ) {
const input = inputs[i];
if (val === 'off') {
input.dataset.previousDisabledValue = input.disabled;
input.disabled = true;
} else {
input.disabled = input.dataset.previousDisabledValue === 'true';
}
}
};
const conduct = (beforeStatus, operation, afterStatus, onSuccess = () => {}, onError = () => {}) => {
setStatusTo(beforeStatus);
switchInputs('off');
operation((err, res, ...warns) => {
warns = warns.filter( (w) => w );
if (err || warns.length) {
showErrors(err, ...warns);
} else {
setStatusTo(afterStatus);
}
switchInputs('on');
if (!err) {
onSuccess(res);
} else {
onError(err);
}
});
};
const infoIcon = `<svg class="icon" style="position: relative; top: 0.08em">
<use xlink:href="#icon-info"></use>
</svg>`;
const infoSign = function infoSign(tooltip) {
return `<div class="desc">
${infoIcon}
<div class="tooltip">${tooltip}</div>
</div>`;
};
const infoUrl = (url) => `<a href="${url}" class="right-bottom-icon info-url">${infoIcon}</a>`;
const camelToDash = (name) => name.replace(/([A-Z])/g, (_, p1) => '-' + p1.toLowerCase());
// const dashToCamel = (name) => name.replace(/-(.)/g, (_, p1) => p1.toUpperCase());
const appendInfoRow = (mountEl, conf, inputTypeAndName,
{
idPrefix = 'mods-',
ifDashify = true,
htmlAfterLabel = ''
} = {}) => {
mountEl.classList.add('info-row', 'hor-flex');
if (conf.ifDisabled) {
mountEl.title = 'В РАЗРАБОТКЕ!';
}
const iddy = idPrefix + ( ifDashify ? camelToDash(conf.key) : conf.key );
mountEl.innerHTML = `
<input ${inputTypeAndName} id=${iddy} ${conf.ifDisabled ? 'disabled' : ''}>
<div class="label-container">
<label for="${iddy}">${conf.label}</label>
${htmlAfterLabel}
</div>` + (conf.desc ? infoSign(conf.desc) : infoUrl(conf.url));
};
{ // PAC-PROVIDERS starts.
const ul = document.getElementById('list-of-providers');
const _firstChildOrNull = ul.firstChild;
for(
const provConf of antiCensorRu.getSortedEntriesForProviders()
) {
const li = document.createElement('li');
appendInfoRow(li, provConf, 'type="radio" name="pacProvider"',
{ idPrefix: '',
ifDashify: false,
htmlAfterLabel: `
&nbsp;<a href class="link-button update-button"
id="update-${provConf.key}">[обновить]</a>`
}
);
li.querySelector('.link-button').onclick =
() => {
conduct(
'Обновляем...', (cb) => antiCensorRu.syncWithPacProviderAsync(cb),
'Обновлено.'
);
return false;
};
ul.insertBefore( li, _firstChildOrNull );
}
checkChosenProvider();
const radioClickHandler = function radioClickHandler(event) {
const pacKey = event.target.id;
if (
pacKey === (
antiCensorRu.getCurrentPacProviderKey() || 'none'
)
) {
return false;
}
if (pacKey === 'none') {
conduct(
'Отключение...',
(cb) => antiCensorRu.clearPacAsync(cb),
'Отключено.',
checkChosenProvider
);
} else {
conduct(
'Установка...',
(cb) => antiCensorRu.installPacAsync(pacKey, cb),
'PAC-скрипт установлен.',
checkChosenProvider
);
}
return false;
};
document.querySelectorAll('input[name=pacProvider]').forEach((radio) => {
radio.onclick = radioClickHandler;
});
} // PAC-PROVIDERS ends.
// IF MINI
if (backgroundPage.apis.version.ifMini) {
document.documentElement.classList.add('if-version-mini');
}
// IF INSIDE OPTIONS TAB
const currentTab = await new Promise(
(resolve) => chrome.tabs.query(
{active: true, currentWindow: true},
([tab]) => resolve(tab)
)
);
const ifInsideOptions = !currentTab || currentTab.url.startsWith('chrome://extensions/?options=');
if (ifInsideOptions) {
document.documentElement.classList.add('if-options-page');
}
{ // KITCHEN PANELS starts.
const pacKitchen = backgroundPage.apis.pacKitchen;
const modKeyToLi = {};
const applyMods = function applyMods(newMods) {
conduct(
'Применяем настройки...',
(cb) => pacKitchen.keepCookedNowAsync(newMods, cb),
'Настройки применены.',
() => {
document.getElementById('apply-mods').disabled = true;
}
);
};
{ // EXCEPTIONS TAB starts.
{ // EXC-MODS starts.
const emodsList = document.getElementById('exc-mods');
const _firstChildOrNull = emodsList.firstChild;
for(const conf of pacKitchen.getOrderedConfigs('exceptions')) {
const li = document.createElement('li');
appendInfoRow(li, conf, `type="checkbox" ${conf.value ? 'checked' : ''}`);
emodsList.insertBefore( li, _firstChildOrNull );
const key = conf.key;
modKeyToLi[key] = li;
li.querySelector('input').onclick = function() {
const oldMods = pacKitchen.getPacMods();
oldMods[key] = this.checked;
applyMods(oldMods);
};
};
}// EXC-MODS ends.
{ // EXC-EDITOR starts.
const excEditor = document.getElementById('exc-editor');
const validateHost = function validateHost(host) {
const ValidHostnameRegex = /^(([a-zA-Z0-9]|[a-zA-Z0-9][a-zA-Z0-9\-]*[a-zA-Z0-9])\.)*([A-Za-z0-9]|[A-Za-z0-9][A-Za-z0-9\-]*[A-Za-z0-9])$/;
if(!ValidHostnameRegex.test(host)) {
showErrors(new TypeError('Должно быть только доменное имя, без протокола, порта и пути. Попробуйте ещё раз.'));
return false;
}
return true;
};
const labelIfProxied = '✔';
const labelIfNotProxied = '✘';
const labelIfAuto = '↻';
const addOption = function addOption(host, yesNoUndefined) {
const opt = document.createElement('option');
// `value` may be changed for hiding line in the tooltip.
opt.value = host;
opt.dataset.host = host;
switch(yesNoUndefined) {
case true:
opt.label = labelIfProxied;
break;
case false:
opt.label = labelIfNotProxied;
break;
default:
opt.label = labelIfAuto;
}
const editorHost = excEditor.value.trim();
if (host === editorHost) {
excList.insertBefore( opt, excList.firstChild );
} else {
excList.appendChild(opt);
}
return opt;
};
const thisYes = document.getElementById('this-yes');
const thisNo = document.getElementById('this-no');
const thisAuto = document.getElementById('this-auto');
const yesClass = 'if-yes';
const noClass = 'if-no';
function moveCursorIfNeeded() {
const nu = excEditor.dataset.moveCursorTo;
if(nu !== undefined) {
excEditor.setSelectionRange(nu, nu);
delete excEditor.dataset.moveCursorTo;
}
}
const hideOpt = (opt) => opt.value = '\n';
const unhideOptAndAddSpace = (opt) => opt.value = opt.dataset.host + ' ';
const excList = document.getElementById('exc-list');
excEditor.onkeydown = function(event) {
moveCursorIfNeeded();
if(event.key === 'Enter') {
// Hide all.
excList.childNodes.forEach( hideOpt );
}
return true;
};
const renderExceptionsPanelFromExcList = function renderExceptionsPanelFromExcList(event) {
// If triangle button on right of datalist input clicked.
let ifTriangleClicked = false;
const ifClick = event && event.type === 'click';
{
const maxIndentFromRightInPx = 15;
ifTriangleClicked = ifClick
&& !excEditor.selectionStart && !excEditor.selectionEnd
&& event.x > excEditor.getBoundingClientRect().right - maxIndentFromRightInPx;
}
const setInputValue = (newValue) => {
if (ifClick && !ifTriangleClicked) {
// Don't jerk cursor on simple clicks.
return;
}
// See bug in my comment to http://stackoverflow.com/a/32394157/521957
// First click on empty input may be still ignored.
const nu = excEditor.selectionStart + newValue.length - excEditor.value.length;
excEditor.value = newValue;
excEditor.dataset.moveCursorTo = nu;
window.setTimeout(moveCursorIfNeeded, 0);
};
const originalHost = excEditor.value.trim();
const ifInit = !event;
const currentHost = ifTriangleClicked ? '' : (originalHost || (ifInit ? '' : ' '));
setInputValue(currentHost);
let exactOpt = false;
let editedOpt = false;
excList.childNodes.forEach(
(opt) => {
unhideOptAndAddSpace(opt);
if(opt.label === labelIfAuto) {
editedOpt = opt;
return;
}
if (opt.dataset.host === originalHost) {
exactOpt = opt;
}
}
);
thisAuto.checked = true;
excEditor.parentNode.classList.remove(noClass, yesClass);
const ifInputEmpty = !originalHost;
if (ifTriangleClicked || ifInputEmpty) {
// Show all opts.
if (editedOpt) {
// Example of editedOpt.value: 'abcde ' <- Mind the space (see unhideOptAndAddSpace)!
const ifBackspacedOneChar = ifInputEmpty && editedOpt.value.length < 3;
if (ifBackspacedOneChar) {
editedOpt.remove();
}
}
return true;
}
if (editedOpt) {
const ifEditedOptAlreadyExists = editedOpt.dataset.host === originalHost;
if(ifEditedOptAlreadyExists) {
hideOpt(editedOpt);
return true;
}
// Not exact! Update!
editedOpt.remove();
}
if (!exactOpt) {
editedOpt = addOption(originalHost, undefined);
if (!ifClick) {
// New value was typed -- don't show tooltip.
hideOpt(editedOpt);
}
return true;
}
// Exact found!
excList.childNodes.forEach(hideOpt);
if(exactOpt.label === labelIfProxied) {
thisYes.checked = true;
excEditor.parentNode.classList.add(yesClass);
} else {
thisNo.checked = true;
excEditor.parentNode.classList.add(noClass);
}
return true;
};
excEditor.onclick = excEditor.oninput = renderExceptionsPanelFromExcList;
if (currentTab && !currentTab.url.startsWith('chrome')) {
excEditor.value = new URL(currentTab.url).hostname;
} else {
// Show placeholder.
excEditor.value = '';
}
{ // Populate selector.
const pacMods = pacKitchen.getPacMods();
for(const host of Object.keys(pacMods.exceptions || {}).sort()) {
addOption(host, pacMods.exceptions[host]);
}
renderExceptionsPanelFromExcList(); // Colorize input.
}
document.getElementById('exc-radio').onclick = function(event) {
/* ON CLICK */
if(event.target.tagName !== 'INPUT') {
// Only label on checkbox.
return true;
}
const host = excEditor.value.trim();
const pacMods = pacKitchen.getPacMods();
pacMods.exceptions = pacMods.exceptions || {};
let fixOptions;
const curOptOrNull = excList.querySelector(`[data-host="${host}"]`);
if (thisAuto.checked) {
delete pacMods.exceptions[host];
fixOptions = () => {
curOptOrNull && curOptOrNull.remove();
}
} else {
// YES or NO checked.
const ifYesClicked = thisYes.checked;
if (!validateHost(host)) {
return false;
}
if (ifYesClicked && !pacMods.filteredCustomsString) {
showErrors( new TypeError(
'Проксировать СВОИ сайты можно только при наличии СВОИХ прокси (см. «Модификаторы» ). Нет своих прокси, удовлетворяющих вашим требованиям.'
));
return false;
}
//const ifNew = !(host in pacMods.exceptions);
pacMods.exceptions[host] = ifYesClicked;
// Change label.
fixOptions = () => {
if (curOptOrNull) {
curOptOrNull.label = ifYesClicked ? labelIfProxied : labelIfNotProxied;
} else {
addOption(host, ifYesClicked);
}
};
}
conduct(
'Применяем исключения...',
(cb) => pacKitchen.keepCookedNowAsync(pacMods, cb),
'Исключения применены. Не забывайте о кэше!',
() => {
fixOptions();
// Window may be closed before this line executes.
renderExceptionsPanelFromExcList();
}
);
return false; // Don't check before operation is finished.
};
} // EXC-EDITOR ends.
} // EXCEPTIONS TAB ends.
{ // PAC MODS TAB starts.
const modsList = document.getElementById('pac-mods');
const _firstChildOrNull = modsList.firstChild;
for(const conf of pacKitchen.getOrderedConfigs('general')) {
const li = document.createElement('li');
appendInfoRow(li, conf, `type="checkbox" ${conf.value ? 'checked' : ''}`);
modsList.insertBefore( li, _firstChildOrNull );
modKeyToLi[conf.key] = li;
};
} // PAC MODS TAB ends.
const customProxyStringKey = 'customProxyStringRaw';
const uiRaw = 'ui-proxy-string-raw';
{ // OWN PROXIES TAB starts.
const ownsList = document.getElementById('own-mods');
const _firstChildOrNull = ownsList.firstChild;
for(const conf of pacKitchen.getOrderedConfigs('ownProxies')) {
const li = document.createElement('li');
appendInfoRow(li, conf, `type="checkbox" ${conf.value ? 'checked' : ''}`);
const key = conf.key;
const ifMultiline = key === customProxyStringKey;
if (ifMultiline) {
li.style.flexWrap = 'wrap';
li.innerHTML += `
<textarea
spellcheck="false"
placeholder="SOCKS5 localhost:9050; # Tor Expert
SOCKS5 localhost:9150; # Tor Browser
HTTPS 11.22.33.44:3143;
PROXY foobar.com:8080; # Not HTTP!">${conf.value || localStorage.getItem(uiRaw) || ''}</textarea>
`;
li.querySelector('textarea').onkeyup = function() {
this.dispatchEvent( new Event('change', {'bubbles': true}) );
};
}
ownsList.insertBefore( li, _firstChildOrNull );
modKeyToLi[key] = li;
}
} // OWN PROXIES TAB ends.
{ // APPLY MODS PANEL starts.
document.getElementById('apply-mods').onclick = () => {
const oldMods = pacKitchen.getPacMods();
for(const key of Object.keys(modKeyToLi)) {
oldMods[key] = modKeyToLi[key].querySelector('input').checked;
};
{
// OWN PROXY
const liPs = modKeyToLi[customProxyStringKey];
oldMods[customProxyStringKey]
= liPs.querySelector('input').checked
&& liPs.querySelector('textarea').value.trim();
const taVal = liPs.querySelector('textarea').value;
if (oldMods[customProxyStringKey] !== false) {
const ifValidArr = taVal
.trim()
.replace(/#.*$/mg, '')
.split(/\s*[;\n\r]+\s*/g)
.filter( (str) => str );
const ifValid = ifValidArr.every(
(str) =>
/^(?:DIRECT|(?:(?:HTTPS|PROXY|SOCKS(?:4|5)?)\s+\S+))$/g
.test(str.trim())
);
if (!(ifValidArr.length && ifValid)) {
return showErrors(new TypeError(
'Неверный формат своих прокси. Свертесь с <a href="https://rebrand.ly/ac-own-proxy" data-in-bg="true">документацией</a>.'
));
}
oldMods[customProxyStringKey] = taVal;
} else {
localStorage.setItem(uiRaw, taVal);
}
}
applyMods(oldMods);
};
document.getElementById('reset-mods').onclick = () => {
const ifSure = backgroundPage.confirm('Сбросить все модификаторы и ИСКЛЮЧЕНИЯ?');
if (!ifSure) {
return false;
}
conduct(
'Сбрасываем...',
(cb) => {
pacKitchen.resetToDefaults();
backgroundPage.utils.fireRequest('ip-to-host-reset-to-defaults', cb);
},
'Откройте окно заново для отображения эффекта.',
() => window.close()
);
}
} // APPLY MODS PANEL ends.
} // KITCHEN PANELS ends.
// NOTIFICATIONS PANEL
const conPanel = document.getElementById('list-of-notifiers');
errorHandlers.getEventsMap().forEach( (value, name) => {
const li = document.createElement('li');
li.innerHTML = `
<input type="checkbox" id="if-on-${name}"/>
<label for="if-on-${name}">${value}</label>`;
const box = li.querySelector('input');
box.checked = backgroundPage.apis.errorHandlers.isOn(name);
box.onclick = function() {
const id = this.id.replace('if-on-', '');
return backgroundPage.apis.errorHandlers.switch(
this.checked ? 'on' : 'off',
id
);
};
conPanel.appendChild(li);
});
if( !errorHandlers.ifControllable ) {
document.getElementById('which-extension').innerHTML
= backgroundPage.utils.messages.whichExtensionHtml();
document.querySelectorAll('.if-not-controlled').forEach( (node) => {
node.style.display = 'block';
});
}
setStatusTo('');
if (antiCensorRu.ifFirstInstall) {
const id = antiCensorRu.getCurrentPacProviderKey() || 'none';
document.querySelector('#update-' + id).click();
}
document.documentElement.style.display = 'initial';
console.log(Date.now() - START);
})
);

View File

@ -1,608 +0,0 @@
<!DOCTYPE html>
<html style="display: none; will-change: contents, display">
<head>
<meta charset="utf-8">
<title>Настройки</title>
<style>
:root {
--ribbon-color: #4169e1;
--blue-bg: dodgerblue;
--default-grey: #bfbfbf;
--cr-options-headline: #d3d3d3;
--cr-icon-selected: #d7d7d7;
--cr-popup-border: #bababa;
--cr-grey-panel: #f2f2f2;
max-width: 28em;
}
body {
margin: 0;
}
a, a:visited {
color: var(--ribbon-color);
text-decoration: none;
}
a:hover {
text-decoration: underline;
}
label {
user-select: none;
}
div, section, header, ul, ol {
margin: 0;
padding: 0;
}
header {
margin-bottom: 0.3em
}
ul, ol {
list-style-type: none;
}
:root:not(.if-options-page) ul,
:root:not(.if-options-page) ol {
/*Here is a flex bug:
() antizapret [update] (i)
() anticensority very_long_foobar [update] (i) <- Sic!
Also: options page is wider, check it too.
But: fixed 100% width conflicts with margins/paddings.
So: use only when needed and avoid margins.
FYI: setting left-margin fixes problem too, but margins are not wanted.
Fix this problem below:
*/
display: inline-block;
min-width: 100%;
}
:root.if-options-page ul,
:root.if-options-page ol,
#list-of-notifiers {
margin-left: 0.4em;
}
li, .nowrap {
display: block;
white-space: nowrap;
word-break: keep-all;
}
li, li > * {
vertical-align: middle;
}
input[type="radio"], input[type="checkbox"] {
flex-shrink: 0;
}
input[type="radio"], label {
cursor: pointer;
}
hr {
border: none;
border-top: 1px solid var(--cr-popup-border);
margin: 0 0 0.6em 0;
padding: 0;
}
em {
font-style: normal;
text-decoration: underline;
}
/* COMMON 1 */
.hor-padded {
padding-left: 1.4em;
padding-right: 1.4em;
}
.horizontal-list,
.horizontal-list li {
line-height: 100%;
}
.horizontal-list li {
display: inline-block;
}
/* NOT CONTROLLED */
.if-not-controlled {
display: none;
background-color: red;
color: white;
font-weight: bold;
text-align: center;
padding-top: 1em;
padding-bottom: 1em;
border-bottom: 1px solid var(--default-grey);
}
:root.if-options-page .if-not-controlled {
padding-top: 0;
padding-bottom: 0;
}
.if-not-controlled a {
color: white;
}
/* MINI VS FULL */
:root:not(.if-version-mini) .only-for-mini-version {
display: none;
}
:root.if-version-mini .only-for-full-version {
display: none;
}
/* OPTIONS PAGE */
:root:not(.if-options-page) .only-for-options-page {
display: none;
}
:root.if-options-page .hidden-for-options-page {
display: none;
}
/* ACCORDION (OR TABBED STATEFUL UI) */
.off {
display: none;
}
section[data-for] {
padding: 0.6em 0 1em;
}
section[data-for] li label {
display: inline-block; /* Needed for ::first-letter below. */
}
section[data-for] li label::first-letter {
text-transform: uppercase;
}
:root.if-options-page section[data-for] {
padding-bottom: 0.6em;
}
:root.if-options-page section[data-for]:not(:last-child),
.underlined {
border-bottom: 1px solid var(--cr-options-headline);
}
/* HIDE starts. */
:root:not(.if-options-page) #acc-pac:not(:checked) ~ .main-nav section[data-for="acc-pac"],
:root:not(.if-options-page) #acc-exc:not(:checked) ~ .main-nav section[data-for="acc-exc"],
:root:not(.if-options-page) #acc-own-mods:not(:checked) ~ .main-nav section[data-for="acc-own-mods"],
:root:not(.if-options-page) #acc-mods:not(:checked) ~ .main-nav section[data-for="acc-mods"],
:root:not(.if-options-page) #acc-ntf:not(:checked) ~ .main-nav section[data-for="acc-ntf"],
/* One button shared between two sections: */
:root:not(.if-options-page) #acc-own-mods:not(:checked) + #acc-mods:not(:checked) ~ .main-nav #apply-mods-section
{
/* Hide, but preclude width resizes. */
height: 0px !important;
line-height: 0px !important;
padding-top: 0 !important;
padding-bottom: 0 !important;
margin-top: 0 !important;
margin-bottom: 0 !important;
border: none !important;
display: block;
visibility: hidden;
transform: scaleY(0) !important;
}
:root:not(.if-options-page) #acc-pac:not(:checked) ~ .main-nav section[data-for="acc-pac"] *,
:root:not(.if-options-page) #acc-exc:not(:checked) ~ .main-nav section[data-for="acc-exc"] *,
:root:not(.if-options-page) #acc-own-mods:not(:checked) ~ .main-nav section[data-for="acc-own-mods"] *,
:root:not(.if-options-page) #acc-mods:not(:checked) ~ .main-nav section[data-for="acc-mods"] *,
:root:not(.if-options-page) #acc-ntf:not(:checked) ~ .main-nav section[data-for="acc-ntf"] *,
/* One button shared between two sections: */
:root:not(.if-options-page) #acc-own-mods:not(:checked) + #acc-mods:not(:checked) ~ .main-nav #apply-mods-section *
{
margin-top: 0 !important;
margin-bottom: 0 !important;
}
/* HIDE ends. */
.nav-labels {
background-color: var(--cr-grey-panel);
text-align: center;
}
.nav-labels li label {
display: inline-block;
border: 1px solid var(--ribbon-color);
border-radius: 0.2em;
background-color: white;
color: var(--ribbon-color);
padding: 0.2em 0.3em 0.3em 0.2em;
line-height: 0.8em;
margin: 0.1em 0;
}
.nav-labels li label:hover {
background-color: var(--blue-bg);
color: white;
/*
border-color: white;
border-style: dotted;*/
}
/* LABELS starts. */
#acc-pac:checked ~ .nav-labels label[for="acc-pac"]:not(:hover),
#acc-exc:checked ~ .nav-labels label[for="acc-exc"]:not(:hover),
#acc-own-mods:checked ~ .nav-labels label[for="acc-own-mods"]:not(:hover),
#acc-mods:checked ~ .nav-labels label[for="acc-mods"]:not(:hover),
#acc-ntf:checked ~ .nav-labels label[for="acc-ntf"]:not(:hover)
{
background-color: var(--blue-bg);
color: white;
}
/* ★★★★★ */
.nav-labels label:before {
content: '★';
padding-right: 0.1em;
visibility: hidden;
}
.nav-labels li label:hover:before,
#acc-pac:checked ~ .nav-labels label[for="acc-pac"]:before,
#acc-exc:checked ~ .nav-labels label[for="acc-exc"]:before,
#acc-own-mods:checked ~ .nav-labels label[for="acc-own-mods"]:before,
#acc-mods:checked ~ .nav-labels label[for="acc-mods"]:before,
#acc-ntf:checked ~ .nav-labels label[for="acc-ntf"]:before
{
visibility: initial;
}
/* LABELS ends. */
/* COMMON 2 */
/* INFO SIGNS */
input:disabled + .label-container label {
color: var(--default-grey);
pointer-events: none;
}
.info-row {
position: relative;
}
.right-bottom-icon {
margin-left: 0.1em;
vertical-align: bottom;
}
.info-url, .info-url:hover {
text-decoration: none;
}
/* Source: https://jsfiddle.net/greypants/zgCb7/ */
.desc {
text-align: right;
color: var(--ribbon-color);
cursor: help;
padding-left: 0.3em;
}
.tooltip {
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 .tooltip {
display: block;
}
.tooltip a,
.tooltip em {
color: white;
}
.desc .tooltip:after {
border-left: solid transparent 0.5em;
border-bottom: solid var(--ribbon-color) 0.5em;
position: absolute;
top: -0.5em;
content: "";
width: 0;
right: 0;
height: 0;
}
/* This bridges the gap so you can mouse into the tooltip without it disappearing. */
.desc .tooltip:before {
position: absolute;
top: -1em;
content: "";
display: block;
height: 1.2em;
left: 75%;
width: calc(25% + 0.6em);
}
/* TAB_1: PAC PROVIDER */
.update-button {
visibility: hidden;
}
input:checked ~ .label-container .update-button {
visibility: inherit;
}
label[for="onlyOwnSites"] + .update-button {
display: none;
}
#none:checked ~ .label-container label {
color: red;
}
#update-message {
white-space: nowrap;
margin-top: 0.5em;
}
/* TAB_2: PAC MODS */
#mods-custom-proxy-string-raw ~ textarea {
width: 100%;
max-width: 38.5em; /* ~418px, layout breaks if more */
height: 7em;
margin-top: 0.3em;
font-size: 0.9em;
}
#mods-custom-proxy-string-raw:not(:checked) ~ textarea {
display: none;
}
/* TAB_3: EXCEPTIONS */
/* EXC-EDITOR starts. */
#exc-address-container {
display: flex;
align-items: center;
width: 100%;
}
#exc-address-container > a {
border-bottom: 1px solid transparent;
margin-left: 0.2em;
}
#exc-address {
width: 100%;
display: flex;
align-items: baseline;
--exc-hieght: 1.6em;
font-size: 1em;
border-bottom: 1px solid var(--ribbon-color) !important;
}
input#exc-editor {
border: none;
width: 100%;
background: inherit;
/* The two below align '.' (dot) vertically. */
max-height: var(--exc-hieght) !important;
min-height: var(--exc-hieght) !important;
}
#exc-radio {
display: flex;
justify-content: space-around;
margin-top: 0.5em;
}
[name="if-proxy-this-site"]:checked + label {
font-weight: bold;
}
#exc-address.if-yes {
background-color: lightgreen;
}
#exc-address.if-no {
background-color: pink;
}
option.if-proxied {
color: var(--ribbon-color);
}
option:not(.if-proxied) {
color: red;
}
/* EXC-EDITOR ends. */
#exc-mods {
padding-top: 1em;
}
#exc-mods input#mods-if-mind-exceptions:not(:checked) + .label-container label {
color: red;
}
/* CONTROL RAW = BUTTON + LINK */
.hor-flex {
display: flex;
align-items: baseline;
justify-content: space-between;
width: 100%;
}
.control-row {
margin: 1em 0 1em 0;
}
.hor-flex input:not([type="button"]) {
align-self: flex-end;
}
.label-container {
flex-grow: 9;
padding-left: 0.3em;
}
/* STATUS */
#status-row {
padding: 0 0.3em 1em;
}
#status {
display: inline-block;
}
.other-version {
font-size: 1.7em;
color: var(--ribbon-color);
margin-left: 0.1em;
}
.other-version:hover {
text-decoration: none;
}
.full-line-height,
.full-line-height * {
line-height: 100%;
}
@font-face {
font-family: "emoji";
src:url("../lib/fonts/emoji.woff") format("woff");
font-weight: normal;
font-style: normal;
}
.emoji {
font-family: "emoji";
}
svg.icon {
display: inline-block;
width: 1em;
height: 1em;
stroke-width: 0;
stroke: currentColor;
fill: currentColor;
}
</style>
</head>
<body>
<section class="if-not-controlled hor-padded" id="which-extension"></section>
<input type="radio" name="accordion" class="off" id="acc-pac" checked/>
<input type="radio" name="accordion" class="off" id="acc-exc"/>
<input type="radio" name="accordion" class="off" id="acc-own-mods"/>
<input type="radio" name="accordion" class="off" id="acc-mods"/>
<input type="radio" name="accordion" class="off" id="acc-ntf"/>
<nav class="nav-labels hidden-for-options-page">
<ul class='horizontal-list'>
<li><label for="acc-pac" class="nav-label">PAC-скрипт</label></li>
<li><label for="acc-exc" class="nav-label">Исключения</label></li>
<li><label for="acc-own-mods" class="nav-label">Свои прокси</label></li>
<li><label for="acc-mods" class="nav-label">Модификаторы</label></li>
<li><label for="acc-ntf" class="nav-label">Уведомления</label></li>
</ul>
<hr/>
</nav>
<nav class="hor-padded main-nav">
<section data-for="acc-pac">
<header class="only-for-options-page">PAC-скрипт:</header>
<ul id="list-of-providers">
<li class="info-row hor-flex"><input type="radio" name="pacProvider" id="none" checked> <div class="label-container"><label for="none">Отключить</label></div></li>
</ul>
<div id="update-message" class="hor-flex" style="align-items: center">
<div>Обновлялись: <span class="update-date">...</span></div>
<div class="full-line-height">
<a class="only-for-mini-version other-version emoji" href="https://rebrand.ly/ac-versions"
title="Полная версия">🏋</a>
<a class="only-for-full-version other-version emoji" href="https://rebrand.ly/ac-versions"
title="Версия для слабых машин">🐌</a>
</div>
</div>
</section>
<section class="only-for-options-page nowrap underlined">
Редактор исключений доступен толко для <a href="chrome://newtab">вкладок</a>.
</section>
<section data-for="acc-exc" class="hidden-for-options-page">
<section class="exc-editor-section" style="padding-bottom: 1em;">
<div>Проксировать указанный сайт?</div>
<div id="exc-address-container">
<div id="exc-address">
<span>*.</span><input placeholder="navalny.com" list="exc-list" name="browser" id="exc-editor" style=""/>
</div>
<!--a href class="emoji">⇄</a-->
<a href="../exceptions/index.html" title="импорт/экспорт"><svg
class="icon"
><use xlink:href="#icon-import-export"></use></svg>
</a>
</div>
<datalist id="exc-list"></datalist>
<ol class="horizontal-list" id="exc-radio">
<li><input id="this-auto" type="radio" checked name="if-proxy-this-site"/>
<label for="this-auto"><!--span class="emoji">🔄(looks fat)</span--><svg
class="icon"
style="position: relative; top: 0.15em;"><use xlink:href="#icon-loop-round"></use></svg>&nbsp;авто</label>
</li>
<li><input id="this-yes" type="radio" name="if-proxy-this-site"/> <label for="this-yes">&nbsp;да</label></li>
<li><input id="this-no" type="radio" name="if-proxy-this-site"/> <label for="this-no">&nbsp;нет</label></li>
</ol>
</section>
<ul id="exc-mods"></ul>
</section>
<section data-for="acc-own-mods">
<ul id="own-mods"></ul>
</section>
<section data-for="acc-mods">
<ul id="pac-mods"></ul>
</section>
<section id="apply-mods-section" class="control-row hor-flex" style="margin-top: 0.2em">
<input type="button" value="Применить" id="apply-mods" disabled/>
<a href id="reset-mods">К изначальным!</a>
</section>
<section data-for="acc-ntf">
<header>Я <span style="color: #f93a17"></span>едомления:</header>
<ul id="list-of-notifiers"></ul>
</section>
</nav>
<hr/>
<div class="hor-padded">
<section id="status-row">
<div id="status" style="will-change: contents">Загрузка...</div>
</section>
<footer class="control-row hor-flex nowrap">
<input type="button" value="Готово" class="close-button">
<a href="../troubleshoot/index.html">
Проблемы?
</a>
</footer>
</div>
<script src="./index.js"></script>
<script src="../lib/keep-links-clickable.js"></script>
<!-- ICONS -->
<svg style="display: none" version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink">
<symbol id="icon-info" viewBox="0 0 100 100">
<title>info</title>
<circle shape-rendering="geometricPrecision" fill="none" stroke="currentColor" stroke-width="7" cx="50" cy="50" r="45"/>
<path shape-rendering="crispEdges" d="M 55,40 V 80 H 45 V 40 z m 0,-20 V 35 H 45 V 20 Z"/>
</symbol>
<symbol id="icon-loop-round" viewBox="0 0 32 32">
<title>loop-round</title>
<path d="M27.802 5.197c-2.925-3.194-7.13-5.197-11.803-5.197-8.837 0-16 7.163-16 16h3c0-7.18 5.82-13 13-13 3.844 0 7.298 1.669 9.678 4.322l-4.678 4.678h11v-11l-4.198 4.197z"/>
<path d="M29 16c0 7.18-5.82 13-13 13-3.844 0-7.298-1.669-9.678-4.322l4.678-4.678h-11v11l4.197-4.197c2.925 3.194 7.13 5.197 11.803 5.197 8.837 0 16-7.163 16-16h-3z"/>
</symbol>
<symbol id="icon-import-export" viewBox="0 0 32 32">
<title>import-export</title>
<g transform="rotate(0 16 16)">
<path d="M7 22 h 25 v 4 h -25 v 5 l -7-7 7-7 v5 z"/>
<path d="M25 10 h-25 v-4 h 25 v -5 l 7 7 -7 7 z"/>
</g>
<!-- With bars on peaks.
<path d="M30 0h2v16h-2v-16z"></path>
<path d="M0 16h2v16h-2v-16z"></path>
<path d="M10 22 h 22 v 4 h -22 v 5 l -7-7 7-7 v5 z"></path>
<path d="M22 10 h-22 v-4 h 22 v -5 l 7 7 -7 7 z"></path-->
</symbol>
</svg>
</body>
</html>

View File

@ -9,7 +9,6 @@
"babel-plugin-dynamic-import-webpack": "^1.0.1", "babel-plugin-dynamic-import-webpack": "^1.0.1",
"babel-preset-flow": "^6.23.0", "babel-preset-flow": "^6.23.0",
"babel-preset-react": "^6.24.1", "babel-preset-react": "^6.24.1",
"babili-webpack-plugin": "^0.0.11",
"concat-stream": "^1.6.0", "concat-stream": "^1.6.0",
"csjs-inject": "^1.0.1", "csjs-inject": "^1.0.1",
"flow-bin": "^0.45.0", "flow-bin": "^0.45.0",
@ -24,7 +23,7 @@
"build:dev:nocomp": "NODE_ENV=development webpack --define process.env.NODE_ENV=\"'development'\" --env=dev", "build:dev:nocomp": "NODE_ENV=development webpack --define process.env.NODE_ENV=\"'development'\" --env=dev",
"build:dev": "NODE_ENV=development webpack --debug --define process.env.NODE_ENV=\"'development'\" --output-pathinfo --env=dev", "build:dev": "NODE_ENV=development webpack --debug --define process.env.NODE_ENV=\"'development'\" --output-pathinfo --env=dev",
"gulp": "cd .. && npm run gulp", "gulp": "cd .. && npm run gulp",
"build": "npm run build:dev", "build": "npm run build:prod",
"start": "cd .. && npm start" "start": "cd .. && npm start"
}, },
"dependencies": { "dependencies": {

View File

@ -79,23 +79,32 @@ export default function getApp(theState) {
headers: new Headers(headers), headers: new Headers(headers),
}; };
const ghUrl = `https://api.github.com/repos/anticensorship-russia/for-testing-github-api/issues/1/comments${query}`; const ghUrl = `https://api.github.com/repos/anticensority/chromium-extension/issues/10/comments${query}`;
const [comments, etag] = await fetch(
const [error, comments, etag] = await fetch(
ghUrl, ghUrl,
params params
).then( ).then(
(res) => !( res.status >= 200 && res.status < 300 || res.status === 304 ) (res) => !( res.status >= 200 && res.status < 300 || res.status === 304 )
? Promise.reject(new Error(`Полечен ответ с неудачным кодом ${res.status}.`)) ? Promise.reject({message: `Получен ответ с неудачным кодом ${res.status}.`, data: res})
: res : res
).then( ).then(
(res) => Promise.all([ (res) => Promise.all([
null,
res.status !== 304 ? res.json() : false, res.status !== 304 ? res.json() : false,
res.headers.get('ETag') res.headers.get('ETag')
]), ]),
(err) => { (err) => {
this.showErrors({message: 'Не удалось достать новости: что-то не так с сетью.', wrapped: err}); const statusCode = err.data && err.data.status;
return [false, false]; const ifCritical = null;
this.showErrors(ifCritical, {
message: statusCode === 403
? 'Слишком много запросов :-( Сообщите разработчику, как вам удалось всё истратить.'
: 'Не удалось достать новости: что-то не так с сетью.',
wrapped: err,
});
return [err, false, false];
} }
); );
@ -103,21 +112,25 @@ export default function getApp(theState) {
localStorage[uiComEtag] = etag; localStorage[uiComEtag] = etag;
} }
if (error) {
return; // Let the user see the error message and contemplate.
}
const ifNews = (() => { const ifNewsWasSet = (() => {
if (!(comments && comments.length)) { if (comments === false) {
const news = JSON.parse(localStorage[uiLastNewsArr]); // 304
if (news) { const json = localStorage[uiLastNewsArr];
const news = json && JSON.parse(json);
if (news && news.length) {
this.setNewsStatusTo(news); this.setNewsStatusTo(news);
return true; return true;
} }
return false; return false;
} }
let minDate; let minDate; // Minimal date among all news-comments.
const news = []; const news = comments.reduce((acc, comment) => {
comments.forEach((comment) => {
const curDate = comment.updated_at || comment.created_at; const curDate = comment.updated_at || comment.created_at;
const newsTitle = this.getNewsHeadline( comment.body ); const newsTitle = this.getNewsHeadline( comment.body );
@ -125,21 +138,25 @@ export default function getApp(theState) {
if (!minDate || curDate <= minDate) { if (!minDate || curDate <= minDate) {
minDate = curDate; minDate = curDate;
} }
news.push([newsTitle, comment.html_url]); acc.push([newsTitle, comment.html_url]);
} }
return acc;
}); }, []);
if (!news.length) { // Response with empty news is cached too.
return false;
}
localStorage[uiComDate] = minDate;
localStorage[uiLastNewsArr] = JSON.stringify(news); localStorage[uiLastNewsArr] = JSON.stringify(news);
this.setNewsStatusTo(news); if (news.length) {
return true; if (minDate) {
localStorage[uiComDate] = minDate;
}
this.setNewsStatusTo(news);
return true;
}
return false;
})(); })();
if (!ifNews) { if (!ifNewsWasSet) {
this.setStatusTo('Ничего нового.'); this.setStatusTo('Хорошего настроения Вам!');
} }
} }
@ -171,18 +188,11 @@ export default function getApp(theState) {
: () => {}; : () => {};
const warns = args; const warns = args;
const warningHtml = warns const errToHtmlMessage = (error) => {
.map(
(w) => w && w.message || ''
)
.filter( (m) => m )
.map( (m) => '✘ ' + m )
.join('<br/>');
let messageHtml = ''; let messageHtml = '';
if (err) { let wrapped = error.wrapped;
let wrapped = err.wrapped; messageHtml = error.message || '';
messageHtml = err.message || '';
while( wrapped ) { while( wrapped ) {
const deeperMsg = wrapped && wrapped.message; const deeperMsg = wrapped && wrapped.message;
@ -191,7 +201,20 @@ export default function getApp(theState) {
} }
wrapped = wrapped.wrapped; wrapped = wrapped.wrapped;
} }
} return messageHtml;
};
let messageHtml = err ? errToHtmlMessage(err) : '';
const warningHtml = warns
.filter((w) => w)
.map(
(w) => errToHtmlMessage(w)
)
.map( (m) => '✘ ' + m )
.join('<br/>');
messageHtml = messageHtml.trim(); messageHtml = messageHtml.trim();
if (warningHtml) { if (warningHtml) {
messageHtml = messageHtml ? messageHtml + '<br/>' + warningHtml : warningHtml; messageHtml = messageHtml ? messageHtml + '<br/>' + warningHtml : warningHtml;

View File

@ -14,6 +14,7 @@ export default function getApplyMods(theState) {
props.apis.pacKitchen.resetToDefaults(); props.apis.pacKitchen.resetToDefaults();
props.bgWindow.utils.fireRequest('ip-to-host-reset-to-defaults', cb); props.bgWindow.utils.fireRequest('ip-to-host-reset-to-defaults', cb);
window.localStorage.clear();
}, },
'Откройте окно заново для отображения эффекта.', 'Откройте окно заново для отображения эффекта.',

View File

@ -83,6 +83,8 @@ export default function getExcEditor(theState) {
sortedListOfOptions: this.modsToOpts(pacMods), sortedListOfOptions: this.modsToOpts(pacMods),
isHostHidden: {} isHostHidden: {}
}; };
this.handleRadioClick = this.handleRadioClick.bind(this);
this.handleInputOrClick = this.handleInputOrClick.bind(this);
} }
@ -135,7 +137,7 @@ export default function getExcEditor(theState) {
case 'this-no': case 'this-no':
if (ifYesClicked && !pacMods.filteredCustomsString) { if (ifYesClicked && !pacMods.filteredCustomsString) {
this.props.funs.showErrors( new TypeError( this.props.funs.showErrors( new TypeError(
'Проксировать СВОИ сайты можно только при наличии СВОИХ прокси (см. «Модификаторы» ). Нет своих прокси, удовлетворяющих вашим требованиям.' 'Проксировать СВОИ сайты можно только при наличии СВОИХ прокси. Нет своих прокси, удовлетворяющих вашим требованиям.'
)); ));
return false; return false;
} }
@ -289,9 +291,6 @@ export default function getExcEditor(theState) {
}, undefined); }, undefined);
const onradio = this.handleRadioClick.bind(this);
const oninput = this.handleInputOrClick.bind(this);
return ( return (
<section style="padding-bottom: 1em;"> <section style="padding-bottom: 1em;">
<div>Проксировать указанный сайт?</div> <div>Проксировать указанный сайт?</div>
@ -301,8 +300,8 @@ export default function getExcEditor(theState) {
value={this.state.trimmedInputValueOrSpace} value={this.state.trimmedInputValueOrSpace}
ref={(inputNode) => { this.rawInput = inputNode; }} ref={(inputNode) => { this.rawInput = inputNode; }}
onKeyDown={this.handleKeyDown.bind(this)} onKeyDown={this.handleKeyDown.bind(this)}
onInput={oninput} onInput={this.handleInputOrClick}
onClick={oninput} onClick={this.handleInputOrClick}
/> />
</div> </div>
{/*<a href class="emoji">⇄</a>*/} {/*<a href class="emoji">⇄</a>*/}
@ -325,13 +324,13 @@ export default function getExcEditor(theState) {
} }
</datalist> </datalist>
<ol class="horizontalList middledChildren" id="exc-radio"> <ol class="horizontalList middledChildren" id="exc-radio">
<li><input id="this-auto" type="radio" checked name="if-proxy-this-site" onClick={onradio}/>{' '} <li><input id="this-auto" type="radio" checked name="if-proxy-this-site" onClick={this.handleRadioClick}/>{' '}
<label for="this-auto">{/*<span class="emoji">🔄(looks fat)</span>*/}<svg <label for="this-auto">{/*<span class="emoji">🔄(looks fat)</span>*/}<svg
class="icon" class="icon"
style="position: relative; top: 0.15em;"><use xlink:href="#iconLoopRound"></use></svg>&nbsp;авто</label> style="position: relative; top: 0.15em;"><use xlink:href="#iconLoopRound"></use></svg>&nbsp;авто</label>
</li> </li>
<li> <li>
<input id="this-yes" type="radio" name="if-proxy-this-site" checked={inputProxyingState === true} onClick={onradio}/> <input id="this-yes" type="radio" name="if-proxy-this-site" checked={inputProxyingState === true} onClick={this.handleRadioClick}/>
{' '}<label for="this-yes"> {' '}<label for="this-yes">
<span <span
class="emoji____buggy" class="emoji____buggy"
@ -339,7 +338,7 @@ export default function getExcEditor(theState) {
</label> </label>
</li> </li>
<li> <li>
<input id="this-no" type="radio" name="if-proxy-this-site" checked={inputProxyingState === false} onClick={onradio}/> <input id="this-no" type="radio" name="if-proxy-this-site" checked={inputProxyingState === false} onClick={this.handleRadioClick}/>
{' '}<label for="this-no"><span class="emoji"></span>&nbsp;нет</label></li> {' '}<label for="this-no"><span class="emoji"></span>&nbsp;нет</label></li>
</ol> </ol>
</section> </section>

View File

@ -71,10 +71,10 @@ export default function getExceptions(theState) {
key: 'lookupLastErrors', key: 'lookupLastErrors',
desc: 'Собирать последние ошибки в запросах, чтобы вручную добавлять избранные из них в исключения.', desc: 'Собирать последние ошибки в запросах, чтобы вручную добавлять избранные из них в исключения.',
}} }}
checked={props.bgWindow.apis.lastErrors.ifCollecting} checked={props.bgWindow.apis.lastNetErrors.ifCollecting}
onChange={(event) => { onChange={(event) => {
props.bgWindow.apis.lastErrors.ifCollecting = event.target.checked; props.bgWindow.apis.lastNetErrors.ifCollecting = event.target.checked;
props.funs.setStatusTo('Сделано.'); props.funs.setStatusTo('Сделано.');
}} }}

View File

@ -88,9 +88,6 @@ export default function getInfoLi() {
/* CHILDREN */ /* CHILDREN */
input:not(:checked) ~ .children {
display: none;
}
.children { .children {
flex-grow: 9999; flex-grow: 9999;
} }
@ -110,17 +107,30 @@ export default function getInfoLi() {
}; };
return function InfoLi(props) { return function InfoLi(originalProps) {
props = Object.assign({}, { const props = Object.assign({}, {
idPrefix: '', idPrefix: '',
ifDashify: false, ifDashify: false,
}, props); }, originalProps);
const iddy = props.idPrefix + ( props.ifDashify ? camelToDash(props.conf.key) : props.conf.key ); const iddy = props.idPrefix + ( props.ifDashify ? camelToDash(props.conf.key) : props.conf.key );
const inputProps = {
id: iddy,
name: props.name,
type: props.type,
checked: props.checked,
onClick: props.onClick,
onChange: props.onChange,
class: props.class,
disabled: props.ifInputsDisabled,
};
delete inputProps.children;
return ( return (
<li class={scopedCss.infoRow + ' horFlex'} style={ props.children && 'flex-wrap: wrap'}> <li class={scopedCss.infoRow + ' horFlex'} style={ props.children && 'flex-wrap: wrap'}>
{ createElement('input', Object.assign({}, props, {id: iddy})) } {createElement('input', inputProps)}
<div class={scopedCss.labelContainer}> <div class={scopedCss.labelContainer}>
<label for={iddy} dangerouslySetInnerHTML={{__html: props.conf.label}}></label> <label for={iddy} dangerouslySetInnerHTML={{__html: props.conf.label}}></label>
{props.nodeAfterLabel} {props.nodeAfterLabel}
@ -136,8 +146,8 @@ export default function getInfoLi() {
: (<span>&nbsp;</span>) // Affects vertical align of flexbox items. : (<span>&nbsp;</span>) // Affects vertical align of flexbox items.
) )
} }
{ props.checked && props.children } {/* props.checked && props.children */}
{/* props.checked && (<div class={scopedCss.children}>{props.children}</div>)*/} {props.checked && props.children && (<div class={scopedCss.children}>{props.children}</div>)}
</li> </li>
); );

View File

@ -64,6 +64,7 @@ export default function getModList(theState) {
checked={this.state.checks[index]} checked={this.state.checks[index]}
key={index} key={index}
onChange={(event) => this.handleCheck(confMeta, event.target.checked)} onChange={(event) => this.handleCheck(confMeta, event.target.checked)}
ifInputsDisabled={props.ifInputsDisabled}
> >
{child} {child}
</InfoLi>); </InfoLi>);

View File

@ -68,6 +68,8 @@ export default function getPacChooser(theState) {
onSuccess onSuccess
); );
}; };
this.radioClickHandler = this.radioClickHandler.bind(this);
this.updateClickHandler = this.updateClickHandler.bind(this);
} }
@ -77,6 +79,13 @@ export default function getPacChooser(theState) {
} }
updateClickHandler(event) {
event.preventDefault();
this.updatePac();
}
radioClickHandler(event) { radioClickHandler(event) {
const checkChosenProvider = () => const checkChosenProvider = () =>
@ -120,18 +129,13 @@ export default function getPacChooser(theState) {
{ {
[...props.apis.antiCensorRu.getSortedEntriesForProviders(), {key: 'none', label: 'Отключить'}].map((provConf) => [...props.apis.antiCensorRu.getSortedEntriesForProviders(), {key: 'none', label: 'Отключить'}].map((provConf) =>
(<InfoLi (<InfoLi
onClick={this.radioClickHandler.bind(this)} onClick={this.radioClickHandler}
conf={provConf} conf={provConf}
type="radio" type="radio"
name="pacProvider" name="pacProvider"
checked={iddyToCheck === provConf.key} checked={iddyToCheck === provConf.key}
disabled={props.ifInputsDisabled} ifInputsDisabled={props.ifInputsDisabled}
nodeAfterLabel={<a href="" class={scopedCss.updateButton} onClick={(evt) => { nodeAfterLabel={<a href="" class={scopedCss.updateButton} onClick={this.updateClickHandler}>[обновить]</a>}
evt.preventDefault();
this.updatePac();
}}>[обновить]</a>}
/>) />)
) )
} }

View File

@ -7,10 +7,6 @@ export default function getProxyEditor(theState) {
const scopedCss = css` const scopedCss = css`
form.root {
width: 100%;
}
table.editor { table.editor {
border-collapse: collapse; border-collapse: collapse;
/*border-style: hidden;*/ /*border-style: hidden;*/
@ -19,6 +15,10 @@ export default function getProxyEditor(theState) {
background-color: #f3f5f6; background-color: #f3f5f6;
} }
.tabledEditor, .exportsEditor {
/* Empty, but not undefined. */
}
table.editor ::-webkit-input-placeholder { table.editor ::-webkit-input-placeholder {
color: #c9c9c9; color: #c9c9c9;
} }
@ -110,9 +110,10 @@ export default function getProxyEditor(theState) {
} }
/* LAST COLUMN: BUTTONS */ /* LAST COLUMN: BUTTONS */
table.editor tr > *:nth-last-child(1), table.editor tr > *:nth-last-child(1), /* Buttons */
table.editor tr > *:nth-last-child(2), table.tabledEditor tr > *:nth-last-child(2), /* Port */
table.editor tr.proxyRow > td:first-child { table.tabledEditor tr.proxyRow > td:first-child /* Type */
{
text-align: center; text-align: center;
padding: 0; padding: 0;
position: relative; position: relative;
@ -258,8 +259,8 @@ export default function getProxyEditor(theState) {
render(props) { render(props) {
return ( return (
<form onSubmit={linkEvent(this, this.handleSubmit)} class={scopedCss.root}> <form onSubmit={linkEvent(this, this.handleSubmit)}>
<table class={scopedCss.editor}> <table class={scopedCss.editor + ' ' + scopedCss.tabledEditor}>
<thead> <thead>
<tr> <tr>
<th colspan="2" class={scopedCss.shrink}>протокол</th> <th colspan="2" class={scopedCss.shrink}>протокол</th>
@ -453,8 +454,8 @@ export default function getProxyEditor(theState) {
const reset = linkEvent(this, this.resetState); const reset = linkEvent(this, this.resetState);
return ( return (
<form onSubmit={linkEvent(this, this.handleSubmit)} class={scopedCss.root}> <form onSubmit={linkEvent(this, this.handleSubmit)}>
<table class={scopedCss.editor}> <table class={scopedCss.editor + ' ' + scopedCss.exportsEditor}>
<thead> <thead>
<tr> <tr>
<th style="width: 100%"> <th style="width: 100%">

View File

@ -40,7 +40,7 @@ export default function append(document, { flags }) {
ul, ol { ul, ol {
list-style-type: none; list-style-type: none;
} }
li, .nowrap { .nowrap {
white-space: nowrap; white-space: nowrap;
word-break: keep-all; word-break: keep-all;
} }

View File

@ -5,7 +5,6 @@
chrome.webNavigation.onErrorOccurred.addListener((details) => { chrome.webNavigation.onErrorOccurred.addListener((details) => {
const tabId = details.tabId; const tabId = details.tabId;
console.log(details.url, details.error, details);
if ( !(details.frameId === 0 && tabId >= 0) || if ( !(details.frameId === 0 && tabId >= 0) ||
[ [
'net::ERR_BLOCKED_BY_CLIENT', 'net::ERR_BLOCKED_BY_CLIENT',
@ -14,11 +13,9 @@
return; return;
} }
console.log(details.url, details.error, details);
chrome.browserAction.setPopup({ chrome.browserAction.setPopup({
tabId, tabId,
popup: './pages/options/index.html#tab=exceptions&status=Правый клик по иконке = меню инструментов!', popup: './pages/options/index.html#tab=exceptions&status=Правый клик по иконке меню инструментов!',
}); });
window.chrome.browserAction.setBadgeBackgroundColor({ window.chrome.browserAction.setBadgeBackgroundColor({

View File

@ -1,20 +1,39 @@
'use strict'; 'use strict';
{ {
const chromified = window.utils.chromified;
const lastErrors = []; const lastErrors = [];
const lastErrorsLength = 20; const lastErrorsLength = 20;
const that = window.apis.lastErrors = { const IF_COLL_KEY = 'err-to-exc-if-coll';
ifCollecting: false,
const privates = {
ifCollecting: window.localStorage[IF_COLL_KEY] || false,
};
const that = window.apis.lastNetErrors = {
get ifCollecting() {
return privates.ifCollecting;
},
set ifCollecting(newValue) {
privates.ifCollecting = window.localStorage[IF_COLL_KEY] = newValue;
},
get: () => lastErrors, get: () => lastErrors,
} }
chrome.webRequest.onErrorOccurred.addListener((details) => { chrome.webRequest.onErrorOccurred.addListener(chromified((err/*Ignored*/, details) => {
if (!that.ifCollecting || [ if (!that.ifCollecting || [
'net::ERR_BLOCKED_BY_CLIENT', 'net::ERR_BLOCKED_BY_CLIENT',
'net::ERR_ABORTED', 'net::ERR_ABORTED',
'net::ERR_CACHE_MISS',
'net::ERR_INSUFFICIENT_RESOURCES',
].includes(details.error) ) { ].includes(details.error) ) {
return; return;
} }
@ -25,11 +44,11 @@
} }
lastErrors.unshift(details); lastErrors.unshift(details);
if (lastErrors.length > lastErrorsLenght) { if (lastErrors.length > lastErrorsLength) {
lastErrors.pop(); lastErrors.pop();
} }
}, }),
{urls: ['<all_urls>']} {urls: ['<all_urls>']}
); );

View File

@ -12,6 +12,7 @@
margin: 0 auto; margin: 0 auto;
} }
table { table {
width: 100%;
border-collapse: collapse; border-collapse: collapse;
background-color: #f3f5f6; background-color: #f3f5f6;
margin-bottom: 1em; margin-bottom: 1em;
@ -41,6 +42,7 @@
</head> </head>
<body> <body>
<h3>Список последних ошибок</h3> <h3>Список последних ошибок</h3>
Новые сверху, количество ограничено 20тью.
<table> <table>
<thead> <thead>
<tr> <tr>

View File

@ -5,7 +5,7 @@ chrome.runtime.getBackgroundPage( (bgWindow) =>
window, 'LERR', () => { window, 'LERR', () => {
const tbody = document.getElementById('errorsTable'); const tbody = document.getElementById('errorsTable');
const errors = bgWindow.apis.lastErrors.get().map( const errors = bgWindow.apis.lastNetErrors.get().map(
({url, error}, index) => ({ message: error, hostname: new URL(url).hostname, ifChecked: false }) ({url, error}, index) => ({ message: error, hostname: new URL(url).hostname, ifChecked: false })
); );
@ -13,6 +13,10 @@ chrome.runtime.getBackgroundPage( (bgWindow) =>
const exc = bgWindow.apis.pacKitchen.getPacMods().exceptions || {}; const exc = bgWindow.apis.pacKitchen.getPacMods().exceptions || {};
tbody.innerHTML = ''; tbody.innerHTML = '';
if (!errors.length) {
tbody.innerHTML = '<tr><td colspan="4">Ошибок пока не было.</td></tr>';
return;
}
errors.forEach((err, index) => { errors.forEach((err, index) => {
const ifProxy = exc[err.hostname]; const ifProxy = exc[err.hostname];

View File

@ -1,7 +1,7 @@
'use strict'; 'use strict';
const commonContext = { const commonContext = {
version: '0.33', version: '1.0',
}; };
exports.contexts = {}; exports.contexts = {};