mirror of
https://codeberg.org/claui/mobile-config-firefox.git
synced 2024-11-09 19:30:15 +00:00
411 lines
No EOL
11 KiB
JavaScript
411 lines
No EOL
11 KiB
JavaScript
'use strict';
|
|
|
|
const DB = new (function(){
|
|
this.content = null;
|
|
this.query = (q,list) => {
|
|
let nlist = [];
|
|
for(let key of list || this.keys){
|
|
if(this.content[key].includes(q)){
|
|
nlist.push(key)
|
|
}
|
|
}
|
|
return nlist
|
|
};
|
|
this.init = (obj) => {
|
|
this.content = obj
|
|
}
|
|
this._keys = null;
|
|
Object.defineProperty(this,"keys",{ get:() => {
|
|
if(this.content && !this._keys){
|
|
this._keys = Object.keys(this.content).sort();
|
|
}
|
|
return this._keys
|
|
}});
|
|
this.getTagsForFile = (name) => {
|
|
return this.content[name];
|
|
}
|
|
})();
|
|
|
|
function initDB(obj){
|
|
window.DB = DB;
|
|
DB.init(obj.content);
|
|
}
|
|
|
|
function fetchWithType(url){
|
|
return new Promise((resolve,reject)=>{
|
|
const ext = url.substring(url.lastIndexOf(".")+1);
|
|
let expected = (ext === "json") ? "application/json" : (ext === "css") ? "text/css" : null;
|
|
if(!expected){
|
|
reject("unsupported file extension");
|
|
}
|
|
fetch(url)
|
|
.then(response =>{
|
|
const contentType = response.headers.get('content-type');
|
|
if (!contentType || !contentType.includes(expected)) {
|
|
reject(`Oops, we got ${contentType} but expected ${expected}`);
|
|
}
|
|
if(ext === "json"){
|
|
response.json()
|
|
.then((obj) => resolve({file:url,content:obj}));
|
|
}else{
|
|
response.text()
|
|
.then((text) => resolve({file:url,content:text,name:url}))
|
|
|
|
}
|
|
},except => reject(except))
|
|
});
|
|
}
|
|
|
|
let currentCategory = new (function(){
|
|
let currentPrimaryNode = null;
|
|
let currentSecondaryNode = null;
|
|
// TODO make filenames store ONLY the top level fileNames
|
|
//
|
|
|
|
let currentTopLevelFileNames = null;
|
|
|
|
this.set = function(t,secondary){
|
|
if(secondary){
|
|
currentSecondaryNode && currentSecondaryNode.classList.remove("currentCategory");
|
|
currentSecondaryNode = t;
|
|
currentSecondaryNode.classList.add("currentCategory");
|
|
}else{
|
|
currentPrimaryNode && currentPrimaryNode.classList.remove("currentCategory");
|
|
currentPrimaryNode = t;
|
|
currentPrimaryNode.classList.add("currentCategory");
|
|
|
|
currentSecondaryNode && currentSecondaryNode.classList.remove("currentCategory");
|
|
currentSecondaryNode = null;
|
|
}
|
|
if(!secondary){
|
|
currentTopLevelFileNames = DB.query(t.textContent);
|
|
}
|
|
|
|
};
|
|
|
|
this.getFileNames = function(node,secondary){
|
|
if(secondary){
|
|
return DB.query(node.textContent,currentTopLevelFileNames)
|
|
}
|
|
return currentTopLevelFileNames
|
|
}
|
|
return this
|
|
})()
|
|
|
|
function getText(node){
|
|
return `${node.textContent}.css`
|
|
}
|
|
|
|
function getSecondaryCategories(list){
|
|
let a = [];
|
|
for (let file of list){
|
|
a.push(DB.getTagsForFile(file));
|
|
}
|
|
a = a.flat();
|
|
a.sort();
|
|
let ret = [];
|
|
let i = 0;
|
|
ret[0] = a[0];
|
|
for(let f of a){
|
|
if(ret[i] != f){
|
|
ret[++i] = f
|
|
}
|
|
}
|
|
return ret
|
|
}
|
|
|
|
function showMatchingTargets(fileNames,setSelected = false){
|
|
{
|
|
let ui = document.getElementById("ui");
|
|
if(ui.classList.contains("no-content")){
|
|
ui.classList.remove("no-content")
|
|
}
|
|
}
|
|
let bonus = 0;
|
|
for(let c of Array.from(document.querySelectorAll(".target"))){
|
|
if(fileNames.includes(c.dataset.filename)){
|
|
c.classList.remove("hidden");
|
|
setSelected && selectedTarget.add(c)
|
|
}else{
|
|
if(c.classList.contains("selected")){
|
|
bonus++
|
|
}else{
|
|
c.classList.add("hidden");
|
|
}
|
|
|
|
}
|
|
}
|
|
const container = document.getElementById("targets");
|
|
const width = container.getBoundingClientRect().width;
|
|
const horizontal_items = Math.max(1,Math.min(Math.floor(width / 180),4));
|
|
const real_items = fileNames.length + bonus;
|
|
const full_rows = Math.ceil(real_items/horizontal_items);
|
|
document.getElementById("targets").setAttribute("style",`--grid-rows:${full_rows};--grid-columns:${Math.ceil(real_items/full_rows)}`);
|
|
}
|
|
|
|
function onCategoryClicked(categoryNode,isSecondary = false){
|
|
|
|
currentCategory.set(categoryNode,isSecondary);
|
|
|
|
let secondaryCategoriesNode = document.querySelector("#secondaryCategories");
|
|
let fileNames = currentCategory.getFileNames(categoryNode,isSecondary);
|
|
if(!isSecondary){
|
|
|
|
if(fileNames.length > 9 && categoryNode.textContent != "legacy"){
|
|
let matchingSecondaries = getSecondaryCategories(fileNames);
|
|
for(let child of Array.from(secondaryCategoriesNode.children)){
|
|
matchingSecondaries.includes(child.textContent) ? child.classList.remove("hidden") : child.classList.add("hidden")
|
|
}
|
|
document.getElementById("categories").classList.add("blurred");
|
|
}else{
|
|
document.getElementById("categories").classList.remove("blurred");
|
|
|
|
}
|
|
}
|
|
showMatchingTargets(fileNames);
|
|
return
|
|
}
|
|
|
|
|
|
async function onTargetClicked(target,append = false){
|
|
const text = typeof target === "string"
|
|
? target
|
|
: target.dataset.filename;
|
|
|
|
fetchWithType(`chrome/${text}`)
|
|
.then(obj => {
|
|
let box = document.getElementById("previewBox");
|
|
if(append){
|
|
if(obj.file.endsWith("window_control_placeholder_support.css")){
|
|
obj.insertMode = box.InsertModes.Prepend;
|
|
box.insertContent(obj);
|
|
}else{
|
|
obj.insertMode = box.InsertModes.AppendLines;
|
|
box.insertContent(obj);
|
|
}
|
|
}else{
|
|
box.value = obj
|
|
}
|
|
})
|
|
.catch(e => console.log(e))
|
|
}
|
|
|
|
function onFilenameClicked(box,ctrlKey){
|
|
if(typeof box === "string"){
|
|
box = document.querySelector(`.target[title="${box}"]`);
|
|
}
|
|
if(!box){ return }
|
|
if(!box.classList.contains("selected")){
|
|
if(ctrlKey && selectedTarget.getIt()){
|
|
selectedTarget.add(box);
|
|
}else{
|
|
selectedTarget.set(box);
|
|
}
|
|
onTargetClicked(box,ctrlKey);
|
|
selectedTarget.setUrlSearchParams()
|
|
}else{
|
|
if(ctrlKey){
|
|
selectedTarget.deselect(box);
|
|
let previewbox = document.getElementById("previewBox");
|
|
let preview = previewbox.getNamedSection(`chrome/${box.dataset.filename}`);
|
|
if(preview){
|
|
preview.remove();
|
|
}
|
|
selectedTarget.setUrlSearchParams()
|
|
}
|
|
}
|
|
}
|
|
|
|
function onSomeClicked(e){
|
|
let cl = e.target.parentNode.id;
|
|
switch(cl){
|
|
case "categories":
|
|
onCategoryClicked(e.target);
|
|
break;
|
|
case "secondaryCategories":
|
|
onCategoryClicked(e.target,true/* isSecondary */);
|
|
break;
|
|
case "targets":
|
|
onFilenameClicked(e.target,e.ctrlKey);
|
|
|
|
break;
|
|
default:
|
|
|
|
break;
|
|
}
|
|
}
|
|
|
|
const selectedTarget = new(function(){
|
|
const selected = new Set();
|
|
const state_object = {};
|
|
this.set = (el) => {
|
|
this.clear();
|
|
el.classList.add("selected");
|
|
el.classList.remove("hidden");
|
|
selected.add(el);
|
|
}
|
|
this.getIt = () =>{ return selected.values().next().value };
|
|
this.add = (el) => {
|
|
selected.add(el);
|
|
el.classList.add("selected");
|
|
el.classList.remove("hidden");
|
|
};
|
|
this.deselect = (el) => {
|
|
el.classList.remove("selected");
|
|
return selected.delete(el)
|
|
};
|
|
this.clear = () => {
|
|
selected.forEach(el=>el.classList.remove("selected"));
|
|
selected.clear();
|
|
return true
|
|
}
|
|
this.setUrlSearchParams = () => {
|
|
let t = [];
|
|
for(let value of selected.values()){
|
|
t.push(value.getAttribute("title")+".css")
|
|
}
|
|
history.replaceState(state_object,"",`?file=${t.map(encodeURIComponent).join(",")}`);
|
|
}
|
|
})();
|
|
|
|
function createCategories(){
|
|
|
|
const CAT_PARENT = document.getElementById("categories");
|
|
const CAT_SECOND = document.getElementById("secondaryCategories");
|
|
CAT_PARENT.addEventListener("click",onSomeClicked,{passive:true});
|
|
CAT_SECOND.addEventListener("click",onSomeClicked,{passive:true});
|
|
|
|
const TAR_PARENT = document.getElementById("targets");
|
|
TAR_PARENT.addEventListener("click",onSomeClicked,{passive:true});
|
|
|
|
const createNode = function(name,type,isDeprecated){
|
|
let node = document.createElement("div");
|
|
node.classList.add(type);
|
|
if(type === "target"){
|
|
|
|
let link = node.appendChild(document.createElement("a"));
|
|
node.classList.add("hidden");
|
|
link.href = `https://github.com/MrOtherGuy/firefox-csshacks/tree/master/chrome/${isDeprecated?"deprecated/":""}${name}`;
|
|
link.title = "See on Github";
|
|
link.target = "_blank";
|
|
const content = name.substring(0,name.lastIndexOf("."));
|
|
node.appendChild(document.createElement("span")).textContent = content;
|
|
node.dataset.filename = `${content}.css`;
|
|
node.setAttribute("title",content);
|
|
}else{
|
|
node.textContent = name.name;
|
|
name.value > 0 && node.setAttribute("data-value",name.value);
|
|
}
|
|
|
|
return node;
|
|
}
|
|
|
|
const CAT_NAMES = (function(){
|
|
let list = [];
|
|
|
|
for(let key of DB.keys){
|
|
let things = DB.content[key];
|
|
TAR_PARENT.appendChild(createNode(key,"target",things.includes("legacy")));
|
|
for(let t of things){
|
|
list.push(t)
|
|
}
|
|
}
|
|
list.sort();
|
|
let ret = [];
|
|
let ns = [0];
|
|
ret[0] = list[0];
|
|
let i = 0;
|
|
for(let item of list){
|
|
if(ret[i]!=item){
|
|
ret[++i]=item;
|
|
ns[i]=0;
|
|
}else{
|
|
ns[i] += (item === "legacy" ? -1 : 1);
|
|
}
|
|
}
|
|
let map = ret.map((a,i)=>({name:a,value:ns[i]+1}))
|
|
return map
|
|
//return map.sort((a,b)=>(a.value > b.value?-1:a.value < b.value ? 1:0))
|
|
})();
|
|
|
|
for(let cat of CAT_NAMES){
|
|
CAT_PARENT.appendChild(createNode(cat,"category"));
|
|
CAT_SECOND.appendChild(createNode(cat,"category"));
|
|
}
|
|
|
|
}
|
|
|
|
async function handleSearchQuery(){
|
|
let params = (new URL(document.location)).searchParams;
|
|
let param = params.get("tag");
|
|
if(param){
|
|
let cats = document.querySelectorAll("#categories > .category");
|
|
for(let cat of cats){
|
|
if(cat.textContent === param){
|
|
onCategoryClicked(cat);
|
|
return
|
|
}
|
|
}
|
|
return
|
|
}
|
|
param = params.get("file");
|
|
if(param){
|
|
let files = param.split(",").filter(a => DB.keys.includes(a));
|
|
let box = document.getElementById("previewBox");
|
|
if(files.length === 0 ){
|
|
return
|
|
}
|
|
|
|
const promises = files.map(file=>fetchWithType(`chrome/${file}`).catch(e=>""));
|
|
|
|
Promise.all(promises)
|
|
.then(responses => {
|
|
showMatchingTargets(files,true);
|
|
responses.forEach((res)=>{
|
|
res.insertMode = res.file.endsWith("window_control_placeholder_support.css") ? box.InsertModes.Prepend : box.InsertModes.AppendLines;
|
|
box.insertContent(res)
|
|
})
|
|
});
|
|
|
|
}
|
|
}
|
|
|
|
function showUI(){
|
|
document.getElementById("placeholder").remove();
|
|
document.getElementById("ui").classList.remove("hidden");
|
|
}
|
|
|
|
function waitForDelay(t){
|
|
t = Number(t) || 10;
|
|
return new Promise(res =>{
|
|
setTimeout(res,t)
|
|
})
|
|
}
|
|
|
|
document.onreadystatechange = (function () {
|
|
|
|
if (document.readyState === "complete") {
|
|
function linkClicked(ev){
|
|
if(ev.originalTarget instanceof HTMLAnchorElement){
|
|
let ref = ev.originalTarget.href;
|
|
if(!ref){
|
|
return
|
|
}
|
|
let fileName = ref.slice(ref.lastIndexOf("/"));
|
|
if(fileName.endsWith(".css")){
|
|
onFilenameClicked(fileName.slice(1,-4),ev.ctrlKey);
|
|
ev.preventDefault();
|
|
}
|
|
}
|
|
}
|
|
document.getElementById("previewBox").addEventListener("click",linkClicked);
|
|
|
|
fetchWithType("html_resources/tagmap.json")
|
|
.then(initDB)
|
|
.then(createCategories)
|
|
.then(handleSearchQuery)
|
|
.then(()=>waitForDelay(300))
|
|
.then(showUI)
|
|
.catch(e => console.log(e))
|
|
}
|
|
}); |