|  |  | @@ -2331,6 +2331,7 @@ function configViewOptions(entity, view) { | 
		
	
		
			
			|  |  |  | if (val.editing) { | 
		
	
		
			
			|  |  |  | const name = document.createElement("input"); | 
		
	
		
			
			|  |  |  | name.placeholder = "Enter name..."; | 
		
	
		
			
			|  |  |  | name.value = val.name; | 
		
	
		
			
			|  |  |  | holder.appendChild(name); | 
		
	
		
			
			|  |  |  | 
 | 
		
	
		
			
			|  |  |  | holder.addEventListener("keydown", (e) => { | 
		
	
	
		
			
				|  |  | @@ -2339,6 +2340,7 @@ function configViewOptions(entity, view) { | 
		
	
		
			
			|  |  |  | 
 | 
		
	
		
			
			|  |  |  | const input = document.createElement("input"); | 
		
	
		
			
			|  |  |  | input.placeholder = "Enter measurement..."; | 
		
	
		
			
			|  |  |  | input.value = val.text; | 
		
	
		
			
			|  |  |  | holder.appendChild(input); | 
		
	
		
			
			|  |  |  | 
 | 
		
	
		
			
			|  |  |  | input.addEventListener("keydown", (e) => { | 
		
	
	
		
			
				|  |  | @@ -2374,6 +2376,7 @@ function configViewOptions(entity, view) { | 
		
	
		
			
			|  |  |  | power: power, | 
		
	
		
			
			|  |  |  | type: unitType, | 
		
	
		
			
			|  |  |  | base: baseValue, | 
		
	
		
			
			|  |  |  | custom: true | 
		
	
		
			
			|  |  |  | }; | 
		
	
		
			
			|  |  |  | 
 | 
		
	
		
			
			|  |  |  | // since we might have changed unit types, we should | 
		
	
	
		
			
				|  |  | @@ -2381,15 +2384,33 @@ function configViewOptions(entity, view) { | 
		
	
		
			
			|  |  |  | entity.currentView.units[key] = undefined; | 
		
	
		
			
			|  |  |  | 
 | 
		
	
		
			
			|  |  |  | defineAttributeGetters(entity.views[view]); | 
		
	
		
			
			|  |  |  | 
 | 
		
	
		
			
			|  |  |  | configViewOptions(entity, view); | 
		
	
		
			
			|  |  |  | updateSizes(); | 
		
	
		
			
			|  |  |  | }); | 
		
	
		
			
			|  |  |  | } else { | 
		
	
		
			
			|  |  |  | const label = document.createElement("div"); | 
		
	
		
			
			|  |  |  | label.classList.add("options-label"); | 
		
	
		
			
			|  |  |  | label.classList.add("attribute-label"); | 
		
	
		
			
			|  |  |  | label.innerText = val.name; | 
		
	
		
			
			|  |  |  | 
 | 
		
	
		
			
			|  |  |  | holder.appendChild(label); | 
		
	
		
			
			|  |  |  |  | 
		
	
		
			
			|  |  |  | const editButton = document.createElement("button"); | 
		
	
		
			
			|  |  |  | editButton.classList.add("attribute-edit-button"); | 
		
	
		
			
			|  |  |  | const editButtonIcon = document.createElement("i"); | 
		
	
		
			
			|  |  |  | editButtonIcon.classList.add("fas"); | 
		
	
		
			
			|  |  |  | editButtonIcon.classList.add("fa-edit"); | 
		
	
		
			
			|  |  |  | 
 | 
		
	
		
			
			|  |  |  | editButton.addEventListener("click", e => { | 
		
	
		
			
			|  |  |  | entity.currentView.attributes[key] = { | 
		
	
		
			
			|  |  |  | name: val.name, | 
		
	
		
			
			|  |  |  | text: entity.currentView[key], | 
		
	
		
			
			|  |  |  | editing: true | 
		
	
		
			
			|  |  |  | } | 
		
	
		
			
			|  |  |  | configViewOptions(entity, view); | 
		
	
		
			
			|  |  |  | }); | 
		
	
		
			
			|  |  |  | 
 | 
		
	
		
			
			|  |  |  | editButton.appendChild(editButtonIcon); | 
		
	
		
			
			|  |  |  | label.appendChild(editButton); | 
		
	
		
			
			|  |  |  | 
 | 
		
	
		
			
			|  |  |  | const row = document.createElement("div"); | 
		
	
		
			
			|  |  |  | row.classList.add("options-row"); | 
		
	
	
		
			
				|  |  | @@ -2506,8 +2527,9 @@ function configViewOptions(entity, view) { | 
		
	
		
			
			|  |  |  | 
 | 
		
	
		
			
			|  |  |  | customButton.addEventListener("click", e => { | 
		
	
		
			
			|  |  |  | entity.currentView.attributes["custom" + (Object.keys(entity.currentView.attributes).length + 1)] = { | 
		
	
		
			
			|  |  |  | name: "Custom", | 
		
	
		
			
			|  |  |  | editing: true | 
		
	
		
			
			|  |  |  | name: "", | 
		
	
		
			
			|  |  |  | text: "", | 
		
	
		
			
			|  |  |  | editing: true, | 
		
	
		
			
			|  |  |  | } | 
		
	
		
			
			|  |  |  | configViewOptions(entity, view); | 
		
	
		
			
			|  |  |  | }); | 
		
	
	
		
			
				|  |  | @@ -4934,7 +4956,7 @@ document.addEventListener("DOMContentLoaded", () => { | 
		
	
		
			
			|  |  |  | scenes["Empty"](); | 
		
	
		
			
			|  |  |  | } else { | 
		
	
		
			
			|  |  |  | try { | 
		
	
		
			
			|  |  |  | const data = JSON.parse(b64DecodeUnicode(param)); | 
		
	
		
			
			|  |  |  | const data = JSON.parse(b64DecodeUnicode(param), math.reviver); | 
		
	
		
			
			|  |  |  | if (data.entities === undefined) { | 
		
	
		
			
			|  |  |  | return; | 
		
	
		
			
			|  |  |  | } | 
		
	
	
		
			
				|  |  | @@ -5935,7 +5957,7 @@ function loadScene(name = "default") { | 
		
	
		
			
			|  |  |  | } | 
		
	
		
			
			|  |  |  | try { | 
		
	
		
			
			|  |  |  | const data = JSON.parse( | 
		
	
		
			
			|  |  |  | localStorage.getItem("macrovision-save-" + name) | 
		
	
		
			
			|  |  |  | localStorage.getItem("macrovision-save-" + name), math.reviver | 
		
	
		
			
			|  |  |  | ); | 
		
	
		
			
			|  |  |  | if (data === null) { | 
		
	
		
			
			|  |  |  | console.error("Couldn't load " + name); | 
		
	
	
		
			
				|  |  | @@ -5987,7 +6009,7 @@ function exportScene() { | 
		
	
		
			
			|  |  |  | .filter(([key, entity]) => entity.ephemeral !== true) | 
		
	
		
			
			|  |  |  | .forEach(([key, entity]) => { | 
		
	
		
			
			|  |  |  | const element = document.querySelector("#entity-" + key); | 
		
	
		
			
			|  |  |  | results.entities.push({ | 
		
	
		
			
			|  |  |  | const entityData = { | 
		
	
		
			
			|  |  |  | name: entity.identifier, | 
		
	
		
			
			|  |  |  | customName: entity.name, | 
		
	
		
			
			|  |  |  | scale: entity.scale, | 
		
	
	
		
			
				|  |  | @@ -5999,7 +6021,22 @@ function exportScene() { | 
		
	
		
			
			|  |  |  | y: element.dataset.y, | 
		
	
		
			
			|  |  |  | priority: entity.priority, | 
		
	
		
			
			|  |  |  | brightness: entity.brightness, | 
		
	
		
			
			|  |  |  | }; | 
		
	
		
			
			|  |  |  | 
 | 
		
	
		
			
			|  |  |  | entityData.views = {}; | 
		
	
		
			
			|  |  |  | 
 | 
		
	
		
			
			|  |  |  | Object.entries(entity.views).forEach(([viewId, viewData]) => { | 
		
	
		
			
			|  |  |  | Object.entries(viewData.attributes).forEach(([attrId, attrData]) => { | 
		
	
		
			
			|  |  |  | if (attrData.custom) { | 
		
	
		
			
			|  |  |  | if (entityData.views[viewId] === undefined) { | 
		
	
		
			
			|  |  |  | entityData.views[viewId] = {}; | 
		
	
		
			
			|  |  |  | } | 
		
	
		
			
			|  |  |  | entityData.views[viewId][attrId] = attrData; | 
		
	
		
			
			|  |  |  | } | 
		
	
		
			
			|  |  |  | }); | 
		
	
		
			
			|  |  |  | }); | 
		
	
		
			
			|  |  |  | 
 | 
		
	
		
			
			|  |  |  | results.entities.push(entityData); | 
		
	
		
			
			|  |  |  | }); | 
		
	
		
			
			|  |  |  | 
 | 
		
	
		
			
			|  |  |  | const unit = document.querySelector("#options-height-unit").value; | 
		
	
	
		
			
				|  |  | @@ -6076,7 +6113,7 @@ function pasteScene() { | 
		
	
		
			
			|  |  |  | navigator.clipboard | 
		
	
		
			
			|  |  |  | .readText() | 
		
	
		
			
			|  |  |  | .then((text) => { | 
		
	
		
			
			|  |  |  | const data = JSON.parse(text); | 
		
	
		
			
			|  |  |  | const data = JSON.parse(text, math.reviver); | 
		
	
		
			
			|  |  |  | if (data.entities === undefined) { | 
		
	
		
			
			|  |  |  | return; | 
		
	
		
			
			|  |  |  | } | 
		
	
	
		
			
				|  |  | @@ -6086,7 +6123,7 @@ function pasteScene() { | 
		
	
		
			
			|  |  |  | 
 | 
		
	
		
			
			|  |  |  | importScene(data); | 
		
	
		
			
			|  |  |  | }) | 
		
	
		
			
			|  |  |  | .catch((err) => alert(err)); | 
		
	
		
			
			|  |  |  | .catch((err) => { toast("Something went wrong when importing: " + err), console.error(err) }); | 
		
	
		
			
			|  |  |  | } catch (err) { | 
		
	
		
			
			|  |  |  | console.error(err); | 
		
	
		
			
			|  |  |  | 
 | 
		
	
	
		
			
				|  |  | @@ -6154,6 +6191,11 @@ const migrationDefs = [ | 
		
	
		
			
			|  |  |  | entity.flipped = false; | 
		
	
		
			
			|  |  |  | }); | 
		
	
		
			
			|  |  |  | } | 
		
	
		
			
			|  |  |  | /* | 
		
	
		
			
			|  |  |  | Migration: 5 -> 6 | 
		
	
		
			
			|  |  |  | 
 | 
		
	
		
			
			|  |  |  | Entities can now have custom attributes | 
		
	
		
			
			|  |  |  | */ | 
		
	
		
			
			|  |  |  | ]; | 
		
	
		
			
			|  |  |  | 
 | 
		
	
		
			
			|  |  |  | function migrateScene(data) { | 
		
	
	
		
			
				|  |  | @@ -6184,6 +6226,16 @@ function importScene(data) { | 
		
	
		
			
			|  |  |  | entity.priority = entityInfo.priority; | 
		
	
		
			
			|  |  |  | entity.brightness = entityInfo.brightness; | 
		
	
		
			
			|  |  |  | entity.form = entityInfo.form; | 
		
	
		
			
			|  |  |  | 
 | 
		
	
		
			
			|  |  |  | Object.entries(entityInfo.views).forEach(([viewId, viewData]) => { | 
		
	
		
			
			|  |  |  | if (entityInfo.views[viewId] !== undefined) { | 
		
	
		
			
			|  |  |  | Object.entries(entityInfo.views[viewId]).forEach(([attrId, attrData]) => { | 
		
	
		
			
			|  |  |  | entity.views[viewId].attributes[attrId] = attrData; | 
		
	
		
			
			|  |  |  | }); | 
		
	
		
			
			|  |  |  | } | 
		
	
		
			
			|  |  |  | }); | 
		
	
		
			
			|  |  |  | 
 | 
		
	
		
			
			|  |  |  | Object.keys(entityInfo.views).forEach(key => defineAttributeGetters(entity.views[key])); | 
		
	
		
			
			|  |  |  | displayEntity(entity, entityInfo.view, entityInfo.x, entityInfo.y); | 
		
	
		
			
			|  |  |  | }); | 
		
	
		
			
			|  |  |  | 
 | 
		
	
	
		
			
				|  |  | 
 |