193 lines
5.4 KiB
JavaScript
193 lines
5.4 KiB
JavaScript
/* global $ $$ $remove animateElement getEventKeyName moveFocus */// dom.js
|
|
/* global API */// msg.js
|
|
/* global getActiveTab */// toolbox.js
|
|
/* global resortEntries tabURL */// popup.js
|
|
/* global t */// localization.js
|
|
'use strict';
|
|
|
|
const MODAL_SHOWN = 'data-display'; // attribute name
|
|
|
|
const Events = {
|
|
|
|
async configure(event) {
|
|
const {styleId, styleIsUsercss} = getClickedStyleElement(event);
|
|
if (styleIsUsercss) {
|
|
const [style] = await Promise.all([
|
|
API.styles.get(styleId),
|
|
require(['/popup/hotkeys']), /* global hotkeys */
|
|
require(['/js/dlg/config-dialog']), /* global configDialog */
|
|
]);
|
|
hotkeys.setState(false);
|
|
await configDialog(style);
|
|
hotkeys.setState(true);
|
|
} else {
|
|
Events.openURLandHide.call(this, event);
|
|
}
|
|
},
|
|
|
|
copyContent(event) {
|
|
event.preventDefault();
|
|
const target = document.activeElement;
|
|
const message = $('.copy-message');
|
|
navigator.clipboard.writeText(target.textContent);
|
|
target.classList.add('copied');
|
|
message.classList.add('show-message');
|
|
setTimeout(() => {
|
|
target.classList.remove('copied');
|
|
message.classList.remove('show-message');
|
|
}, 1000);
|
|
},
|
|
|
|
delete(event) {
|
|
const entry = getClickedStyleElement(event);
|
|
const box = $('#confirm');
|
|
box.dataset.id = entry.styleId;
|
|
$('b', box).textContent = $('.style-name', entry).textContent;
|
|
Events.showModal(box, '[data-cmd=cancel]');
|
|
},
|
|
|
|
getExcludeRule(type) {
|
|
const u = new URL(tabURL);
|
|
return type === 'domain'
|
|
? u.origin + '/*'
|
|
: escapeGlob(u.origin + u.pathname); // current page
|
|
},
|
|
|
|
async hideModal(box, {animate} = {}) {
|
|
window.off('keydown', box._onkeydown);
|
|
box._onkeydown = null;
|
|
if (animate) {
|
|
box.style.animationName = '';
|
|
await animateElement(box, 'lights-on');
|
|
}
|
|
box.removeAttribute(MODAL_SHOWN);
|
|
},
|
|
|
|
indicator(event) {
|
|
const entry = getClickedStyleElement(event);
|
|
const info = t.template.regexpProblemExplanation.cloneNode(true);
|
|
$remove('#' + info.id);
|
|
$$('a', info).forEach(el => (el.onclick = Events.openURLandHide));
|
|
$$('button', info).forEach(el => (el.onclick = closeExplanation));
|
|
entry.appendChild(info);
|
|
},
|
|
|
|
isStyleExcluded({exclusions}, type) {
|
|
if (!exclusions) {
|
|
return false;
|
|
}
|
|
const rule = Events.getExcludeRule(type);
|
|
return exclusions.includes(rule);
|
|
},
|
|
|
|
maybeEdit(event) {
|
|
if (!(
|
|
event.button === 0 && (event.ctrlKey || event.metaKey) ||
|
|
event.button === 1 ||
|
|
event.button === 2)) {
|
|
return;
|
|
}
|
|
// open an editor on middleclick
|
|
const el = event.target;
|
|
if (el.matches('.entry, .style-edit-link') || el.closest('.style-name')) {
|
|
this.onmouseup = () => $('.style-edit-link', this).click();
|
|
this.oncontextmenu = event => event.preventDefault();
|
|
event.preventDefault();
|
|
return;
|
|
}
|
|
},
|
|
|
|
name(event) {
|
|
$('input', this).dispatchEvent(new MouseEvent('click'));
|
|
event.preventDefault();
|
|
},
|
|
|
|
async openEditor(event, options) {
|
|
event.preventDefault();
|
|
await API.openEditor(options);
|
|
window.close();
|
|
},
|
|
|
|
async openManager(event) {
|
|
event.preventDefault();
|
|
const isSearch = tabURL && (event.shiftKey || event.button === 2 || event.detail === 'site');
|
|
await API.openManage(isSearch ? {search: tabURL, searchMode: 'url'} : {});
|
|
window.close();
|
|
},
|
|
|
|
async openURLandHide(event) {
|
|
event.preventDefault();
|
|
await API.openURL({
|
|
url: this.href || this.dataset.href,
|
|
index: (await getActiveTab()).index + 1,
|
|
message: this._sendMessage,
|
|
});
|
|
window.close();
|
|
},
|
|
|
|
showModal(box, cancelButtonSelector) {
|
|
const oldBox = $(`[${MODAL_SHOWN}]`);
|
|
if (oldBox) box.style.animationName = 'none';
|
|
// '' would be fine but 'true' is backward-compatible with the existing userstyles
|
|
box.setAttribute(MODAL_SHOWN, 'true');
|
|
box._onkeydown = e => {
|
|
const key = getEventKeyName(e);
|
|
switch (key) {
|
|
case 'Tab':
|
|
case 'Shift-Tab':
|
|
e.preventDefault();
|
|
moveFocus(box, e.shiftKey ? -1 : 1);
|
|
break;
|
|
case 'Escape': {
|
|
e.preventDefault();
|
|
window.onkeydown = null;
|
|
$(cancelButtonSelector, box).click();
|
|
break;
|
|
}
|
|
}
|
|
};
|
|
window.on('keydown', box._onkeydown);
|
|
moveFocus(box, 0);
|
|
if (oldBox) Events.hideModal(oldBox);
|
|
},
|
|
|
|
async toggleState(event) {
|
|
// when fired on checkbox, prevent the parent label from seeing the event, see #501
|
|
event.stopPropagation();
|
|
await API.styles.toggle((getClickedStyleElement(event) || {}).styleId, this.checked);
|
|
resortEntries();
|
|
},
|
|
|
|
toggleExclude(event, type) {
|
|
const entry = getClickedStyleElement(event);
|
|
if (event.target.checked) {
|
|
API.styles.addExclusion(entry.styleMeta.id, Events.getExcludeRule(type));
|
|
} else {
|
|
API.styles.removeExclusion(entry.styleMeta.id, Events.getExcludeRule(type));
|
|
}
|
|
},
|
|
|
|
toggleMenu(event) {
|
|
const entry = getClickedStyleElement(event);
|
|
const menu = $('.menu', entry);
|
|
if (menu.hasAttribute(MODAL_SHOWN)) {
|
|
Events.hideModal(menu, {animate: true});
|
|
} else {
|
|
$('.menu-title', entry).textContent = $('.style-name', entry).textContent;
|
|
Events.showModal(menu, '.menu-close');
|
|
}
|
|
},
|
|
};
|
|
|
|
function closeExplanation() {
|
|
$('#regexp-explanation').remove();
|
|
}
|
|
|
|
function escapeGlob(text) {
|
|
return text.replace(/\*/g, '\\*');
|
|
}
|
|
|
|
function getClickedStyleElement(event) {
|
|
return event.target.closest('.entry');
|
|
}
|