192 lines
5.9 KiB
JavaScript
192 lines
5.9 KiB
JavaScript
/*
|
|
global messageBox t $create getActiveTab tryRegExp animateElement $ API
|
|
*/
|
|
'use strict';
|
|
|
|
/* exported popupExclusions */
|
|
const popupExclusions = (() => {
|
|
|
|
// return matches on url ending to prevent duplicates in the exclusion list
|
|
// e.g. http://test.com and http://test.com/* are equivalent
|
|
// this function would return ['', '/*']
|
|
function exclusionExists(array, value) {
|
|
const match = [];
|
|
['', '*', '/', '/*'].forEach(ending => {
|
|
if (array.includes(value + ending)) {
|
|
match.push(ending);
|
|
}
|
|
});
|
|
return match;
|
|
}
|
|
|
|
/* Modal in Popup.html */
|
|
function processURL(url) {
|
|
const results = [];
|
|
const protocol = url.match(/\w+:\/\//);
|
|
// remove ending '/', protocol, hash & search strings
|
|
const parts = url.replace(/\/$/, '').replace(/(\w+:\/\/|[#?].*$)/g, '').split('/');
|
|
const domain = parts[0].split('.');
|
|
/*
|
|
Domain: a.b.com
|
|
Domain: b.com
|
|
Prefix: https://a.b.com
|
|
Prefix: https://a.b.com/current
|
|
Prefix: https://a.b.com/current/page
|
|
*/
|
|
while (parts.length > 1) {
|
|
results.push([t('excludedPrefix'), protocol + parts.join('/')]);
|
|
parts.pop();
|
|
}
|
|
while (domain.length > 1) {
|
|
results.push([t('excludedDomain'), domain.join('.')]);
|
|
domain.shift();
|
|
}
|
|
return results.reverse();
|
|
}
|
|
|
|
function shortenURL(text) {
|
|
const len = text.length;
|
|
let prefix = '\u2026';
|
|
// account for URL that end with a '/'
|
|
let index = (text.endsWith('/') ? text.substring(0, len - 1) : text).lastIndexOf('/');
|
|
if (index < 0 || len - index < 2) {
|
|
index = 0;
|
|
prefix = '';
|
|
}
|
|
return prefix + text.substring(index, len);
|
|
}
|
|
|
|
function createOption(option) {
|
|
// ["Domain/Prefix", "{url}"]
|
|
return $create('option', {
|
|
value: option[1],
|
|
title: option[1],
|
|
textContent: `${option[0]}: ${shortenURL(option[1])}`
|
|
});
|
|
}
|
|
|
|
function getMultiOptions({select, selectedOnly} = {}) {
|
|
return [...select.children].reduce((acc, opt) => {
|
|
if (selectedOnly && opt.selected || !selectedOnly) {
|
|
acc.push(opt.value);
|
|
}
|
|
return acc;
|
|
}, []);
|
|
}
|
|
|
|
function updatePopupContent(url) {
|
|
const options = processURL(url);
|
|
const renderBin = document.createDocumentFragment();
|
|
options.map(option => renderBin.appendChild(createOption(option)));
|
|
$('#popup-exclusions').textContent = '';
|
|
$('#popup-exclusions').appendChild(renderBin);
|
|
}
|
|
|
|
function getIframeURLs(style) {
|
|
getActiveTab().then(tab => {
|
|
if (tab && tab.status === 'complete') {
|
|
chrome.webNavigation.getAllFrames({
|
|
tabId: tab.id
|
|
}, frames => {
|
|
const urls = frames.reduce((acc, frame) => [...acc, ...processURL(frame.url)], []);
|
|
updateSelections(style, urls);
|
|
});
|
|
}
|
|
});
|
|
}
|
|
|
|
function selectExclusions(exclusions) {
|
|
const select = $('#exclude select');
|
|
const excludes = Object.keys(exclusions || {});
|
|
[...select.children].forEach(option => {
|
|
if (exclusionExists(excludes, option.value).length) {
|
|
option.selected = true;
|
|
}
|
|
});
|
|
}
|
|
|
|
function updateSelections(style, newOptions = []) {
|
|
const wrap = $('#exclude');
|
|
const select = $('select', wrap);
|
|
if (newOptions.length) {
|
|
const currentOptions = [...select.children].map(opt => opt.value);
|
|
newOptions.forEach(opt => {
|
|
if (!currentOptions.includes(opt[1])) {
|
|
select.appendChild(createOption(opt));
|
|
// newOptions may have duplicates (e.g. multiple iframes from same source)
|
|
currentOptions.push(opt[1]);
|
|
}
|
|
});
|
|
select.size = select.children.length;
|
|
// hide select, then calculate & adjust height
|
|
select.style.height = '0';
|
|
document.body.style.height = `${select.scrollHeight + wrap.offsetHeight}px`;
|
|
select.style.height = '';
|
|
}
|
|
selectExclusions(style.exclusions);
|
|
}
|
|
|
|
function isExcluded(matchUrl, exclusions = {}) {
|
|
const values = Object.values(exclusions);
|
|
return values.length && values.some(exclude => tryRegExp(exclude).test(matchUrl));
|
|
}
|
|
|
|
function openPopupDialog(entry, tabURL) {
|
|
const style = entry.styleMeta;
|
|
updateSelections(style, updatePopupContent(tabURL));
|
|
getIframeURLs(style);
|
|
const box = $('#exclude');
|
|
box.dataset.display = true;
|
|
box.style.cssText = '';
|
|
$('strong', box).textContent = style.name;
|
|
$('[data-cmd="ok"]', box).focus();
|
|
$('[data-cmd="ok"]', box).onclick = () => confirm(true);
|
|
$('[data-cmd="cancel"]', box).onclick = () => confirm(false);
|
|
window.onkeydown = event => {
|
|
const keyCode = event.keyCode || event.which;
|
|
if (!event.shiftKey && !event.ctrlKey && !event.altKey && !event.metaKey
|
|
&& (keyCode === 13 || keyCode === 27)) {
|
|
event.preventDefault();
|
|
confirm(keyCode === 13);
|
|
}
|
|
};
|
|
function confirm(ok) {
|
|
window.onkeydown = null;
|
|
animateElement(box, {
|
|
className: 'lights-on',
|
|
onComplete: () => (box.dataset.display = false),
|
|
});
|
|
document.body.style.height = '';
|
|
const excluded = isExcluded(tabURL, style.exclusions);
|
|
if (ok) {
|
|
handlePopupSave(style);
|
|
entry.styleMeta = style;
|
|
entry.classList.toggle('excluded', excluded);
|
|
}
|
|
}
|
|
return Promise.resolve();
|
|
}
|
|
|
|
function handlePopupSave(style) {
|
|
if (!Array.isArray(style.exclusions)) {
|
|
style.exclusions = [];
|
|
}
|
|
const current = new Set(style.exclusions);
|
|
const select = $('#popup-exclusions', messageBox.element);
|
|
const all = getMultiOptions({select});
|
|
const selected = new Set(getMultiOptions({select, selectedOnly: true}));
|
|
for (const value of all) {
|
|
if (current.has(value) && !selected.has(value)) {
|
|
const index = style.exclusions.indexOf(value);
|
|
style.exclusions.splice(index, 1);
|
|
}
|
|
if (!current.has(value) && selected.has(value)) {
|
|
style.exclusions.push(value);
|
|
}
|
|
}
|
|
API.setStyleExclusions(style.id, style.exclusions);
|
|
}
|
|
|
|
return {openPopupDialog, selectExclusions, isExcluded};
|
|
})();
|