2023-11-28 18:32:40 +01:00
|
|
|
"use strict";
|
|
|
|
|
|
|
|
const fieldFuncDefault = (value) => {
|
|
|
|
return [Text(value)];
|
|
|
|
};
|
|
|
|
|
|
|
|
function makeId(d) {
|
|
|
|
return `${d.test}/${d.channel}`;
|
|
|
|
}
|
|
|
|
|
|
|
|
function rowAttribs(d) {
|
2024-02-15 23:35:40 +01:00
|
|
|
return d.fail ? {"class": "fail"} : null;
|
2023-11-28 18:32:40 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
function sectionLink(value, d) {
|
|
|
|
const id = makeId(d);
|
|
|
|
const a = Tag('a', {href: '#'+id}, value);
|
2024-02-15 23:35:40 +01:00
|
|
|
//a.addEventListener('click', () => { $(id).setAttribute('open', true); });
|
2023-11-28 18:32:40 +01:00
|
|
|
return [a];
|
|
|
|
}
|
|
|
|
|
2024-02-15 23:35:40 +01:00
|
|
|
// fonts: Twemoji, Noto, OpenMoji
|
|
|
|
const emojiFontReplacementTable = {
|
|
|
|
"🌑":"Twemoji",
|
|
|
|
"☀":"Noto",
|
|
|
|
"🧲":"Noto",
|
|
|
|
"☢":"Noto",
|
|
|
|
"🔦":"OpenMoji",
|
|
|
|
"🎆":"Noto",
|
|
|
|
"💎":"Noto",
|
|
|
|
"🏀":"Noto",
|
|
|
|
"🖌":"Noto",
|
|
|
|
//"❌":"Noto",
|
|
|
|
"🏁":"Noto",
|
|
|
|
"🛑":"Noto",
|
|
|
|
"⛔":"Noto",
|
|
|
|
}
|
|
|
|
function emojiFont(emoji) {
|
|
|
|
let emojiFontReplacement = emojiFontReplacementTable[emoji];
|
|
|
|
emojiFontReplacement = emojiFontReplacement ? " " + emojiFontReplacement : "";
|
|
|
|
return emojiFontReplacement;
|
|
|
|
}
|
|
|
|
|
|
|
|
function emojiDetect(string) {
|
|
|
|
const regex_emoji = /\p{Extended_Pictographic}/gu; // it's not complete but it's enough for case
|
|
|
|
const matches = [...string.matchAll(regex_emoji)];
|
|
|
|
return matches;
|
|
|
|
}
|
|
|
|
function emojiToTag(string) {
|
|
|
|
const results = emojiDetect(string);
|
|
|
|
let lastIndex = 0;
|
|
|
|
const tagArray = [];
|
|
|
|
for (let result of results) {
|
|
|
|
let emoji = result[0];
|
|
|
|
if (result.index > lastIndex) {
|
|
|
|
tagArray.push(Text(string.slice(lastIndex, result.index)));
|
|
|
|
}
|
|
|
|
tagArray.push(Tag('span', {"class": "emoji" + emojiFont(emoji)}, emoji));
|
|
|
|
lastIndex = result.index + emoji.length;
|
|
|
|
}
|
|
|
|
if (lastIndex < string.length) {
|
|
|
|
tagArray.push(Text(string.slice(lastIndex)));
|
|
|
|
}
|
|
|
|
return tagArray;
|
|
|
|
}
|
|
|
|
function channelWithEmoji(value) {
|
|
|
|
const replacements = {
|
|
|
|
"full": "🖼",
|
|
|
|
"basecolor": "🎨",
|
|
|
|
"emissive": "☢",
|
|
|
|
"nshade": "🥬", //🏔🌄
|
|
|
|
"ngeom": "🥒", //⛰
|
|
|
|
"lighting": "☀",//🗿
|
|
|
|
"direct": "🔦",//🔦🎇☄
|
|
|
|
"direct_diffuse": "🔦🎆",
|
|
|
|
"direct_specular": "🔦💎",
|
|
|
|
"diffuse": "🎆",
|
|
|
|
"specular": "💎",
|
|
|
|
"material": "🖌",
|
|
|
|
"indirect": "🏀",//🏀🏓🥏
|
|
|
|
"indirect_specular": "🏀💎",
|
|
|
|
"indirect_diffuse": "🏀🎆"
|
|
|
|
}
|
|
|
|
const emoji = replacements[value];
|
|
|
|
if (emoji) {
|
|
|
|
return [
|
|
|
|
...emojiToTag(emoji),
|
|
|
|
Tag("span", {"class": "text-left-margin"}, value)
|
|
|
|
];
|
|
|
|
} else {
|
|
|
|
return [];
|
|
|
|
}
|
|
|
|
}
|
|
|
|
function colorPct(value) {
|
|
|
|
let fixedPercent = value.toFixed(3);
|
|
|
|
//fixedPercent = (fixedPercent < 10 ? '0' : '') + fixedPercent;
|
|
|
|
let css_class = "";
|
|
|
|
if (value < 1) {
|
|
|
|
css_class = "green";
|
|
|
|
} else
|
|
|
|
if (value < 5) {
|
|
|
|
css_class = "yellow";
|
|
|
|
} else {
|
|
|
|
css_class = "red";
|
|
|
|
}
|
|
|
|
return [Tag("span", {"class": css_class}, fixedPercent)];
|
|
|
|
}
|
|
|
|
function isPassed(value) {
|
|
|
|
const font = value ? emojiFont("❌") : emojiFont("✔");
|
|
|
|
return [
|
|
|
|
Tag("span", {"class": "emoji" + font},
|
|
|
|
value ? "❌" : "✅" // ✔✅
|
|
|
|
)
|
|
|
|
];
|
|
|
|
}
|
|
|
|
|
2023-11-28 18:32:40 +01:00
|
|
|
function makeTable(fields, data, attrs_func) {
|
|
|
|
const table = Tag('table', null, null, [
|
|
|
|
Tag('tr', null, null, (() => {
|
|
|
|
let tds = [];
|
|
|
|
for (const f of fields) {
|
2024-02-15 23:35:40 +01:00
|
|
|
tds.push(Tag('th', {"class": "table-sticky"}, null, [Text(f.label)]));
|
2023-11-28 18:32:40 +01:00
|
|
|
}
|
|
|
|
return tds;
|
|
|
|
})()),
|
|
|
|
]);
|
|
|
|
|
2024-02-15 23:35:40 +01:00
|
|
|
|
2023-11-28 18:32:40 +01:00
|
|
|
for (const di in data) {
|
|
|
|
let d = data[di];
|
|
|
|
const attrs = attrs_func ? attrs_func(d) : null;
|
|
|
|
table.appendChild(Tag('tr', attrs, null, (() => {
|
|
|
|
let ret = [];
|
|
|
|
for (const f of fields) {
|
|
|
|
const value = d[f.field];
|
|
|
|
if (value === undefined) {
|
|
|
|
ret.push(Tag('td'));
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
|
|
|
|
const field_func = f.tags_func ? f.tags_func : fieldFuncDefault;
|
|
|
|
const tags = field_func(value, d);
|
|
|
|
|
|
|
|
// if (f.good && f.bad) {
|
|
|
|
// const rating = linearStep(value, f.good, f.bad);
|
|
|
|
// const c = {
|
|
|
|
// r: 224 + 31 * clamp(2 - rating * 2, 0, 1),
|
|
|
|
// g: 224 + 31 * clamp(rating * 2, 0, 1),
|
|
|
|
// b: 224,
|
|
|
|
// };
|
|
|
|
// attrs = {style: `background-color: rgb(${c.r}, ${c.g}, ${c.b})`};
|
|
|
|
// }
|
2024-02-15 23:35:40 +01:00
|
|
|
ret.push(Tag('td', null, null, tags));
|
2023-11-28 18:32:40 +01:00
|
|
|
}
|
|
|
|
return ret;
|
|
|
|
})()));
|
|
|
|
}
|
|
|
|
|
|
|
|
return table;
|
|
|
|
}
|
|
|
|
|
|
|
|
function buildTestResultsTable(data) {
|
|
|
|
const fields = [
|
|
|
|
{label: 'Test', field: 'test', tags_func: sectionLink},
|
2024-02-15 23:35:40 +01:00
|
|
|
{label: 'Channel', field: 'channel', tags_func: channelWithEmoji},
|
|
|
|
{label: 'Δ, %', field: 'diff_pct', tags_func: colorPct},
|
|
|
|
{label: 'Passed', field: 'fail', tags_func: isPassed},
|
2023-11-28 18:32:40 +01:00
|
|
|
];
|
|
|
|
|
|
|
|
return [makeTable(fields, data, rowAttribs)];
|
|
|
|
}
|
|
|
|
|
|
|
|
// Filter out all success
|
|
|
|
function filterData(data) {
|
|
|
|
return data
|
2024-02-15 23:35:40 +01:00
|
|
|
.filter((d) => { return d.diff_pct !== 0; })
|
|
|
|
.sort((l, r) => {
|
|
|
|
return l.diff_pct < r.diff_pct ? 1 : -1; // RTFM
|
|
|
|
});
|
2023-11-28 18:32:40 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
function buildSummary(data) {
|
|
|
|
const total = data.length;
|
2024-02-15 23:35:40 +01:00
|
|
|
const fails = data.filter((d) => { return !!d.fail; }).length;
|
2023-11-28 18:32:40 +01:00
|
|
|
const fails_pct = fails * 100.0 / total;
|
2024-02-15 23:35:40 +01:00
|
|
|
return emojiToTag(`🧪 Tests 💥: ${fails}/${total}🏁 (⚠${fails_pct.toFixed(3)}%)`);
|
2023-11-28 18:32:40 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
function buildTestResultImages(data) {
|
|
|
|
return data.flatMap((d) => {
|
2024-02-15 23:35:40 +01:00
|
|
|
//return Tag('details', {id: makeId(d)}, null, [
|
|
|
|
return Tag('div', {id: makeId(d), "class": "meta-block"}, null, [
|
|
|
|
//Tag('summary', null, `${d.test}/${d.channel} δ=${d.diff_pct}`),
|
|
|
|
Tag("a", {"class": "text-link", "href": `#${d.test}/${d.channel}`}, null, [
|
|
|
|
Tag('h3', null, `${d.test}/${d.channel} δ=${d.diff_pct}`)
|
|
|
|
]),
|
|
|
|
Tag('div', {"class": "image-container"}, null, [
|
|
|
|
Tag('div', {"class": "block block-gold"}, null, [Tag('img', {src: d.image_gold, loading: "lazy", "class": "gold"})]),
|
|
|
|
Tag('div', {"class": "block block-test"}, null, [Tag('img', {src: d.image_test, loading: "lazy", "class": "test"})]),
|
|
|
|
Tag('div', {"class": "block block-diff"}, null, [Tag('img', {src: d.image_diff, loading: "lazy", "class": "diff"})]),
|
|
|
|
Tag('div', {"class": "separator"}, null, null),
|
|
|
|
])
|
|
|
|
//Tag('img', {src: d.image_flip}),
|
2023-11-28 18:32:40 +01:00
|
|
|
]);
|
|
|
|
});
|
|
|
|
}
|
|
|
|
|
2024-02-15 23:35:40 +01:00
|
|
|
|
|
|
|
|
|
|
|
function buildData(table, images, data, sort, filter, exact_match) {
|
|
|
|
if (sort) {
|
|
|
|
data = filterData(data);
|
|
|
|
}
|
|
|
|
if (filter) {
|
2024-02-16 00:27:50 +01:00
|
|
|
let test = "", channel = "";
|
|
|
|
const args = filter.split(" ");
|
|
|
|
if (args.length > 1) {
|
|
|
|
test = args[0];
|
|
|
|
channel = args[1]
|
2024-02-15 23:35:40 +01:00
|
|
|
} else {
|
2024-02-16 00:27:50 +01:00
|
|
|
test = filter;
|
|
|
|
channel = filter;
|
2024-02-15 23:35:40 +01:00
|
|
|
}
|
2024-02-16 00:27:50 +01:00
|
|
|
data = data.filter((d) => {
|
|
|
|
const test_result = exact_match ? d.test === test : d.test.includes(test);
|
|
|
|
const channel_result = exact_match ? d.channel === channel : d.channel.includes(channel);
|
|
|
|
if (args.length > 1) {
|
|
|
|
return test_result && channel_result;
|
|
|
|
} else {
|
|
|
|
return test_result || channel_result;
|
|
|
|
}
|
|
|
|
});
|
2024-02-15 23:35:40 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
table.replaceChildren(...buildTestResultsTable(data));
|
|
|
|
images.replaceChildren(...buildTestResultImages(data));
|
|
|
|
|
|
|
|
for (let block of images.querySelectorAll(".image-container")) {
|
|
|
|
block.addEventListener("click", function() {
|
|
|
|
let diffElement = this.querySelector(".block-diff");
|
|
|
|
let goldElement = this.querySelector(".block-gold");
|
|
|
|
diffElement.classList.toggle("show_diff");
|
|
|
|
goldElement.classList.toggle("anim_force_off");
|
|
|
|
});
|
|
|
|
};
|
|
|
|
|
|
|
|
for (let tr of document.querySelectorAll("table tr")) {
|
2024-12-17 23:03:24 +01:00
|
|
|
tr.addEventListener("click", function(event) {
|
|
|
|
this.parentElement.querySelector("tr.active")?.classList.toggle("active");
|
|
|
|
event.stopPropagation();
|
|
|
|
this.classList.toggle("active");
|
2024-02-15 23:35:40 +01:00
|
|
|
this.querySelector("a").click();
|
|
|
|
});
|
|
|
|
}
|
|
|
|
|
|
|
|
saveToLocalStorage("rendertest_tablesort", sort);
|
|
|
|
}
|
|
|
|
const buildDataSlowMode = debounce((table, images, data, sort, filter, exact_match) => {
|
|
|
|
buildData(table, images, data, sort, filter, exact_match);
|
|
|
|
}, ()=>{}, 250);
|
|
|
|
|
|
|
|
|
|
|
|
const saveToLocalStorage = debounce((key, value) => {
|
|
|
|
localStorage.setItem(key, value);
|
|
|
|
}, ()=>{}, 250);
|
|
|
|
const loadFromLocalStorage = (key, default_option) => {
|
|
|
|
const value = localStorage.getItem(key);
|
|
|
|
if (isNaN(value)) {
|
|
|
|
switch(value) { // ugly textual localStorage
|
|
|
|
case "true":
|
|
|
|
return true;
|
|
|
|
case "false":
|
|
|
|
return false;
|
|
|
|
case "null":
|
|
|
|
return (default_option === "" || default_option) ? default_option : null;
|
|
|
|
default:
|
|
|
|
return value;
|
|
|
|
}
|
|
|
|
} else
|
|
|
|
if (value === null) {
|
|
|
|
return (default_option === "" || default_option) ? default_option : null;
|
|
|
|
} else
|
|
|
|
if (value === "") {
|
|
|
|
return value;
|
|
|
|
}
|
|
|
|
return parseFloat(value); // fix !!"0" = true
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
function uglyChecked(value) { // fast hack
|
|
|
|
return value ? {"checked": "checked"} : {};
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2023-11-28 18:32:40 +01:00
|
|
|
window.onload = () => {
|
2024-02-15 23:35:40 +01:00
|
|
|
const theme = loadFromLocalStorage("rendertest_theme", "");
|
|
|
|
|
|
|
|
const linkElements = document.querySelectorAll('link[rel^="stylesheet"][data-theme]');
|
|
|
|
function updateLinkRel(linkElements, id) {
|
|
|
|
for (const link of linkElements) {
|
|
|
|
const themeName = link.getAttribute("data-theme");
|
|
|
|
link.disabled = themeName !== id;
|
|
|
|
link.rel = themeName === id && link.rel.includes("alternate")
|
|
|
|
? link.rel.replace(" alternate", "")
|
|
|
|
: link.rel + " alternate";
|
|
|
|
}
|
|
|
|
}
|
|
|
|
let themes = [];
|
|
|
|
for (const linkElement of linkElements) {
|
|
|
|
const themeName = linkElement.getAttribute("data-theme");
|
|
|
|
const emojiTags = emojiToTag(linkElement.title);
|
|
|
|
let checked = uglyChecked(!linkElement.rel.includes("alternate"));
|
|
|
|
if (theme) {
|
|
|
|
checked = uglyChecked(theme === themeName);
|
|
|
|
}
|
|
|
|
themes.push(
|
|
|
|
Tag("label", null, null, [
|
|
|
|
Tag("input", {"type": "radio", "class": "theme-button", "id": themeName, "name": "theme", ...checked}, null, null,
|
|
|
|
"click", (e) => {
|
|
|
|
updateLinkRel(linkElements, e.target.id);
|
|
|
|
saveToLocalStorage("rendertest_theme", e.target.id);
|
|
|
|
}),
|
|
|
|
...emojiTags
|
|
|
|
])
|
|
|
|
)
|
|
|
|
};
|
|
|
|
|
|
|
|
function changeAnimationDuration(newDuration) {
|
|
|
|
document.documentElement.style.setProperty('--animation-duration', newDuration + 'ms');
|
|
|
|
}
|
|
|
|
function syncSliderValues(event) {
|
|
|
|
changeAnimationDuration(event.target.value);
|
|
|
|
const value = event.target.value;
|
|
|
|
const input = event.target;
|
|
|
|
const otherInput = input === rangeInput ? numberInput : rangeInput;
|
|
|
|
otherInput.value = value;
|
|
|
|
saveToLocalStorage("rendertest_switchfrequency", value);
|
|
|
|
}
|
|
|
|
|
|
|
|
let gridContainer, sidebarOptions, numberInput, rangeInput, table, images, vpaddin_input, filter, exactmatch_input;
|
|
|
|
|
|
|
|
const switchFrequency = loadFromLocalStorage("rendertest_switchfrequency", 700);
|
|
|
|
let tableSort = loadFromLocalStorage("rendertest_tablesort", false);
|
|
|
|
const sidebarPos = loadFromLocalStorage("rendertest_sidebarpos", "left");
|
|
|
|
const sidebarMode = loadFromLocalStorage("rendertest_sidebarmode", "minimize");
|
|
|
|
const sidebarSize = loadFromLocalStorage("rendertest_sidebarsize", "default");
|
|
|
|
const vpadding = loadFromLocalStorage("rendertest_vpadding", true);
|
|
|
|
const all_diff = loadFromLocalStorage("rendertest_all_diff", false);
|
|
|
|
const diffMode = loadFromLocalStorage("rendertest_diffmode", "click");
|
|
|
|
const imageCompareMode = loadFromLocalStorage("rendertest_imagecomparemode", "switch");
|
|
|
|
const imagePosition = loadFromLocalStorage("rendertest_imageposition", "center");
|
|
|
|
const filter_value = loadFromLocalStorage("rendertest_filter", "");
|
|
|
|
const exactMatch = loadFromLocalStorage("rendertest_exactmatch", false);
|
|
|
|
let isPaused = loadFromLocalStorage("rendertest_paused", false);
|
2023-11-28 18:32:40 +01:00
|
|
|
|
2024-02-15 23:35:40 +01:00
|
|
|
if (theme) {
|
|
|
|
updateLinkRel(linkElements, theme);
|
|
|
|
}
|
|
|
|
|
|
|
|
// TODO: simplification and bindings
|
|
|
|
gridContainer = Tag("div", {"class": "grid-container"}, null, [
|
|
|
|
Tag("div", {"class": "sidebar"}, null, [
|
|
|
|
sidebarOptions = Tag("div", {"class": "panel", "id": "options"}, null, [
|
|
|
|
Tag("div", {"class": "theme-options"}, null, [
|
|
|
|
Tag("b", null, null, [
|
|
|
|
Tag("span", {"class": "emoji"}, "🎨"),
|
|
|
|
Text(" Theme")
|
|
|
|
]),
|
|
|
|
...themes
|
|
|
|
]),
|
|
|
|
Tag("div", null, null, [
|
|
|
|
Tag("b", null, null, [
|
|
|
|
Tag("span", {"class": "emoji"}, "🌈"),
|
|
|
|
Text(" Show diff mode on")
|
|
|
|
]),
|
|
|
|
Tag("label", null, null, [
|
|
|
|
Tag("input", {"type": "radio", "name": "diff_mode", "value": "click", ...uglyChecked(diffMode === "click")}, null, null, "input", (e) => {
|
|
|
|
images.classList.toggle("diff_separate");
|
|
|
|
saveToLocalStorage("rendertest_diffmode", e.target.value);
|
|
|
|
}),
|
|
|
|
Tag("span", {"class": "emoji"}, "🖱"),
|
|
|
|
Text(" Click (LMB)")
|
|
|
|
]),
|
|
|
|
Tag("label", null, null, [
|
|
|
|
Tag("input", {"type": "radio", "name": "diff_mode", "value": "separate", ...uglyChecked(diffMode === "separate")}, null, null, "input", (e) => {
|
|
|
|
images.classList.toggle("diff_separate");
|
|
|
|
saveToLocalStorage("rendertest_diffmode", e.target.value);
|
|
|
|
}),
|
|
|
|
Tag("span", {"class": "emoji"}, "🎏"),
|
|
|
|
Text(" Separate")
|
|
|
|
])
|
|
|
|
]),
|
|
|
|
Tag("div", null, null, [
|
|
|
|
Tag("b", null, null, [
|
|
|
|
Tag("span", {"class": "emoji"}, "🤼"),
|
|
|
|
Text(" Image compare mode")
|
|
|
|
]),
|
|
|
|
Tag("label", null, null, [
|
|
|
|
Tag("input", {"type": "radio", "name": "compare_mode", "value": "switch", ...uglyChecked(imageCompareMode === "switch")}, null, null, "input", (e) => {
|
|
|
|
images.classList.toggle("split_compare");
|
|
|
|
saveToLocalStorage("rendertest_imagecomparemode", e.target.value);
|
|
|
|
for (let blocks of images.querySelectorAll(".block-gold")) {
|
|
|
|
blocks.removeAttribute("style"); // FIXME
|
|
|
|
}
|
|
|
|
for (let seps of images.querySelectorAll(".separator")) {
|
|
|
|
seps.removeAttribute("style"); // FIXME
|
|
|
|
}
|
|
|
|
}),
|
|
|
|
Tag("span", {"class": "emoji"}, "🎞"),
|
|
|
|
Text(" Switch images")
|
|
|
|
]),
|
|
|
|
Tag("label", {"title": "under construction"}, null, [
|
|
|
|
Tag("input", {"type": "radio", "name": "compare_mode", "value": "split", ...uglyChecked(imageCompareMode === "split")}, null, null, "input", (e) => {
|
|
|
|
images.classList.toggle("split_compare");
|
|
|
|
saveToLocalStorage("rendertest_imagecomparemode", e.target.value);
|
|
|
|
}),
|
|
|
|
Tag("span", {"class": "emoji"}, "🪓"),
|
|
|
|
Text(" Split images")
|
|
|
|
])
|
|
|
|
]),
|
|
|
|
Tag("div", null, null, [
|
|
|
|
Tag("b", null, null, [
|
|
|
|
Tag("span", {"class": "emoji"}, "🎢"),
|
|
|
|
Text(" Speed switch image")
|
|
|
|
]),
|
|
|
|
rangeInput = Tag("input", {"type": "range", "style": "width: 140px;", "min": "10", "max": "1000", "value": switchFrequency}, null, null, "input", syncSliderValues),
|
|
|
|
numberInput = Tag("input", {"type": "number", "size": "5", "min": "10", "max": "1000", "value": switchFrequency}, null, null, "input", syncSliderValues),
|
|
|
|
Text(" ms "),
|
|
|
|
Tag("button", null, isPaused ? "resume" : "pause", null, "click", (e) => {
|
|
|
|
//e.target.textContent = isPaused ? "❄ pause" : "🔥 resume";
|
|
|
|
e.target.textContent = isPaused ? "pause" : "resume";
|
|
|
|
changeAnimationDuration(isPaused ? numberInput.value : 0);
|
|
|
|
isPaused = !isPaused;
|
|
|
|
saveToLocalStorage("rendertest_paused", isPaused);
|
|
|
|
})
|
|
|
|
]),
|
|
|
|
/* future
|
|
|
|
Tag("details", null, null, [
|
|
|
|
Tag("summary", null, "Advanced options"),
|
|
|
|
|
|
|
|
]),
|
|
|
|
*/
|
|
|
|
Tag("div", null, null, [
|
|
|
|
Tag("b", null, null, [
|
|
|
|
Tag("span", {"class": "emoji"}, "🧻"),
|
|
|
|
Text(" Sidebar")
|
|
|
|
]),
|
|
|
|
Tag("label", null, null, [
|
|
|
|
Tag("input", {"type": "radio", "name": "sidebar-mode", "value": "minimize", ...uglyChecked(sidebarMode === "minimize")}, null, null, "input", (e) => {
|
|
|
|
sidebarOptions.classList.toggle("sticky");
|
|
|
|
saveToLocalStorage("rendertest_sidebarmode", e.target.value);
|
|
|
|
|
|
|
|
}),
|
|
|
|
Text(" auto minimize")
|
|
|
|
]),
|
|
|
|
Tag("label", null, null, [
|
|
|
|
Tag("input", {"type": "radio", "name": "sidebar-mode", "value": "always", ...uglyChecked(sidebarMode === "always")}, null, null, "input", (e) => {
|
|
|
|
sidebarOptions.classList.toggle("sticky");
|
|
|
|
saveToLocalStorage("rendertest_sidebarmode", e.target.value);
|
|
|
|
}),
|
|
|
|
Text(" show always")
|
|
|
|
]),
|
|
|
|
Tag("ul", {"class": "list"}, null, [
|
|
|
|
Tag("li", null, null, [
|
|
|
|
Tag("span", {"class": "emoji Noto"}, "🧲"),
|
|
|
|
Text(" position"),
|
|
|
|
Tag("label", null, null, [
|
|
|
|
Tag("input", {"type": "radio", "name": "sidebar_pos", "value": "left", ...uglyChecked(sidebarPos == "left")}, null, null, "input", (e) => {
|
|
|
|
gridContainer.classList.toggle("reversed");
|
|
|
|
saveToLocalStorage("rendertest_sidebarpos", e.target.value);
|
|
|
|
}),
|
|
|
|
Text(" left")
|
|
|
|
]),
|
|
|
|
Tag("label", null, null, [
|
|
|
|
Tag("input", {"type": "radio", "name": "sidebar_pos", "value": "right", ...uglyChecked(sidebarPos == "right")}, null, null, "input", (e) => {
|
|
|
|
gridContainer.classList.toggle("reversed");
|
|
|
|
saveToLocalStorage("rendertest_sidebarpos", e.target.value);
|
|
|
|
}),
|
|
|
|
Text(" right")
|
|
|
|
]),
|
|
|
|
]),
|
|
|
|
Tag("li", null, null, [
|
|
|
|
Tag("span", {"class": "emoji"}, "📏"),
|
|
|
|
Text(" size"),
|
|
|
|
Tag("label", null, null, [
|
|
|
|
Tag("input", {"type": "radio", "name": "sidebar-size", "value": "default", ...uglyChecked(sidebarSize == "default")}, null, null, "input", (e) => {
|
|
|
|
document.body.classList.toggle("mini");
|
|
|
|
saveToLocalStorage("rendertest_sidebarsize", e.target.value);
|
|
|
|
}),
|
|
|
|
Text(" default")
|
|
|
|
]),
|
|
|
|
Tag("label", null, null, [
|
|
|
|
Tag("input", {"type": "radio", "name": "sidebar-size", "value": "mini", ...uglyChecked(sidebarSize == "mini")}, null, null, "input", (e) => {
|
|
|
|
document.body.classList.toggle("mini");
|
|
|
|
saveToLocalStorage("rendertest_sidebarsize", e.target.value);
|
|
|
|
}),
|
|
|
|
Text(" mini")
|
|
|
|
])
|
|
|
|
]),
|
|
|
|
]),
|
|
|
|
]),
|
|
|
|
Tag("div", null, null, [
|
|
|
|
Tag("b", null, null, [
|
|
|
|
Tag("span", {"class": "emoji"}, "⚖"),
|
|
|
|
Text(" Image position")
|
|
|
|
]),
|
|
|
|
Tag("label", null, null, [
|
|
|
|
Tag("input", {"type": "radio", "name": "image_position", "value": "relative", ...uglyChecked(imagePosition === "relative")}, null, null, "input", (e) => {
|
|
|
|
images.classList.toggle("center");
|
|
|
|
saveToLocalStorage("rendertest_imageposition", e.target.value);
|
|
|
|
}),
|
|
|
|
Text(" relative")
|
|
|
|
]),
|
|
|
|
Tag("label", null, null, [
|
|
|
|
Tag("input", {"type": "radio", "name": "image_position", "value": "center", ...uglyChecked(imagePosition === "center")}, null, null, "input", (e) => {
|
|
|
|
images.classList.toggle("center");
|
|
|
|
saveToLocalStorage("rendertest_imageposition", e.target.value);
|
|
|
|
}),
|
|
|
|
Text(" center ")
|
|
|
|
]),
|
|
|
|
Tag("b", null, null, [
|
|
|
|
Tag("span", {"class": "emoji Noto"}, "🖼"),
|
|
|
|
Tag("label", null, null, [
|
|
|
|
Text(" vertical padding"),
|
|
|
|
vpaddin_input = Tag("input", {"type": "checkbox", "name": "vpadding", ...uglyChecked(vpadding)}, null, null, "change", (e) => {
|
|
|
|
images.classList.toggle("vpadding");
|
|
|
|
saveToLocalStorage("rendertest_vpadding", e.target.checked);
|
|
|
|
})
|
|
|
|
])
|
|
|
|
])
|
|
|
|
]),
|
|
|
|
Tag("div", null, null, [
|
|
|
|
Tag("b", null, null, [
|
|
|
|
Tag("span", {"class": "emoji"}, "📷"),
|
|
|
|
Tag("label", null, null, [
|
|
|
|
Text(" Toggle diff for all"),
|
|
|
|
Tag("input", {"type": "checkbox", "name": "all_diff", ...uglyChecked(all_diff)}, null, null, "change", (e) => {
|
|
|
|
images.classList.toggle("all_diff");
|
|
|
|
//vpaddin_input.disabled = !vpaddin_input.disabled;
|
|
|
|
//images.classList.toggle("vpadding");
|
|
|
|
saveToLocalStorage("rendertest_all_diff", e.target.checked);
|
|
|
|
})
|
|
|
|
])
|
|
|
|
])
|
|
|
|
]),
|
|
|
|
Tag("div", null, null, [
|
|
|
|
Tag("b", null, null, [
|
|
|
|
Tag("span", {"class": "emoji"}, "📊"),
|
|
|
|
Text(" Sort table by Failed")
|
|
|
|
]),
|
|
|
|
Tag("label", null, null, [
|
|
|
|
Tag("input", {"type": "radio", "name": "sort_table", "value": "yes", ...uglyChecked(tableSort)}, null, null, "input", (e) => {
|
|
|
|
buildData(table, images, data, true, filter.value, exactmatch_input.checked);
|
|
|
|
// TODO: rewrite use reactive programming
|
|
|
|
tableSort = true;
|
|
|
|
}),
|
|
|
|
Text(" yes")
|
|
|
|
]),
|
|
|
|
Tag("label", null, null, [
|
|
|
|
Tag("input", {"type": "radio", "name": "sort_table", "value": "no", ...uglyChecked(!tableSort)}, null, null, "input", (e) => {
|
|
|
|
buildData(table, images, data, false, filter.value, exactmatch_input.checked);
|
|
|
|
tableSort = false;
|
|
|
|
}),
|
|
|
|
Text(" no")
|
|
|
|
])
|
|
|
|
])
|
|
|
|
]),
|
|
|
|
Tag("div", {"class": "panel", "id": "table"}, null, [
|
|
|
|
Tag("h2", null, "List of things that are not perfect"),
|
|
|
|
Tag("label", {"class": "filter sticky"}, "Filter", [
|
2024-02-16 00:40:39 +01:00
|
|
|
filter = Tag("input", {"type": "input", "title": "Hotkey: ALT+F", "name": "filter", "value": filter_value}, null, null, "input", (e) => {
|
2024-02-15 23:35:40 +01:00
|
|
|
saveToLocalStorage("rendertest_filter", e.target.value);
|
|
|
|
buildDataSlowMode(table, images, data, tableSort, e.target.value, exactmatch_input.checked);
|
|
|
|
}),
|
|
|
|
Tag("label", null, null, [
|
|
|
|
exactmatch_input = Tag("input", {"type": "checkbox", "name": "exactmatch", ...uglyChecked(exactMatch)}, null, null, "change", (e) => {
|
|
|
|
saveToLocalStorage("rendertest_exactmatch", e.target.checked);
|
|
|
|
buildDataSlowMode(table, images, data, tableSort, filter.value, exactmatch_input.checked);
|
|
|
|
}),
|
|
|
|
Text("exact match")
|
|
|
|
])
|
|
|
|
]),
|
|
|
|
table = Tag("div", {"id": "fail_table"})
|
|
|
|
])
|
|
|
|
]),
|
|
|
|
Tag("div", {"class": "content"}, null, [
|
|
|
|
Tag("h1", null, "Rendertest report"),
|
|
|
|
Tag("h2", null, "Summary"),
|
|
|
|
Tag("div", {"id": "summary"}, null, [...buildSummary(data)]),
|
|
|
|
Tag("h2", null, "Images of things that are not perfect ", [
|
|
|
|
Tag("span", {"class": "emoji"}, "⤵")
|
|
|
|
]),
|
|
|
|
images = Tag("div", {"id": "fail_images", "class": "vpadding"})
|
|
|
|
])
|
|
|
|
])
|
|
|
|
document.body.appendChild(gridContainer);
|
|
|
|
|
|
|
|
buildData(table, images, data, tableSort, filter_value, exactmatch_input.checked);
|
|
|
|
|
|
|
|
// TODO: remove this
|
|
|
|
if (sidebarPos === "right") {
|
|
|
|
gridContainer.classList.toggle("reversed");
|
|
|
|
}
|
|
|
|
if (sidebarMode === "always") {
|
|
|
|
sidebarOptions.classList.toggle("sticky");
|
|
|
|
}
|
|
|
|
if (sidebarSize === "mini") {
|
|
|
|
document.body.classList.toggle("mini");
|
|
|
|
}
|
|
|
|
if (imagePosition === "center") {
|
|
|
|
images.classList.toggle("center");
|
|
|
|
}
|
|
|
|
if (!vpadding) {
|
|
|
|
images.classList.toggle("vpadding");
|
|
|
|
}
|
|
|
|
if (all_diff) { // TODO: better UX
|
|
|
|
images.classList.toggle("all_diff");
|
|
|
|
//vpaddin_input.disabled = !vpaddin_input.disabled;
|
|
|
|
//images.classList.toggle("vpadding");
|
|
|
|
}
|
|
|
|
if (diffMode === "separate") {
|
|
|
|
images.classList.toggle("diff_separate");
|
|
|
|
}
|
|
|
|
if (imageCompareMode === "split") {
|
|
|
|
images.classList.toggle("split_compare");
|
|
|
|
}
|
|
|
|
changeAnimationDuration(isPaused ? 0 : numberInput.value);
|
|
|
|
|
|
|
|
// stop animation when not in the viewport
|
|
|
|
function handleIntersection(entries, observer) {
|
|
|
|
for (let entry of entries) {
|
|
|
|
if (!entry.isIntersecting) {
|
|
|
|
entry.target.classList.add("anim_off");
|
|
|
|
} else {
|
|
|
|
entry.target.classList.remove("anim_off");
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// TODO: auto subs&unsubs
|
|
|
|
|
|
|
|
const observer = new IntersectionObserver(handleIntersection, {
|
|
|
|
root: null,
|
|
|
|
threshold: 0.1
|
|
|
|
});
|
|
|
|
for (let element of document.querySelectorAll(".block-gold")) {
|
|
|
|
observer.observe(element);
|
|
|
|
}
|
|
|
|
|
|
|
|
function getPointerPositionPercentage(container, event) {
|
|
|
|
const rect = container.getBoundingClientRect();
|
|
|
|
let x;
|
|
|
|
switch (event.type) {
|
|
|
|
case "mousemove":
|
|
|
|
x = event.clientX - rect.left;
|
|
|
|
break;
|
|
|
|
case "touchmove": // TODO: testing
|
|
|
|
x = event.touches[0].clientX - rect.left;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
const percent = (x / rect.width) * 100;
|
|
|
|
return percent;
|
|
|
|
}
|
|
|
|
images.addEventListener("mousemove", (event) => {
|
|
|
|
if (images.classList.contains("split_compare")) {
|
|
|
|
const target = event.target.closest(".image-container");
|
|
|
|
if (target) {
|
|
|
|
event.preventDefault();
|
|
|
|
const percent = getPointerPositionPercentage(target, event);
|
|
|
|
const separator = target.querySelector(".separator");
|
|
|
|
const compare = target.querySelector(".block-gold");
|
|
|
|
compare.style.width = `${percent}%`;
|
|
|
|
separator.style.left = `${percent}%`;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
});
|
2024-02-16 00:38:37 +01:00
|
|
|
|
|
|
|
document.addEventListener("keydown", function(event) {
|
|
|
|
if (event.altKey) {
|
|
|
|
if (event.key === 'f') {
|
|
|
|
event.preventDefault();
|
|
|
|
filter.focus();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
});
|
|
|
|
|
2023-11-28 18:32:40 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
|