site_fursona_refs/html/main.js

496 lines
21 KiB
JavaScript

var nullFunc = () => { };
var poppingState = false;
var closingGallery = false;
var closingFilterBuilder = false;
/* jshint ignore:start */
var do_eval = (exp) => eval(exp);
/* jshint ignore:end */
function onPopState(event) {
resetAppState();
let hash = event.target.location.hash;
poppingState = true;
if (hash.startsWith('#js:'))
do_eval(decodeURIComponent(hash.substr(4)));
poppingState = false;
closingGallery = false;
closingFilterBuilder = false;
}
function onInit() {
colorAccuracyJSWall.classList.add('nodisplay');
colorAccuracyJSProtectedContent.classList.remove('nodisplay');
fillStaticPartOfColorPalette();
let hash = window.location.hash;
poppingState = true;
if (hash === '')
window.history.replaceState(null, document.title, '#');
else if (hash.startsWith('#js:'))
do_eval(decodeURIComponent(hash.substr(4)));
poppingState = false;
updateColorPaletteAverage();
}
window.onpopstate = onPopState;
var imgQualities = {
'thumb': imgs_thumb,
'small': imgs_small,
'dcent': imgs_dcent,
'large': imgs_large,
'full': imgs_full
};
var qualityName = {
'thumb': 'Thumbnail',
'small': 'Small',
'dcent': 'Decent',
'large': 'Large',
'full': 'Full'
};
function formatFileSize(bytecount) {
units = ['B', 'KB', 'MB', 'GB', 'TB'];
magnitude = 0;
while (bytecount >= 2048) {
bytecount /= 1024;
magnitude++;
}
bytecount = Math.round(bytecount * 10) / 10;
return `${bytecount} ${units[magnitude]}`;
}
function openGallery(image, quality) {
if (closingGallery) { setTimeout(closeGallery, 50); return; }
if (typeof (quality) == typeof (undefined)) quality = 'dcent';
if (!poppingState)
window.history.pushState(null, document.title,
`#js:openGallery(${encodeURIComponent(JSON.stringify(image))}, ${encodeURIComponent(JSON.stringify(quality))})`
);
galleryBrowser.classList.remove('nodisplay');
item = images[image];
img = imgQualities[quality][image];
galleryImage.style.backgroundImage = `url('./${img}')`;
// Qualities highlight
if (quality == 'thumb') qualityThumb.style.background = "#619A3588";
else qualityThumb.style.background = null;
if (quality == 'small') qualitySmall.style.background = "#619A3588";
else qualitySmall.style.background = null;
if (quality == 'dcent') qualityDcent.style.background = "#619A3588";
else qualityDcent.style.background = null;
if (quality == 'large') qualityLarge.style.background = "#619A3588";
else qualityLarge.style.background = null;
if (quality == 'full') qualityFull.style.background = "#619A3588";
else qualityFull.style.background = null;
// Set callback
seeThumb.setAttribute('onclick', `openGallery(${JSON.stringify(image)}, 'thumb')`);
seeSmall.setAttribute('onclick', `openGallery(${JSON.stringify(image)}, 'small')`);
seeDcent.setAttribute('onclick', `openGallery(${JSON.stringify(image)}, 'dcent')`);
seeLarge.setAttribute('onclick', `openGallery(${JSON.stringify(image)}, 'large')`);
seeFull.setAttribute('onclick', `openGallery(${JSON.stringify(image)}, 'full')`);
// Set download targets
downloadThumb.href = './' + imgQualities.thumb[image];
downloadSmall.href = './' + imgQualities.small[image];
downloadDcent.href = './' + imgQualities.dcent[image];
downloadLarge.href = './' + imgQualities.large[image];
downloadFull.href = './' + imgQualities.full[image];
// Set download names
downloadThumb.setAttribute('download', `${name} - ${image} - ${artists[item.artist].name} - ${item.date.join('-')} (${qualityName.thumb}).${formats[image].thumb}`);
downloadSmall.setAttribute('download', `${name} - ${image} - ${artists[item.artist].name} - ${item.date.join('-')} (${qualityName.small}).${formats[image].small}`);
downloadDcent.setAttribute('download', `${name} - ${image} - ${artists[item.artist].name} - ${item.date.join('-')} (${qualityName.dcent}).${formats[image].dcent}`);
downloadLarge.setAttribute('download', `${name} - ${image} - ${artists[item.artist].name} - ${item.date.join('-')} (${qualityName.large}).${formats[image].large}`);
downloadFull.setAttribute('download', `${name} - ${image} - ${artists[item.artist].name} - ${item.date.join('-')} (${qualityName.full}).${formats[image].full}`);
// Set image sizes
sizeThumb.innerText = `(${formatFileSize(sizes[image].thumb)}) - ${formats[image].thumb}`;
sizeSmall.innerText = `(${formatFileSize(sizes[image].small)}) - ${formats[image].small}`;
sizeDcent.innerText = `(${formatFileSize(sizes[image].dcent)}) - ${formats[image].dcent}`;
sizeLarge.innerText = `(${formatFileSize(sizes[image].large)}) - ${formats[image].large}`;
sizeFull.innerText = `(${formatFileSize(sizes[image].full)}) - ${formats[image].full}`;
}
var checkVisible = (elem) => {
let posY = (elm) => {
let test = elm;
let top = 0;
while (!!test && test.tagName.toLowerCase() !== "body") {
top += test.offsetTop;
test = test.offsetParent;
}
return top;
};
let viewPortHeight = () => {
let de = document.documentElement;
if (!!window.innerWidth) { return window.innerHeight; }
else if (de && !isNaN(de.clientHeight)) { return de.clientHeight; }
return 0;
};
let scrollY = () => {
if (window.pageYOffset) { return window.pageYOffset; }
return Math.max(document.documentElement.scrollTop, document.body.scrollTop);
};
let checkvisible = (elm) => {
let vpH = viewPortHeight(); // Viewport Height
let st = scrollY(); // Scroll Top
let y = posY(elm);
let dy = elm.offsetHeight;
return ((vpH + st) > y) && (st < (y + dy));
};
return checkvisible(elem);
};
function filterArtist(artist) {
resetAppState();
if (!poppingState) {
window.history.pushState(null, document.title,
`#js:filterArtist(${encodeURIComponent(JSON.stringify(artist))})`
);
}
document.querySelector('#sideCard>.controlButtons>.showFiltered').classList.add('selected');
nestedElements.classList.add('nodisplay');
listElements.classList.remove('nodisplay');
filterNav.classList.remove('nodisplay');
let item = artists[artist];
filterIcon.className = "fa fa-user";
filterText.innerText = item.name;
for (let key in images) {
image = images[key];
let hideable_row = document.getElementById('row_' + key);
if (image.artist == artist) {
document.getElementById('list-' + key).classList.remove('nodisplay');
if (hideable_row !== null) hideable_row.classList.remove('nodisplay');
} else {
document.getElementById('list-' + key).classList.add('nodisplay');
if (hideable_row !== null) hideable_row.classList.add('nodisplay');
}
}
updateColorPaletteAverage();
}
function filterTag(tag) {
resetAppState();
if (!poppingState)
window.history.pushState(null, document.title,
`#js:filterTag(${encodeURIComponent(JSON.stringify(tag))})`
);
document.querySelector('#sideCard>.controlButtons>.showFiltered').classList.add('selected');
nestedElements.classList.add('nodisplay');
listElements.classList.remove('nodisplay');
filterNav.classList.remove('nodisplay');
let item = tags[tag];
filterIcon.className = "fa fa-" + item.icon;
filterText.innerText = item.name;
for (let key in images) {
image = images[key];
let hideable_row = document.getElementById('row_' + key);
if ((image.technology == tag) || (image.tags.indexOf(tag) >= 0)) {
document.getElementById('list-' + key).classList.remove('nodisplay');
if (hideable_row !== null) hideable_row.classList.remove('nodisplay');
} else {
document.getElementById('list-' + key).classList.add('nodisplay');
if (hideable_row !== null) hideable_row.classList.add('nodisplay');
}
}
updateColorPaletteAverage();
}
function showAsList() {
resetAppState();
if (!poppingState)
window.history.pushState(null, document.title,
`#js:showAsList()`
);
nestedElements.classList.add('nodisplay');
listElements.classList.remove('nodisplay');
filterNav.classList.add('nodisplay');
for (let key in images) {
image = images[key];
let hideable_row = document.getElementById('row_' + key);
document.getElementById('list-' + key).classList.remove('nodisplay');
if (hideable_row !== null) hideable_row.classList.remove('nodisplay');
}
}
function buildFilters() {
if (closingFilterBuilder) { setTimeout(closeFilterBuilder, 50); return; }
resetAppState();
if (!poppingState)
window.history.pushState(null, document.title,
`#js:buildFilters()`
);
document.querySelector('#sideCard>.controlButtons>.showFiltered').classList.add('selected');
document.getElementById('filterBuilder').classList.remove('nodisplay');
}
function resetAppState(skipScroll) {
document.querySelector('#sideCard>.controlButtons>.showNested').classList.remove('selected');
document.querySelector('#sideCard>.controlButtons>.showListed').classList.remove('selected');
document.querySelector('#sideCard>.controlButtons>.showFiltered').classList.remove('selected');
document.getElementById('filterBuilder').classList.add('nodisplay');
nestedElements.classList.remove('nodisplay');
listElements.classList.add('nodisplay');
filterNav.classList.add('nodisplay');
galleryBrowser.classList.add('nodisplay');
for (let item of Array.from(document.querySelectorAll('#colorAccuracy tr.hideablePaletteItem')))
item.classList.remove('nodisplay');
for (let item of Array.from(document.querySelectorAll('#sideCard>.fastNavigation>.fastNavigationCard'))) {
item.classList.remove('nodisplay');
item.classList.remove('displaying');
item.onclick = null;
}
updateColorPaletteAverage();
if (!skipScroll)
window.scrollTo(0, 0);
}
var clearFilters = () => { resetAppState(); window.history.back(); };
var closeGallery = () => { resetAppState(true); closingGallery = true; window.history.back(); };
var closeFilterBuilder = () => { resetAppState(true); closingFilterBuilder = true; window.history.back(); };
var rgxHexColor = /^#([0-9a-fA-F]{2})([0-9a-fA-F]{2})([0-9a-fA-F]{2})$/;
var parseHexColor = x => rgxHexColor.exec(x).slice(1).map(i => parseInt(i, 16));
var toHexColor = l => '#' + (l.map(i => ('0' + Math.round(i).toString(16)).slice(-2)).join('')).toUpperCase();
var zip = (...rows) => [...rows[0]].map((_, c) => rows.map(row => row[c]));
var euclideanDistance2 = (a1, a2) => zip(a1, a2).map(x => x[0] - x[1]).map(x => x * x).reduce((a, v) => a + v, 0);
var euclideanDistance = (a1, a2) => Math.sqrt(euclideanDistance2(a1, a2));
var avg = (...values) => [...values].reduce((acc, val) => acc + val) / values.length;
var mixColorChannel = (...values) => Math.sqrt(values.map(v => v * v).reduce((a, v) => a + v) / values.length);
function inflateFontAwesome(icon) {
let fa = document.createElement('i');
fa.setAttribute('class', `fa fa-${icon}`);
fa.setAttribute('aria-hidden', 'true');
return fa;
}
function round(number, digits) {
if (digits === undefined) digits = 0;
let factor = Math.pow(10, digits);
return Math.round(number * factor) / factor;
}
function colorSimilarity(color1, color2) {
let c1 = parseHexColor(color1).map(x => x / 255);
let c2 = parseHexColor(color2).map(x => x / 255);
return 1 - (euclideanDistance(c1, c2) / Math.sqrt(3));
}
function colorAverage(...colors) {
let engine = avg; // Easy to implement; bad colors
engine = mixColorChannel; // Simulates light mixing with gamma=2.0
if (colors.length == 0)
return null;
else
return toHexColor(
zip(
...colors.map(parseHexColor)
).map(ch => engine(...ch))
);
}
function determineLightOrDark(color) { // True: light, False: dark
// https://en.wikipedia.org/wiki/Grayscale#Luma_coding_in_video_systems
let y = [0.2126, 0.7152, 0.0722];
let c = parseHexColor(color);
let hsp = Math.sqrt(
zip(
y,
c.map(x => x * x)
).map(
v => v[0] * v[1]
).reduce(
(a, v) => a + v
)
);
return hsp > 127;
}
function updateColorPaletteAverage() {
let fields = Array.from(document.querySelectorAll('#row_avg td'));
for (let fld of fields) {
fld.setAttribute("class", "clr_" + fld.id.slice(8));
fld.setAttribute("title", "");
fld.style.background = null;
while (fld.firstChild) fld.firstChild.remove();
}
let relevantColors = {};
for (let color of color_list) {
if (relevantColors[color] === undefined)
relevantColors[color] = [];
for (let idx in images) {
let hideable_row = document.getElementById('row_' + idx);
let image = images[idx];
let colorUsed = ((image.color || {})[color] || {}).colorUsed;
if (colorUsed !== null && colorUsed !== undefined && !hideable_row.classList.contains('nodisplay')) {
relevantColors[color].push(colorUsed);
}
}
}
for (let idx in relevantColors) {
let colorsShown = relevantColors[idx];
let cell = document.querySelector('#row_avg td.clr_' + idx);
let canonical = canonical_colors[idx].color;
let avgColor = colorAverage(...colorsShown);
if (avgColor !== null) {
cell.style.background = avgColor;
let isLight = determineLightOrDark(avgColor);
let similarity = round(colorSimilarity(avgColor, canonical) * 100, 2);
cell.classList.add(isLight ? 'blackText' : 'whiteText');
cell.appendChild(document.createTextNode(avgColor));
cell.appendChild(document.createElement('br'));
cell.appendChild(document.createTextNode(`(${similarity}%)`));
cell.title = `${similarity}% similar to the ideal value`;
}
}
}
function fillStaticPartOfColorPalette() {
//bodyparts => Display names for body parts
//canonical_colors => "Right" colors
let basecolors = color_list.slice(0, color_repeated[false]);
let deviatedcolors = color_list.slice(color_repeated[false]);
for (let color of basecolors) {
let canonical = canonical_colors[color].color;
for (let imgid in images) {
let image = images[imgid];
let colordef = image.color[color];
if (colordef !== undefined) {
let cell = `#colorAccuracy table #row_${imgid} .clr_${color}`;
cell = document.querySelector(cell);
while (cell.firstChild) cell.firstChild.remove();
let similarity = round(colorSimilarity(colordef.colorUsed, canonical) * 100, 2);
cell.style.background = colordef.colorUsed;
cell.classList.add(colordef.colorIsLight ? 'blackText' : 'whiteText');
cell.appendChild(document.createTextNode(colordef.colorUsed));
cell.appendChild(document.createElement('br'));
cell.appendChild(document.createTextNode(`(${similarity}%)`));
cell.title = `"${bodyparts[colordef.targetOnImage]}" color\n${similarity}% similar to the ideal value`;
}
}
}
for (let color of deviatedcolors) {
let canonical = canonical_colors[color].color;
for (let imgid in images) {
let image = images[imgid];
let colordef = image.color[color];
if (colordef !== undefined) {
let cell = `#colorAccuracy table #row_${imgid} .clr_${color}`;
cell = document.querySelector(cell);
while (cell.firstChild) cell.firstChild.remove();
if (colordef === null) {
cell.appendChild(inflateFontAwesome('2x fa-times-circle'));
cell.title = 'This feature is missing on the artwork';
cell.classList.add('featureMissing');
} else {
let shouldBeUsing = canonical;
if (image.color[colordef.targetAppropriate])
shouldBeUsing = image.color[colordef.targetAppropriate].colorUsed;
let problem = (colordef.colorUsed != shouldBeUsing);
cell.style.background = colordef.colorUsed;
cell.classList.add(colordef.colorIsLight ? 'blackText' : 'whiteText');
cell.appendChild(document.createTextNode(colordef.colorUsed));
cell.appendChild(document.createElement('br'));
cell.appendChild(inflateFontAwesome(problem ? 'warning' : 'info-circle'));
let title = `"${bodyparts[colordef.targetOnImage]}" color\n`;
if (problem)
title += `Should have the same color as "${bodyparts[colordef.targetAppropriate]}"`;
else
title += `Same color as "${bodyparts[colordef.targetAppropriate]}"`;
let parent = canonical;
let parentColordef = image.color[colordef.targetAppropriate];
if (parentColordef !== undefined && parentColordef !== null)
parent = parentColordef.colorUsed;
let sp = round(colorSimilarity(colordef.colorUsed, parent) * 100, 2);
let sc = round(colorSimilarity(colordef.colorUsed, canonical) * 100, 2);
title += '\n';
if (parentColordef !== undefined && parentColordef !== null)
title += `${sp}% similar to the one used as "${bodyparts[colordef.targetAppropriate]}"\n`;
title += `${sc}% similar to the ideal value`;
cell.title = title;
}
}
}
}
}
function onInitRegister() {
if ('serviceWorker' in navigator) {
navigator.serviceWorker.register('/sw.js').then(function (registration) {
console.log('ServiceWorker registration successful with scope: ', registration.scope);
}, function (err) {
console.log('ServiceWorker registration failed: ', err);
});
}
}
function keepFastNavigationUpdated() {
setTimeout(keepFastNavigationUpdated, 30);
document.querySelectorAll('#sideCard>fastNavigation>fastNavigationCard');
let nestedElement = document.getElementById('nestedElements');
let listedElement = document.getElementById('listElements');
let nestedButton = document.querySelector('#sideCard>.controlButtons>.showNested');
let listedButton = document.querySelector('#sideCard>.controlButtons>.showListed');
let filterButton = document.querySelector('#sideCard>.controlButtons>.showFiltered');
if (filterButton.classList.contains('selected')) {
nestedButton.classList.remove('selected');
listedButton.classList.remove('selected');
} else {
if (nestedElement.offsetParent === null)
nestedButton.classList.remove('selected');
else
nestedButton.classList.add('selected');
if (listedElement.offsetParent === null)
listedButton.classList.remove('selected');
else
listedButton.classList.add('selected');
}
let main = document.getElementById('main');
let sidebarsHidden = document.getElementById('sideCard').offsetParent === null;
if (sidebarsHidden) main.classList.add('noSidebars');
else main.classList.remove('noSidebars');
let imagesElement = [
nestedElement,
listedElement,
].filter(x => !x.classList.contains('nodisplay'))[0];
let generateSmoothScrollCallback = (element) => (() => {
element.scrollIntoView({
behavior: 'smooth',
block: 'center',
inline: 'center'
});
if (!checkVisible(element))
setTimeout(generateSmoothScrollCallback(element), 100);
});
for (let cardElement of Array.from(imagesElement.getElementsByClassName('card'))) {
let element = cardElement.getElementsByTagName('img')[0];
let imagenum = parseInt(cardElement.getAttribute('id').split('-')[1]);
let fastNavElem = document.getElementById(`fastnav-${imagenum}`);
let detailsElem = document.getElementById(`details-${imagenum}`);
if (checkVisible(element))
fastNavElem.classList.add('displaying');
else
fastNavElem.classList.remove('displaying');
if (checkVisible(element))
detailsElem.classList.remove('collapsed');
else
detailsElem.classList.add('collapsed');
if (element.offsetParent === null)
fastNavElem.classList.add('nodisplay');
else
fastNavElem.classList.remove('nodisplay');
fastNavElem.onclick = generateSmoothScrollCallback(element);
let titleElem = detailsElem.getElementsByTagName('span')[0];
titleElem.onclick = generateSmoothScrollCallback(element);
titleElem.getElementsByTagName('img')[0].onclick = generateSmoothScrollCallback(element);
titleElem.getElementsByTagName('span')[0].onclick = generateSmoothScrollCallback(element);
}
}
setTimeout(keepFastNavigationUpdated, 10);
onInit();
onInitRegister();