From a55a4500ee8262d4c9c50d7c769ebc8cb742bb93 Mon Sep 17 00:00:00 2001 From: Florrie Date: Sat, 23 Mar 2019 19:25:26 -0300 Subject: Rule Scope --- extension/popup/main.js | 374 +++++++++++++++++++++++++++++++++++++++++------- 1 file changed, 319 insertions(+), 55 deletions(-) (limited to 'extension/popup/main.js') diff --git a/extension/popup/main.js b/extension/popup/main.js index 06aca18..c628447 100644 --- a/extension/popup/main.js +++ b/extension/popup/main.js @@ -1,3 +1,15 @@ +import {getRuleScoreOnPage, getURLParts} from '../lib.js'; + +function last(arr) { + return arr[arr.length - 1]; +} + +function clearChildren(el) { + while (el.firstChild) { + el.removeChild(el.firstChild); + } +} + function changeScreen(id) { for (const screen of document.getElementsByClassName('screen')) { if (screen.id === id) { @@ -8,9 +20,19 @@ function changeScreen(id) { } } +function changeContentScreen(id) { + for (const screen of document.getElementsByClassName('content-screen')) { + if (screen.id === id) { + screen.classList.add('visible'); + } else { + screen.classList.remove('visible'); + } + } +} + function loadTrackList(opts) { - const {tab, hostname, siteSettings} = opts; - const site = siteSettings[hostname] || []; + const {tab, rule, saveRule, deleteRule} = opts; + const {music} = rule; return browser.storage.sync.get('tracks').then(({tracks = []}) => { const ul = document.getElementById('track-list'); while (ul.firstChild) { @@ -32,28 +54,20 @@ function loadTrackList(opts) { label.appendChild(checkbox); checkbox.type = 'checkbox'; - checkbox.checked = site.includes(track); - checkbox.title = `Toggles whether the track "${track}" will play when this site is opened.` + checkbox.checked = music.includes(track); + checkbox.title = `Toggle whether the track "${track}" will play when this site is opened.` checkbox.addEventListener('click', () => { if (checkbox.checked) { - if (!site.includes(track)) { - site.push(track); + if (!music.includes(track)) { + music.push(track); } } else { - if (site.includes(track)) { - site.splice(site.indexOf(track), 1); + if (music.includes(track)) { + music.splice(music.indexOf(track), 1); } } - - if (!siteSettings[hostname]) { - siteSettings[hostname] = site; - } - - disableButton.style.display = 'inline-block'; - - browser.storage.sync.set({siteSettings}) - .then(() => browser.runtime.sendMessage({hostname})); + saveRule(); }); label.appendChild(document.createTextNode(' ' + track)); @@ -62,7 +76,7 @@ function loadTrackList(opts) { li.appendChild(deleteButton); deleteButton.appendChild(document.createTextNode('Delete...')); - deleteButton.title = `Deletes the track from all sites. You will be confirmed first.`; + deleteButton.title = `Delete the track from all site configuration. You will be confirmed first.`; deleteButton.addEventListener('click', () => { if (confirm(`This will delete "${track}" from ALL sites - this cannot be undone. Are you sure?`)) { @@ -91,62 +105,312 @@ function loadTrackList(opts) { actionLi.appendChild(addButton); addButton.appendChild(document.createTextNode('Create Track')); - addButton.title = `Creates a new track, which will be an option present in all sites.`; + addButton.title = `Create a new track, which will be an option present in all sites.`; addButton.addEventListener('click', () => { browser.tabs.sendMessage(tab.id, {createTrack: true}); window.close(); }); - const disableButton = document.createElement('button'); - actionLi.appendChild(disableButton); + const deleteRuleButton = document.createElement('button'); + actionLi.appendChild(deleteRuleButton); - disableButton.appendChild(document.createTextNode('Disable Site')); - disableButton.title = `Removes the entry for this site altogether. It won't change BGM when you open it again.`; + deleteRuleButton.appendChild(document.createTextNode('Delete Rule')); + deleteRuleButton.title = `Remove this rule altogether. It will be discarded and won't affect the BGM anymore.`; - disableButton.addEventListener('click', () => { - changeScreen('loading-screen'); - delete siteSettings[hostname]; - browser.storage.sync.set({siteSettings}) - .then(() => loadTrackList(opts)) - .then(() => changeScreen('main-screen')); + deleteRuleButton.addEventListener('click', () => { + if (confirm(`Are you sure you want to delete this entry? (${getWildcardRepresentation(rule)})`)) { + deleteRule(); + } }); + }); +} + +function getWildcardRepresentation(rule, urlString = rule.sourceURL) { + const {hostnameMatch, pathnameMatch} = rule; + const {hostnameParts, pathnameParts} = getURLParts(urlString); + + let string = 'http(s)://'; - if (!(hostname in siteSettings)) { - disableButton.style.display = 'none'; + if (hostnameMatch.length !== hostnameParts.length) { + if (hostnameMatch.length > 0) { + string += '*.'; + } else { + string += '*'; } - }); + } + + string += hostnameMatch.slice().reverse().join('.'); + + if (pathnameMatch.length) { + string += '/'; + } + + string += pathnameMatch.join('/'); + + if (!pathnameMatch.length || !last(pathnameMatch).includes('.')) { + string += '/*'; + } + + return string; } -Promise.all([ - (async function() { - const [[tab], {disableEverywhere: disableEverywhereStatus}] = await Promise.all([ - browser.tabs.query({active: true, currentWindow: true}), - browser.storage.sync.get('disableEverywhere') - ]); +function loadRuleScreen({tab, rule, saveRule, deleteRule}) { + if (getRuleScoreOnPage(rule, tab.url) > 0) { + rule.sourceURL = tab.url; + } + + const heading = document.getElementById('hostname'); + while (heading.firstChild) { + heading.removeChild(heading.firstChild); + } + + const hostnameSpans = []; + const pathnameSpans = []; + + const isSelected = s => s.dataset.selected === 'yes'; + + const setSelected = (s, val) => { + s.dataset.selected = val ? 'yes' : 'no'; + }; + + const toggleSelected = s => { + setSelected(s, !isSelected(s)); + }; + + const updateWildcardRepresentation = () => { + const string = getWildcardRepresentation(rule, rule.sourceURL); + + const el = document.getElementById('wildcard-representation'); + while (el.firstChild) { + el.removeChild(el.firstChild); + } + el.appendChild(document.createTextNode(string)); + }; + + const addPart = (span, allSpans, save) => { + heading.appendChild(span); + + span.style.cursor = 'pointer'; + + const getSubordinateSpans = () => { + const index = allSpans.indexOf(span); + return allSpans.slice(0, index + 1); + }; + + const getSuperiorSpans = () => { + const index = allSpans.indexOf(span); + return allSpans.slice(index + 1); + }; + + const updateStyle = s => { + if (s.dataset.selected === 'yes') { + s.style.color = '#000000'; + } else { + s.style.color = '#777777'; + } + }; + + updateStyle(span); + + span.addEventListener('mouseenter', () => { + for (const s of getSubordinateSpans()) { + s.style.color = 'orange'; + } + }); + + span.addEventListener('mouseleave', () => { + for (const s of getSubordinateSpans()) { + updateStyle(s); + } + }); - const {hostname} = new URL(tab.url); - document.getElementById('hostname').appendChild(document.createTextNode(hostname)); + span.addEventListener('click', () => { + if (last(allSpans.filter(isSelected)) === span) { + toggleSelected(span); + } else { + setSelected(span, true); + } - const disableEverywhere = document.getElementById('disable-everywhere'); + for (const s of getSubordinateSpans()) { + setSelected(s, isSelected(span)); + updateStyle(s); + } - disableEverywhere.checked = disableEverywhereStatus; + for (const s of getSuperiorSpans()) { + setSelected(s, false); + updateStyle(s); + } - disableEverywhere.addEventListener('click', () => { - browser.storage.sync.set({disableEverywhere: disableEverywhere.checked}) - .then(() => browser.runtime.sendMessage({hostname})); + save(getSubordinateSpans().filter(isSelected).map(s => s.dataset.partValue)); + }); + }; + + const {hostnameMatch, pathnameMatch} = rule; + const {hostnameParts, pathnameParts} = getURLParts(rule.sourceURL); + + for (let i = 0; i < hostnameParts.length; i++) { + const part = hostnameParts[i]; + const span = document.createElement('span'); + span.appendChild(document.createTextNode(part)); + if (i < hostnameParts.length - 1) { + span.appendChild(document.createTextNode('.')); + } + span.dataset.partValue = part; + setSelected(span, hostnameMatch[hostnameParts.length - i - 1] === part); + hostnameSpans.unshift(span); + addPart(span, hostnameSpans, val => { + rule.hostnameMatch = val; + updateWildcardRepresentation(); + saveRule(); }); + } + + for (let i = 0; i < pathnameParts.length; i++) { + const part = pathnameParts[i]; + const span = document.createElement('span'); + span.appendChild(document.createTextNode('/')); + span.appendChild(document.createTextNode(part)); + span.dataset.partValue = part; + setSelected(span, pathnameMatch[i] === part); + pathnameSpans.push(span); + addPart(span, pathnameSpans, val => { + rule.pathnameMatch = val; + updateWildcardRepresentation(); + saveRule(); + }); + } + + updateWildcardRepresentation(); + + if (hostname) { + return loadTrackList({tab, rule, saveRule, deleteRule}) + .then(() => changeScreen('main-screen')); + } else { + changeScreen('invalid-host-screen'); + } +} + +function loadRuleList({tab, siteSettings, selectRule}) { + const createRuleItem = rule => { + const li = document.createElement('li'); + + li.classList.add('rule'); + li.dataset.wildcardRepresentation = getWildcardRepresentation(rule, rule.sourceURL || tab.url); + li.appendChild(document.createTextNode(li.dataset.wildcardRepresentation)); + li.title = li.dataset.wildcardRepresentation; + + li.addEventListener('click', () => { + selectRule(rule); + }); + + return li; + }; + + const allRulesList = document.getElementById('all-rules'); + clearChildren(allRulesList); + + const allRules = siteSettings; + + const allRuleItems = allRules.map(createRuleItem); + + allRuleItems.sort((a, b) => a.dataset.wildcardRepresentation < b.dataset.wildcardRepresentation ? -1 : 1); + + for (const item of allRuleItems) { + allRulesList.appendChild(item); + } + + const rulesOnThisPageList = document.getElementById('rules-on-this-page'); + clearChildren(rulesOnThisPageList); + + const createRuleLI = document.createElement('li'); + rulesOnThisPageList.appendChild(createRuleLI); + + const createRuleButton = document.createElement('button'); + createRuleLI.appendChild(createRuleButton); + + createRuleButton.appendChild(document.createTextNode('Create Rule')); + + createRuleButton.addEventListener('click', () => { + const {hostnameParts} = getURLParts(tab.url); + const rule = { + sourceURL: tab.url, + hostnameMatch: hostnameParts.slice().reverse(), + pathnameMatch: [], + music: [] + }; + selectRule(rule); + }); - return {tab, hostname}; - })(), - browser.storage.sync.get('siteSettings') - .then(({siteSettings = {}}) => siteSettings) -]) - .then(([{tab, hostname}, siteSettings]) => { - if (hostname) { - return loadTrackList({tab, hostname, siteSettings}) - .then(() => changeScreen('main-screen')); + const rulesOnThisPage = allRules.map(rule => ({ + rule, + score: getRuleScoreOnPage(rule, tab.url) + })).filter(obj => obj.score > 0); + + for (const obj of rulesOnThisPage) { + obj.item = createRuleItem(obj.rule); + } + + rulesOnThisPage.sort((a, b) => { + if (a.score === b.score) { + return a.item.dataset.wildcardRepresentation < b.item.dataset.wildcardRepresentation ? -1 : 1; } else { - changeScreen('invalid-host-screen'); + return b.score - a.score; } }); + + for (const {item} of rulesOnThisPage) { + rulesOnThisPageList.appendChild(item); + } +} + +Promise.all([ + browser.tabs.query({active: true, currentWindow: true}), + browser.storage.sync.get(['disableEverywhere', 'siteSettings']) +]).then(([[tab], {disableEverywhere: disableEverywhereStatus, siteSettings}]) => { + if (!Array.isArray(siteSettings)) { + siteSettings = []; + } + + console.log(siteSettings); + + const reloadRuleList = () => { + return browser.storage.sync.get('siteSettings').then(({siteSettings}) => loadRuleList({ + tab, siteSettings, + selectRule: rule => { + return loadRuleScreen({ + rule, tab, + saveRule: () => { + if (!siteSettings.includes(rule)) { + siteSettings.push(rule); + } + return browser.storage.sync.set({siteSettings}) + .then(() => browser.runtime.sendMessage({music: rule.music})); + }, + deleteRule: () => { + changeScreen('loading-screen'); + siteSettings = siteSettings.filter(r => r !== rule); + reloadRuleList(); + return browser.storage.sync.set({siteSettings}) + .then(() => reloadRuleList()) + .then(() => { + changeScreen('main-screen') + changeContentScreen('no-rule-content'); + }); + } + }).then(() => changeContentScreen('rule-content')); + } + })); + }; + + const disableEverywhere = document.getElementById('disable-everywhere'); + + disableEverywhere.checked = disableEverywhereStatus; + + disableEverywhere.addEventListener('click', () => { + browser.storage.sync.set({disableEverywhere: disableEverywhere.checked}) + .then(() => browser.runtime.sendMessage({urlString: tab.url})); + }); + + reloadRuleList().then(() => changeScreen('main-screen')); +}); -- cgit 1.3.0-6-gf8a5