@@ -1452,7 +1452,7 @@ function drawHorizontalScale(ifDirty = false) {
// calling the constructor -- e.g. making a list of authors and
// owners. So, this function is used to generate that information.
// It is invoked like makeEntity so that it can be dropped in easily,
// but returns an object that lets you construct many copies of an entity,
// but returns an object that lets you construct many copies of an entity,
// rather than creating a new entity.
function createEntityMaker(info, views, sizes, forms) {
const maker = {};
@@ -3679,15 +3679,6 @@ document.addEventListener("DOMContentLoaded", () => {
prepareMenu();
prepareEntities();
document.querySelector("#open-help").addEventListener("click", (e) => {
setHelpDate();
document.querySelector("#open-help").classList.remove("highlighted");
window.open(
"https://www.notion.so/Macrovision-5c7f9377424743358ddf6db5671f439e",
"_blank"
);
});
document
.querySelector("#copy-screenshot")
.addEventListener("click", (e) => {
@@ -3734,10 +3725,18 @@ document.addEventListener("DOMContentLoaded", () => {
e.stopPropagation();
});
document.querySelector("#sidebar-menu").addEventListener("touchstart", (e) => {
e.stopPropagation();
});
document.addEventListener("click", (e) => {
document.querySelector("#sidebar-menu").classList.remove("visible");
});
document.addEventListener("touchstart", (e) => {
document.querySelector("#sidebar-menu").classList.remove("visible");
});
document
.querySelector("#toggle-settings")
.addEventListener("click", (e) => {
@@ -3767,10 +3766,96 @@ document.addEventListener("DOMContentLoaded", () => {
e.stopPropagation();
});
document.querySelector("#settings-menu").addEventListener("touchstart", (e) => {
e.stopPropagation();
});
document.addEventListener("click", (e) => {
document.querySelector("#settings-menu").classList.remove("visible");
});
document.addEventListener("touchstart", (e) => {
document.querySelector("#settings-menu").classList.remove("visible");
});
document.querySelector("#toggle-filters").addEventListener("click", (e) => {
const popoutMenu = document.querySelector("#filters-menu");
if (popoutMenu.classList.contains("visible")) {
popoutMenu.classList.remove("visible");
} else {
document
.querySelectorAll(".popout-menu")
.forEach((menu) => menu.classList.remove("visible"));
const rect = e.target.getBoundingClientRect();
popoutMenu.classList.add("visible");
popoutMenu.style.left = rect.x + rect.width + 10 + "px";
popoutMenu.style.top = rect.y + rect.height + 10 + "px";
let menuWidth = popoutMenu.getBoundingClientRect().width;
let screenWidth = window.innerWidth;
if (menuWidth * 1.5 > screenWidth) {
popoutMenu.style.left = 25 + "px";
}
}
e.stopPropagation();
});
document.querySelector("#filters-menu").addEventListener("click", (e) => {
e.stopPropagation();
});
document.querySelector("#filters-menu").addEventListener("touchstart", (e) => {
e.stopPropagation();
});
document.addEventListener("click", (e) => {
document.querySelector("#filters-menu").classList.remove("visible");
});
document.addEventListener("touchstart", (e) => {
document.querySelector("#filters-menu").classList.remove("visible");
});
document.querySelector("#toggle-info").addEventListener("click", (e) => {
const popoutMenu = document.querySelector("#info-menu");
if (popoutMenu.classList.contains("visible")) {
popoutMenu.classList.remove("visible");
} else {
document
.querySelectorAll(".popout-menu")
.forEach((menu) => menu.classList.remove("visible"));
const rect = e.target.getBoundingClientRect();
popoutMenu.classList.add("visible");
popoutMenu.style.left = rect.x + rect.width + 10 + "px";
popoutMenu.style.top = rect.y + rect.height + 10 + "px";
let menuWidth = popoutMenu.getBoundingClientRect().width;
let screenWidth = window.innerWidth;
if (menuWidth * 1.5 > screenWidth) {
popoutMenu.style.left = 25 + "px";
}
}
e.stopPropagation();
});
document.querySelector("#info-menu").addEventListener("click", (e) => {
e.stopPropagation();
});
document.querySelector("#info-menu").addEventListener("touchstart", (e) => {
e.stopPropagation();
});
document.addEventListener("click", (e) => {
document.querySelector("#info-menu").classList.remove("visible");
});
document.addEventListener("touchstart", (e) => {
document.querySelector("#info-menu").classList.remove("visible");
});
window.addEventListener("unload", () => {
saveScene("autosave");
setUserSettings(exportUserSettings());
@@ -4845,6 +4930,8 @@ document.addEventListener("DOMContentLoaded", () => {
);
}
};
updateFilter();
});
let searchText = "";
@@ -4898,13 +4985,6 @@ function makeCustomEntity(url, x = 0.5, y = 0.5) {
}
const filterDefs = {
none: {
id: "none",
name: "No Filter",
extract: (maker) => [],
render: (name) => name,
sort: (tag1, tag2) => tag1[1].localeCompare(tag2[1]),
},
author: {
id: "author",
name: "Authors",
@@ -5027,6 +5107,8 @@ const filterDefs = {
},
};
const filterStates = {};
const sizeCategories = {
atomic: math.unit(100, "angstroms"),
microscopic: math.unit(100, "micrometers"),
@@ -5079,20 +5161,18 @@ function prepareEntities() {
return x.name.localeCompare(y.name);
});
const holder = document.querySelector("#spawners");
const filterHolder = document.querySelector("#filters ");
const filterMenu = document.querySelector("#filters-menu ");
const categorySelect = document.createElement("select");
categorySelect.id = "category-picker";
const filterSelect = document.createElement("select");
filterSelect.id = "filter-picker";
holder.appendChild(categorySelect);
filterHolder.appendChild(filterSelect);
const filterSets = {};
Object.values(filterDefs).forEach((filter) => {
filterSets[filter.id] = new Set();
filterStates[filter.id] = false;
});
Object.entries(availableEntities).forEach(([category, entityList]) => {
@@ -5183,76 +5263,36 @@ function prepareEntities() {
});
Object.values(filterDefs).forEach((filter) => {
const option = document.createElement("option");
option.innerText = filter.name;
option.value = filter.id;
filterSelect.appendChild(option);
const filterHolder = document.createElement("label");
filterHolder.setAttribute("for", "filter-toggle-" + filter.id);
filterHolder.classList.add("filter-holder");
const filterToggle = document.createElement("input");
filterToggle.type = "checkbox";
filterToggle.id = "filter-toggle-" + filter.id;
filterHolder.appendChild(filterToggle);
filterToggle.addEventListener("input", e => {
filterStates[filter.id] = filterToggle.checked
if (filterToggle.checked) {
filterHolder.classList.add("enabled");
} else {
filterHolder.classList.remove("enabled");
}
clearFilter();
updateFilter();
});
const filterLabel = document.createElement("div");
filterLabel.innerText = filter.name;
filterHolder.appendChild(filterLabel);
const filterNameSelect = document.createElement("select");
filterNameSelect.classList.add("filter-select");
filterNameSelect.id = "filter-" + filter.id;
filterHolder.appendChild(filterNameSelect);
const button = document.createElement("button");
button.classList.add("filter-button");
button.id = "create-filtered-" + filter.id + "-button";
filterHolder.appendChild(button);
const counter = document.createElement("div");
counter.classList.add("button-counter");
counter.innerText = "10";
button.appendChild(counter);
const i = document.createElement("i");
i.classList.add("fas");
i.classList.add("fa-plus");
button.appendChild(i);
button.addEventListener("click", (e) => {
const makers = Array.from(
document.querySelector(".entity-select.category-visible")
).filter((element) => !element.classList.contains("filtered"));
const count = makers.length + 2;
let index = 1;
if (makers.length > 50) {
if (
!confirm(
"Really spawn " + makers.length + " things at once?"
)
) {
return;
}
}
const worldWidth =
(config.height.toNumber("meters") / canvasHeight) * canvasWidth;
const spawned = makers.map((element) => {
const category =
document.querySelector("#category-picker").value;
const maker = availableEntities[category][element.value];
const entity = maker.constructor();
displayEntity(
entity,
entity.view,
-worldWidth * 0.45 +
config.x +
(worldWidth * 0.9 * index) / (count - 1),
config.y
);
index += 1;
return entityIndex - 1;
});
updateSizes(true);
if (config.autoFitAdd) {
let targets = {};
spawned.forEach((key) => {
targets[key] = entities[key];
});
fitEntities(targets);
}
});
filterMenu.appendChild(filterHolder);
Array.from(filterSets[filter.id])
.map((name) => [name, filter.render(name)])
@@ -5269,6 +5309,14 @@ function prepareEntities() {
});
});
const spawnButton = document.createElement("button");
spawnButton.id = "spawn-all"
spawnButton.addEventListener("click", e => {
spawnAll();
});
filterMenu.appendChild(spawnButton);
console.log(
"Loaded " + Object.keys(availableEntitiesByName).length + " entities"
);
@@ -5298,20 +5346,49 @@ function prepareEntities() {
recomputeFilters();
filterSelect.addEventListener("input", (e) => {
const oldSelect = document.querySelector(
".filter-select.category-visible"
);
if (oldSelect) oldSelect.classList.remove("category-visible");
ratioInfo = document.body.querySelector(".extra-info");
}
const newSelect = document.querySelector("#filter-" + e.target.value);
if (newSelect && e.target.value != "none")
newSelect.classList.add("category-visible");
function spawnAll() {
const makers = Array.from(
document.querySelector(".entity-select.category-visible")
).filter((element) => !element.classList.contains("filtered"));
const count = makers.length + 2;
let index = 1;
updateFilter();
if (makers.length > 50) {
if (!confirm("Really spawn " + makers.length + " things at once?")) {
return;
}
}
const worldWidth =
(config.height.toNumber("meters") / canvasHeight) * canvasWidth;
const spawned = makers.map((element) => {
const category = document.querySelector("#category-picker").value;
const maker = availableEntities[category][element.value];
const entity = maker.constructor();
displayEntity(
entity,
entity.view,
-worldWidth * 0.45 +
config.x +
(worldWidth * 0.9 * index) / (count - 1),
config.y
);
index += 1;
return entityIndex - 1;
});
updateSizes(true);
ratioInfo = document.body.querySelector(".extra-info");
if (config.autoFitAdd) {
let targets = {};
spawned.forEach((key) => {
targets[key] = entities[key];
});
fitEntities(targets);
}
}
// Only display authors and owners if they appear
@@ -5325,75 +5402,64 @@ function recomputeFilters() {
filterSets[filter.id] = new Set();
});
document
.querySelectorAll(".entity-select.category-visible > option")
.forEach((element) => {
const entity = availableEntities[category][element.value];
Object.values(filterDefs).forEach((filter) => {
filter.extract(entity).forEach((result) => {
filterSets[filter.id].add(result);
});
availableEntities[category].forEach((entity) => {
Object.values(filterDefs).forEach((filter) => {
filter.extract(entity).forEach((result) => {
filterSets[filter.id].add(result);
});
});
});
Object.values(filterDefs).forEach((filter) => {
filterStates[filter.id] = false;
document.querySelector("#filter-toggle-" + filter.id).checked = false
document.querySelector("#filter-toggle-" + filter.id).dispatchEvent(new Event("click"))
// always show the "none" option
let found = filter.id == "none";
const filterSelect = document.querySelector("#filter-" + filter.id);
const filterSelectHolder = filterSelect.parentElement;
filterSelect.querySelectorAll("option").forEach((element) => {
if (
filterSets[filter.id].has(element.value) ||
filter.id == "none"
) {
element.classList.remove("filtered");
element.disabled = false;
found = true;
} else {
element.classList.add("filtered");
element.disabled = true;
}
});
document
.querySelectorAll("#filter-" + filter.id + " > option")
.forEach((element) => {
if (
filterSets[filter.id].has(element.value) ||
filter.id == "none"
) {
element.classList.remove("filtered");
element.disabled = false;
found = true;
} else {
element.classList.add("filtered");
element.disabled = true;
}
});
const filterOption = document.querySelector(
"#filter-picker > option[value='" + filter.id + "']"
);
if (found) {
filterOption.classList.remove("filtered");
filterOption.disabled = false;
filterSelectHolder.style.display = "";
} else {
filterOption.classList.add("filtered");
filterOption.disabled = true;
filterSelectHolder.style.display = "none";
}
});
document.querySelector("#filter-picker").value = "none";
document.querySelector("#filter-picker").dispatchEvent(new Event("input"));
}
function updateFilter() {
const category = document.querySelector("#category-picker").value;
const type = document.querySelector("#filter-picker").value;
const filterKeySelect = document.querySelector(
".filter-select.category-visible"
);
clearFilter();
const types = Object.values(filterDefs).filter(def => filterStates[def.id]).map(def => def.id)
const keys = {
const noFilter = !filterKeySelect;
}
types.forEach(type => {
const filterKeySelect = document.querySelector("#filter-" + type);
keys[type] = filterKeySelect.value;
})
clearFilter();
let key;
let current = document.querySelector(
".entity-select.category-visible"
).value;
if (!noFilter) {
key = filterKeySelect.value;
current;
}
let replace = current == "";
let first = null;
@@ -5403,16 +5469,18 @@ function updateFilter() {
document
.querySelectorAll(".entity-select.category-visible > option")
.forEach((element) => {
let keep = noFilter;
let keep = true
if (
!noFilter &&
filterDefs[type]
.extract(availableEntities[category][element.value])
.indexOf(key) >= 0
) {
keep = true;
}
types.forEach(type => {
if (
!(filterDefs[type]
.extract(availableEntities[category][element.value])
.indexOf(keys[type]) >= 0)
) {
keep = false;
}
})
if (
searchText != "" &&
@@ -5438,13 +5506,9 @@ function updateFilter() {
}
});
const button = document.querySelector(
".filter-select.category-visible + button"
);
if (button) {
button.querySelector(".button-counter").innerText = count;
}
const button = document.querySelector("#spawn-all")
button.innerText = "Spawn " + count + " filtered " + (count == 1 ? "entity" : "entities") + ".";
if (replace) {
document.querySelector(".entity-select.category-visible").value = first;