ultrawidify/res/popup/js/popup.js

795 lines
24 KiB
JavaScript

if(Debug.debug)
console.log("[popup.js] loading popup script!");
document.getElementById("uw-version").textContent = browser.runtime.getManifest().version;
var selectedMenu = "";
var selectedSubitem = {
'siteSettings': undefined,
'videoSettings': undefined,
}
var hasVideos = false;
var zoom_videoScale = 1;
var _config;
var _changeAr_button_shortcuts = { "autoar":"none", "reset":"none", "219":"none", "189":"none", "169":"none", "custom":"none" }
var comms = new Comms();
var settings = new Settings(undefined, () => updateConfig());
var site = undefined;
// aside from hostname, this can have two additional values:
// __playing — commands get sent to all frames with videos of status 'playing'
// __all — commands get sent to all frames
var selectedFrame = '__playing';
var port = browser.runtime.connect({name: 'popup-port'});
port.onMessage.addListener( (m,p) => processReceivedMessage(m,p));
var _video_settings_tab_items = [];
//#region build ui
var tablist = {
'extensionSettings': new MenuItem('_menu_item_settings_ext', 'Extension settings', '', () => showMenu('extensionSettings')),
'siteSettings': new MenuItem('_menu_item_settings_site', 'Site settings', 'Settings for current site', () => showMenu('siteSettings')),
'videoSettings': new MenuItem('_menu_item_settings_video', 'Video settings', 'Crop & stretch options for videos on current page', () => showMenu('videoSettings')),
'about': new MenuItem('_menu_item_about', 'About Ultrawidify', '', () => showMenu('about'))
};
for (let t in tablist) {
tablist[t].appendTo(document.getElementById('tablist'));
}
function loadFrames(videoTab) {
tablist['siteSettings'].removeSubitems();
tablist['videoSettings'].removeSubitems();
function onTabitemClick(item) {
tablist[selectedMenu].selectSubitem(item);
selectedSubitem[selectedMenu] = item;
}
for (var option of [{id: '__playing', label: 'Currently playing'}, {id: '__all', label: 'All'}]) {
const id = option.id;
var newItem = new TabItem(
undefined,
option.id,
option.label,
false,
() => onTabitemClick(id)
);
tablist['siteSettings'].insertSubitem(newItem);
tablist['videoSettings'].insertSubitem(newItem);
}
for (var frame in videoTab.frames) {
const nid = `${videoTab.id}-${videoTab.frames[frame].id}`;
var newItem = new TabItem(
undefined,
nid,
videoTab.frames[frame].host,
videoTab.frames[frame].url != videoTab.url,
(click) => onTabitemClick(nid)
);
tablist['siteSettings'].insertSubitem(newItem);
tablist['videoSettings'].insertSubitem(newItem);
}
if (! selectedSubitem.siteSettings) {
tablist['siteSettings'].selectFirstSubitem();
} else {
tablist['siteSettings'].selectSubitem(selectedSubitem.siteSettings)
}
if (! selectedSubitem.videoSettings) {
tablist['videoSettings'].selectFirstSubitem();
} else {
tablist['videoSettings'].selectSubitem(selectedSubitem.videoSettings);
}
}
//#endregion
async function processReceivedMessage(message, port){
if (Debug.debug) {
console.log("[popup.js] received message", message)
}
if(message.cmd === 'set-current-site'){
if (site) {
if (!site.host) {
// dunno why this fix is needed, but sometimes it is
site.host = site.tabHostname;
}
}
if (!site || site.host !== message.site.host) {
port.postMessage({cmd: 'get-current-zoom'});
}
site = message.site;
loadConfig(site.host);
loadFrames(site);
} else if (message.cmd === 'set-current-zoom') {
setCurrentZoom(message.zoom);
}
}
async function updateConfig() {
if (Debug.debug) {
console.log("[popup.js] settings changed. updating popup if site exists. Site:", site.host);
}
if (site && site.host) {
loadConfig(site.host);
}
}
async function setCurrentZoom(scale) {
zoom_videoScale = scale;
if(Debug.debug) {
console.log("[popup.js::setCurrentZoom] we're setting zoom:", zoom_videoScale);
}
VideoPanel.inputs.zoomSlider.value = Math.log2(zoom_videoScale);
VideoPanel.labels.zoomLevel.textContent = (zoom_videoScale * 100).toFixed();
}
function hideWarning(warn){
// document.getElementById(warn).classList.add("hidden");
}
function stringToKeyCombo(key_in){
var keys_in = key_in.split("_");
var keys_out = "";
for(key of keys_in){
if(key == "ctrlKey")
keys_out += "ctrl + ";
else if(key == "shiftKey")
keys_out += "shift + ";
else if(key == "altKey")
keys_out += "alt + ";
else
keys_out += key;
}
return keys_out;
}
function configurePopupTabs(site) {
if (!selectedMenu) {
showMenu('videoSettings');
} else {
showMenu(selectedMenu);
}
return;
// todo: this can potentially be removed
// Determine which tabs can we touch.
// If extension is disabled, we can't touch 'site settings' and 'video settings'
// If extension is enabled, but site is disabled, we can't touch 'video settings'
var extensionEnabled = settings.extensionEnabled();
var extensionEnabledForSite = settings.extensionEnabledForSite(site);
if (extensionEnabledForSite || extensionEnabled) {
tablist['videoSettings'].enable();
} else {
tablist['videoSettings'].disable();
}
// if popup isn't being opened for the first time, there's no reason to switch
// we're already in this tab
if (!selectedMenu) {
showMenu('videoSettings');
}
}
function basicCommandHandler(cmdArray) {
for (cmd of cmdArray) {
port.postMessage({
cmd: cmd.action,
arg: cmd.arg
});
}
}
function buildKeyboardShortcutString(keypress) {
var shortcutCombo = '';
if (keypress.ctrlKey) {
shortcutCombo += 'Ctrl + ';
}
if (keypress.shiftKey) {
shortcutCombo += 'Shift + ';
}
if (keypress.metaKey) {
shortcutCombo += 'Meta + ';
}
if (keypress.altKey) {
shortcutCombo += 'Alt + ';
}
shortcutCombo += keypress.key.toUpperCase();
return shortcutCombo;
}
function processButtonsForPopupCategory(category, buttons) {
if (buttons.length === 0) {
category.container.hide();
} else {
category.buttonContainer.removeChildren();
category.container.show();
category.buttonContainer.show();
for (var button of buttons) {
var shortcutCombo = '';
if (button.shortcut && button.shortcut[0].key) {
shortcutCombo = buildKeyboardShortcutString(button.shortcut[0]);
}
const cmd = button.cmd;
var nb = new ShortcutButton(
'',
button.label,
shortcutCombo,
() => basicCommandHandler(cmd),
['w24']
)
nb.appendTo(category.buttonContainer);
}
}
}
function configureGlobalTab() {
return; // todo: revisit
if (Debug.debug) {
console.log("[popup.js] Configuring global tab (ExtPanel).",
"\nextension mode: ", settings.active.extensionMode,
"\narDetect mode: ", settings.active.arDetect.mode,
"\nvideo float mode:", settings.active.miscFullscreenSettings.videoFloat,
"\nstretch mode: ", settings.active.stretch.initialMode,
"\n..")
}
for(var button in ExtPanel.extOptions) {
ExtPanel.extOptions[button].classList.remove("selected");
}
for(var button in ExtPanel.arOptions) {
ExtPanel.arOptions[button].classList.remove("selected");
}
for(var button in ExtPanel.alignment) {
ExtPanel.alignment[button].classList.remove("selected");
}
for(var button in ExtPanel.stretch) {
ExtPanel.stretch[button].classList.remove("selected");
}
ExtPanel.extOptions[settings.active.extensionMode].classList.add("selected");
ExtPanel.arOptions[settings.active.arDetect.mode].classList.add("selected");
ExtPanel.alignment[settings.active.miscFullscreenSettings.videoFloat].classList.add("selected");
ExtPanel.stretch[settings.active.stretch.initialMode].classList.add("selected");
}
function configureSitesTab(site) {
return; // todo: revisit
if (Debug.debug) {
console.log("[popup.js] Configuring sites tab (SitePanel).",
"\nsite: ", site,
"\nextension mode: ", settings.active.sites[site.host] ? settings.active.sites[site.host].status : 'no site-special settings for this site',
"\narDetect mode: ", settings.active.sites[site.host] ? settings.active.sites[site.host].arStatus : 'no site-special settings for this site',
"\nvideo float mode:", settings.active.sites[site.host] ? settings.active.sites[site.host].videoFloat : 'no site-special settings for this site',
"\ndefault ar: ", settings.active.sites[site.host] ? settings.active.sites[site.host].ar : 'no site-special settings for this site',
"\nstretch mode: ", settings.active.sites[site.host] ? settings.active.sites[site.host].stretch : 'no site-special settings for this site',
"\n...")
}
for(const button in SitePanel.extOptions) {
SitePanel.extOptions[button].classList.remove("selected");
}
for(const button in SitePanel.arOptions) {
SitePanel.arOptions[button].classList.remove("selected");
}
for(const button in SitePanel.alignment) {
SitePanel.alignment[button].classList.remove("selected");
}
for(const button in SitePanel.stretch) {
SitePanel.stretch[button].classList.remove("selected");
}
if (settings.active.sites[site.host] && settings.active.sites[site.host]) {
console.log("settings for", site, "exist!")
SitePanel.extOptions[settings.active.sites[site.host].status].classList.add("selected");
SitePanel.arOptions[settings.active.sites[site.host].arStatus].classList.add("selected");
} else {
SitePanel.extOptions.default.classList.add("selected");
SitePanel.arOptions.default.classList.add("selected");
}
// optional settings:
if (settings.active.sites[site.host] && settings.active.sites[site.host].videoAlignment) {
SitePanel.alignment[settings.active.sites[site.host].videoAlignment].classList.add("selected");
} else {
SitePanel.alignment.default.classList.add('selected');
}
if(settings.active.sites[site.host] && settings.active.sites[site.host].stretch !== undefined) { // can be 0
SitePanel.stretch[settings.active.sites[site.host].stretch].classList.add("selected");
} else {
SitePanel.stretch['-1'].classList.add("selected");
}
}
function configureVideoTab(site) {
const popupButtons = settings.getActionsForSite(site).filter(action => action.popup === true);
const cropButtons = popupButtons.filter(action => action.cmd.length === 1 && action.cmd[0].action === 'set-ar');
const stretchButtons = popupButtons.filter(action => action.cmd.length === 1 && action.cmd[0].action === 'set-stretch');
const alignButtons = popupButtons.filter(action => action.cmd.length === 1 && action.cmd[0].action === 'set-alignment');
processButtonsForPopupCategory(VideoPanel.elements.cropSettings, cropButtons);
processButtonsForPopupCategory(VideoPanel.elements.stretchSettings, stretchButtons);
processButtonsForPopupCategory(VideoPanel.elements.alignmentSettings, alignButtons);
// todo: get min, max from settings
VideoPanel.inputs.zoomSlider.min = Math.log2(0.5);
VideoPanel.inputs.zoomSlider.max = Math.log2(8);
VideoPanel.inputs.zoomSlider.value = Math.log2(zoom_videoScale);
VideoPanel.inputs.zoomSlider.addEventListener('input', (event) => {
var newZoom = Math.pow(2, VideoPanel.inputs.zoomSlider.value);
// save value so it doesn't get reset next time the popup updates
zoom_videoScale = newZoom;
// update zoom% label
VideoPanel.labels.zoomLevel.textContent = (newZoom * 100).toFixed();
// send the command to bg script
var command = {
cmd: 'set-zoom',
zoom: newZoom
};
port.postMessage(command);
});
}
async function loadConfig(site){
if (Debug.debug) {
console.log("\n\n-------------------------------------\n[popup.js::loadConfig] loading config. conf object:", settings.active);
}
configurePopupTabs(site);
configureGlobalTab();
configureSitesTab(site);
configureVideoTab(site);
if (Debug.debug) {
console.log("[popup.js::loadConfig] config loaded\n-----------------------\n\n");
}
}
function removeAll(itemArray) {
for(item of itemArray) {
item.remove();
}
}
function unselect(itemArray, extraClasses) {
for(item of itemArray) {
item.classList.remove('selected');
if (extraClasses) {
item.classList.remove(extraClasses);
}
}
}
async function getSite(){
if (Debug.debug) {
console.log("[popup.js] requesting current site");
}
try {
port.postMessage({cmd: 'get-current-site'});
} catch (e) {
console.log("[popup::getSite] sending get-current-site failed for some reason. Reason:", e)
}
}
function openMenu(menu){
if(Debug.debug){
console.log("[popup.js::openMenu] trying to open menu", menu, "\n element: ", Menu[menu]);
}
for(var m in Menu){
if(Menu[m])
Menu[m].classList.add("hidden");
}
for(var m in MenuTab){
if(MenuTab[m])
MenuTab[m].classList.remove("selected");
}
Menu[menu].classList.remove("hidden");
MenuTab[menu].classList.add("selected");
selectedMenu = menu;
}
function showMenu(tab) {
if (!tablist) {
// todo: fix & remove this
return;
}
for (const i in tablist) {
tablist[i].unselect();
tablist[i].hideSubitems();
}
tablist[tab].select();
tablist[tab].showSubitems();
// todo: display the correct tab
selectedMenu = tab;
}
function getCustomAspectRatio() {
var textBox_value = document.getElementById("_input_custom_ar").value.trim();
// validate value - this spaghett will match the following stuff
// [int]/[int]
// 1:[float]
// [float]
if (! /(^[0-9]+\/[0-9]+$|^(1:)?[0-9]+\.?[0-9]*$)/.test(textBox_value)) {
return false; // validation failed!
}
if (! isNaN(parseFloat(textBox_value))) {
return parseFloat(textBox_value);
}
if (/\//.test(textBox_value)) {
const vars = textBox_value.split('/');
return parseInt(vars[0])/parseInt(vars[1]); // non-ints shouldn't make it past regex
}
if (/:/.test(textBox_value)) {
const vars = textBox_value.split(':');
return parseFloat(vars[1]);
}
// we should never come this far.
// If we do, then there's something wrong with the input and our regex
return false;
}
function validateCustomAr(){
const valid = getCustomAspectRatio() !== false;
const inputField = document.getElementById("_input_custom_ar");
const valueSaveButton = document.getElementById("_b_changeAr_save_custom_ar");
if (valid) {
inputField.classList.remove("invalid-input");
valueSaveButton.classList.remove("disabled-button");
} else {
inputField.classList.add("invalid-input");
valueSaveButton.classList.add("disabled-button");
}
}
function validateAutoArTimeout(){
const inputField = document.getElementById("_input_autoAr_timer");
const valueSaveButton = document.getElementById("_b_autoar_save_autoar_timer");
if (! isNaN(parseInt(inputField.value.trim().value()))) {
inputField.classList.remove("invalid-input");
valueSaveButton.classList.remove("disabled-button");
} else {
inputField.classList.add("invalid-input");
valueSaveButton.classList.add("disabled-button");
}
}
document.addEventListener("click", (e) => {
if(Debug.debug) {
console.log("[popup.js] something clicked. event:", e, JSON.stringify(e));
}
function getcmd(e){
var command = {};
command.sender = "popup";
command.receiver = "uwbg";
if(e.target.classList.contains("disabled"))
return;
// if(e.target.classList.contains("menu-item")){
// if(Debug.debug) {
// console.log("[popup.js::eventListener] clicked on a tab. Class list:", e.target.classList);
// }
// if(e.target.classList.contains("_menu_tab_settings_ext")){
// openMenu("extensionSettings");
// } else if(e.target.classList.contains("_menu_tab_settings_site")){
// openMenu("siteSettings");
// } else if(e.target.classList.contains("_menu_tab_settings_video")){
// openMenu("videoSettings");
// } else if(e.target.classList.contains("_menu_tab_about")){
// openMenu("about");
// }
// // don't send commands
// return;
// }
if(e.target.classList.contains("_ext")) {
var command = {};
if(e.target.classList.contains("_ext_global_options")){
if (e.target.classList.contains("_blacklist")) {
settings.active.extensionMode = "blacklist";
} else if (e.target.classList.contains("_whitelist")) {
settings.active.extensionMode = "whitelist";
} else {
settings.active.extensionMode = "disabled";
}
settings.save();
return;
} else if (e.target.classList.contains("_ext_site_options")) {
var mode;
if(e.target.classList.contains("_blacklist")){
mode = "disabled";
} else if(e.target.classList.contains("_whitelist")) {
mode = "enabled";
} else {
mode = "default";
}
if(settings.active.sites[site.host]) {
settings.active.sites[site.host].status = mode;
settings.active.sites[site.host].statusEmbedded = mode;
} else {
settings.active.sites[site.host] = {
status: mode,
statusEmbedded: mode,
arStatus: 'default',
type: 'user-defined'
}
}
settings.save();
return;
}
}
if(e.target.classList.contains("_changeAr")){
if(e.target.classList.contains("_ar_auto")){
command.cmd = "autoar-start";
command.enabled = true;
return command;
}
}
if(e.target.classList.contains("_stretch")){
// stretch, global
if (e.target.classList.contains("_ar_stretch_global")) {
if (e.target.classList.contains("_none")) {
settings.active.stretch.initialMode = 0;
} else if (e.target.classList.contains("_basic")) {
settings.active.stretch.initialMode = 1;
} else if (e.target.classList.contains("_hybrid")) {
settings.active.stretch.initialMode = 2;
} else if (e.target.classList.contains("_conditional")) {
settings.active.stretch.initialMode = 3;
}
settings.save();
return;
}
// stretch, site
if (e.target.classList.contains("_ar_stretch_site")) {
if (e.target.classList.contains("_none")) {
settings.active.sites[site.host].stretch = 0;
} else if (e.target.classList.contains("_basic")) {
settings.active.sites[site.host].stretch = 1;
} else if (e.target.classList.contains("_hybrid")) {
settings.active.sites[site.host].stretch = 2;
} else if (e.target.classList.contains("_conditional")) {
settings.active.sites[site.host].stretch = 3;
} else {
delete(settings.active.sites[site.host].stretch);
}
settings.save();
return;
}
}
if(e.target.classList.contains("_autoAr")){
if(e.target.classList.contains("_ar_global_options")){
if (e.target.classList.contains("_blacklist")) {
settings.active.arDetect.mode = "blacklist";
} else if (e.target.classList.contains("_whitelist")) {
settings.active.arDetect.mode = "whitelist";
} else {
settings.active.arDetect.mode = "disabled";
}
settings.save();
return;
} else if (e.target.classList.contains("_save_autoAr_timer")) {
var value = parseInt(document.getElementById("_input_autoAr_timer").value.trim());
if(! isNaN(value)){
var timeout = parseInt(value);
settings.active.arDetect.timer_playing = timeout;
settings.save();
}
return;
} else if (e.target.classList.contains("_ar_site_options")) {
var mode;
if(e.target.classList.contains("_disabled")){
mode = "disabled";
} else if(e.target.classList.contains("_enabled")) {
mode = "enabled";
} else {
mode = "default";
}
if(settings.active.sites[site.host]) {
settings.active.sites[site.host].arStatus = mode;
} else {
settings.active.sites[site.host] = {
status: settings.active.extensionMode,
statusEmbedded: settings.active.extensionMode,
arStatus: mode,
type: 'user-defined'
}
}
settings.save();
return;
}
}
if (e.target.classList.contains("_align_ext")) {
if (e.target.classList.contains("_align_ext_left")) {
settings.active.miscFullscreenSettings.videoFloat = 'left';
} else if (e.target.classList.contains("_align_ext_center")) {
settings.active.miscFullscreenSettings.videoFloat = 'center';
} else if (e.target.classList.contains("_align_ext_right")) {
settings.active.miscFullscreenSettings.videoFloat = 'right';
}
settings.save();
return;
}
if (e.target.classList.contains("_align_site")) {
if (!site) {
return;
}
if (e.target.classList.contains("_align_site_left")) {
settings.active.sites[site.host].videoAlignment = 'left';
} else if (e.target.classList.contains("_align_site_center")) {
settings.active.sites[site.host].videoAlignment = 'center';
} else if (e.target.classList.contains("_align_site_right")) {
settings.active.sites[site.host].videoAlignment = 'right';
} else {
// default case — remove this object
delete(settings.active.sites[site.host].videoAlignment);
}
settings.save();
return;
}
if (e.target.classList.contains("_align")) {
command.cmd = "set-alignment";
if (e.target.classList.contains("_align_video_left")) {
command.mode = 'left';
} else if (e.target.classList.contains("_align_video_center")) {
command.mode = 'center';
} else if (e.target.classList.contains("_align_video_right")) {
command.mode = 'right';
}
return command;
}
//#region zoom buttons
if (e.target.classList.contains("_zoom_show_shortcuts")) {
VideoPanel.misc.zoomShortcuts.classList.remove("hidden");
VideoPanel.buttons.zoom.hideShortcuts.classList.remove("hidden");
VideoPanel.buttons.zoom.showShortcuts.classList.add("hidden");
return;
}
if (e.target.classList.contains("_zoom_hide_shortcuts")) {
VideoPanel.misc.zoomShortcuts.classList.add("hidden");
VideoPanel.buttons.zoom.hideShortcuts.classList.add("hidden");
VideoPanel.buttons.zoom.showShortcuts.classList.remove("hidden");
return;
}
if (e.target.classList.contains("_zoom_reset")) {
zoom_videoScale = scale;
VideoPanel.labels.zoomLevel.textContent = 100;
VideoPanel.inputs.zoomSlider.value = 0; // log₂(1)
command.cmd = 'set-zoom';
command.zoom = 1;
return command;
}
//#endregion
//#region show/hide custom ar
if (e.target.classList.contains("_changeAr_show_customAr")) {
VideoPanel.misc.customArChanger.classList.remove("hidden");
VideoPanel.buttons.changeAr.showCustomAr.classList.add("hidden");
VideoPanel.buttons.changeAr.hideCustomAr.classList.remove("hidden");
return;
}
if (e.target.classList.contains("_changeAr_hide_customAr")) {
VideoPanel.misc.customArChanger.classList.add("hidden");
VideoPanel.buttons.changeAr.showCustomAr.classList.remove("hidden");
VideoPanel.buttons.changeAr.hideCustomAr.classList.add("hidden");
return;
}
//#endregion
}
var command = getcmd(e);
if(Debug.debug) {
console.log("[popup.js] Got command (can be undefined):", command, JSON.stringify(command))
}
if(command)
port.postMessage(command);
return true;
});
async function sleep(t) {
return new Promise( (resolve,reject) => {
setTimeout(() => resolve(), t);
});
}
async function popup_init() {
// let's init settings and check if they're loaded
await settings.init();
if (Debug.debug) {
console.log("[popup] Are settings loaded?", settings)
}
// autoarFrequencyInputField.addEventListener("blur", (event) => {
// validateAutoArTimeout();
// });
// autoarFrequencyInputField.addEventListener("mouseleave", (event) => {
// validateAutoArTimeout();
// });
hideWarning("script-not-running-warning");
while (true) {
getSite();
await sleep(5000);
}
}
popup_init();