Attribution is provided for groups of files that share a common prefix. Each group can have one or more authors. Each file has one source. This information is displayed when an entity is selected.tags/v0.1.0
| @@ -68,7 +68,7 @@ body.toggle-entity-name .entity-name { | |||||
| #main-area { | #main-area { | ||||
| display: flex; | display: flex; | ||||
| min-width: 100vw; | min-width: 100vw; | ||||
| height: 100%; | |||||
| height: 90vh; | |||||
| flex-direction: row; | flex-direction: row; | ||||
| } | } | ||||
| @@ -80,11 +80,14 @@ body.toggle-entity-name .entity-name { | |||||
| flex-direction: column; | flex-direction: column; | ||||
| background: #444; | background: #444; | ||||
| overflow-y: scroll; | overflow-y: scroll; | ||||
| height: 100%; | |||||
| scrollbar-color: #e1e1e1 #888; | scrollbar-color: #e1e1e1 #888; | ||||
| scrollbar-width: thin; | scrollbar-width: thin; | ||||
| } | } | ||||
| #options-attribution { | |||||
| display: none; | |||||
| } | |||||
| #options::-webkit-scrollbar { | #options::-webkit-scrollbar { | ||||
| width: 3px; | width: 3px; | ||||
| height: 2px; | height: 2px; | ||||
| @@ -352,4 +355,4 @@ body.toggle-bottom-name .bottom-name { | |||||
| a { | a { | ||||
| color: #999; | color: #999; | ||||
| } | |||||
| } | |||||
| @@ -15,6 +15,7 @@ | |||||
| <script src="presets/vehicles.js"></script> | <script src="presets/vehicles.js"></script> | ||||
| <script src="presets/cities.js"></script> | <script src="presets/cities.js"></script> | ||||
| <script src="presets/scenes.js"></script> | <script src="presets/scenes.js"></script> | ||||
| <script src="media/attribution.js"></script> | |||||
| <script src="macrovision.js"></script> | <script src="macrovision.js"></script> | ||||
| <meta name="theme-color" content="#000000" /> | <meta name="theme-color" content="#000000" /> | ||||
| <meta name="description" content="How big are they anyway?" /> | <meta name="description" content="How big are they anyway?" /> | ||||
| @@ -120,6 +121,19 @@ | |||||
| <div class="options-header">View options</div> | <div class="options-header">View options</div> | ||||
| <span id="options-view"> | <span id="options-view"> | ||||
| </span> | </span> | ||||
| <div class="options-header">Attribution</div> | |||||
| <span id="options-attribution"> | |||||
| <div class="options-label"> | |||||
| Authors | |||||
| </div> | |||||
| <span id="options-attribution-authors"> | |||||
| </span> | |||||
| <div class="options-label"> | |||||
| Source | |||||
| </div> | |||||
| <span id="options-attribution-source"> | |||||
| </span> | |||||
| </span> | |||||
| </div> | </div> | ||||
| <div id="world"> | <div id="world"> | ||||
| <div id="entities"> | <div id="entities"> | ||||
| @@ -345,6 +345,9 @@ function deselect() { | |||||
| if (selected) { | if (selected) { | ||||
| selected.classList.remove("selected"); | selected.classList.remove("selected"); | ||||
| } | } | ||||
| clearAttribution(); | |||||
| selected = null; | selected = null; | ||||
| clearViewList(); | clearViewList(); | ||||
| clearEntityOptions(); | clearEntityOptions(); | ||||
| @@ -357,6 +360,8 @@ function select(target) { | |||||
| selectedEntity = entities[target.dataset.key]; | selectedEntity = entities[target.dataset.key]; | ||||
| selected.classList.add("selected"); | selected.classList.add("selected"); | ||||
| displayAttribution(selectedEntity.views[selectedEntity.view].image.source); | |||||
| configViewList(selectedEntity, selectedEntity.view); | configViewList(selectedEntity, selectedEntity.view); | ||||
| configEntityOptions(selectedEntity, selectedEntity.view); | configEntityOptions(selectedEntity, selectedEntity.view); | ||||
| @@ -672,6 +677,47 @@ function removeAllEntities() { | |||||
| }); | }); | ||||
| } | } | ||||
| function clearAttribution() { | |||||
| document.querySelector("#options-attribution").style.display = "none"; | |||||
| } | |||||
| function displayAttribution(file) { | |||||
| document.querySelector("#options-attribution").style.display = "inline"; | |||||
| const authors = authorsOfFull(file); | |||||
| const source = sourceOf(file); | |||||
| const authorHolder = document.querySelector("#options-attribution-authors"); | |||||
| const sourceHolder = document.querySelector("#options-attribution-source"); | |||||
| if (authors === []) { | |||||
| authorHolder.innerText = "Unknown"; | |||||
| } else if (authors === undefined) { | |||||
| authorHolder.innerText = "Not yet entered"; | |||||
| } else { | |||||
| authorHolder.innerHTML = ""; | |||||
| const list = document.createElement("ul"); | |||||
| authorHolder.appendChild(list); | |||||
| authors.forEach(author => { | |||||
| const authorEntry = document.createElement("li"); | |||||
| const link = document.createElement("a"); | |||||
| link.href = author.url; | |||||
| link.innerText = author.name; | |||||
| authorEntry.appendChild(link); | |||||
| list.appendChild(authorEntry); | |||||
| }); | |||||
| } | |||||
| if (source === null) { | |||||
| sourceHolder.innerText = "No Link" | |||||
| } else if (source === undefined) { | |||||
| sourceHolder.innerText = "Not yet entered"; | |||||
| } else { | |||||
| sourceHolder.innerText = source; | |||||
| } | |||||
| } | |||||
| function removeEntity(element) { | function removeEntity(element) { | ||||
| if (selected == element) { | if (selected == element) { | ||||
| deselect(); | deselect(); | ||||
| @@ -701,6 +747,8 @@ function displayEntity(entity, view, x, y) { | |||||
| const image = entity.views[view].image; | const image = entity.views[view].image; | ||||
| img.src = image.source; | img.src = image.source; | ||||
| displayAttribution(image.source); | |||||
| if (image.bottom !== undefined) { | if (image.bottom !== undefined) { | ||||
| img.style.setProperty("--offset", ((-1 + image.bottom) * 100) + "%") | img.style.setProperty("--offset", ((-1 + image.bottom) * 100) + "%") | ||||
| } else { | } else { | ||||
| @@ -855,6 +903,8 @@ document.addEventListener("DOMContentLoaded", () => { | |||||
| const image = entities[selected.dataset.key].views[e.target.value].image; | const image = entities[selected.dataset.key].views[e.target.value].image; | ||||
| selected.querySelector(".entity-image").src = image.source; | selected.querySelector(".entity-image").src = image.source; | ||||
| displayAttribution(image.source); | |||||
| if (image.bottom !== undefined) { | if (image.bottom !== undefined) { | ||||
| selected.querySelector(".entity-image").style.setProperty("--offset", ((-1 + image.bottom) * 100) + "%") | selected.querySelector(".entity-image").style.setProperty("--offset", ((-1 + image.bottom) * 100) + "%") | ||||
| } else { | } else { | ||||
| @@ -0,0 +1,77 @@ | |||||
| const attributionData = { | |||||
| sources: [ | |||||
| { | |||||
| prefix: "./media/buildings/", | |||||
| files: [ | |||||
| { name: "house.svg", source: null }, | |||||
| { name: "mailbox.svg", source: null }, | |||||
| { name: "mobile-home.svg", source: null }, | |||||
| ], | |||||
| authors: [ | |||||
| "chemicalcrux" | |||||
| ] | |||||
| }, | |||||
| { | |||||
| prefix: "./media/buildings/skyscrapers/", | |||||
| files: [ | |||||
| { name: "wide.svg", source: null }, | |||||
| { name: "medium.svg", source: null }, | |||||
| { name: "slender.svg", source: null }, | |||||
| { name: "narrow.svg", source: null }, | |||||
| ], | |||||
| authors: [ | |||||
| "chemicalcrux" | |||||
| ] | |||||
| } | |||||
| ], | |||||
| authors: { | |||||
| "chemicalcrux": { | |||||
| name: "chemicalcrux", | |||||
| url: "https://www.furaffinity.net/user/chemicalcrux" | |||||
| } | |||||
| } | |||||
| } | |||||
| const attribution = {}; | |||||
| function prepareAttribution() { | |||||
| attribution["files"] = {}; | |||||
| attributionData.sources.forEach(citation => { | |||||
| citation.files.forEach(file => { | |||||
| attribution.files[citation.prefix + file.name] = { | |||||
| authors: citation.authors, | |||||
| source: file.source | |||||
| } | |||||
| }) | |||||
| }); | |||||
| } | |||||
| function authorsOf(file) { | |||||
| if (attribution.files[file]) | |||||
| return attribution.files[file].authors; | |||||
| else | |||||
| return undefined; | |||||
| } | |||||
| function authorsOfFull(file) { | |||||
| if (attribution.files[file]) { | |||||
| const result = []; | |||||
| attribution.files[file].authors.forEach(author => { | |||||
| result.push(attributionData.authors[author]); | |||||
| }); | |||||
| return result; | |||||
| } | |||||
| else | |||||
| return undefined; | |||||
| } | |||||
| function sourceOf(file) { | |||||
| if (attribution.files[file]) | |||||
| return attribution.files[file].source; | |||||
| else | |||||
| return undefined; | |||||
| } | |||||
| prepareAttribution(); | |||||