let activeTags = [] let modifiers = [] let customModifiersGroupElement = undefined let customModifiersInitialContent let editorModifierEntries = document.querySelector("#editor-modifiers-entries") let editorModifierTagsList = document.querySelector("#editor-inputs-tags-list") let editorTagsContainer = document.querySelector("#editor-inputs-tags-container") let modifierCardSizeSlider = document.querySelector("#modifier-card-size-slider") let previewImageField = document.querySelector("#preview-image") let modifierSettingsBtn = document.querySelector("#modifier-settings-btn") let modifierSettingsOverlay = document.querySelector("#modifier-settings-config") let customModifiersTextBox = document.querySelector("#custom-modifiers-input") let customModifierEntriesToolbar = document.querySelector("#editor-modifiers-entries-toolbar") const modifierThumbnailPath = "media/modifier-thumbnails" const activeCardClass = "modifier-card-active" const CUSTOM_MODIFIERS_KEY = "customModifiers" function createModifierCard(name, previews, removeBy) { const modifierCard = document.createElement("div") let style = previewImageField.value let styleIndex = style == "portrait" ? 0 : 1 modifierCard.className = "modifier-card" modifierCard.innerHTML = `
+

Modifier Image

` const image = modifierCard.querySelector(".modifier-card-image") const errorText = modifierCard.querySelector(".modifier-card-error-label") const label = modifierCard.querySelector(".modifier-card-label") errorText.innerText = "No Image" if (typeof previews == "object") { image.src = previews[styleIndex] // portrait image.setAttribute("preview-type", style) } else { image.remove() } const maxLabelLength = 30 const cardLabel = removeBy ? name.replace("by ", "") : name if (cardLabel.length <= maxLabelLength) { label.querySelector("p").innerText = cardLabel } else { const tooltipText = document.createElement("span") tooltipText.className = "tooltip-text" tooltipText.innerText = name label.classList.add("tooltip") label.appendChild(tooltipText) label.querySelector("p").innerText = cardLabel.substring(0, maxLabelLength) + "..." } label.querySelector("p").dataset.fullName = name // preserve the full name return modifierCard } function createModifierGroup(modifierGroup, initiallyExpanded, removeBy) { const title = modifierGroup.category const modifiers = modifierGroup.modifiers const titleEl = document.createElement("h5") titleEl.className = "collapsible" titleEl.innerText = title const modifiersEl = document.createElement("div") modifiersEl.classList.add("collapsible-content", "editor-modifiers-leaf") if (initiallyExpanded === true) { titleEl.className += " active" } modifiers.forEach((modObj) => { const modifierName = modObj.modifier const modifierPreviews = modObj?.previews?.map( (preview) => `${IMAGE_REGEX.test(preview.image) ? preview.image : modifierThumbnailPath + "/" + preview.path}` ) const modifierCard = createModifierCard(modifierName, modifierPreviews, removeBy) if (typeof modifierCard == "object") { modifiersEl.appendChild(modifierCard) const trimmedName = trimModifiers(modifierName) modifierCard.addEventListener("click", () => { if (activeTags.map((x) => trimModifiers(x.name)).includes(trimmedName)) { // remove modifier from active array activeTags = activeTags.filter((x) => trimModifiers(x.name) != trimmedName) toggleCardState(trimmedName, false) } else { // add modifier to active array activeTags.push({ name: modifierName, element: modifierCard.cloneNode(true), originElement: modifierCard, previews: modifierPreviews, }) toggleCardState(trimmedName, true) } refreshTagsList() document.dispatchEvent(new Event("refreshImageModifiers")) }) } }) let brk = document.createElement("br") brk.style.clear = "both" modifiersEl.appendChild(brk) let e = document.createElement("div") e.className = "modifier-category" e.appendChild(titleEl) e.appendChild(modifiersEl) editorModifierEntries.insertBefore(e, customModifierEntriesToolbar.nextSibling) return e } function trimModifiers(tag) { // Remove trailing '-' and/or '+' tag = tag.replace(/[-+]+$/, "") // Remove parentheses at beginning and end return tag.replace(/^[(]+|[\s)]+$/g, "") } async function loadModifiers() { try { let res = await fetch("/get/modifiers") if (res.status === 200) { res = await res.json() modifiers = res // update global variable res.reverse() res.forEach((modifierGroup, idx) => { createModifierGroup(modifierGroup, idx === res.length - 1, modifierGroup === "Artist" ? true : false) // only remove "By " for artists }) createCollapsibles(editorModifierEntries) } } catch (e) { console.error("error fetching modifiers", e) } loadCustomModifiers() resizeModifierCards(modifierCardSizeSlider.value) document.dispatchEvent(new Event("loadImageModifiers")) } function refreshModifiersState(newTags, inactiveTags) { // clear existing modifiers document .querySelector("#editor-modifiers") .querySelectorAll(".modifier-card") .forEach((modifierCard) => { const modifierName = modifierCard.querySelector(".modifier-card-label p").dataset.fullName // pick the full modifier name if (activeTags.map((x) => x.name).includes(modifierName)) { modifierCard.classList.remove(activeCardClass) modifierCard.querySelector(".modifier-card-image-overlay").innerText = "+" } }) activeTags = [] // set new modifiers newTags.forEach((tag) => { let found = false document .querySelector("#editor-modifiers") .querySelectorAll(".modifier-card") .forEach((modifierCard) => { const modifierName = modifierCard.querySelector(".modifier-card-label p").dataset.fullName const shortModifierName = modifierCard.querySelector(".modifier-card-label p").innerText if (trimModifiers(tag) == trimModifiers(modifierName)) { // add modifier to active array if (!activeTags.map((x) => x.name).includes(tag)) { // only add each tag once even if several custom modifier cards share the same tag const imageModifierCard = modifierCard.cloneNode(true) imageModifierCard.querySelector(".modifier-card-label p").innerText = tag.replace( modifierName, shortModifierName ) activeTags.push({ name: tag, element: imageModifierCard, originElement: modifierCard, }) } modifierCard.classList.add(activeCardClass) modifierCard.querySelector(".modifier-card-image-overlay").innerText = "-" found = true } }) if (found == false) { // custom tag went missing, create one here let modifierCard = createModifierCard(tag, undefined, false) // create a modifier card for the missing tag, no image modifierCard.addEventListener("click", () => { if (activeTags.map((x) => x.name).includes(tag)) { // remove modifier from active array activeTags = activeTags.filter((x) => x.name != tag) modifierCard.classList.remove(activeCardClass) modifierCard.querySelector(".modifier-card-image-overlay").innerText = "+" } refreshTagsList() }) activeTags.push({ name: tag, element: modifierCard, originElement: undefined, // no origin element for missing tags }) } }) refreshTagsList(inactiveTags) } function refreshInactiveTags(inactiveTags) { // update inactive tags if (inactiveTags !== undefined && inactiveTags.length > 0) { activeTags.forEach((tag) => { if (inactiveTags.find((element) => element === tag.name) !== undefined) { tag.inactive = true } }) } // update cards let overlays = document.querySelector("#editor-inputs-tags-list").querySelectorAll(".modifier-card-overlay") overlays.forEach((i) => { let modifierName = i.parentElement.getElementsByClassName("modifier-card-label")[0].getElementsByTagName("p")[0] .dataset.fullName if (inactiveTags?.find((element) => element === modifierName) !== undefined) { i.parentElement.classList.add("modifier-toggle-inactive") } }) } function refreshTagsList(inactiveTags) { editorModifierTagsList.innerHTML = "" if (activeTags.length == 0) { editorTagsContainer.style.display = "none" return } else { editorTagsContainer.style.display = "block" } activeTags.forEach((tag, index) => { tag.element.querySelector(".modifier-card-image-overlay").innerText = "-" tag.element.classList.add("modifier-card-tiny") editorModifierTagsList.appendChild(tag.element) tag.element.addEventListener("click", () => { let idx = activeTags.findIndex((o) => { return o.name === tag.name }) if (idx !== -1) { toggleCardState(activeTags[idx].name, false) activeTags.splice(idx, 1) refreshTagsList() } document.dispatchEvent(new Event("refreshImageModifiers")) }) }) let brk = document.createElement("br") brk.style.clear = "both" editorModifierTagsList.appendChild(brk) refreshInactiveTags(inactiveTags) document.dispatchEvent(new Event("refreshImageModifiers")) // notify plugins that the image tags have been refreshed } function toggleCardState(modifierName, makeActive) { document .querySelector("#editor-modifiers") .querySelectorAll(".modifier-card") .forEach((card) => { const name = card.querySelector(".modifier-card-label").innerText if ( trimModifiers(modifierName) == trimModifiers(name) || trimModifiers(modifierName) == "by " + trimModifiers(name) ) { if (makeActive) { card.classList.add(activeCardClass) card.querySelector(".modifier-card-image-overlay").innerText = "-" } else { card.classList.remove(activeCardClass) card.querySelector(".modifier-card-image-overlay").innerText = "+" } } }) } function changePreviewImages(val) { const previewImages = document.querySelectorAll(".modifier-card-image-container img") let previewArr = [] modifiers.map((x) => x.modifiers).forEach((x) => previewArr.push(...x.map((m) => m.previews))) previewArr = previewArr.map((x) => { let obj = {} x.forEach((preview) => { obj[preview.name] = preview.path }) return obj }) previewImages.forEach((previewImage) => { const currentPreviewType = previewImage.getAttribute("preview-type") const relativePreviewPath = previewImage.src.split(modifierThumbnailPath + "/").pop() const previews = previewArr.find((preview) => relativePreviewPath == preview[currentPreviewType]) if (typeof previews == "object") { let preview = null if (val == "portrait") { preview = previews.portrait } else if (val == "landscape") { preview = previews.landscape } if (preview != null) { previewImage.src = `${modifierThumbnailPath}/${preview}` previewImage.setAttribute("preview-type", val) } } }) } function resizeModifierCards(val) { const cardSizePrefix = "modifier-card-size_" const modifierCardClass = "modifier-card" const modifierCards = document.querySelectorAll(`.${modifierCardClass}`) const cardSize = (n) => `${cardSizePrefix}${n}` modifierCards.forEach((card) => { // remove existing size classes const classes = card.className.split(" ").filter((c) => !c.startsWith(cardSizePrefix)) card.className = classes.join(" ").trim() if (val != 0) { card.classList.add(cardSize(val)) } }) } modifierCardSizeSlider.onchange = () => resizeModifierCards(modifierCardSizeSlider.value) previewImageField.onchange = () => changePreviewImages(previewImageField.value) modifierSettingsBtn.addEventListener("click", function(e) { modifierSettingsOverlay.classList.add("active") customModifiersTextBox.setSelectionRange(0, 0) customModifiersTextBox.focus() customModifiersInitialContent = customModifiersTextBox.value // preserve the initial content e.stopPropagation() }) modifierSettingsOverlay.addEventListener("keydown", function(e) { switch (e.key) { case "Escape": // Escape to cancel customModifiersTextBox.value = customModifiersInitialContent // undo the changes modifierSettingsOverlay.classList.remove("active") e.stopPropagation() break case "Enter": if (e.ctrlKey) { // Ctrl+Enter to confirm modifierSettingsOverlay.classList.remove("active") e.stopPropagation() break } } }) function saveCustomModifiers() { localStorage.setItem(CUSTOM_MODIFIERS_KEY, customModifiersTextBox.value.trim()) loadCustomModifiers() } function loadCustomModifiers() { PLUGINS["MODIFIERS_LOAD"].forEach((fn) => fn.loader.call()) } customModifiersTextBox.addEventListener("change", saveCustomModifiers)