Loading web/_js/config.js +76 −0 Original line number Diff line number Diff line Loading @@ -363,6 +363,82 @@ window.defaultPeriod = defaultPeriod const useNumericalId = true window.useNumericalId = useNumericalId const externalLinksConfig = [ { name: "Website", id: "website", generateLink: (link) => link, listingClass: "bi-globe", generateListingName: (link) => { try { const urlObject = new URL(link) return urlObject.hostname.replace(/^www./, "") } catch (e) { return "Website" } }, displayHTML: "{urlid}", placeholder: "https://example.org", configureInputField: (inputField) => { inputField.type = "url" inputField.placeholder = "https://example.com" inputField.pattern = "https?://.*" inputField.title = "Website URL using the http:// or https:// protocol" } }, { name: "Discord", id: "discord", generateLink: (link) => "https://discord.gg/" + link, generateListingName: (link) => link, listingClass: "bi-discord", editorPrefix: "discord.gg/", placeholder: "r/example", configureInputField: (inputField) => { inputField.placeholder = "pJkm23b2nA" }, extractId: (content) => { const discordPattern = /^(?:(?:https?:\/\/)?(?:www\.)?(?:(?:discord)?\.?gg|discord(?:app)?\.com\/invite)\/)?([^\s/]+?)(?=\b)$/ id = content.trim().match(discordPattern)?.[1] if (id) { return id; } return content; } }, { name: "Subreddit", id: "subreddit", generateLink: (link) => "https://reddit.com/" + link, listingClass: "bi-reddit", generateListingName: (link) => "r/" + link, editorPrefix: "reddit.com/", placeholder: "pJkm23b2nA", configureInputField: (inputField) => { inputField.placeholder = "r/example" inputField.pattern = "^r\/[A-Za-z0-9][A-Za-z0-9_]{1,50}$" inputField.title = "Subreddit in format of r/example" inputField.minLength = "4" inputField.maxLength = "50" }, extractId: (content) => { const subredditPattern = /^(?:(?:(?:(?:(?:https?:\/\/)?(?:(?:www|old|new|np)\.)?)?reddit\.com)?\/)?[rR]\/)?([A-Za-z0-9][A-Za-z0-9_]{1,20})(?:\/[^" ]*)*$/ id = content.trim().match(subredditPattern)?.[1] if (id) { return id; } return content; }, formatIdInEditor: (content) => { if (content != "") { return "r/" + content; } return ""; } } ]; console.info(`%cThe 2023 r/place Atlas %cCopyright (c) 2017 Roland Rytz <roland@draemm.li> Copyright (c) 2023 Place Atlas Initiative and contributors Loading web/_js/main/draw.js +137 −231 Original line number Diff line number Diff line Loading @@ -35,24 +35,12 @@ const exportModal = new bootstrap.Modal(exportModalElement) const nameField = document.getElementById("nameField") const descriptionField = document.getElementById("descriptionField") const websiteGroup = document.getElementById("websiteGroup") const subredditGroup = document.getElementById("subredditGroup") const discordGroup = document.getElementById("discordGroup") const wikiGroup = document.getElementById("wikiGroup") const exportArea = document.getElementById("exportString") const subredditPattern = /^(?:(?:(?:(?:(?:https?:\/\/)?(?:(?:www|old|new|np)\.)?)?reddit\.com)?\/)?[rR]\/)?([A-Za-z0-9][A-Za-z0-9_]{1,20})(?:\/[^" ]*)*$/ const discordPattern = /^(?:(?:https?:\/\/)?(?:www\.)?(?:(?:discord)?\.?gg|discord(?:app)?\.com\/invite)\/)?([^\s/]+?)(?=\b)$/ let entry let path = [] let center = [canvasCenter.x, canvasCenter.y] let websiteGroupElements = [] let subredditGroupElements = [] let discordGroupElements = [] let wikiGroupElements = [] let pathWithPeriods = [] let periodGroupElements = [] Loading Loading @@ -89,6 +77,15 @@ baseInputField.type = "text" }) }) let linkEditButtonListener = new WeakMap(); // store the "click" listener of button for later unmapping let externalLinksConfigIndexById = {}; let linkGroupElements = {}; for (let i in externalLinksConfig) { externalLinksConfigIndexById[externalLinksConfig[i].id] = i; linkGroupElements[externalLinksConfig[i].id] = [] } // https://gist.github.com/codeguy/6684588?permalink_comment_id=3243980#gistcomment-3243980 function slugify(text) { return text Loading Loading @@ -310,15 +307,19 @@ function initDraw() { exportObject.center[key] = calculateCenter(value).map(int => int - 0.5) }) const inputWebsite = websiteGroupElements.map(element => element.value.trim()).filter(element => element) const inputSubreddit = subredditGroupElements.map(element => element.value.trim().match(subredditPattern)?.[1]).filter(element => element) const inputDiscord = discordGroupElements.map(element => element.value.trim().match(discordPattern)?.[1]).filter(element => element) const inputWiki = wikiGroupElements.map(element => element.value.trim().replace(/ /g, '_')).filter(element => element) for (const linkConfig of externalLinksConfig) { const groupsElement = linkGroupElements[linkConfig.id]; if (inputWebsite.length) exportObject.links.website = inputWebsite if (inputSubreddit.length) exportObject.links.subreddit = inputSubreddit if (inputDiscord.length) exportObject.links.discord = inputDiscord if (inputWiki.length) exportObject.links.wiki = inputWiki let linksToExport = []; const exportArray = groupsElement .map(element => element.value.trim()) .map(element => linkConfig.extractId ? linkConfig.extractId(element) : element) .filter(element => element); if (exportArray.length) { exportObject.links[linkConfig.id] = exportArray } } return exportObject } Loading Loading @@ -449,26 +450,14 @@ function initDraw() { nameField.value = "" descriptionField.value = "" // Clears input array websiteGroupElements = [] subredditGroupElements = [] discordGroupElements = [] wikiGroupElements = [] // Rebuilds multi-input list websiteGroup.replaceChildren() subredditGroup.replaceChildren() discordGroup.replaceChildren() /** * @instanceonly * Temporarily remove wikifield * Lack of use, used incorrectly more than it is used correctly. */ // wikiGroup.replaceChildren() addWebsiteFields("", 0, [0]) addSubredditFields("", 0, [0]) addDiscordFields("", 0, [0]) addWikiFields("", 0, [0]) for (const linkConfig of externalLinksConfig) { document.getElementById(linkConfig.id + "Group").replaceChildren(); } for (const linkConfig of externalLinksConfig) { clearLinkGroup(linkConfig.id); } // Resets periods pathWithPeriods = [] Loading Loading @@ -574,225 +563,143 @@ function initDraw() { return atlasAll[id] } function addFieldButton(inputButton, inputGroup, array, index, name) { if (inputButton.title === "Remove " + name) { removeFieldButton(inputGroup, array, index) return } inputButton.className = "btn btn-outline-secondary" inputButton.title = "Remove " + name inputButton.innerHTML = '<i class="bi bi-trash-fill" aria-hidden="true"></i>' if (name === "website") { addWebsiteFields(null, array.length, array) } else if (name === "subreddit") { addSubredditFields(null, array.length, array) } else if (name === "Discord invite") { addDiscordFields(null, array.length, array) } else if (name === "wiki page") { addWikiFields(null, array.length, array) } function getLinkGroup(linkTypeId) { return document.getElementById(linkTypeId + "Group"); } function removeFieldButton(inputGroup, array, index) { delete array[index] inputGroup.remove() function getLinkConfig(linkTypeId) { return externalLinksConfig[externalLinksConfigIndexById[linkTypeId]] } function addWebsiteFields(link, index, array) { const inputGroup = baseInputGroup.cloneNode() websiteGroup.appendChild(inputGroup) const inputField = baseInputField.cloneNode() inputField.type = "url" inputField.id = "websiteField" + index inputField.placeholder = "https://example.com" inputField.pattern = "https?://.*" inputField.title = "Website URL using the http:// or https:// protocol" inputField.setAttribute("aria-labelledby", "websiteLabel") inputField.value = link inputGroup.appendChild(inputField) websiteGroupElements.push(inputField) const inputButton = document.createElement("button") inputButton.type = "button" // If button is the last in the array give it the add button if (array.length === index + 1) { inputButton.className = "btn btn-secondary" inputButton.title = "Add website" inputButton.innerHTML = '<i class="bi bi-plus-lg" aria-hidden="true"></i>' inputButton.addEventListener('click', () => addFieldButton(inputButton, inputGroup, websiteGroupElements, index, "website")) } else { inputButton.className = "btn btn-outline-secondary" inputButton.title = "Remove website" inputButton.innerHTML = '<i class="bi bi-trash-fill" aria-hidden="true"></i>' inputButton.addEventListener('click', () => removeFieldButton(inputGroup, websiteGroupElements, index)) } inputGroup.appendChild(inputButton) /** * Clear the link group of the provided tag in the editor, ensuring there only remain a single empty field * @param {String} linkTypeId */ function clearLinkGroup(linkTypeId) { getLinkGroup(linkTypeId).replaceChildren() linkGroupElements[linkTypeId] = [] addFieldGeneric("", linkTypeId); } function addSubredditFields(link, index, array) { const inputGroup = baseInputGroup.cloneNode() subredditGroup.appendChild(inputGroup) function addFieldGeneric(link, linkTypeId) { const inputGroup = baseInputGroup.cloneNode(); const linkConfig = getLinkConfig(linkTypeId) let linkGroup = getLinkGroup(linkTypeId) linkGroup.appendChild(inputGroup); let addonId = null; if (linkConfig.editorPrefix) { const inputAddon = baseInputAddon.cloneNode() inputAddon.id = "subredditField" + index + "-addon" inputAddon.textContent = "reddit.com/" inputAddon.textContent = linkConfig.editorPrefix addonId = linkTypeId + "Field" + linkGroupElements.length + "-addon" inputAddon.id = addonId inputGroup.appendChild(inputAddon) } const inputField = baseInputField.cloneNode() inputField.id = "subredditField" + index inputField.placeholder = "r/example" inputField.pattern = "^r\/[A-Za-z0-9][A-Za-z0-9_]{1,50}$" inputField.title = "Subreddit in format of r/example" inputField.minLength = "4" inputField.maxLength = "50" inputField.setAttribute("aria-labelledby", "subredditLabel") inputField.setAttribute("aria-describedby", "subredditField" + index + "-addon") if (link) { inputField.value = "r/" + link const inputField = baseInputField.cloneNode(); inputField.id = linkTypeId + "Field" + linkGroupElements.length if (linkConfig.formatIdInEditor) { inputField.value = linkConfig.formatIdInEditor(link) } else { inputField.value = "" inputField.value = link } inputGroup.appendChild(inputField) subredditGroupElements.push(inputField) const inputButton = document.createElement("button") inputButton.type = "button" // If button is the last in the array give it the add button if (array.length === index + 1) { inputButton.className = "btn btn-secondary" inputButton.title = "Add subreddit" inputButton.innerHTML = '<i class="bi bi-plus-lg" aria-hidden="true"></i>' inputButton.addEventListener('click', () => addFieldButton(inputButton, inputGroup, subredditGroupElements, index, "subreddit")) } else { inputButton.className = "btn btn-outline-secondary" inputButton.title = "Remove subreddit" inputButton.innerHTML = '<i class="bi bi-trash-fill" aria-hidden="true"></i>' inputButton.addEventListener('click', () => removeFieldButton(inputGroup, subredditGroupElements, index)) inputField.setAttribute("aria-labelledby", linkTypeId + "Label") if (addonId) { inputField.setAttribute("aria-describedby", addonId) } if (linkConfig.extractId) { inputField.addEventListener('paste', event => { let paste = (event.clipboardData || window.clipboardData).getData('text') paste = paste.trim().match(subredditPattern)?.[1] if (paste) { event.target.value = "r/" + paste let valueId = linkConfig.extractId(paste); if (valueId) { if (linkConfig.formatIdInEditor) { event.target.value = linkConfig.formatIdInEditor(valueId) } else { event.target.value = valueId } event.preventDefault() } }) inputGroup.appendChild(inputButton) } function addDiscordFields(link, index, array) { const inputGroup = baseInputGroup.cloneNode() discordGroup.appendChild(inputGroup) const inputAddon = baseInputAddon.cloneNode() inputAddon.id = "discordField" + index + "-addon" inputAddon.textContent = "discord.gg/" inputGroup.appendChild(inputAddon) const inputField = baseInputField.cloneNode() inputField.id = "discordField" + index inputField.placeholder = "pJkm23b2nA" inputField.setAttribute("aria-labelledby", "discordLabel") inputField.setAttribute("aria-describedby", "discordField" + index + "-addon") inputField.value = link inputGroup.appendChild(inputField) discordGroupElements.push(inputField) linkConfig.configureInputField(inputField) inputGroup.appendChild(inputField); linkGroupElements[linkTypeId].push(inputField); const inputButton = document.createElement("button") inputButton.type = "button" // If button is the last in the array give it the add button if (array.length === index + 1) { inputButton.className = "btn btn-secondary" inputButton.title = "Add Discord invite" inputButton.innerHTML = '<i class="bi bi-plus-lg" aria-hidden="true"></i>' inputButton.addEventListener('click', () => addFieldButton(inputButton, inputGroup, discordGroupElements, index, "Discord invite")) } else { inputButton.className = "btn btn-outline-secondary" inputButton.title = "Remove Discord invite" inputButton.innerHTML = '<i class="bi bi-trash-fill" aria-hidden="true"></i>' inputButton.addEventListener('click', () => removeFieldButton(inputGroup, discordGroupElements, index)) } inputGroup.appendChild(inputButton) inputField.addEventListener('paste', event => { let paste = (event.clipboardData || window.clipboardData).getData('text') paste = paste.trim().match(discordPattern)?.[1] if (paste) { event.target.value = paste event.preventDefault() refreshLinkGroupButtons(linkTypeId) } }) inputGroup.appendChild(inputButton) function refreshLinkGroupButtons(linkTypeId) { const linkGroup = getLinkGroup(linkTypeId); const linkConfig = getLinkConfig(linkTypeId) const children = linkGroup.children; for (let i = 0; i < children.length; i++) { const button = children[i].getElementsByTagName("button")[0]; if (linkEditButtonListener.get(button) != undefined) { button.removeEventListener('click', linkEditButtonListener.get(button)) } let listener; if (children.length == i + 1) { button.className = "btn btn-secondary"; button.title = "Add " + linkConfig.name button.innerHTML = '<i class="bi bi-plus-lg" aria-hidden="true"></i>' function addWikiFields(link, index, array) { const inputGroup = baseInputGroup.cloneNode() // wikiGroup.appendChild(inputGroup) const inputField = baseInputField.cloneNode() inputField.id = "wikiField" + index inputField.placeholder = "Page title" inputField.setAttribute("aria-labelledby", "wikiLabel") inputField.value = link inputGroup.appendChild(inputField) wikiGroupElements.push(inputField) const inputButton = document.createElement("button") inputButton.type = "button" // If button is the last in the array give it the add button if (array.length === index + 1) { inputButton.className = "btn btn-secondary" inputButton.title = "Add wiki page" inputButton.innerHTML = '<i class="bi bi-plus-lg" aria-hidden="true"></i>' inputButton.addEventListener('click', () => addFieldButton(inputButton, inputGroup, wikiGroupElements, index, "wiki page")) listener = () => addFieldGeneric("", linkTypeId); } else { inputButton.className = "btn btn-outline-secondary" inputButton.title = "Remove wiki page" inputButton.innerHTML = '<i class="bi bi-trash-fill" aria-hidden="true"></i>' inputButton.addEventListener('click', () => removeFieldButton(inputGroup, wikiGroupElements, index)) button.className = "btn btn-outline-secondary" button.title = "Remove " + linkConfig.name button.innerHTML = '<i class="bi bi-trash-fill" aria-hidden="true"></i>' const childToRemove = children[i]; listener = () => { linkGroupElements[linkTypeId].splice(i, 1); linkGroup.removeChild(childToRemove); refreshLinkGroupButtons(linkTypeId); } } linkEditButtonListener.set(button, listener) button.addEventListener('click', listener) } inputGroup.appendChild(inputButton) } const params = new URLSearchParams(document.location.search) const entryId = params.get('id') entry = getEntry(entryId) const linkContainer = document.getElementById("linkContainer"); linkContainer.replaceChildren(); for (const linkConfig of externalLinksConfig) { linkLabel = document.createElement("label"); linkLabel.id = linkConfig.id + "Label"; linkLabel.className = "form-label"; linkLabel.textContent = linkConfig.name; linkContainer.appendChild(linkLabel); linkGroup = document.createElement("div"); linkGroup.id = linkConfig.id + "Group"; linkGroup.className = "mb-3 d-flex flex-column gap-2"; linkContainer.appendChild(linkGroup); } if (entry) { nameField.value = entry.name descriptionField.value = entry.description if (entry.links.website.length) { entry.links.website.forEach((link, index, array) => { addWebsiteFields(link, index, array) }) } else { addWebsiteFields("", -1, entry.links.website) } if (entry.links.subreddit.length) { entry.links.subreddit.forEach((link, index, array) => { addSubredditFields(link, index, array) }) } else { addSubredditFields("", -1, entry.links.subreddit) for (const linkConfig of externalLinksConfig) { if (entry.links[linkConfig.id].length) { for (const link of entry.links[linkConfig.id]) { addFieldGeneric(link, linkConfig.id) } if (entry.links.discord.length) { entry.links.discord.forEach((link, index, array) => { addDiscordFields(link, index, array) }) } else { addDiscordFields("", -1, entry.links.discord) clearLinkGroup(linkConfig.id) } if (entry.links.wiki.length) { entry.links.wiki.forEach((link, index, array) => { addWikiFields(link, index, array) }) } else { addWikiFields("", -1, entry.links.wiki) } redoButton.disabled = true undoButton.disabled = false Loading @@ -808,10 +715,9 @@ function initDraw() { pathWithPeriods.push([formatPeriod(currentPeriod, null, currentVariation), []]) // Builds multi-input list addWebsiteFields("", 0, [0]) addSubredditFields("", 0, [0]) addDiscordFields("", 0, [0]) addWikiFields("", 0, [0]) for (const linkConfig of externalLinksConfig) { clearLinkGroup(linkConfig.id); } } initPeriodGroups() Loading web/_js/main/infoblock.js +21 −55 Original line number Diff line number Diff line Loading @@ -115,62 +115,28 @@ function createInfoBlock(entry, mode = 0) { } } if (entry.links?.subreddit?.length) { const subredditGroupElement = baseGroupElement.cloneNode() linkListElement.appendChild(subredditGroupElement) entry.links.subreddit.forEach(subreddit => { if (!subreddit) return subreddit = "r/" + subreddit const subredditLinkElement = baseLinkElement.cloneNode() subredditLinkElement.href = "https://reddit.com/" + subreddit subredditLinkElement.innerHTML = `<i class="bi bi-reddit" aria-hidden="true"></i> ${subreddit}` subredditGroupElement.appendChild(subredditLinkElement) }) } if (entry.links?.website?.length) { const websiteGroupElement = baseGroupElement.cloneNode() linkListElement.appendChild(websiteGroupElement) for (const linkConfig of externalLinksConfig) { if (entry.links) { if (entry.links[linkConfig.id]?.length) { const groupElement = baseGroupElement.cloneNode() linkListElement.appendChild(groupElement) entry.links.website.forEach(link => { entry.links[linkConfig.id].forEach(link => { if (!link) return const websiteLinkElement = baseLinkElement.cloneNode() websiteLinkElement.href = link try { const urlObject = new URL(link) websiteLinkElement.innerHTML = `<i class="bi bi-globe" aria-hidden="true"></i> ${urlObject.hostname.replace(/^www./, "")}` } catch (e) { websiteLinkElement.innerHTML = `<i class="bi bi-globe" aria-hidden="true"></i> Website` } websiteGroupElement.appendChild(websiteLinkElement) }) } const linkElement = baseLinkElement.cloneNode() linkElement.href = linkConfig.generateLink(link) if (entry.links?.discord?.length) { const discordGroupElement = baseGroupElement.cloneNode() linkListElement.appendChild(discordGroupElement) let logoChild = document.createElement("i"); logoChild.classList.add("bi"); logoChild.classList.add(linkConfig.listingClass); logoChild.setAttribute("aria-hidden", "true"); linkElement.appendChild(logoChild); entry.links.discord.forEach(link => { if (!link) return const discordLinkElement = baseLinkElement.cloneNode() discordLinkElement.href = "https://discord.gg/" + link discordLinkElement.innerHTML = `<i class="bi bi-discord" aria-hidden="true"></i> ${link}` discordGroupElement.appendChild(discordLinkElement) linkElement.append(" " + linkConfig.generateListingName(link)) groupElement.appendChild(linkElement) }) } if (entry.links?.wiki?.length) { const wikiGroupElement = baseGroupElement.cloneNode() linkListElement.appendChild(wikiGroupElement) entry.links.wiki.forEach(link => { if (!link) return const wikiLinkElement = baseLinkElement.cloneNode() wikiLinkElement.href = "https://place-wiki.stefanocoding.me/wiki/" + link.replace(/ /g, '_') wikiLinkElement.innerHTML = `<i class="bi bi-book" aria-hidden="true"></i> r/place Wiki Article` wikiGroupElement.appendChild(wikiLinkElement) }) } } // Adds id footer Loading web/_js/main/main.js +0 −4 Original line number Diff line number Diff line Loading @@ -509,10 +509,6 @@ function generateAtlasAll(atlas = atlasAll) { entry._index = index const currentLinks = entry.links entry.links = { website: [], subreddit: [], discord: [], wiki: [], ...currentLinks } const currentPath = entry.path Loading web/atlas.json +22 −16 Original line number Diff line number Diff line [ { "id": -1, "name": "i", "description": "test", "links": {}, "name": "a", "description": "b", "links": { "website": [ "https://example.org" ], "discord": [ "123214" ], "subreddit": [ "example" ] }, "path": { "0": [ [ 406, 263 ], [ 501, 288 331, 298 ], [ 383, 326 382, 413 ], [ 356, 243 518, 247 ] ] }, "center": { "0": [ 401, 291 394, 328 ] } }] No newline at end of file Loading
web/_js/config.js +76 −0 Original line number Diff line number Diff line Loading @@ -363,6 +363,82 @@ window.defaultPeriod = defaultPeriod const useNumericalId = true window.useNumericalId = useNumericalId const externalLinksConfig = [ { name: "Website", id: "website", generateLink: (link) => link, listingClass: "bi-globe", generateListingName: (link) => { try { const urlObject = new URL(link) return urlObject.hostname.replace(/^www./, "") } catch (e) { return "Website" } }, displayHTML: "{urlid}", placeholder: "https://example.org", configureInputField: (inputField) => { inputField.type = "url" inputField.placeholder = "https://example.com" inputField.pattern = "https?://.*" inputField.title = "Website URL using the http:// or https:// protocol" } }, { name: "Discord", id: "discord", generateLink: (link) => "https://discord.gg/" + link, generateListingName: (link) => link, listingClass: "bi-discord", editorPrefix: "discord.gg/", placeholder: "r/example", configureInputField: (inputField) => { inputField.placeholder = "pJkm23b2nA" }, extractId: (content) => { const discordPattern = /^(?:(?:https?:\/\/)?(?:www\.)?(?:(?:discord)?\.?gg|discord(?:app)?\.com\/invite)\/)?([^\s/]+?)(?=\b)$/ id = content.trim().match(discordPattern)?.[1] if (id) { return id; } return content; } }, { name: "Subreddit", id: "subreddit", generateLink: (link) => "https://reddit.com/" + link, listingClass: "bi-reddit", generateListingName: (link) => "r/" + link, editorPrefix: "reddit.com/", placeholder: "pJkm23b2nA", configureInputField: (inputField) => { inputField.placeholder = "r/example" inputField.pattern = "^r\/[A-Za-z0-9][A-Za-z0-9_]{1,50}$" inputField.title = "Subreddit in format of r/example" inputField.minLength = "4" inputField.maxLength = "50" }, extractId: (content) => { const subredditPattern = /^(?:(?:(?:(?:(?:https?:\/\/)?(?:(?:www|old|new|np)\.)?)?reddit\.com)?\/)?[rR]\/)?([A-Za-z0-9][A-Za-z0-9_]{1,20})(?:\/[^" ]*)*$/ id = content.trim().match(subredditPattern)?.[1] if (id) { return id; } return content; }, formatIdInEditor: (content) => { if (content != "") { return "r/" + content; } return ""; } } ]; console.info(`%cThe 2023 r/place Atlas %cCopyright (c) 2017 Roland Rytz <roland@draemm.li> Copyright (c) 2023 Place Atlas Initiative and contributors Loading
web/_js/main/draw.js +137 −231 Original line number Diff line number Diff line Loading @@ -35,24 +35,12 @@ const exportModal = new bootstrap.Modal(exportModalElement) const nameField = document.getElementById("nameField") const descriptionField = document.getElementById("descriptionField") const websiteGroup = document.getElementById("websiteGroup") const subredditGroup = document.getElementById("subredditGroup") const discordGroup = document.getElementById("discordGroup") const wikiGroup = document.getElementById("wikiGroup") const exportArea = document.getElementById("exportString") const subredditPattern = /^(?:(?:(?:(?:(?:https?:\/\/)?(?:(?:www|old|new|np)\.)?)?reddit\.com)?\/)?[rR]\/)?([A-Za-z0-9][A-Za-z0-9_]{1,20})(?:\/[^" ]*)*$/ const discordPattern = /^(?:(?:https?:\/\/)?(?:www\.)?(?:(?:discord)?\.?gg|discord(?:app)?\.com\/invite)\/)?([^\s/]+?)(?=\b)$/ let entry let path = [] let center = [canvasCenter.x, canvasCenter.y] let websiteGroupElements = [] let subredditGroupElements = [] let discordGroupElements = [] let wikiGroupElements = [] let pathWithPeriods = [] let periodGroupElements = [] Loading Loading @@ -89,6 +77,15 @@ baseInputField.type = "text" }) }) let linkEditButtonListener = new WeakMap(); // store the "click" listener of button for later unmapping let externalLinksConfigIndexById = {}; let linkGroupElements = {}; for (let i in externalLinksConfig) { externalLinksConfigIndexById[externalLinksConfig[i].id] = i; linkGroupElements[externalLinksConfig[i].id] = [] } // https://gist.github.com/codeguy/6684588?permalink_comment_id=3243980#gistcomment-3243980 function slugify(text) { return text Loading Loading @@ -310,15 +307,19 @@ function initDraw() { exportObject.center[key] = calculateCenter(value).map(int => int - 0.5) }) const inputWebsite = websiteGroupElements.map(element => element.value.trim()).filter(element => element) const inputSubreddit = subredditGroupElements.map(element => element.value.trim().match(subredditPattern)?.[1]).filter(element => element) const inputDiscord = discordGroupElements.map(element => element.value.trim().match(discordPattern)?.[1]).filter(element => element) const inputWiki = wikiGroupElements.map(element => element.value.trim().replace(/ /g, '_')).filter(element => element) for (const linkConfig of externalLinksConfig) { const groupsElement = linkGroupElements[linkConfig.id]; if (inputWebsite.length) exportObject.links.website = inputWebsite if (inputSubreddit.length) exportObject.links.subreddit = inputSubreddit if (inputDiscord.length) exportObject.links.discord = inputDiscord if (inputWiki.length) exportObject.links.wiki = inputWiki let linksToExport = []; const exportArray = groupsElement .map(element => element.value.trim()) .map(element => linkConfig.extractId ? linkConfig.extractId(element) : element) .filter(element => element); if (exportArray.length) { exportObject.links[linkConfig.id] = exportArray } } return exportObject } Loading Loading @@ -449,26 +450,14 @@ function initDraw() { nameField.value = "" descriptionField.value = "" // Clears input array websiteGroupElements = [] subredditGroupElements = [] discordGroupElements = [] wikiGroupElements = [] // Rebuilds multi-input list websiteGroup.replaceChildren() subredditGroup.replaceChildren() discordGroup.replaceChildren() /** * @instanceonly * Temporarily remove wikifield * Lack of use, used incorrectly more than it is used correctly. */ // wikiGroup.replaceChildren() addWebsiteFields("", 0, [0]) addSubredditFields("", 0, [0]) addDiscordFields("", 0, [0]) addWikiFields("", 0, [0]) for (const linkConfig of externalLinksConfig) { document.getElementById(linkConfig.id + "Group").replaceChildren(); } for (const linkConfig of externalLinksConfig) { clearLinkGroup(linkConfig.id); } // Resets periods pathWithPeriods = [] Loading Loading @@ -574,225 +563,143 @@ function initDraw() { return atlasAll[id] } function addFieldButton(inputButton, inputGroup, array, index, name) { if (inputButton.title === "Remove " + name) { removeFieldButton(inputGroup, array, index) return } inputButton.className = "btn btn-outline-secondary" inputButton.title = "Remove " + name inputButton.innerHTML = '<i class="bi bi-trash-fill" aria-hidden="true"></i>' if (name === "website") { addWebsiteFields(null, array.length, array) } else if (name === "subreddit") { addSubredditFields(null, array.length, array) } else if (name === "Discord invite") { addDiscordFields(null, array.length, array) } else if (name === "wiki page") { addWikiFields(null, array.length, array) } function getLinkGroup(linkTypeId) { return document.getElementById(linkTypeId + "Group"); } function removeFieldButton(inputGroup, array, index) { delete array[index] inputGroup.remove() function getLinkConfig(linkTypeId) { return externalLinksConfig[externalLinksConfigIndexById[linkTypeId]] } function addWebsiteFields(link, index, array) { const inputGroup = baseInputGroup.cloneNode() websiteGroup.appendChild(inputGroup) const inputField = baseInputField.cloneNode() inputField.type = "url" inputField.id = "websiteField" + index inputField.placeholder = "https://example.com" inputField.pattern = "https?://.*" inputField.title = "Website URL using the http:// or https:// protocol" inputField.setAttribute("aria-labelledby", "websiteLabel") inputField.value = link inputGroup.appendChild(inputField) websiteGroupElements.push(inputField) const inputButton = document.createElement("button") inputButton.type = "button" // If button is the last in the array give it the add button if (array.length === index + 1) { inputButton.className = "btn btn-secondary" inputButton.title = "Add website" inputButton.innerHTML = '<i class="bi bi-plus-lg" aria-hidden="true"></i>' inputButton.addEventListener('click', () => addFieldButton(inputButton, inputGroup, websiteGroupElements, index, "website")) } else { inputButton.className = "btn btn-outline-secondary" inputButton.title = "Remove website" inputButton.innerHTML = '<i class="bi bi-trash-fill" aria-hidden="true"></i>' inputButton.addEventListener('click', () => removeFieldButton(inputGroup, websiteGroupElements, index)) } inputGroup.appendChild(inputButton) /** * Clear the link group of the provided tag in the editor, ensuring there only remain a single empty field * @param {String} linkTypeId */ function clearLinkGroup(linkTypeId) { getLinkGroup(linkTypeId).replaceChildren() linkGroupElements[linkTypeId] = [] addFieldGeneric("", linkTypeId); } function addSubredditFields(link, index, array) { const inputGroup = baseInputGroup.cloneNode() subredditGroup.appendChild(inputGroup) function addFieldGeneric(link, linkTypeId) { const inputGroup = baseInputGroup.cloneNode(); const linkConfig = getLinkConfig(linkTypeId) let linkGroup = getLinkGroup(linkTypeId) linkGroup.appendChild(inputGroup); let addonId = null; if (linkConfig.editorPrefix) { const inputAddon = baseInputAddon.cloneNode() inputAddon.id = "subredditField" + index + "-addon" inputAddon.textContent = "reddit.com/" inputAddon.textContent = linkConfig.editorPrefix addonId = linkTypeId + "Field" + linkGroupElements.length + "-addon" inputAddon.id = addonId inputGroup.appendChild(inputAddon) } const inputField = baseInputField.cloneNode() inputField.id = "subredditField" + index inputField.placeholder = "r/example" inputField.pattern = "^r\/[A-Za-z0-9][A-Za-z0-9_]{1,50}$" inputField.title = "Subreddit in format of r/example" inputField.minLength = "4" inputField.maxLength = "50" inputField.setAttribute("aria-labelledby", "subredditLabel") inputField.setAttribute("aria-describedby", "subredditField" + index + "-addon") if (link) { inputField.value = "r/" + link const inputField = baseInputField.cloneNode(); inputField.id = linkTypeId + "Field" + linkGroupElements.length if (linkConfig.formatIdInEditor) { inputField.value = linkConfig.formatIdInEditor(link) } else { inputField.value = "" inputField.value = link } inputGroup.appendChild(inputField) subredditGroupElements.push(inputField) const inputButton = document.createElement("button") inputButton.type = "button" // If button is the last in the array give it the add button if (array.length === index + 1) { inputButton.className = "btn btn-secondary" inputButton.title = "Add subreddit" inputButton.innerHTML = '<i class="bi bi-plus-lg" aria-hidden="true"></i>' inputButton.addEventListener('click', () => addFieldButton(inputButton, inputGroup, subredditGroupElements, index, "subreddit")) } else { inputButton.className = "btn btn-outline-secondary" inputButton.title = "Remove subreddit" inputButton.innerHTML = '<i class="bi bi-trash-fill" aria-hidden="true"></i>' inputButton.addEventListener('click', () => removeFieldButton(inputGroup, subredditGroupElements, index)) inputField.setAttribute("aria-labelledby", linkTypeId + "Label") if (addonId) { inputField.setAttribute("aria-describedby", addonId) } if (linkConfig.extractId) { inputField.addEventListener('paste', event => { let paste = (event.clipboardData || window.clipboardData).getData('text') paste = paste.trim().match(subredditPattern)?.[1] if (paste) { event.target.value = "r/" + paste let valueId = linkConfig.extractId(paste); if (valueId) { if (linkConfig.formatIdInEditor) { event.target.value = linkConfig.formatIdInEditor(valueId) } else { event.target.value = valueId } event.preventDefault() } }) inputGroup.appendChild(inputButton) } function addDiscordFields(link, index, array) { const inputGroup = baseInputGroup.cloneNode() discordGroup.appendChild(inputGroup) const inputAddon = baseInputAddon.cloneNode() inputAddon.id = "discordField" + index + "-addon" inputAddon.textContent = "discord.gg/" inputGroup.appendChild(inputAddon) const inputField = baseInputField.cloneNode() inputField.id = "discordField" + index inputField.placeholder = "pJkm23b2nA" inputField.setAttribute("aria-labelledby", "discordLabel") inputField.setAttribute("aria-describedby", "discordField" + index + "-addon") inputField.value = link inputGroup.appendChild(inputField) discordGroupElements.push(inputField) linkConfig.configureInputField(inputField) inputGroup.appendChild(inputField); linkGroupElements[linkTypeId].push(inputField); const inputButton = document.createElement("button") inputButton.type = "button" // If button is the last in the array give it the add button if (array.length === index + 1) { inputButton.className = "btn btn-secondary" inputButton.title = "Add Discord invite" inputButton.innerHTML = '<i class="bi bi-plus-lg" aria-hidden="true"></i>' inputButton.addEventListener('click', () => addFieldButton(inputButton, inputGroup, discordGroupElements, index, "Discord invite")) } else { inputButton.className = "btn btn-outline-secondary" inputButton.title = "Remove Discord invite" inputButton.innerHTML = '<i class="bi bi-trash-fill" aria-hidden="true"></i>' inputButton.addEventListener('click', () => removeFieldButton(inputGroup, discordGroupElements, index)) } inputGroup.appendChild(inputButton) inputField.addEventListener('paste', event => { let paste = (event.clipboardData || window.clipboardData).getData('text') paste = paste.trim().match(discordPattern)?.[1] if (paste) { event.target.value = paste event.preventDefault() refreshLinkGroupButtons(linkTypeId) } }) inputGroup.appendChild(inputButton) function refreshLinkGroupButtons(linkTypeId) { const linkGroup = getLinkGroup(linkTypeId); const linkConfig = getLinkConfig(linkTypeId) const children = linkGroup.children; for (let i = 0; i < children.length; i++) { const button = children[i].getElementsByTagName("button")[0]; if (linkEditButtonListener.get(button) != undefined) { button.removeEventListener('click', linkEditButtonListener.get(button)) } let listener; if (children.length == i + 1) { button.className = "btn btn-secondary"; button.title = "Add " + linkConfig.name button.innerHTML = '<i class="bi bi-plus-lg" aria-hidden="true"></i>' function addWikiFields(link, index, array) { const inputGroup = baseInputGroup.cloneNode() // wikiGroup.appendChild(inputGroup) const inputField = baseInputField.cloneNode() inputField.id = "wikiField" + index inputField.placeholder = "Page title" inputField.setAttribute("aria-labelledby", "wikiLabel") inputField.value = link inputGroup.appendChild(inputField) wikiGroupElements.push(inputField) const inputButton = document.createElement("button") inputButton.type = "button" // If button is the last in the array give it the add button if (array.length === index + 1) { inputButton.className = "btn btn-secondary" inputButton.title = "Add wiki page" inputButton.innerHTML = '<i class="bi bi-plus-lg" aria-hidden="true"></i>' inputButton.addEventListener('click', () => addFieldButton(inputButton, inputGroup, wikiGroupElements, index, "wiki page")) listener = () => addFieldGeneric("", linkTypeId); } else { inputButton.className = "btn btn-outline-secondary" inputButton.title = "Remove wiki page" inputButton.innerHTML = '<i class="bi bi-trash-fill" aria-hidden="true"></i>' inputButton.addEventListener('click', () => removeFieldButton(inputGroup, wikiGroupElements, index)) button.className = "btn btn-outline-secondary" button.title = "Remove " + linkConfig.name button.innerHTML = '<i class="bi bi-trash-fill" aria-hidden="true"></i>' const childToRemove = children[i]; listener = () => { linkGroupElements[linkTypeId].splice(i, 1); linkGroup.removeChild(childToRemove); refreshLinkGroupButtons(linkTypeId); } } linkEditButtonListener.set(button, listener) button.addEventListener('click', listener) } inputGroup.appendChild(inputButton) } const params = new URLSearchParams(document.location.search) const entryId = params.get('id') entry = getEntry(entryId) const linkContainer = document.getElementById("linkContainer"); linkContainer.replaceChildren(); for (const linkConfig of externalLinksConfig) { linkLabel = document.createElement("label"); linkLabel.id = linkConfig.id + "Label"; linkLabel.className = "form-label"; linkLabel.textContent = linkConfig.name; linkContainer.appendChild(linkLabel); linkGroup = document.createElement("div"); linkGroup.id = linkConfig.id + "Group"; linkGroup.className = "mb-3 d-flex flex-column gap-2"; linkContainer.appendChild(linkGroup); } if (entry) { nameField.value = entry.name descriptionField.value = entry.description if (entry.links.website.length) { entry.links.website.forEach((link, index, array) => { addWebsiteFields(link, index, array) }) } else { addWebsiteFields("", -1, entry.links.website) } if (entry.links.subreddit.length) { entry.links.subreddit.forEach((link, index, array) => { addSubredditFields(link, index, array) }) } else { addSubredditFields("", -1, entry.links.subreddit) for (const linkConfig of externalLinksConfig) { if (entry.links[linkConfig.id].length) { for (const link of entry.links[linkConfig.id]) { addFieldGeneric(link, linkConfig.id) } if (entry.links.discord.length) { entry.links.discord.forEach((link, index, array) => { addDiscordFields(link, index, array) }) } else { addDiscordFields("", -1, entry.links.discord) clearLinkGroup(linkConfig.id) } if (entry.links.wiki.length) { entry.links.wiki.forEach((link, index, array) => { addWikiFields(link, index, array) }) } else { addWikiFields("", -1, entry.links.wiki) } redoButton.disabled = true undoButton.disabled = false Loading @@ -808,10 +715,9 @@ function initDraw() { pathWithPeriods.push([formatPeriod(currentPeriod, null, currentVariation), []]) // Builds multi-input list addWebsiteFields("", 0, [0]) addSubredditFields("", 0, [0]) addDiscordFields("", 0, [0]) addWikiFields("", 0, [0]) for (const linkConfig of externalLinksConfig) { clearLinkGroup(linkConfig.id); } } initPeriodGroups() Loading
web/_js/main/infoblock.js +21 −55 Original line number Diff line number Diff line Loading @@ -115,62 +115,28 @@ function createInfoBlock(entry, mode = 0) { } } if (entry.links?.subreddit?.length) { const subredditGroupElement = baseGroupElement.cloneNode() linkListElement.appendChild(subredditGroupElement) entry.links.subreddit.forEach(subreddit => { if (!subreddit) return subreddit = "r/" + subreddit const subredditLinkElement = baseLinkElement.cloneNode() subredditLinkElement.href = "https://reddit.com/" + subreddit subredditLinkElement.innerHTML = `<i class="bi bi-reddit" aria-hidden="true"></i> ${subreddit}` subredditGroupElement.appendChild(subredditLinkElement) }) } if (entry.links?.website?.length) { const websiteGroupElement = baseGroupElement.cloneNode() linkListElement.appendChild(websiteGroupElement) for (const linkConfig of externalLinksConfig) { if (entry.links) { if (entry.links[linkConfig.id]?.length) { const groupElement = baseGroupElement.cloneNode() linkListElement.appendChild(groupElement) entry.links.website.forEach(link => { entry.links[linkConfig.id].forEach(link => { if (!link) return const websiteLinkElement = baseLinkElement.cloneNode() websiteLinkElement.href = link try { const urlObject = new URL(link) websiteLinkElement.innerHTML = `<i class="bi bi-globe" aria-hidden="true"></i> ${urlObject.hostname.replace(/^www./, "")}` } catch (e) { websiteLinkElement.innerHTML = `<i class="bi bi-globe" aria-hidden="true"></i> Website` } websiteGroupElement.appendChild(websiteLinkElement) }) } const linkElement = baseLinkElement.cloneNode() linkElement.href = linkConfig.generateLink(link) if (entry.links?.discord?.length) { const discordGroupElement = baseGroupElement.cloneNode() linkListElement.appendChild(discordGroupElement) let logoChild = document.createElement("i"); logoChild.classList.add("bi"); logoChild.classList.add(linkConfig.listingClass); logoChild.setAttribute("aria-hidden", "true"); linkElement.appendChild(logoChild); entry.links.discord.forEach(link => { if (!link) return const discordLinkElement = baseLinkElement.cloneNode() discordLinkElement.href = "https://discord.gg/" + link discordLinkElement.innerHTML = `<i class="bi bi-discord" aria-hidden="true"></i> ${link}` discordGroupElement.appendChild(discordLinkElement) linkElement.append(" " + linkConfig.generateListingName(link)) groupElement.appendChild(linkElement) }) } if (entry.links?.wiki?.length) { const wikiGroupElement = baseGroupElement.cloneNode() linkListElement.appendChild(wikiGroupElement) entry.links.wiki.forEach(link => { if (!link) return const wikiLinkElement = baseLinkElement.cloneNode() wikiLinkElement.href = "https://place-wiki.stefanocoding.me/wiki/" + link.replace(/ /g, '_') wikiLinkElement.innerHTML = `<i class="bi bi-book" aria-hidden="true"></i> r/place Wiki Article` wikiGroupElement.appendChild(wikiLinkElement) }) } } // Adds id footer Loading
web/_js/main/main.js +0 −4 Original line number Diff line number Diff line Loading @@ -509,10 +509,6 @@ function generateAtlasAll(atlas = atlasAll) { entry._index = index const currentLinks = entry.links entry.links = { website: [], subreddit: [], discord: [], wiki: [], ...currentLinks } const currentPath = entry.path Loading
web/atlas.json +22 −16 Original line number Diff line number Diff line [ { "id": -1, "name": "i", "description": "test", "links": {}, "name": "a", "description": "b", "links": { "website": [ "https://example.org" ], "discord": [ "123214" ], "subreddit": [ "example" ] }, "path": { "0": [ [ 406, 263 ], [ 501, 288 331, 298 ], [ 383, 326 382, 413 ], [ 356, 243 518, 247 ] ] }, "center": { "0": [ 401, 291 394, 328 ] } }] No newline at end of file