/* Interactive Tiers Original code from: https://github.com/silverweed/tiers Modified by: pinapelz Licensed Under WTFPL */ '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; let all_headers = []; let headers_orig_min_width; let untiered_images; let tierlist_div; let dragged_image; let dragged_item; let drop_placeholder; 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 = 'auto'; }); window.addEventListener('click', (evt) => { if (evt.target == modal) { modal.style.display = 'none'; document.body.style.overflow = 'auto'; } }); 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", ''); evt.dataTransfer.effectAllowed = 'move'; dragged_image = evt.target; dragged_item = wrap_image_in_item(dragged_image); dragged_image.classList.add("dragged"); drop_placeholder = document.createElement('span'); drop_placeholder.classList.add('item', 'drop-placeholder'); let rect = dragged_item.getBoundingClientRect(); drop_placeholder.style.width = `${Math.max(30, Math.ceil(rect.width))}px`; drop_placeholder.style.height = `${Math.max(30, Math.ceil(rect.height))}px`; }); img.addEventListener("mouseenter", (evt) => { const title = evt.target.dataset.title; if (title) { evt.target.title = title; } }); img.addEventListener("dragend", () => { if (dragged_image) { dragged_image.classList.remove("dragged"); } if (drop_placeholder && drop_placeholder.parentNode) { drop_placeholder.parentNode.removeChild(drop_placeholder); } dragged_image = null; dragged_item = null; drop_placeholder = 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.html'), 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 = ``; let inlineTitle = `