Grey icon if turned off, change exception editor ui/behavior

This commit is contained in:
Ilya Ig. Petrov 2017-02-01 18:41:10 +00:00
parent c5c1ac559e
commit ca8d753372
7 changed files with 192 additions and 107 deletions

View File

@ -106,10 +106,27 @@
}, },
areSettingsNotControlledFor(details) { /*
* Possible values for levelOfControl:
*
* 1. "not_controllable"
* 2. "controlled_by_other_extensions"
* 3. "controllable_by_this_extension"
* 4. "controlled_by_this_extension"
*
* See: https://developer.chrome.com/extensions/proxy
* */
return ['controlled_by_other', 'not_controllable']
.some( (prefix) => details.levelOfControl.startsWith(prefix) ); areSettingsControllableFor(details) {
return details.levelOfControl.endsWith('this_extension');
},
areSettingsControlledFor(details) {
return details.levelOfControl.startsWith('controlled_by_this');
}, },

View File

@ -109,19 +109,28 @@
}, },
ifNotControlled: null, ifControlled: null,
ifControllable: null,
isNotControlled(details) { isControllable(details) {
this.ifNotControlled = window.utils.areSettingsNotControlledFor(details); this.ifControllable = window.utils.areSettingsControllableFor(details);
if (this.ifNotControlled) {
if (this.ifControllable) {
this.ifControlled = window.utils.areSettingsControlledFor(details);
} else {
this.ifControlled = false;
}
if (this.ifControlled) {
chrome.browserAction.setIcon( {path: './icons/default-128.png'} );
} else {
chrome.browserAction.setIcon({ chrome.browserAction.setIcon({
path: './icons/default-grayscale-128.png', path: './icons/default-grayscale-128.png',
}); });
} else {
chrome.browserAction.setIcon( {path: './icons/default-128.png'} );
} }
return this.ifNotControlled;
return this.ifControllable;
}, },
@ -192,7 +201,7 @@
chrome.proxy.settings.get( chrome.proxy.settings.get(
{}, {},
(details) => handlers.isNotControlled(details) (details) => handlers.isControllable(details)
); );
chrome.notifications.onClicked.addListener( function(notId) { chrome.notifications.onClicked.addListener( function(notId) {
@ -211,7 +220,7 @@
chrome.proxy.onProxyError.addListener((details) => { chrome.proxy.onProxyError.addListener((details) => {
if (handlers.ifNotControlled) { if (!handlers.ifControlled) {
return; return;
} }
/* /*
@ -233,7 +242,7 @@
console.log('Proxy settings changed.', details); console.log('Proxy settings changed.', details);
const noCon = 'no-control'; const noCon = 'no-control';
if ( handlers.isNotControlled(details) ) { if ( !handlers.isControllable(details) ) {
handlers.mayNotifyVoid( handlers.mayNotifyVoid(
noCon, noCon,
chrome.i18n.getMessage('noControl'), chrome.i18n.getMessage('noControl'),

View File

@ -296,7 +296,7 @@
keepCookedNowAsync(pacMods = mandatory(), cb = throwIfError) { keepCookedNowAsync(pacMods = mandatory(), cb = throwIfError) {
console.log('Keep cooked now...'); console.log('Keep cooked now...', cb);
if (typeof(pacMods) === 'function') { if (typeof(pacMods) === 'function') {
cb = pacMods; cb = pacMods;
pacMods = this.getCurrentConfigs(); pacMods = this.getCurrentConfigs();

View File

@ -69,7 +69,7 @@
} }
chrome.proxy.settings.get({}, (details) => { chrome.proxy.settings.get({}, (details) => {
if ( window.utils.areSettingsNotControlledFor( details ) ) { if ( !window.utils.areSettingsControlledFor( details ) ) {
console.warn('Failed, other extension is in control.'); console.warn('Failed, other extension is in control.');
return cb( return cb(

View File

@ -1,10 +1,10 @@
{ {
"manifest_version": 2, "manifest_version": 2,
"name": "__MSG_extName__ 0.18", "name": "__MSG_extName__ 0.19",
"default_locale": "ru", "default_locale": "ru",
"description": "__MSG_extDesc__", "description": "__MSG_extDesc__",
"version": "0.0.0.18", "version": "0.0.0.19",
"icons": { "icons": {
"128": "/icons/default-128.png" "128": "/icons/default-128.png"
}, },
@ -37,7 +37,7 @@
] ]
}, },
"browser_action": { "browser_action": {
"default_title": "Этот сайт благословлён 0.18", "default_title": "Этот сайт благословлён 0.19",
"default_popup": "/pages/choose-pac-provider/index.html" "default_popup": "/pages/choose-pac-provider/index.html"
}, },
"options_ui": { "options_ui": {

View File

@ -6,6 +6,7 @@
:root { :root {
--ribbon-color: #4169e1; --ribbon-color: #4169e1;
--default-grey: #bfbfbf; --default-grey: #bfbfbf;
max-width: 27em;
} }
body { body {
margin: 0; margin: 0;
@ -64,35 +65,52 @@
color: white; color: white;
} }
: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) */ /* ACCORDION (OR TABBED STATEFUL UI) */
.off { .off {
display: none; display: none;
} }
.acc-padded {
section[data-for] {
padding: 0.6em 0.5em 1em; padding: 0.6em 0.5em 1em;
} }
:root.if-options-page section[data-for] {
padding-bottom: 0.6em;
}
:root.if-options-page section[data-for]:not(:last-child) {
border-bottom: 1px solid var(--default-grey);
}
/* HIDE */ /* HIDE */
#acc-pac:not(:checked) ~ .main-nav section[data-for="acc-pac"].hideable, :root:not(.if-options-page) #acc-pac:not(:checked) ~ .main-nav section[data-for="acc-pac"],
#acc-exc:not(:checked) ~ .main-nav section[data-for="acc-exc"].hideable, :root:not(.if-options-page) #acc-exc:not(:checked) ~ .main-nav section[data-for="acc-exc"],
#acc-mods:not(:checked) ~ .main-nav section[data-for="acc-mods"].hideable, :root:not(.if-options-page) #acc-mods:not(:checked) ~ .main-nav section[data-for="acc-mods"],
#acc-ntf:not(:checked) ~ .main-nav section[data-for="acc-ntf"].hideable :root:not(.if-options-page) #acc-ntf:not(:checked) ~ .main-nav section[data-for="acc-ntf"]
{ {
/* Hide, but preclude width resizes. */ /* Hide, but preclude width resizes. */
height: 0px !important; height: 0px !important;
line-height: 0px !important; line-height: 0px !important;
padding: 0; padding-top: 0 !important;
margin: 0; padding-bottom: 0 !important;
margin-top: 0 !important;
margin-bottom: 0 !important;
border: none !important;
display: block; display: block;
visibility: hidden; visibility: hidden;
transform: scaleY(0); transform: scaleY(0) !important;
} }
#acc-pac:not(:checked) ~ .main-nav section[data-for="acc-pac"].hideable *, :root:not(.if-options-page) #acc-pac:not(:checked) ~ .main-nav section[data-for="acc-pac"] *,
#acc-exc:not(:checked) ~ .main-nav section[data-for="acc-exc"].hideable *, :root:not(.if-options-page) #acc-exc:not(:checked) ~ .main-nav section[data-for="acc-exc"] *,
#acc-mods:not(:checked) ~ .main-nav section[data-for="acc-mods"].hideable *, :root:not(.if-options-page) #acc-mods:not(:checked) ~ .main-nav section[data-for="acc-mods"] *,
#acc-ntf:not(:checked) ~ .main-nav section[data-for="acc-ntf"].hideable * :root:not(.if-options-page) #acc-ntf:not(:checked) ~ .main-nav section[data-for="acc-ntf"] *
{ {
margin-top: 0 !important; margin-top: 0 !important;
margin-bottom: 0 !important; margin-bottom: 0 !important;
@ -258,27 +276,34 @@
} }
/* TAB_3 EXCEPTIONS */ /* TAB_3 EXCEPTIONS */
#exc-address {
#exc-editor { display: flex;
border-radius: 0 !important; align-items: baseline;
border: none !important; width: 100%;
--exc-hieght: 1.6em;
border-bottom: 1px solid var(--ribbon-color) !important; border-bottom: 1px solid var(--ribbon-color) !important;
// I don't understand this three, but they have effect. font-size: 1em;
max-height: 1.6em !important; }
min-height: 1.6em !important; input#exc-editor {
height: 1em !important; 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 { #exc-radio {
display: flex; display: flex;
justify-content: space-around; justify-content: space-around;
margin-top: 0.5em;
} }
[name="if-proxy-this-site"]:checked + label { [name="if-proxy-this-site"]:checked + label {
font-weight: bold; font-weight: bold;
} }
#exc-editor.if-yes { #exc-address.if-yes {
background-color: lightgreen; background-color: lightgreen;
} }
#exc-editor.if-no { #exc-address.if-no {
background-color: pink; background-color: pink;
} }
@ -332,53 +357,51 @@
<nav class="hor-padded main-nav"> <nav class="hor-padded main-nav">
<section data-for="acc-pac" class="hideable"> <section data-for="acc-pac">
<div class="acc-padded"> <ul id="list-of-providers">
<ul id="list-of-providers"> <li><input type="radio" name="pacProvider" id="none" checked> <label for="none">Отключить</label></li>
<li><input type="radio" name="pacProvider" id="none" checked> <label for="none">Отключить</label></li> </ul>
</ul> <div id="update-message">
<div id="update-message"> Обновлялись: <span class="update-date">...</span>
Обновлялись: <span class="update-date">...</span>
</div>
</div> </div>
</section> </section>
<section data-for="acc-exc" class="hideable"> <section data-for="acc-exc" class="only-for-options-page">
Редактор исключений доступен толко для <a href="chrome://newtab">вкладок</a>.
</section>
<section data-for="acc-exc" class="hidden-for-options-page">
<div>Проксировать указанный сайт?</div>
<div style="display: flex; justify-content: space-around;"> <div id="exc-address">
<span>Проксировать</span> <span>*.</span><input placeholder="navalny.com" list="exc-list" name="browser" id="exc-editor" style=""/>
<input placeholder="navalny.com" list="exc-list" name="browser" id="exc-editor"/>?
<datalist id="exc-list"></datalist>
</div> </div>
<ol class="acc-padded horizontal-list" id="exc-radio"> <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">🔄&#xFE0E; авто</label></li> <li><input id="this-auto" type="radio" checked name="if-proxy-this-site"/> <label for="this-auto">🔄&#xFE0E; авто</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-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> <li><input id="this-no" type="radio" name="if-proxy-this-site"/> <label for="this-no">&nbsp;нет</label></li>
</ol> </ol>
</section>
<section data-for="acc-mods" class="hideable">
<div class="acc-padded">
<ul id="pac-mods">
<li class="control-row">
<input type="button" value="Применить" id="apply-mods" disabled/>
<a href id="reset-mods" class="link-button">К изначальным!</a>
</li>
</ul>
</div>
</section> </section>
<section data-for="acc-ntf" class="hideable"> <section data-for="acc-mods">
<ul id="pac-mods">
<li class="control-row">
<input type="button" value="Применить" id="apply-mods" disabled/>
<a href id="reset-mods" class="link-button">К изначальным!</a>
</li>
</ul>
</section>
<section data-for="acc-ntf">
<header>Я ❤️ yведомления:</header> <header>Я ❤️ yведомления:</header>
<div class="acc-padded"> <ul id="list-of-handlers" style="margin-left: 0.4em; margin-top: 0.4em;"></ul>
<ul id="list-of-handlers"></ul>
</div>
</section> </section>

View File

@ -234,13 +234,7 @@ chrome.runtime.getBackgroundPage( (backgroundPage) =>
const ifInsideOptions = !currentTab || currentTab.url.startsWith('chrome://extensions/?options='); const ifInsideOptions = !currentTab || currentTab.url.startsWith('chrome://extensions/?options=');
if (ifInsideOptions) { if (ifInsideOptions) {
const hidClass = 'hideable'; document.documentElement.classList.add('if-options-page')
for(const el of document.querySelectorAll('.' + hidClass)) {
el.classList.remove(hidClass);
}
for(const el of document.querySelectorAll('.hidden-for-options-page')) {
el.style.display = 'none';
}
} }
// EXCEPTIONS PANEL // EXCEPTIONS PANEL
@ -253,10 +247,6 @@ chrome.runtime.getBackgroundPage( (backgroundPage) =>
const excEditor = document.getElementById('exc-editor'); const excEditor = document.getElementById('exc-editor');
if (currentTab && !currentTab.url.startsWith('chrome')) {
excEditor.value = new URL(currentTab.url).hostname;
}
const validateHost = function validateHost(host) { 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])$/; 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])$/;
@ -269,13 +259,24 @@ chrome.runtime.getBackgroundPage( (backgroundPage) =>
}; };
const ifProxyHtml = '✔'; const ifProxyHtml = '✔';
const ifNotProxyHtml = '✘';
const ifAutoHtml = '🔄';
const addOption = function addOption(host, ifProxy) { const addOption = function addOption(host, yesNoUndefined) {
const opt = document.createElement('option'); const opt = document.createElement('option');
opt.value = host; opt.value = host;
opt.dataset.host = host; opt.dataset.host = host;
opt.innerHTML = ifProxy ? ifProxyHtml : '✘'; switch(yesNoUndefined) {
case true:
opt.innerHTML = ifProxyHtml;
break;
case false:
opt.innerHTML = ifNotProxyHtml;
break;
default:
opt.innerHTML = ifAutoHtml;
}
const editorHost = excEditor.value.trim(); const editorHost = excEditor.value.trim();
if (host === editorHost) { if (host === editorHost) {
excList.insertBefore( opt, excList.firstChild ); excList.insertBefore( opt, excList.firstChild );
@ -304,7 +305,7 @@ chrome.runtime.getBackgroundPage( (backgroundPage) =>
const hideOpt = (opt) => opt.value = '\n'; const hideOpt = (opt) => opt.value = '\n';
const unhideOpt = (opt) => opt.value = opt.dataset.host + ' '; const unhideOpt = (opt) => opt.value = opt.dataset.host + ' ';
const excList = document.getElementById('exc-list'); const excList = document.getElementById('exc-list');
excEditor.onkeydown = function(event) { excEditor.onkeydown = function(event) {
@ -319,65 +320,99 @@ chrome.runtime.getBackgroundPage( (backgroundPage) =>
excEditor.onclick = excEditor.oninput = function(event) { excEditor.onclick = excEditor.oninput = function(event) {
// If triangle button on right of datalist input clicked.
let ifTriangleClicked = false;
const ifClick = event && event.type === 'click';
{
const minIndentFromRightInPx = 15;
if( ifClick
&& !this.selectionStart && !this.selectionStart
&& event.x > this.getBoundingClientRect().right - minIndentFromRightInPx
) {
ifTriangleClicked = true;
}
}
const setInputValue = (newValue) => { const setInputValue = (newValue) => {
if (event && event.type === 'click') { if (ifClick && !ifTriangleClicked) {
// Don't jerk cursor on simple clicks.
return; return;
} }
// See bug in my comment to http://stackoverflow.com/a/32394157/521957 // See bug in my comment to http://stackoverflow.com/a/32394157/521957
// The only shortcoming: first click on empty input may be still ignored. // First click on empty input may be still ignored.
const nu = this.selectionStart + newValue.length - this.value.length; const nu = this.selectionStart + newValue.length - this.value.length;
this.value = newValue; this.value = newValue;
excEditor.dataset.moveCursorTo = nu; excEditor.dataset.moveCursorTo = nu;
window.setTimeout(moveCursorIfNeeded, 0); window.setTimeout(moveCursorIfNeeded, 0);
} }
const host = this.value.trim() || ' ';
setInputValue(host);
const host = this.value.trim();
setInputValue(ifTriangleClicked ? '' : (host || ' '));
thisAuto.checked = true; thisAuto.checked = true;
this.classList.remove(noClass, yesClass); let exactOpt = false;
excList.childNodes.forEach( excList.childNodes.forEach(
(opt) => { (opt) => {
if(opt.innerHTML === ifAutoHtml) {
return opt.remove();
}
const ifExactMatch = opt.dataset.host === host; const ifExactMatch = opt.dataset.host === host;
if (!ifExactMatch) { if (!ifExactMatch) {
return unhideOpt(opt); return unhideOpt(opt);
} }
exactOpt = opt;
const exactOpt = opt;
hideOpt(exactOpt);
if(exactOpt.innerHTML === ifProxyHtml) {
thisYes.checked = true;
this.classList.add(yesClass);
} else {
thisNo.checked = true;
this.classList.add(noClass);
}
} }
); );
this.parentNode.classList.remove(noClass, yesClass);
if(exactOpt) {
if(ifTriangleClicked) {
unhideOpt(exactOpt);
} else {
hideOpt(exactOpt);
if(exactOpt.innerHTML === ifProxyHtml) {
thisYes.checked = true;
this.parentNode.classList.add(yesClass);
} else {
thisNo.checked = true;
this.parentNode.classList.add(noClass);
}
}
} else if (ifTriangleClicked && host) {
addOption(host, undefined);
}
return true; return true;
}; };
if (currentTab && !currentTab.url.startsWith('chrome')) {
excEditor.value = new URL(currentTab.url).hostname;
} else {
// Show placeholder.
excEditor.value = '';
}
{ // Populate selector. { // Populate selector.
const pacMods = pacKitchen.getPacMods(); const pacMods = pacKitchen.getPacMods();
for(const host of Object.keys(pacMods.exceptions || {}).sort()) { for(const host of Object.keys(pacMods.exceptions || {}).sort()) {
addOption(host, pacMods.exceptions[host]); addOption(host, pacMods.exceptions[host]);
} }
//excEditor.oninput();
} }
excEditor.oninput();
document.getElementById('exc-radio').onclick = function(event) { document.getElementById('exc-radio').onclick = function(event) {
/* ON CLICK */ /* ON CLICK */
if(event.target.tagName !== 'INPUT') { if(event.target.tagName !== 'INPUT') {
// Only label on checkbox.
return true; return true;
} }
@ -398,7 +433,7 @@ chrome.runtime.getBackgroundPage( (backgroundPage) =>
} }
if (thisYes.checked && !pacMods.filteredCustomsString) { if (thisYes.checked && !pacMods.filteredCustomsString) {
showErrors( new TypeError( showErrors( new TypeError(
'Проксировать СВОИ сайты можно только при наличии СВОИХ прокси (см.«Модификаторы»).' 'Проксировать СВОИ сайты можно только при наличии СВОИХ прокси (см. «Модификаторы» ).'
)); ));
return false; return false;
} }
@ -416,6 +451,7 @@ chrome.runtime.getBackgroundPage( (backgroundPage) =>
(opt) => opt.dataset.host === host && opt.remove() (opt) => opt.dataset.host === host && opt.remove()
); );
fixUi(); fixUi();
console.log(excEditor, excEditor.oninput);
excEditor.oninput(); excEditor.oninput();
} }
@ -550,7 +586,7 @@ HTTPS 11.22.33.44:8080;">${conf.value || localStorage.getItem(uiRaw) || ''}</tex
}); });
if( errorHandlers.ifNotControlled ) { if( !errorHandlers.ifControllable ) {
document.getElementById('which-extension').innerHTML document.getElementById('which-extension').innerHTML
= backgroundPage.utils.messages.whichExtensionHtml(); = backgroundPage.utils.messages.whichExtensionHtml();
document.querySelectorAll('.if-not-controlled').forEach( (node) => { document.querySelectorAll('.if-not-controlled').forEach( (node) => {
@ -565,7 +601,7 @@ HTTPS 11.22.33.44:8080;">${conf.value || localStorage.getItem(uiRaw) || ''}</tex
const id = antiCensorRu.getCurrentPacProviderKey() || 'none'; const id = antiCensorRu.getCurrentPacProviderKey() || 'none';
document.querySelector('#update-' + id).click(); document.querySelector('#update-' + id).click();
} }
document.documentElement.style.display = ''; document.documentElement.style.display = 'initial';
console.log(Date.now() - START); console.log(Date.now() - START);