/*
Offline Tierlist Maker
Copyright (C) 2022 silverweed
Everyone is permitted to copy and distribute verbatim or modified
copies of this license document, and changing it is allowed as long
as the name is changed.
DO WHAT THE FUCK YOU WANT TO PUBLIC LICENSE
TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION
0. You just DO WHAT THE FUCK YOU WANT TO.
*/
'use strict';
const MAX_NAME_LEN = 200;
const DEFAULT_TIERS = ['S', 'A', 'B', 'C', 'D', 'E', 'F'];
const TIER_COLORS = [
'#ff6666',
'#f0a731',
'#f4d95b',
'#66ff66',
'#58c8f4',
'#5b76f4',
'#f45bed'
];
let unique_id = 0;
let unsaved_changes = false;
const LAYOUT_HORIZONTAL = 0;
const LAYOUT_VERTICAL = 1;
let cur_layout = LAYOUT_HORIZONTAL;
// Contains [[header, input, label]]
let all_headers = [];
let headers_orig_min_width;
// DOM elems
let untiered_images;
let tierlist_div;
let dragged_image;
function reset_row(row) {
row.querySelectorAll('span.item').forEach((item) => {
for (let i = 0; i < item.children.length; ++i) {
let img = item.children[i];
item.removeChild(img);
untiered_images.appendChild(img);
}
item.parentNode.removeChild(item);
});
}
function hard_reset_list() {
tierlist_div.innerHTML = '';
untiered_images.innerHTML = '';
}
function soft_reset_list() {
tierlist_div.querySelectorAll('.row').forEach(reset_row);
unsaved_changes = true;
}
window.addEventListener('load', () => {
untiered_images = document.querySelector('.images');
tierlist_div = document.querySelector('.tierlist');
for (let i = 0; i < DEFAULT_TIERS.length; ++i) {
add_row(i, DEFAULT_TIERS[i]);
}
recompute_header_colors();
headers_orig_min_width = all_headers[0][0].clientWidth;
make_accept_drop(document.querySelector('.images'));
bind_title_events();
document.getElementById('load-img-input').addEventListener('input', (evt) => {
let images = document.querySelector('.images');
for (let file of evt.target.files) {
let reader = new FileReader();
reader.addEventListener('load', (load_evt) => {
let img = create_img_with_src(load_evt.target.result);
images.appendChild(img);
unsaved_changes = true;
});
reader.readAsDataURL(file);
}
});
document.onpaste = (evt) => {
let clip_data = evt.clipboardData || evt.originalEvent.clipboardData;
let items = clip_data.items;
let images = document.querySelector('.images');
for (let item of items) {
if (item.kind === 'file') {
let blob = item.getAsFile();
let reader = new FileReader();
reader.onload = (load_evt) => {
let img = create_img_with_src(load_evt.target.result);
images.appendChild(img);
unsaved_changes = true;
};
reader.readAsDataURL(blob);
}
}
};
document.getElementById('reset-list-input').addEventListener('click', () => {
if (confirm('Reset Tierlist? (this will place all images back in the pool)')) {
soft_reset_list();
}
});
document.getElementById('export-input').addEventListener('click', () => {
let name = prompt('Please give a name to the file. This will export your tiers as a JSON file so you can continue working on it later');
if (name) {
save_tierlist(`${name}.json`);
}
});
document.getElementById('export-input-html').addEventListener('click', () => {
let name = prompt('This will export your tiers as a single interactive HTML file. Please give a name to the file');
if (name) {
save_tierlist_with_template(`${name}.html`);
}
});
document.getElementById('import-input').addEventListener('input', (evt) => {
if (!evt.target.files) {
return;
}
let file = evt.target.files[0];
let reader = new FileReader();
reader.addEventListener('load', (load_evt) => {
let raw = load_evt.target.result;
let parsed = JSON.parse(raw);
if (!parsed) {
alert("Failed to parse data");
return;
}
hard_reset_list();
load_tierlist(parsed);
});
reader.readAsText(file);
});
bind_trash_events();
bind_toggle_layout_events();
window.addEventListener('beforeunload', (evt) => {
if (!unsaved_changes) return null;
var msg = "You have unsaved changes. Leave anyway?";
(evt || window.event).returnValue = msg;
return msg;
});
void try_load_tierlist_json();
const modal = document.getElementById('image-modal');
const modalImg = document.getElementById('modal-img');
const modalTitle = document.getElementById('modal-title');
const modalDesc = document.getElementById('modal-description');
const modalSave = document.getElementById('modal-save');
const modalClose = document.querySelector('.modal .close');
let currentModalImage = null;
function showModal(img) {
currentModalImage = img;
document.body.style.overflow = 'hidden';
modal.style.display = 'flex';
modalImg.src = img.src;
modalTitle.value = img.dataset.title || '';
modalDesc.value = img.dataset.description || '';
}
modalClose.addEventListener('click', () => {
modal.style.display = 'none';
document.body.style.overflow = 'hidden';
});
modalSave.addEventListener('click', () => {
if (currentModalImage) {
currentModalImage.dataset.title = modalTitle.value;
currentModalImage.dataset.description = modalDesc.value;
}
modal.style.display = 'none';
document.body.style.overflow = 'hidden';
});
window.addEventListener('click', (evt) => {
if (evt.target == modal) {
modal.style.display = 'none';
document.body.style.overflow = 'hidden';
}
});
tierlist_div.addEventListener('click', (evt) => {
if (evt.target.tagName.toUpperCase() === 'IMG') {
showModal(evt.target);
}
});
});
function create_img_with_src(src) {
let img = document.createElement('img');
img.src = src;
img.style.userSelect = 'none';
img.classList.add('draggable');
img.draggable = true;
img.addEventListener("dragstart", (evt) => {
evt.dataTransfer.setData("text/plain", null);
dragged_image = evt.target;
dragged_image.classList.add("dragged");
});
img.addEventListener("mouseenter", (evt) => {
const title = evt.target.dataset.title;
if (title) {
evt.target.title = title;
}
});
img.addEventListener("dragend", (evt) => {
if (dragged_image) {
dragged_image.classList.remove("dragged");
}
dragged_image = null;
});
return img;
}
function save(filename, text) {
unsaved_changes = false;
var el = document.createElement('a');
el.setAttribute('href', 'data:text/html;charset=utf-8,' + encodeURIComponent(text));
el.setAttribute('download', filename);
el.style.display = 'none';
document.body.appendChild(el);
el.click();
document.body.removeChild(el);
}
function save_tierlist(filename) {
let serialized_tierlist = {
title: document.querySelector('.title-label').innerText,
rows: [],
};
tierlist_div.querySelectorAll('.row').forEach((row, i) => {
serialized_tierlist.rows.push({
name: row.querySelector('.header label').innerText.substr(0, MAX_NAME_LEN)
});
serialized_tierlist.rows[i].imgs = [];
row.querySelectorAll('img').forEach((img) => {
serialized_tierlist.rows[i].imgs.push({
src: img.src,
title: img.dataset.title || '',
description: img.dataset.description || ''
});
});
});
let untiered_imgs = document.querySelectorAll('.images img');
if (untiered_imgs.length > 0) {
serialized_tierlist.untiered = [];
untiered_imgs.forEach((img) => {
serialized_tierlist.untiered.push({
src: img.src,
title: img.dataset.title || '',
description: img.dataset.description || ''
});
});
}
save(filename, JSON.stringify(serialized_tierlist));
}
async function save_tierlist_with_template(filename) {
let serialized_tierlist = {
title: document.querySelector('.title-label').innerText,
rows: []
};
tierlist_div.querySelectorAll('.row').forEach((row, i) => {
serialized_tierlist.rows.push({
name: row.querySelector('.header').innerText.substr(0, MAX_NAME_LEN)
});
serialized_tierlist.rows[i].imgs = [];
row.querySelectorAll('img').forEach((img) => {
serialized_tierlist.rows[i].imgs.push({
src: img.src,
title: img.dataset.title || '',
description: img.dataset.description || ''
});
});
});
let untiered_imgs = document.querySelectorAll('.images img');
if (untiered_imgs.length > 0) {
serialized_tierlist.untiered = [];
untiered_imgs.forEach((img) => {
serialized_tierlist.untiered.push({
src: img.src,
title: img.dataset.title || '',
description: img.dataset.description || ''
});
});
}
try {
// Fetch resources
let [templateResponse, jsResponse, cssResponse] = await Promise.all([
fetch('/tiers'),
fetch('/tiers.js'),
fetch('/tiers.css')
]);
let [templateHTML, scriptContent, styleContent] = await Promise.all([
templateResponse.text(),
jsResponse.text(),
cssResponse.text()
]);
// Inject the EMBEDDED_JSON inside a script tag
let jsonScript = ``;
let inlineScript = ``;
let inlineCSS = ``;
templateHTML = templateHTML.replace("", inlineCSS + "\n");
let updatedHTML = templateHTML.replace("