CanvasBlocker/options/optionsGui.js

487 lines
14 KiB
JavaScript

/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
(function(){
"use strict";
let scope;
if ((typeof exports) !== "undefined"){
scope = exports;
}
else {
scope = require.register("./optionsGui", {});
}
const extension = require("../lib/extension");
const logging = require("../lib/logging");
function createDescription(setting){
const c = document.createElement("div");
c.className = "content";
const title = document.createElement("span");
title.className = "title";
title.textContent = extension.getTranslation(setting.name + "_title");
c.appendChild(title);
let descriptionText = extension.getTranslation(setting.name + "_description");
if (setting.urlSpecific){
const urlSpecificDescription = extension.getTranslation(setting.name + "_urlSpecific");
if (urlSpecificDescription){
descriptionText += (descriptionText? "\n\n": "") + urlSpecificDescription;
}
}
if (descriptionText){
const info = document.createElement("div");
info.className = "info";
c.appendChild(info);
const description = document.createElement("div");
description.className = "description";
description.textContent = descriptionText;
info.appendChild(description);
}
return c;
}
function createSelect(setting){
const select = document.createElement("select");
select.dataset.type = typeof setting.defaultValue;
setting.options.forEach(function(value){
const option = document.createElement("option");
if (typeof value === typeof setting.defaultValue){
option.value = value;
option.text = extension.getTranslation(setting.name + "_options." + value) || value;
if (setting.defaultValue === value){
option.selected = true;
option.selectedText = option.text;
option.notSelectedText = option.text + extension.getTranslation("labelForDefaultOption");
}
}
else {
option.disabled = true;
option.text = "\u2500".repeat(20);
}
select.appendChild(option);
});
select.update = function(){
Array.from(select.options).forEach(function(option){
if (option.notSelectedText){
option.text = option.notSelectedText;
}
});
const selectedOption = select.options[select.selectedIndex];
if (selectedOption.selectedText){
selectedOption.text = selectedOption.selectedText;
}
};
return select;
}
const inputTypes = {
all: {
updateCallback: function(input, value, defaultValue){
if (input.update){
input.update();
}
input.classList[value === defaultValue? "remove": "add"]("changed");
}
},
number: {
input: function(value){
const input = document.createElement("input");
input.type = "number";
input.value = value;
return input;
},
updateCallback: function(input, value, defaultValue){
input.value = value;
inputTypes.all.updateCallback(input, value, defaultValue);
return input.value;
},
getValue: function(input){
return parseFloat(input.value);
}
},
string: {
input: function(value, setting){
let input;
if (setting && setting.display && setting.display.multiline){
input = document.createElement("textarea");
input.rows = 1;
}
else {
input = document.createElement("input");
input.type = "text";
}
input.value = value;
return input;
},
updateCallback: function(input, value, defaultValue){
input.value = value;
inputTypes.all.updateCallback(input, value, defaultValue);
return input.value;
},
getValue: function(input){
return input.value;
}
},
boolean: {
input: function(value){
const input = document.createElement("input");
input.type = "checkbox";
input.checked = value;
input.style.display = "inline-block";
return input;
},
updateCallback: function(input, value, defaultValue){
input.checked = value;
inputTypes.all.updateCallback(input, value, defaultValue);
return input.checked;
},
getValue: function(input){
return input.checked;
}
},
object: false
};
function createKeyInput(setting, url){
const input = document.createElement("table");
let inSection = false;
setting.keys.forEach(function(key){
if (setting.display.displayedSection){
if (typeof key === "object"){
if (key.level === 1){
inSection = key.name === setting.display.displayedSection;
return;
}
}
if (!inSection){
return;
}
}
let row = document.createElement("tr");
if (typeof key === "object"){
let cell = document.createElement("td");
cell.colSpan = 2;
let h = document.createElement("h" + (2 + (key.level || 1)));
h.textContent = key.message? extension.getTranslation(key.message): key.name;
cell.appendChild(h);
row.appendChild(cell);
input.appendChild(row);
return;
}
let nameCell = document.createElement("td");
nameCell.textContent = setting.display.replaceKeyPattern?
key.replace(setting.display.replaceKeyPattern, ""):
key;
row.appendChild(nameCell);
let keyType = inputTypes[typeof setting.defaultKeyValue];
let keyInput = keyType.input(setting.defaultKeyValue);
let inputCell = document.createElement("td");
inputCell.appendChild(keyInput);
row.appendChild(inputCell);
setting.on(function(){
const container = setting.get(url);
keyType.updateCallback(
keyInput,
container && container.hasOwnProperty(key)?
container[key]:
setting.defaultKeyValue,
setting.defaultKeyValue
);
});
keyInput.addEventListener("change", function(){
const value = keyType.getValue(keyInput);
let container = setting.get(url);
if (!container){
container = setting.defaultValue;
}
container[key] = value;
if (setting.set(container, url)){
logging.message("changed setting", setting.name, "(", key, "):", value);
}
else {
container = setting.get(url);
keyType.updateCallback(
keyInput,
container && container.hasOwnProperty(key)?
container[key]:
setting.defaultKeyValue,
setting.defaultKeyValue
);
logging.message("setting", setting.name, "(", key, ") was not changed");
}
});
input.appendChild(row);
});
return input;
}
function getPopulateUrlTable(setting, type, body){
return function populateUrlTable({newValue}){
body.innerHTML = "";
newValue.forEach(function(entry){
let row = document.createElement("tr");
let urlCell = document.createElement("td");
urlCell.classList.add("url");
urlCell.addEventListener("click", function(){
const input = document.createElement("input");
input.classList.add("urlInput");
input.style.width = urlCell.clientWidth + "px";
input.style.height = urlCell.clientHeight + "px";
urlCell.innerHTML = "";
urlCell.appendChild(input);
input.title = extension.getTranslation("inputURL");
input.value = entry.url;
input.focus();
input.addEventListener("blur", function(){
const url = input.value.trim();
if (url){
entry.url = url;
setting.urlContainer.refresh();
}
urlCell.removeChild(input);
urlCell.textContent = entry.url;
});
input.addEventListener("click", function(event){
event.stopPropagation();
});
});
urlCell.textContent = entry.url;
row.appendChild(urlCell);
let input = createInput(setting, entry.url);
type.updateCallback(input, setting.get(entry.url), setting.defaultValue);
if (!entry.hasOwnProperty(setting.name)){
input.classList.add("notSpecifiedForUrl");
}
let inputCell = document.createElement("td");
inputCell.appendChild(input);
row.appendChild(inputCell);
let clearCell = document.createElement("td");
let clearButton = document.createElement("button");
clearButton.className = "reset";
clearButton.textContent = "\xD7";
clearButton.addEventListener("click", function(){
setting.reset(entry.url);
});
clearCell.appendChild(clearButton);
row.appendChild(clearCell);
body.appendChild(row);
});
};
}
function createUrlSpecificInput(setting, input, type){
const container = document.createElement("div");
container.className = "urlValues " + (setting.getExpand()? "expanded": "collapsed");
container.appendChild(input);
const collapser = document.createElement("button");
collapser.classList.add("collapser");
container.appendChild(collapser);
collapser.addEventListener("click", function(){
setting.setExpand(!setting.getExpand());
});
setting.onExpandChange(function(value){
container.classList[value? "remove": "add"]("collapsed");
container.classList[value? "add": "remove"]("expanded");
});
let urlTable = document.createElement("table");
let caption = document.createElement("caption");
caption.textContent = extension.getTranslation(setting.urlContainer.name + "_title");
urlTable.appendChild(caption);
let body = document.createElement("tbody");
urlTable.appendChild(body);
let foot = document.createElement("tfoot");
let footRow = document.createElement("tr");
let footCell = document.createElement("td");
footCell.colSpan = 3;
let newInput = document.createElement("input");
newInput.className = "inputURL";
newInput.title = extension.getTranslation("inputURL");
const addURLSetting = function(){
const url = newInput.value.trim();
if (url){
setting.set(setting.get(url), url);
newInput.value = "";
newInput.focus();
}
};
newInput.addEventListener("keypress", function(event){
if ([10, 13].indexOf(event.keyCode) !== -1){
addURLSetting();
}
});
footCell.appendChild(newInput);
let footPlus = document.createElement("button");
footPlus.classList.add("add");
footPlus.textContent = "+";
footPlus.addEventListener("click", addURLSetting);
footCell.appendChild(footPlus);
footRow.appendChild(footCell);
foot.appendChild(footRow);
urlTable.appendChild(foot);
container.appendChild(urlTable);
setting.urlContainer.on(getPopulateUrlTable(setting, type, body));
return container;
}
function createInput(setting, url = ""){
const type = inputTypes[typeof setting.defaultValue];
let input;
if (setting.options){
input = createSelect(setting);
}
else {
if (type){
input = type.input(setting.defaultValue, setting);
}
}
if (type){
setting.on(function(){
type.updateCallback(input, setting.get(url), setting.defaultValue);
}, url);
input.addEventListener("change", function(){
const value = type.getValue(input);
if (setting.set(value, url)){
type.updateCallback(input, value, setting.defaultValue);
logging.message("changed setting", setting.name, ":", value);
}
else {
type.updateCallback(input, setting.get(url), setting.defaultValue);
logging.message("setting", setting.name, "was not changed");
}
});
}
else if (setting.keys){
input = createKeyInput(setting, url);
}
if (setting.urlSpecific && url === ""){
return createUrlSpecificInput(setting, input, type);
}
return input || document.createElement("span");
}
function createButton(setting){
const button = document.createElement("button");
button.textContent = extension.getTranslation(setting.name + "_label");
button.addEventListener("click", setting.action);
return button;
}
function createInteraction(setting){
const c = document.createElement("div");
c.className = "content";
let interaction;
if (setting.action){
interaction = createButton(setting);
}
else if (setting.actions){
interaction = document.createElement("span");
setting.actions.forEach(function(action){
const button = createButton(action);
interaction.appendChild(button);
});
}
else if (setting.inputs){
interaction = document.createElement("span");
setting.inputs.forEach(function(inputSetting){
const input = createInput(inputSetting);
input.classList.add("multiple" + setting.inputs.length);
interaction.appendChild(input);
});
}
else {
interaction = createInput(setting);
}
interaction.classList.add("setting");
interaction.dataset.storageName = setting.name;
interaction.dataset.storageType = typeof setting.defaultValue;
c.appendChild(interaction);
return c;
}
function createHide(setting){
const label = document.createElement("label");
label.className = "content hideContent";
label.title = extension.getTranslation("hideSetting");
const input = document.createElement("input");
input.type = "checkbox";
input.className = "hide";
input.checked = setting.getHide();
input.addEventListener("change", function(){
setting.setHide(this.checked);
});
setting.onHideChange(function(value){
input.checked = value;
});
label.appendChild(input);
const display = document.createElement("span");
display.className = "display";
label.appendChild(display);
return label;
}
function createSettingRow(setting){
const tr = document.createElement("tr");
tr.className = "settingRow";
const hide = document.createElement("td");
hide.className = "hideColumn";
hide.appendChild(createHide(setting));
tr.appendChild(hide);
const left = document.createElement("td");
left.appendChild(createDescription(setting));
tr.appendChild(left);
const right = document.createElement("td");
right.appendChild(createInteraction(setting));
tr.appendChild(right);
return tr;
}
scope.createSettingRow = createSettingRow;
function createThead(displayHidden, restContent){
const tHead = document.createElement("thead");
const searchRow = document.createElement("tr");
const hideHeadCell = document.createElement("td");
hideHeadCell.className = "hideColumn";
searchRow.appendChild(hideHeadCell);
const restHeadCell = document.createElement("td");
restHeadCell.colSpan = 2;
if (restContent){
restHeadCell.appendChild(restContent);
}
searchRow.appendChild(restHeadCell);
tHead.appendChild(searchRow);
const displayHiddenRow = document.createElement("tr");
displayHiddenRow.className = "settingRow displayHiddenRow";
displayHiddenRow.appendChild(hideHeadCell.cloneNode());
const displayHiddenDescription = document.createElement("td");
displayHiddenDescription.appendChild(createDescription(displayHidden));
displayHiddenRow.appendChild(displayHiddenDescription);
const displayHiddenInteraction = document.createElement("td");
displayHiddenInteraction.appendChild(createInteraction(displayHidden));
displayHiddenRow.appendChild(displayHiddenInteraction);
tHead.appendChild(displayHiddenRow);
return tHead;
}
scope.createThead = createThead;
}());