Fix: don't recreate element when style update in popup
This commit is contained in:
parent
583ca31d97
commit
75f2561154
|
@ -372,8 +372,7 @@ const styleManager = (() => {
|
||||||
function compileRe(text) {
|
function compileRe(text) {
|
||||||
let re = compiledRe.get(text);
|
let re = compiledRe.get(text);
|
||||||
if (!re) {
|
if (!re) {
|
||||||
// FIXME: it should be `$({text})$` but we don't use the standard for compatibility
|
re = tryRegExp(`^(${text})$`);
|
||||||
re = tryRegExp(`^${text}$`);
|
|
||||||
if (!re) {
|
if (!re) {
|
||||||
re = BAD_MATCHER;
|
re = BAD_MATCHER;
|
||||||
}
|
}
|
||||||
|
|
|
@ -31,7 +31,7 @@
|
||||||
// if (needTransitionPatch(styles)) {
|
// if (needTransitionPatch(styles)) {
|
||||||
// applyTransitionPatch();
|
// applyTransitionPatch();
|
||||||
// }
|
// }
|
||||||
applyStyles(styles);
|
applyStyles(styles.filter(s => s.enabled));
|
||||||
});
|
});
|
||||||
msg.onTab(applyOnMessage);
|
msg.onTab(applyOnMessage);
|
||||||
window.applyOnMessage = applyOnMessage;
|
window.applyOnMessage = applyOnMessage;
|
||||||
|
|
|
@ -87,7 +87,7 @@ const msg = (() => {
|
||||||
}
|
}
|
||||||
|
|
||||||
function broadcastTab(data, filter, options, ignoreExtension = false, target = 'tab') {
|
function broadcastTab(data, filter, options, ignoreExtension = false, target = 'tab') {
|
||||||
return tabQuery()
|
return tabQuery({})
|
||||||
// TODO: send to activated tabs first?
|
// TODO: send to activated tabs first?
|
||||||
.then(tabs => {
|
.then(tabs => {
|
||||||
const requests = [];
|
const requests = [];
|
||||||
|
@ -100,7 +100,7 @@ const msg = (() => {
|
||||||
!tab.url.startsWith('chrome://newtab/') &&
|
!tab.url.startsWith('chrome://newtab/') &&
|
||||||
!isExtension ||
|
!isExtension ||
|
||||||
isExtension && ignoreExtension ||
|
isExtension && ignoreExtension ||
|
||||||
!filter(tab.url)
|
!filter(tab)
|
||||||
) {
|
) {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
183
popup/popup.js
183
popup/popup.js
|
@ -112,34 +112,36 @@ function initPopup() {
|
||||||
}
|
}
|
||||||
|
|
||||||
getActiveTab().then(function ping(tab, retryCountdown = 10) {
|
getActiveTab().then(function ping(tab, retryCountdown = 10) {
|
||||||
msg.sendTab(tab.id, {method: 'ping'}, {frameId: 0}).then(pong => {
|
msg.sendTab(tab.id, {method: 'ping'}, {frameId: 0})
|
||||||
if (pong) {
|
.catch(() => false)
|
||||||
return;
|
.then(pong => {
|
||||||
}
|
if (pong) {
|
||||||
ignoreChromeError();
|
return;
|
||||||
// FF and some Chrome forks (e.g. CentBrowser) implement tab-on-demand
|
}
|
||||||
// so we'll wait a bit to handle popup being invoked right after switching
|
ignoreChromeError();
|
||||||
if (retryCountdown > 0 && (
|
// FF and some Chrome forks (e.g. CentBrowser) implement tab-on-demand
|
||||||
tab.status !== 'complete' ||
|
// so we'll wait a bit to handle popup being invoked right after switching
|
||||||
FIREFOX && tab.url === 'about:blank')) {
|
if (retryCountdown > 0 && (
|
||||||
setTimeout(ping, 100, tab, --retryCountdown);
|
tab.status !== 'complete' ||
|
||||||
return;
|
FIREFOX && tab.url === 'about:blank')) {
|
||||||
}
|
setTimeout(ping, 100, tab, --retryCountdown);
|
||||||
const info = template.unreachableInfo;
|
return;
|
||||||
if (FIREFOX && tabURL.startsWith(URLS.browserWebStore)) {
|
}
|
||||||
$('label', info).textContent = t('unreachableAMO');
|
const info = template.unreachableInfo;
|
||||||
const note = (FIREFOX < 59 ? t('unreachableAMOHintOldFF') : t('unreachableAMOHint')) +
|
if (FIREFOX && tabURL.startsWith(URLS.browserWebStore)) {
|
||||||
(FIREFOX < 60 ? '' : '\n' + t('unreachableAMOHintNewFF'));
|
$('label', info).textContent = t('unreachableAMO');
|
||||||
const renderToken = s => s[0] === '<' ? $create('b', tWordBreak(s.slice(1, -1))) : s;
|
const note = (FIREFOX < 59 ? t('unreachableAMOHintOldFF') : t('unreachableAMOHint')) +
|
||||||
const renderLine = line => $create('p', line.split(/(<.*?>)/).map(renderToken));
|
(FIREFOX < 60 ? '' : '\n' + t('unreachableAMOHintNewFF'));
|
||||||
const noteNode = $create('fragment', note.split('\n').map(renderLine));
|
const renderToken = s => s[0] === '<' ? $create('b', tWordBreak(s.slice(1, -1))) : s;
|
||||||
const target = $('p', info);
|
const renderLine = line => $create('p', line.split(/(<.*?>)/).map(renderToken));
|
||||||
target.parentNode.insertBefore(noteNode, target);
|
const noteNode = $create('fragment', note.split('\n').map(renderLine));
|
||||||
target.remove();
|
const target = $('p', info);
|
||||||
}
|
target.parentNode.insertBefore(noteNode, target);
|
||||||
document.body.classList.add('unreachable');
|
target.remove();
|
||||||
document.body.insertBefore(info, document.body.firstChild);
|
}
|
||||||
});
|
document.body.classList.add('unreachable');
|
||||||
|
document.body.insertBefore(info, document.body.firstChild);
|
||||||
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
// Write new style links
|
// Write new style links
|
||||||
|
@ -251,77 +253,86 @@ function createStyleElement({
|
||||||
check = false,
|
check = false,
|
||||||
container = installed,
|
container = installed,
|
||||||
}) {
|
}) {
|
||||||
const entry = template.style.cloneNode(true);
|
let entry = $(ENTRY_ID_PREFIX + style.id);
|
||||||
|
if (!entry) {
|
||||||
|
entry = template.style.cloneNode(true);
|
||||||
|
entry.setAttribute('style-id', style.id);
|
||||||
|
Object.assign(entry, {
|
||||||
|
id: ENTRY_ID_PREFIX_RAW + style.id,
|
||||||
|
styleId: style.id,
|
||||||
|
styleIsUsercss: Boolean(style.usercssData),
|
||||||
|
onmousedown: handleEvent.maybeEdit,
|
||||||
|
styleMeta: style
|
||||||
|
});
|
||||||
|
const checkbox = $('.checker', entry);
|
||||||
|
Object.assign(checkbox, {
|
||||||
|
id: ENTRY_ID_PREFIX_RAW + style.id,
|
||||||
|
title: t('exclusionsPopupTip'),
|
||||||
|
onclick: handleEvent.toggle,
|
||||||
|
oncontextmenu: handleEvent.openExcludeMenu
|
||||||
|
});
|
||||||
|
const editLink = $('.style-edit-link', entry);
|
||||||
|
Object.assign(editLink, {
|
||||||
|
href: editLink.getAttribute('href') + style.id,
|
||||||
|
onclick: handleEvent.openLink,
|
||||||
|
});
|
||||||
|
const styleName = $('.style-name', entry);
|
||||||
|
Object.assign(styleName, {
|
||||||
|
htmlFor: ENTRY_ID_PREFIX_RAW + style.id,
|
||||||
|
onclick: handleEvent.name,
|
||||||
|
});
|
||||||
|
styleName.checkbox = checkbox;
|
||||||
|
styleName.appendChild(document.createTextNode(' '));
|
||||||
|
const config = $('.configure', entry);
|
||||||
|
if (!style.usercssData && style.updateUrl && style.updateUrl.includes('?') && style.url) {
|
||||||
|
config.target = '_blank';
|
||||||
|
config.title = t('configureStyleOnHomepage');
|
||||||
|
config.dataset.sendMessage = JSON.stringify({method: 'openSettings'});
|
||||||
|
$('use', config).attributes['xlink:href'].nodeValue = '#svg-icon-config-uso';
|
||||||
|
}
|
||||||
|
$('.enable', entry).onclick = handleEvent.toggle;
|
||||||
|
$('.disable', entry).onclick = handleEvent.toggle;
|
||||||
|
$('.delete', entry).onclick = handleEvent.delete;
|
||||||
|
$('.configure', entry).onclick = handleEvent.configure;
|
||||||
|
}
|
||||||
|
|
||||||
|
style = Object.assign(entry.styleMeta, style);
|
||||||
|
|
||||||
|
if (style.enabled) {
|
||||||
|
entry.classList.remove('disabled');
|
||||||
|
entry.classList.add('enabled');
|
||||||
|
$('.checker', entry).checked = true;
|
||||||
|
} else {
|
||||||
|
entry.classList.add('disabled');
|
||||||
|
entry.classList.remove('enabled');
|
||||||
|
$('.checker', entry).checked = false;
|
||||||
|
}
|
||||||
|
|
||||||
const excluded = popupExclusions.isExcluded(tabURL, style.exclusions);
|
const excluded = popupExclusions.isExcluded(tabURL, style.exclusions);
|
||||||
entry.setAttribute('style-id', style.id);
|
entry.classList.toggle('excluded', excluded);
|
||||||
Object.assign(entry, {
|
|
||||||
id: ENTRY_ID_PREFIX_RAW + style.id,
|
|
||||||
styleId: style.id,
|
|
||||||
styleIsUsercss: Boolean(style.usercssData),
|
|
||||||
className: entry.className + ' ' +
|
|
||||||
(style.enabled ? 'enabled' : 'disabled') +
|
|
||||||
(excluded ? ' excluded' : ''),
|
|
||||||
onmousedown: handleEvent.maybeEdit,
|
|
||||||
styleMeta: style
|
|
||||||
});
|
|
||||||
|
|
||||||
const checkbox = $('.checker', entry);
|
|
||||||
Object.assign(checkbox, {
|
|
||||||
id: ENTRY_ID_PREFIX_RAW + style.id,
|
|
||||||
title: t('exclusionsPopupTip'),
|
|
||||||
checked: style.enabled,
|
|
||||||
onclick: handleEvent.toggle,
|
|
||||||
oncontextmenu: handleEvent.openExcludeMenu
|
|
||||||
});
|
|
||||||
|
|
||||||
const editLink = $('.style-edit-link', entry);
|
|
||||||
Object.assign(editLink, {
|
|
||||||
href: editLink.getAttribute('href') + style.id,
|
|
||||||
onclick: handleEvent.openLink,
|
|
||||||
});
|
|
||||||
|
|
||||||
const styleName = $('.style-name', entry);
|
const styleName = $('.style-name', entry);
|
||||||
Object.assign(styleName, {
|
styleName.lastChild.textContent = style.name;
|
||||||
htmlFor: ENTRY_ID_PREFIX_RAW + style.id,
|
setTimeout(() => {
|
||||||
onclick: handleEvent.name,
|
if (styleName.scrollWidth > styleName.clientWidth + 1) {
|
||||||
});
|
styleName.title = styleName.textContent;
|
||||||
styleName.checkbox = checkbox;
|
|
||||||
styleName.appendChild(document.createTextNode(style.name));
|
|
||||||
setTimeout((el = styleName) => {
|
|
||||||
if (el.scrollWidth > el.clientWidth + 1) {
|
|
||||||
el.title = el.textContent;
|
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
const config = $('.configure', entry);
|
const config = $('.configure', entry);
|
||||||
if (!style.usercssData && style.updateUrl && style.updateUrl.includes('?') && style.url) {
|
if (!style.usercssData && style.updateUrl && style.updateUrl.includes('?') && style.url) {
|
||||||
config.href = style.url;
|
config.href = style.url;
|
||||||
config.target = '_blank';
|
} else {
|
||||||
config.title = t('configureStyleOnHomepage');
|
config.href = '';
|
||||||
config.dataset.sendMessage = JSON.stringify({method: 'openSettings'});
|
|
||||||
$('use', config).attributes['xlink:href'].nodeValue = '#svg-icon-config-uso';
|
|
||||||
} else if (!style.usercssData || !Object.keys(style.usercssData.vars || {}).length) {
|
|
||||||
config.style.display = 'none';
|
|
||||||
}
|
}
|
||||||
|
config.style.display =
|
||||||
$('.enable', entry).onclick = handleEvent.toggle;
|
!style.usercssData && config.href ||
|
||||||
$('.disable', entry).onclick = handleEvent.toggle;
|
style.usercssData && Object.keys(style.usercssData.vars || {}).length ?
|
||||||
$('.delete', entry).onclick = handleEvent.delete;
|
'' : 'none';
|
||||||
$('.configure', entry).onclick = handleEvent.configure;
|
|
||||||
|
|
||||||
if (check) detectSloppyRegexps([style]);
|
if (check) detectSloppyRegexps([style]);
|
||||||
|
|
||||||
const oldElement = $(ENTRY_ID_PREFIX + style.id);
|
if (entry.parentNode !== container) {
|
||||||
if (oldElement && oldElement.contains(document.activeElement)) {
|
|
||||||
// preserve the focused element inside
|
|
||||||
const {className} = document.activeElement;
|
|
||||||
oldElement.parentNode.replaceChild(entry, oldElement);
|
|
||||||
// we're not using $() since className may contain multiple tokens
|
|
||||||
const el = entry.getElementsByClassName(className)[0];
|
|
||||||
if (el) el.focus();
|
|
||||||
} else if (oldElement) {
|
|
||||||
oldElement.parentNode.replaceChild(entry, oldElement);
|
|
||||||
} else {
|
|
||||||
container.appendChild(entry);
|
container.appendChild(entry);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in New Issue
Block a user