2023-08-02 08:56:51 -04:00
if ( enabledMods . includes ( "mods/betterMenuScreens.js" ) ) {
2023-07-30 18:46:46 -04:00
const properties = {
meta : [
{ name : "name" , type : "string" , viewOnly : true , required : true } ,
{ name : "category" , type : "string" , required : true } ,
{ name : "desc" , type : "string" } ,
{ name : "behavior" , type : "string" } ,
{ name : "alias" , type : "string" } ,
{ name : "seed" , type : "string" } ,
{ name : "color" , type : [ "color" , "array" ] , viewOnly : true } ,
{ name : "breakInto" , type : [ "string" , "array" ] , viewOnly : true } ,
{ name : "darkText" , type : "boolean" } ,
{ name : "baby" , type : "string" } ,
{ name : "id" , type : "number" , viewOnly : true , creatorIgnore : true }
] ,
default _properties : [
{ name : "viscosity" , type : "number" } ,
{ name : "density" , type : "number" } ,
{ name : "hardness" , type : "number" } ,
{ name : "maxSize" , type : "number" } ,
{ name : "conduct" , type : "number" } ,
{ name : "foodNeed" , type : "number" } ,
{ name : "stain" , type : "number" } ,
] ,
data : [
{ name : "hidden" , type : "boolean" } ,
{ name : "insulate" , type : "boolean" } ,
{ name : "noMix" , type : "boolean" } ,
{ name : "isFood" , type : "boolean" } ,
{ name : "forceAutoGen" , type : "boolean" } ,
{ name : "customColor" , type : "boolean" } ,
{ name : "ignoreAir" , type : "boolean" } ,
{ name : "excludeRandom" , type : "boolean" } ,
] ,
burn : [
{ name : "burn" , type : "number" } ,
{ name : "burnTime" , type : "number" } ,
{ name : "burnInto" , type : [ "string" , "array" ] , viewOnly : true } ,
{ name : "burning" , type : "boolean" } ,
{ name : "fireElement" , type : [ "string" , "array" ] , viewOnly : true } ,
{ name : "fireColor" , type : [ "color" , "array" ] , viewOnly : true } ,
] ,
flip : [
{ name : "flipX" , type : "boolean" } ,
{ name : "flipY" , type : "boolean" } ,
{ name : "flippableX" , type : "boolean" } ,
{ name : "flippableY" , type : "boolean" } ,
] ,
states : [
{ name : "state" , type : "string" } ,
{ name : "stateHigh" , type : "string" } ,
{ name : "stateHighName" , type : "string" } ,
{ name : "stateHighColor" , type : "color" } ,
{ name : "stateHighColorMultiplier" , type : "number" } ,
{ name : "stateLow" , type : "string" } ,
{ name : "stateLowName" , type : "string" } ,
{ name : "stateLowColor" , type : "color" } ,
{ name : "stateLowColorMultiplier" , type : "number" } ,
] ,
temperature : [
{ name : "temp" , type : "number" } ,
{ name : "tempHigh" , type : "number" } ,
{ name : "extraTempHigh" , type : "number" } ,
{ name : "tempLow" , type : "number" } ,
{ name : "extraTempLow" , type : "number" } ,
]
}
const br = ( ) => document . createElement ( "br" ) ;
const createDiv = ( ) => document . createElement ( "div" ) ;
const span = ( innerText ) => {
const element = document . createElement ( "span" ) ;
element . innerText = innerText ;
return element ;
}
const createInput = ( type , disabled , id , className = "" ) => {
const element = document . createElement ( "input" ) ;
element . id = id ;
element . className = className ;
element . type = type ;
element . disabled = disabled ;
return element ;
}
const defaultSettings = { clearElements : false , allowFreeRemoval : false } ;
const toggleSetting = ( setting , target ) => {
target . value = target . value == "ON" ? "OFF" : "ON" ;
target . setAttribute ( "state" , target . getAttribute ( "state" ) == "1" ? "0" : "1" ) ;
const settings _ = Storage . get ( "settings" , defaultSettings ) ;
settings _ [ setting ] = ! settings _ [ setting ] ;
Storage . set ( "settings" , settings _ ) ;
}
class Storage {
static get ( id , fallback = null , setOnNull = false ) {
const res = JSON . parse ( localStorage . getItem ( ` elementsManager/ ${ id } ` ) ) ;
if ( ! res && fallback && setOnNull ) {
Storage . set ( id , fallback ) ;
}
return res ? ? fallback ;
}
static set ( id , value ) {
localStorage . setItem ( ` elementsManager/ ${ id } ` , JSON . stringify ( value ) ) ;
}
static remove ( id ) {
localStorage . removeItem ( ` elementsManager/ ${ id } ` ) ;
}
static append ( id , value ) {
const current = Storage . get ( id , [ ] ) ;
current . push ( value ) ;
Storage . set ( id , current ) ;
}
static filter ( id , condition ) {
Storage . set ( id , Storage . get ( id , [ ] ) . filter ( condition ) ) ;
}
}
const importElements = ( elements _ ) => {
const settings = Storage . get ( "settings" , { clearElements : false , allowFreeRemoval : false } , true ) ;
if ( settings . clearElements ) {
Storage . set ( "elements" , elements _ ) ;
} else {
const beforeElements = Storage . get ( "elements" , [ ] ) ;
Storage . set ( "elements" , beforeElements . concat ( elements _ ) ) ;
}
alert ( ` Successfully imported ${ elements _ . length } element ${ elements _ . length != 1 ? "s" : "" } ` )
}
const exportElements = ( ) => {
let element = document . createElement ( 'a' ) ;
const blob = new Blob ( [ JSON . stringify ( Storage . get ( "elements" , [ ] ) ) ] , { type : "application/json" } ) ;
const url = URL . createObjectURL ( blob ) ;
element . setAttribute ( 'href' , url ) ;
element . setAttribute ( 'download' , "elements.json" ) ;
document . body . appendChild ( element ) ;
element . click ( ) ;
document . body . removeChild ( element ) ;
}
const cssInject = ( ) => {
const style = document . createElement ( "style" ) ;
style . innerHTML = ` .categoryTitle {
font - size : 1.25 em ;
}
# elementManagerParent input [ type = "number" ] , # elementManagerParent input [ type = "text" ] , # elementCreatorParent input [ type = "number" ] , # elementCreatorParent input [ type = "text" ] {
background - color : black ;
vertical - align : middle ;
margin - left : 5 px ;
margin - right : 5 px ;
border : rgb ( 150 , 150 , 150 ) 1 px solid ;
border - radius : 20 px ;
padding : 0.5 em ;
color : white ;
font - size : 1 em ;
font - family : Arial , Helvetica , sans - serif ;
}
# elementsManagerFilter {
background - color : rgb ( 66 , 66 , 66 ) ;
vertical - align : middle ;
margin - left : 5 px ;
margin - right : 5 px ;
width : 75 % ;
color : white ;
font - size : 1 em ;
font - family : 'Press Start 2P' , Arial ;
padding : 0.5 em ;
border : none ;
display : inline - block ;
}
# elementsManagerFilter : focus {
outline : none ;
background - color : rgb ( 60 , 60 , 60 ) ;
}
# elementsList li span : hover , # customElementsList li span : hover , # deletedElementsList li span : hover , . hoverableText : hover {
cursor : pointer ;
color : rgb ( 200 , 200 , 200 ) ;
}
# elementAdd {
background - color : rgb ( 66 , 66 , 66 ) ;
color : white ;
font - size : 1 em ;
font - family : 'Press Start 2P' , Arial ;
width : auto ;
padding : 0.5 em ;
border : none ;
outline : none ;
display : inline - block ;
vertical - align : middle ;
margin - left : 5 px ;
margin - right : 5 px ;
}
# elementAdd : hover {
cursor : pointer ;
background - color : rgb ( 60 , 60 , 60 ) ;
}
# elementsManagerSettingsButton {
background - color : rgb ( 66 , 66 , 66 ) ;
color : white ;
font - size : 1 em ;
font - family : 'Press Start 2P' , Arial ;
width : auto ;
padding : 0.5 em ;
border : none ;
outline : none ;
display : inline - block ;
vertical - align : middle ;
margin - left : 5 px ;
margin - right : 5 px ;
}
# elementsManagerSettingsButton : hover {
cursor : pointer ;
background - color : rgb ( 60 , 60 , 60 ) ;
}
. plusButton {
position : absolute ;
right : 0 px ;
top : 0 px ;
font - size : 2 em ;
padding : 5 px ;
text - align : center ;
border : 1 px solid # fff ;
background - color : # 1 da100 ;
z - index : 12 ;
}
. plusButton : hover {
background - color : # 29 d902 ;
}
. elementRemoveButton {
color : red
}
# elementsList li . elementRemoveButton : hover , # customElementsList li . elementRemoveButton : hover {
color : darkred ;
cursor : pointer ;
}
. createButton {
width : auto ;
padding : 5 px ;
vertical - align : middle ;
font - size : 1.25 em ;
display : block ;
margin - left : 5 px ;
margin - right : 5 px ;
padding : 5 px ;
text - align : center ;
border : 1 px solid # 1 da100 ;
border - radius : 10 px ;
}
. createButton : hover {
background - color : # 1 da100 ;
} `
document . head . appendChild ( style ) ;
}
2023-08-02 08:56:51 -04:00
// ugly way of doing it but probably works
const checkType = ( key , value ) => {
2023-08-03 06:24:45 -04:00
if ( key == "behavior" && ( typeof value == "function" || ( value instanceof Array && value . filter ( e => e instanceof Array && e . filter ( s => typeof s == "string" ) . length == e . length ) . length == value . length ) ) ) return true ;
else if ( key == "behavior" ) return false ;
2023-08-02 08:56:51 -04:00
if ( [ "darkText" , "hidden" , "insulate" , "noMix" , "isFood" , "forceAutoGen" , "customColor" , "ignoreAir" , "excludeRandom" , "burning" , "flipX" , "flipY" , "flippableX" , "flippableY" ] . includes ( key ) && typeof value != "boolean" ) return false ;
if ( [ "name" , "category" , "desc" , "alias" , "seed" , "baby" , "state" , "stateHigh" , "stateHighName" , "stateHighColor" , "stateLow" , "stateLowNmae" , "stateLowColor" ] . includes ( key ) && typeof value != "string" ) return false ;
if ( [ "id" , "burn" , "burnTime" , "stateHighColorMultiplier" , "stateLowColorMutliplier" , "temp" , "tempHigh" , "extraTempHigh" , "tempLow" , "extraTempLow" ] . includes ( key ) && typeof value != "number" ) return false ;
if ( [ "color" , "breakInto" , "burnInto" , "fireElement" , "fireColor" ] . includes ( key ) ) {
if ( value instanceof Array ) return value . filter ( l => typeof l == "string" ) . length == value . length ;
if ( typeof value != "string" ) return false ;
}
return true ;
}
2023-07-30 18:46:46 -04:00
const loadChanges = ( ) => {
const newElements = Storage . get ( "elements" , [ ] ) ;
for ( const element of newElements ) {
const element _ = element ;
2023-08-03 06:24:45 -04:00
if ( Object . keys ( behaviors ) . includes ( element _ [ "behavior" ] ) ) element _ [ "behavior" ] = behaviors [ element _ [ "behavior" ] ] ;
2023-08-02 08:56:51 -04:00
elements [ element . name ] = { } ;
// elements[element.name] = element_;
for ( const key of Object . keys ( element _ ) ) {
const val = element _ [ key ] ;
if ( checkType ( key , val ) ) elements [ element . name ] [ key ] = val ;
else if ( [ "name" , "category" ] . includes ( key ) ) elements [ element . name ] [ key ] = key == "name" ? "NewElement" : "other" ;
}
2023-07-30 18:46:46 -04:00
}
const changes = Storage . get ( "changes" , [ ] ) ;
for ( const change of changes ) {
for ( const key of Object . keys ( change . changes ) ) {
const c = change . changes [ key ] ;
2023-08-02 08:56:51 -04:00
if ( checkType ( key , c ) ) elements [ change . element ] [ key ] = c ;
2023-07-30 18:46:46 -04:00
}
}
const deleted = Storage . get ( "deletedElements" , [ ] ) ;
for ( const element of deleted ) {
delete elements [ element ] ;
}
}
2023-08-03 06:24:45 -04:00
const saveChanges = ( ) => {
2023-07-30 18:46:46 -04:00
const element = Storage . get ( "currentElement" ) ;
2023-08-03 06:24:45 -04:00
const changes = Storage . get ( "tempChanges" , [ ] ) ;
if ( Storage . get ( "elements" , [ ] ) . find ( a => a . name == element ) ) {
const elements _ = Storage . get ( "elements" , [ ] ) ;
for ( const change of changes ) {
elements _ . find ( a => a . name == element ) [ change . property ] = change . value ;
}
Storage . set ( "elements" , elements _ ) ;
2023-07-30 18:46:46 -04:00
} else {
2023-08-03 06:24:45 -04:00
const permChanges = Storage . get ( "changes" , [ ] ) ;
for ( const change of changes ) {
let a ;
if ( a = permChanges . find ( c => c . element == element ) ) {
a . changes [ change . property ] = change . value ;
} else {
let c = { } ;
c [ change . property ] = change . value ;
permChanges . push ( {
element ,
changes : c
} )
}
}
Storage . set ( "changes" , permChanges ) ;
}
}
const applyChange = ( property , value ) => {
// if (element && elements[element])
// elements[element][property] = value;
const element = Storage . get ( "currentElement" ) ;
const changes = Storage . get ( "tempChanges" , [ ] ) ;
changes . push ( { property , value } ) ;
Storage . set ( "tempChanges" , changes ) ;
const nullish = {
string : "" ,
boolean : false ,
number : 0 ,
array : [ ] ,
}
if ( elements [ element ] [ property ] == value || value == nullish [ value instanceof Array ? "array" : typeof value ] ) {
Storage . filter ( "tempChanges" , e => e . property != property ) ;
}
2023-07-30 18:46:46 -04:00
}
const elementsManagerLoader = ( ) => {
const listDiv = createDiv ( ) ;
const list = document . createElement ( "ul" ) ;
list . id = "elementsList" ;
let customElements = Storage . get ( "elements" , [ ] ) ;
let deletedElements = Storage . get ( "deletedElements" , [ ] ) ;
let lastFreeRemoval = Storage . get ( "settings" , { allowFreeRemoval : false , clearElements : false } , true ) . allowFreeRemoval ;
2023-08-02 10:03:19 -04:00
Storage . set ( "lastFreeRemoval" , lastFreeRemoval ) ;
2023-07-30 18:46:46 -04:00
for ( const key of Object . keys ( elements ) . concat ( customElements . map ( e => e . name ) ) . sort ( ( a , b ) => a . localeCompare ( b , undefined , { caseFirst : "false" } ) ) ) {
const element = document . createElement ( "li" ) ;
const text = span ( key ) ; // only the text should be clickable
text . onclick = ( ) => {
Storage . set ( "currentElement" , key ) ;
openMenu ( "elementManager" , true ) ;
}
element . appendChild ( text ) ;
if ( customElements . find ( e => e . name == key ) || lastFreeRemoval ) {
const removeButton = span ( "X" ) ;
removeButton . className = "elementRemoveButton" ;
removeButton . onclick = ( ) => {
if ( confirm ( "Are you sure you want to delete that element?" ) ) {
if ( customElements . find ( e => e . name == key ) ) {
Storage . filter ( "elements" , e => e . name != key ) ;
customElements = customElements . filter ( e => e . name != key ) ;
delete elements [ key ] ;
element . style . display = "none" ;
document . getElementById ( "customElementsList/" + key ) . remove ( ) ;
if ( document . getElementById ( "customElementsList" ) . children . length == 0 ) {
document . getElementById ( "noCustomElementsMessage" ) . style . display = "" ;
}
} else {
Storage . append ( "deletedElements" , key ) ;
delete elements [ key ] ;
element . style . display = "none" ;
document . getElementById ( "noDeletedElementsMessage" ) . style . display = "none" ;
const li = document . createElement ( "li" ) ;
li . innerText = key ;
li . onclick = ( ) => {
if ( confirm ( "Are you sure you want to re-add '" + key + "'?" ) ) {
Storage . filter ( "deletedElements" , a => a != key ) ;
li . style . display = "none" ;
}
}
document . getElementById ( "deletedElementsList" ) . appendChild ( li ) ;
}
}
}
const emptySpan = span ( " " ) ;
emptySpan . className = "elementRemoveButton" ;
element . appendChild ( emptySpan ) ;
element . appendChild ( removeButton )
}
if ( deletedElements . includes ( key ) ) {
element . style . display = "none" ;
}
list . appendChild ( element ) ;
}
const filterInput = document . createElement ( "input" ) ;
filterInput . id = "elementsManagerFilter" ;
filterInput . type = "text" ;
filterInput . placeholder = "Search elements..." ;
filterInput . onkeyup = ( ev ) => {
const val = ev . target . value ;
2023-08-02 10:03:19 -04:00
const deleted = Storage . get ( "deletedElements" , [ ] ) ;
2023-07-30 18:46:46 -04:00
for ( const c of document . getElementById ( "elementsList" ) . children ) {
const span _ = c . querySelector ( "span" ) ;
if ( ! span _ . innerText . toLowerCase ( ) . includes ( val . toLowerCase ( ) ) || deleted . includes ( span _ . innerText ) ) {
c . style . display = "none" ;
} else {
c . style . display = "" ;
}
}
}
const addElement = document . createElement ( "input" ) ;
addElement . id = "elementAdd" ;
addElement . value = "+" ;
addElement . type = "button" ;
addElement . onclick = ( _ ) => {
openMenu ( "elementCreator" , true ) ;
}
const settingsButton = document . createElement ( "input" ) ;
settingsButton . id = "elementsManagerSettingsButton" ;
settingsButton . type = "button" ;
settingsButton . value = "*" ;
settingsButton . onclick = ( _ ) => {
openMenu ( "elementsSettings" , true ) ;
}
listDiv . appendChild ( filterInput ) ;
listDiv . appendChild ( addElement ) ;
listDiv . appendChild ( settingsButton ) ;
listDiv . appendChild ( list ) ;
listDiv . style . overflowY = "scroll" ;
new MenuScreen ( )
. setTitle ( "Elements Manager" )
. setCloseButtonText ( "-" )
. setParentDivId ( "elementsManagerParent" )
. setInnerDivId ( "elementsManager" )
. addNode ( listDiv )
. build ( ) ;
}
const parseColor = ( colorString ) => {
2023-08-01 16:50:09 -04:00
// technically color arrays are handled differently, but ill add it just in case
if ( colorString instanceof Array ) return parseColor ( colorString [ 0 ] ) ;
2023-08-01 16:46:07 -04:00
if ( typeof colorString != "string" ) return "#ffffff" ;
2023-07-30 18:46:46 -04:00
if ( colorString . startsWith ( "rgb(" ) ) {
const color = colorString . replace ( "rgb(" , "" ) . replace ( ")" , "" ) ;
return ` # ${ color . split ( "," ) . map ( a => parseInt ( a ) . toString ( 16 ) ) . join ( "" ) } ` ;
} else {
if ( colorString . startsWith ( "#" ) ) {
const color = colorString . slice ( 1 ) ;
if ( color . length == 3 ) return ` # ${ color . repeat ( 2 ) } ` ;
else if ( color . length == 2 ) return ` # ${ color . repeat ( 3 ) } ` ;
else if ( color . length >= 6 ) return ` # ${ color . slice ( 0 , 6 ) } ` ;
else return ` # ${ color } ` ;
}
}
}
const elementManagerLoader = ( ) => {
const nodes = [ ] ;
for ( const key of Object . keys ( properties ) ) {
const category = createDiv ( ) ;
category . innerHTML += ` <span class="categoryTitle"> ${ key . split ( "_" ) . map ( k => k [ 0 ] . toUpperCase ( ) + k . slice ( 1 ) ) . join ( " " ) } </span><br> `
for ( const prop of properties [ key ] ) {
const id = "elementsManager/" + key + "/" + prop . name ;
const div = createDiv ( ) ;
div . className = "elementsManager/propertyEntry" ;
div . appendChild ( span ( prop . name ) ) ;
if ( prop . viewOnly ) {
if ( prop . type == "boolean" ) {
const el = createInput ( "button" , true , id , "toggleInput" ) ;
div . appendChild ( el ) ;
} else if ( prop . type == "longString" ) {
const el = document . createElement ( "textarea" ) ;
el . id = id ;
el . className = "elementsCode" ;
el . disabled = true ;
div . appendChild ( el ) ;
} else if ( prop . type instanceof Array ) {
const el = createInput ( "text" , true , id ) ;
div . appendChild ( el ) ;
} else {
const el = createInput ( prop . type == "string" ? "text" : prop . type , true , id ) ;
div . appendChild ( el ) ;
}
} else {
if ( prop . type == "boolean" ) {
const el = createInput ( "button" , false , id , "toggleInput" ) ;
el . onclick = ( ev ) => {
ev . target . value = ev . target . value == "ON" ? "OFF" : "ON" ;
ev . target . setAttribute ( "state" , ev . target . getAttribute ( "state" ) == "1" ? "0" : "1" ) ;
applyChange ( prop . name , ev . target . value == "ON" ) ;
}
div . appendChild ( el ) ;
} else if ( prop . type == "longString" ) {
const el = document . createElement ( "textarea" ) ;
el . id = id ;
el . className = "elementsCode" ;
el . onchange = ( ev ) => {
applyChange ( prop . name , ev . target . value ) ;
}
div . appendChild ( el ) ;
} else if ( prop . type instanceof Array ) {
const el = createInput ( "text" , false , id ) ;
el . onchange = ( ev ) => {
applyChange ( prop . name , ev . target . value . split ( ";" ) ) ;
}
div . appendChild ( el ) ;
} else if ( prop . name == "behavior" ) {
const dropdown = document . createElement ( "select" ) ;
dropdown . id = id ;
let i = 0 ;
for ( const bKey of Object . keys ( behaviors ) ) {
const option = document . createElement ( "option" ) ;
option . value = bKey ;
option . id = id + "/option/" + i ;
option . innerText = bKey ;
dropdown . appendChild ( option ) ;
i ++ ;
}
const customOption = document . createElement ( "option" ) ;
customOption . value = "CUSTOM" ;
customOption . id = id + "/option/custom"
customOption . innerText = "Custom Behavior" ;
dropdown . appendChild ( customOption ) ;
dropdown . onchange = ( ev ) => {
if ( ev . target . value == "CUSTOM" ) {
document . getElementById ( id + "/textInput" ) . style . display = "" ;
2023-08-03 06:24:45 -04:00
} else if ( ev . target . value == "NONE" ) {
applyChange ( prop . name , null ) ;
2023-07-30 18:46:46 -04:00
} else {
document . getElementById ( id + "/textInput" ) . style . display = "none" ;
applyChange ( prop . name , ev . target . value ) ;
}
}
2023-08-03 06:24:45 -04:00
const noneOption = document . createElement ( "option" ) ;
noneOption . value = "NONE" ;
noneOption . id = id + "/option/none"
noneOption . innerText = "None" ;
dropdown . appendChild ( noneOption ) ;
2023-07-30 18:46:46 -04:00
const el = createInput ( "text" , false , id + "/textInput" ) ;
el . style . display = "none" ;
el . onchange = ( ev ) => {
if ( document . getElementById ( id ) . value == "CUSTOM" ) {
2023-08-03 06:24:45 -04:00
applyChange ( prop . name , ev . target . value . split ( ";" ) . map ( e => e . split ( "," ) ) ) ;
2023-07-30 18:46:46 -04:00
} else {
ev . target . style . display = "none" ;
}
}
div . appendChild ( dropdown ) ;
div . appendChild ( el ) ;
} else {
const el = createInput ( prop . type == "string" ? "text" : prop . type , false , id ) ;
el . onchange = ( ev ) => {
applyChange ( prop . name , prop . type == "number" ? parseFloat ( ev . target . value ) : ev . target . value ) ;
}
div . appendChild ( el ) ;
}
}
category . appendChild ( div ) ;
}
category . appendChild ( br ( ) ) ;
nodes . push ( category ) ;
}
2023-08-03 06:24:45 -04:00
const saveButton = span ( "Save Changes" ) ;
saveButton . className = "createButton" ;
saveButton . onclick = ( ) => {
saveChanges ( ) ;
Storage . remove ( "tempChanges" ) ;
closeMenu ( ) ;
alert ( "Changes successfully applied" ) ;
}
2023-07-30 18:46:46 -04:00
2023-08-03 06:24:45 -04:00
nodes . push ( br ( ) , saveButton )
2023-07-30 18:46:46 -04:00
new MenuScreen ( )
. setTitle ( "Element Manager" )
. setCloseButtonText ( "<" )
. setParentDivId ( "elementManagerParent" )
. setInnerDivId ( "elementManager" )
. addNode ( nodes )
. build ( ) ;
}
const elementCreatorLoader = ( ) => {
const nodes = [ ] ;
for ( const key of Object . keys ( properties ) ) {
const category = createDiv ( ) ;
category . innerHTML += ` <span class="categoryTitle"> ${ key . split ( "_" ) . map ( k => k [ 0 ] . toUpperCase ( ) + k . slice ( 1 ) ) . join ( " " ) } </span><br> `
for ( const prop of properties [ key ] ) {
if ( prop . creatorIgnore ) continue ;
const div = createDiv ( ) ;
div . className = "elementsManager/propertyEntry" ;
div . innerHTML += ` <span> ${ prop . name } </span> `
const id = "elementsManager/creator/" + key + "/" + prop . name ;
if ( prop . type == "boolean" ) {
const el = createInput ( "button" , false , id , "toggleInput" ) ;
el . onclick = ( ev ) => {
const elementData = Storage . get ( "newElement" , { } ) ;
ev . target . value = ev . target . value == "ON" ? "OFF" : "ON" ;
elementData [ prop . name ] = ev . target . value == "ON" ;
ev . target . setAttribute ( "state" , ev . target . getAttribute ( "state" ) == "1" ? "0" : "1" ) ;
Storage . set ( "newElement" , elementData ) ;
}
div . appendChild ( el ) ;
} else if ( prop . type == "longString" ) {
const el = document . createElement ( "textarea" ) ;
el . id = id ;
el . className = "elementsCode" ;
el . onchange = ( ev ) => {
const elementData = Storage . get ( "newElement" , { } ) ;
elementData [ prop . name ] = ev . target . value ;
Storage . set ( "newElement" , elementData ) ;
}
div . appendChild ( el ) ;
} else if ( prop . type instanceof Array && prop . type [ 0 ] != "color" ) {
const el = createInput ( "text" , false , id ) ;
el . onchange = ( ev ) => {
const elementData = Storage . get ( "newElement" , { } ) ;
elementData [ prop . name ] = ev . target . value . split ( ";" ) ;
Storage . set ( "newElement" , elementData ) ;
}
div . appendChild ( el ) ;
} else if ( prop . name == "behavior" ) {
const dropdown = document . createElement ( "select" ) ;
dropdown . id = id ;
let i = 0 ;
for ( const bKey of Object . keys ( behaviors ) ) {
const option = document . createElement ( "option" ) ;
option . value = bKey ;
option . selected = i == 0 ;
option . innerText = bKey ;
dropdown . appendChild ( option ) ;
i ++ ;
}
const customOption = document . createElement ( "option" ) ;
customOption . value = "CUSTOM" ;
customOption . innerText = "Custom Behavior" ;
dropdown . appendChild ( customOption ) ;
dropdown . onchange = ( ev ) => {
if ( ev . target . value == "CUSTOM" ) {
document . getElementById ( id + "/textInput" ) . style . display = "" ;
} else {
document . getElementById ( id + "/textInput" ) . style . display = "none" ;
const elementData = Storage . get ( "newElement" , { } ) ;
elementData [ prop . name ] = ev . target . value ;
Storage . set ( "newElement" , elementData ) ;
}
}
const el = createInput ( "text" , false , id + "/textInput" ) ;
el . style . display = "none" ;
el . onchange = ( ev ) => {
if ( document . getElementById ( id ) . value == "CUSTOM" ) {
const elementData = Storage . get ( "newElement" , { } ) ;
2023-08-03 06:24:45 -04:00
elementData [ prop . name ] = ev . target . value . split ( ";" ) . map ( e => e . split ( "," ) ) ;
2023-07-30 18:46:46 -04:00
Storage . set ( "newElement" , elementData ) ;
} else {
ev . target . style . display = "none" ;
}
}
div . appendChild ( dropdown ) ;
div . appendChild ( el ) ;
} else {
const el = createInput ( prop . type == "string" ? "text" : prop . type instanceof Array ? "color" : prop . type , false , id ) ;
el . onchange = ( ev ) => {
const elementData = Storage . get ( "newElement" , { } ) ;
if ( prop . type == "number" ) elementData [ prop . name ] = parseFloat ( ev . target . value ) ;
2023-08-01 16:46:07 -04:00
else if ( prop . type == "color" || prop . type [ 0 ] == "color" ) elementData [ prop . name ] = parseColor ( ev . target . value ) ;
2023-07-30 18:46:46 -04:00
else if ( prop . type == "string" ) elementData [ prop . name ] = ev . target . value ;
Storage . set ( "newElement" , elementData ) ;
}
div . appendChild ( el ) ;
}
if ( prop . required ) {
const requiredMessage = span ( " This field is required" ) ;
requiredMessage . id = id + "/required" ;
requiredMessage . style . display = "none" ;
requiredMessage . style . color = "red" ;
div . appendChild ( requiredMessage ) ;
}
category . appendChild ( div ) ;
}
category . appendChild ( br ( ) ) ;
nodes . push ( category ) ;
}
const createButton = span ( "Create Element" ) ;
createButton . className = "createButton" ;
createButton . onclick = ( ) => {
2023-08-02 10:03:19 -04:00
const elementData = Storage . get ( "newElement" , { } ) ;
2023-07-30 18:46:46 -04:00
if ( ! elementData [ "name" ] ) {
document . getElementById ( "elementsManager/creator/meta/name/required" ) . style . display = "" ;
}
if ( ! elementData [ "category" ] ) {
document . getElementById ( "elementsManager/creator/meta/category/required" ) . style . display = "" ;
}
if ( ! elementData || ! elementData [ "name" ] || ! elementData [ "category" ] ) return ;
Storage . append ( "elements" , elementData ) ;
Storage . remove ( "newElement" ) ;
closeMenu ( ) ;
alert ( "Element successfully created" ) ;
}
nodes . push ( br ( ) , createButton ) ;
new MenuScreen ( )
. setTitle ( "Element creator" )
. setParentDivId ( "elementCreatorParent" )
. setInnerDivId ( "elementCreator" )
. appendInnerHtml ( "<span>Takes effect after reload</span>" )
. addNode ( nodes )
. build ( ) ;
}
const readFileAsync = ( file ) => {
return new Promise ( ( resolve , reject ) => {
const reader = new FileReader ( ) ;
reader . onload = ( event ) => {
resolve ( JSON . parse ( event . target . result ) ) ;
} ;
reader . onerror = ( event ) => {
reject ( event . target . error ) ;
} ;
reader . readAsText ( file ) ;
} ) ;
}
const settingsLoader = ( ) => {
const nodes = [ ] ;
let settings = Storage . get ( "settings" , {
clearElements : false ,
allowFreeRemoval : false
} , true ) ;
if ( ! settings ) {
settings = {
clearElementsButton : false ,
allowFreeRemoval : false
} ;
Storage . set ( "settings" , settings ) ;
}
const elements = createDiv ( ) ;
const elementsTitle = span ( "Elements" ) ;
elementsTitle . className = "categoryTitle" ;
elements . appendChild ( elementsTitle ) ;
const importDiv = createDiv ( ) ;
document . addEventListener ( "change" , async ( ev ) => {
if ( ev . target && ev . target . id == "importElements" ) {
const promises = [ ] ;
for ( const file of ev . target . files ) {
promises . push ( readFileAsync ( file ) ) ;
}
const res = await Promise . all ( promises ) ;
const result = [ ] ;
for ( const array of res ) {
for ( const element of array ) {
if ( result . find ( r => r . name == element . name ) ) continue ;
result . push ( element ) ;
}
}
importElements ( result ) ;
}
} )
const importInput = document . createElement ( "input" ) ;
importInput . type = "file" ;
importInput . accept = "application/JSON, application/json" ;
importInput . id = "importElements" ;
importInput . style . display = "none" ;
const importLabel = document . createElement ( "label" ) ;
importLabel . setAttribute ( "for" , "importElements" ) ;
importLabel . innerText = "Import Elements"
importLabel . className = "hoverableText" ;
importDiv . appendChild ( importInput ) ;
importDiv . appendChild ( importLabel ) ;
elements . appendChild ( importDiv ) ;
const exportButton = span ( "Export Elements" ) ;
exportButton . className = "hoverableText" ;
exportButton . onclick = ( ) => { exportElements ( ) } ;
elements . appendChild ( exportButton ) ;
const clearButton = span ( "Clear custom elements" ) ;
clearButton . className = "hoverableText" ;
clearButton . onclick = ( ) => {
if ( confirm ( "Are you sure you want to remove all the custom elements?" ) ) {
Storage . remove ( "elements" ) ;
const customElementsList = document . getElementById ( "customElementsList" ) ;
customElementsList . replaceChildren ( ) ;
const message = document . getElementById ( "noCustomElementsMessage" ) ;
message . style . display = "" ;
}
}
elements . appendChild ( br ( ) ) ;
elements . appendChild ( clearButton ) ;
elements . appendChild ( br ( ) ) ;
const general = createDiv ( ) ;
general . innerHTML += ` <span class="categoryTitle">General</span><br> ` ;
const clearElementsDiv = createDiv ( ) ;
clearElementsDiv . innerHTML += "<span>Clear custom elements on import</span>" ;
const clearElementsButton = document . createElement ( "input" ) ;
clearElementsButton . className = "toggleInput" ;
clearElementsButton . type = "button" ;
if ( settings && settings . clearElements ) {
clearElementsButton . value = "ON" ;
2023-08-02 10:03:19 -04:00
clearElementsButton . setAttribute ( "state" , "1" ) ;
2023-07-30 18:46:46 -04:00
} else {
clearElementsButton . value = "OFF" ;
2023-08-02 10:03:19 -04:00
clearElementsButton . setAttribute ( "state" , "0" ) ;
2023-07-30 18:46:46 -04:00
}
clearElementsButton . onclick = ( ev ) => {
toggleSetting ( "clearElements" , ev . target )
}
clearElementsDiv . appendChild ( clearElementsButton ) ;
general . appendChild ( clearElementsDiv )
const allowRemovalDiv = createDiv ( ) ;
allowRemovalDiv . innerHTML += "<span>Allow free element removal</span>" ;
const allowRemovalButton = document . createElement ( "input" ) ;
allowRemovalButton . className = "toggleInput" ;
allowRemovalButton . type = "button" ;
if ( settings && settings . allowFreeRemoval ) {
allowRemovalButton . value = "ON" ;
2023-08-02 10:03:19 -04:00
allowRemovalButton . setAttribute ( "state" , "1" ) ;
2023-07-30 18:46:46 -04:00
} else {
allowRemovalButton . value = "OFF" ;
2023-08-02 10:03:19 -04:00
allowRemovalButton . setAttribute ( "state" , "0" ) ;
2023-07-30 18:46:46 -04:00
}
allowRemovalButton . onclick = ( ev ) => {
2023-08-02 10:03:19 -04:00
toggleSetting ( "allowFreeRemoval" , ev . target ) ;
2023-07-30 18:46:46 -04:00
}
allowRemovalDiv . appendChild ( allowRemovalButton ) ;
general . appendChild ( allowRemovalDiv ) ;
const customElements = createDiv ( ) ;
const customElementsTitle = span ( "Custom Elements" ) ;
customElementsTitle . className = "categoryTitle" ;
customElements . appendChild ( customElementsTitle ) ;
const list = document . createElement ( "ul" ) ;
list . id = "customElementsList" ;
const elements _ = Storage . get ( "elements" , [ ] ) ;
const deleted = Storage . get ( "deletedElements" , [ ] ) ;
for ( const element of elements _ ) {
const li = document . createElement ( "li" ) ;
li . id = "customElementsList/" + element ;
const text = span ( element . name ) ;
text . onclick = ( ) => {
Storage . set ( "currentElement" , element . name ) ;
openMenu ( "elementManager" , true ) ;
}
li . appendChild ( text ) ;
const removeButton = span ( "X" ) ;
removeButton . className = "elementRemoveButton" ;
removeButton . onclick = ( ) => {
if ( confirm ( "Are you sure you want to delete that element?" ) ) {
delete elements [ element . name ] ;
Storage . filter ( "elements" , a => a . name != element . name ) ;
li . style . display = "none" ;
document . getElementById ( "customElementsList/" + key ) . remove ( ) ;
if ( document . getElementById ( "customElementsList" ) . children . length == 0 ) {
document . getElementById ( "noCustomElementsMessage" ) . style . display = "" ;
}
}
}
const emptySpan = span ( " " )
emptySpan . className = "elementRemoveButton" ;
li . appendChild ( emptySpan ) ;
li . appendChild ( removeButton )
list . appendChild ( li ) ;
}
const message = span ( "There are no custom elements" ) ;
message . id = "noCustomElementsMessage" ;
if ( elements _ . length > 0 ) message . style . display = "none" ;
customElements . appendChild ( br ( ) ) ;
customElements . appendChild ( message ) ;
customElements . appendChild ( list ) ;
const deletedElements = createDiv ( ) ;
const deletedElementsTitle = span ( "Deleted Elements" ) ;
deletedElementsTitle . className = "categoryTitle" ;
deletedElements . appendChild ( deletedElementsTitle ) ;
deletedElements . appendChild ( br ( ) ) ;
const deletedElementsInfo = span ( "Elements will reappear after reload.\nCustom elements get deleted permanently." ) ;
deletedElements . appendChild ( br ( ) ) ;
deletedElements . appendChild ( deletedElementsInfo ) ;
const deletedElementsMessage = span ( "There are no deleted elements" ) ;
deletedElementsMessage . style . display = deleted . length > 0 ? "none" : "" ;
deletedElementsMessage . id = "noDeletedElementsMessage" ;
const deletedElementsList = document . createElement ( "ul" ) ;
deletedElementsList . id = "deletedElementsList" ;
for ( const element of deleted ) {
const li = document . createElement ( "li" ) ;
li . innerText = element ;
li . className = "hoverableText"
li . onclick = ( ) => {
if ( confirm ( "Are you sure you want to re-add '" + element + "'?" ) ) {
Storage . filter ( "deletedElements" , a => a != element ) ;
li . style . display = "none" ;
if ( Storage . get ( "deletedElements" , [ ] ) . length == 0 ) {
document . getElementById ( "noDeletedElementsMessage" ) . style . display = "" ;
}
}
}
deletedElementsList . appendChild ( li ) ;
}
deletedElements . appendChild ( br ( ) ) ;
deletedElements . appendChild ( deletedElementsMessage ) ;
deletedElements . appendChild ( deletedElementsList ) ;
nodes . push ( elements , br ( ) , general , br ( ) , customElements , br ( ) , deletedElements ) ;
new MenuScreen ( )
. setTitle ( "Elements Manager Settings" )
. setParentDivId ( "elementsManagerSettingsParent" )
. setInnerDivId ( "elementsManagerSettings" )
. setCloseButtonText ( "<" )
. addNode ( nodes )
. build ( ) ;
}
menuScreens . elementsManager = {
name : "Elements Manager" ,
parentDiv : "elementsManagerParent" ,
buttonDescription : "Elements manager" ,
show : true ,
loader : elementsManagerLoader
}
menuScreens . elementManager = {
name : "Element Manager" ,
parentDiv : "elementManagerParent" ,
show : false ,
loader : elementManagerLoader ,
preOpen : ( ) => {
2023-08-03 06:24:45 -04:00
Storage . remove ( "tempChanges" ) ;
2023-07-30 18:46:46 -04:00
const currentElement = Storage . get ( "currentElement" ) ;
2023-08-02 10:03:19 -04:00
if ( ! currentElement ) return closeMenu ( ) ;
2023-07-30 18:46:46 -04:00
const element = elements [ currentElement ] ;
2023-08-02 10:03:19 -04:00
if ( ! element ) return closeMenu ( ) ;
2023-07-30 18:46:46 -04:00
for ( const key of Object . keys ( properties ) ) {
for ( const prop of properties [ key ] ) {
const id = "elementsManager/" + key + "/" + prop . name ;
const el = document . getElementById ( id ) ;
if ( prop . type instanceof Array ) {
if ( element [ prop . name ] ) {
if ( element [ prop . name ] instanceof Array ) {
el . setAttribute ( "value" , element [ prop . name ] . join ( ";" ) ) ;
} else {
const type = {
string : "text" ,
number : "number" ,
color : "color"
}
el . setAttribute ( "type" , type [ prop . type [ 0 ] ] ) ;
el . setAttribute ( "value" , parseColor ( element [ prop . name ] ) ) ;
}
} else {
if ( prop . type [ 0 ] == "color" ) {
el . setAttribute ( "type" , "color" ) ;
el . setAttribute ( "value" , "#ff0000" ) ;
}
else el . setAttribute ( "value" , "none" ) ;
}
} else {
if ( element [ prop . name ] ) {
if ( prop . type == "boolean" ) {
el . setAttribute ( "value" , element [ prop . name ] ? "ON" : "OFF" ) ;
el . setAttribute ( "state" , element [ prop . name ] ? "1" : "0" ) ;
} else if ( prop . type == "color" ) {
el . setAttribute ( "value" , parseColor ( element [ prop . name ] ) ) ;
} else if ( prop . name == "behavior" ) {
const behavior = element [ prop . name ] ;
const index = Object . keys ( behaviors ) . map ( b => ` ${ behaviors [ b ] instanceof Array ? behaviors [ b ] . join ( ";" ) : behaviors [ b ] } ` ) . indexOf ( behavior instanceof Array ? behavior . join ( ";" ) : behavior ) ;
if ( index == - 1 ) {
document . getElementById ( id + "/option/custom" ) . selected = true ;
document . getElementById ( id + "/textInput" ) . style . display = "" ;
document . getElementById ( id + "/textInput" ) . setAttribute ( "value" , behavior . join ( ";" ) )
} else {
document . getElementById ( id + "/textInput" ) . style . display = "none" ;
document . getElementById ( id + "/option/" + index ) . selected = true ;
}
} else {
el . setAttribute ( "value" , element [ prop . name ] instanceof Function ? element [ prop . name ] . toString ( ) : element [ prop . name ] ) ;
}
} else if ( prop . name == "name" ) {
el . setAttribute ( "value" , currentElement ) ;
2023-08-03 06:24:45 -04:00
} else if ( prop . name == "behavior" ) {
console . log ( element [ prop . name ] , element ) ;
document . getElementById ( id + "/option/none" ) . selected = true ;
document . getElementById ( id + "/textInput" ) . style . display = "none" ;
2023-07-30 18:46:46 -04:00
} else {
const default _ = {
string : "none" ,
number : 0 ,
color : "#ff0000"
}
if ( prop . type == "boolean" ) {
el . setAttribute ( "value" , "OFF" ) ;
el . setAttribute ( "state" , "0" ) ;
} else {
el . setAttribute ( "value" , default _ [ prop . type ] )
}
}
}
}
}
} ,
2023-08-03 06:24:45 -04:00
close : ( ) => {
if ( ! Storage . get ( "tempChanges" ) || ! Storage . get ( "tempChanges" , [ ] ) . length || confirm ( "Are you sure you want to close the menu without saving the changes?" ) ) {
const menuParent = document . getElementById ( "elementManagerParent" ) ;
menuParent . style . display = "none" ;
Storage . remove ( "tempChanges" ) ;
Storage . remove ( "noClose" ) ;
} else {
Storage . set ( "noClose" , true ) ;
}
} ,
2023-07-30 18:46:46 -04:00
onClose : ( ) => {
2023-08-03 06:24:45 -04:00
if ( ! Storage . get ( "noClose" ) ) {
showingMenu = false ;
openMenu ( "elementsManager" , true ) ;
}
2023-07-30 18:46:46 -04:00
}
}
menuScreens . elementCreator = {
name : "Element Creator" ,
parentDiv : "elementCreatorParent" ,
show : false ,
loader : elementCreatorLoader ,
preOpen : ( ) => {
const elementData = { } ;
for ( const key of Object . keys ( properties ) ) {
for ( const prop of properties [ key ] ) {
if ( prop . creatorIgnore ) continue ;
const el = document . getElementById ( "elementsManager/creator/" + key + "/" + prop . name )
if ( prop . type instanceof Array ) {
el . setAttribute ( "value" , "none" ) ;
} else {
const default _ = {
string : "none" ,
number : 0 ,
color : "#ff0000" ,
boolean : "OFF"
}
if ( prop . name == "name" ) {
let name = ` NewElement ${ Math . floor ( Math . random ( ) * 10000 ) + 1 } ` ;
if ( elements [ name ] ) {
let i = 0 ;
let j = 1 ;
// try 100 times, then increase the pool
while ( elements [ name ] ) {
name = ` NewElement ${ Math . floor ( Math . random ( ) * ( 10000 * i ) ) + 1 } ` ;
i ++ ;
if ( i <= 100 ) {
j ++ ;
i = 0 ;
}
// if already incresed the pool 5 times and there is still nothing, just break and use static name
if ( j <= 5 ) {
name = "NewElement" ;
break ;
}
}
}
el . setAttribute ( "value" , name )
elementData [ prop . name ] = name ;
} else if ( prop . name == "category" ) {
el . setAttribute ( "value" , "other" )
elementData [ prop . name ] = "other" ;
} else if ( prop . name == "behavior" ) {
elementData [ prop . name ] = "POWDER_OLD" ;
} else {
el . setAttribute ( "value" , default _ [ prop . type ] ) ;
}
if ( prop . type == "boolean" ) {
el . setAttribute ( "state" , "0" )
}
}
}
}
Storage . set ( "newElement" , elementData ) ;
} ,
close : ( ) => {
if ( ! Storage . get ( "newElement" ) || confirm ( "Are you sure you want to close the menu without creating the element?" ) ) {
const menuParent = document . getElementById ( "elementCreatorParent" ) ;
menuParent . style . display = "none" ;
Storage . remove ( "newElement" ) ;
Storage . remove ( "noClose" ) ;
} else {
Storage . set ( "noClose" , true ) ;
}
} ,
onClose : ( ) => {
if ( ! Storage . get ( "noClose" ) ) {
showingMenu = false ;
openMenu ( "elementsManager" , true ) ;
}
}
}
menuScreens . elementsSettings = {
name : "Elements Settings" ,
show : false ,
parentDiv : "elementsManagerSettingsParent" ,
loader : settingsLoader ,
preOpen : ( ) => {
const customElements = Storage . get ( "elements" , [ ] ) ;
const message = document . getElementById ( "noCustomElementsMessage" ) ;
const list = document . getElementById ( "customElementsList" ) ;
if ( customElements . length > 0 ) {
message . style . display = "none" ;
list . style . display = "" ;
list . replaceChildren ( ) ;
for ( const element of customElements ) {
const li = document . createElement ( "li" ) ;
const text = span ( element . name ) ;
text . onclick = ( ) => {
Storage . set ( "currentElement" , element . name ) ;
openMenu ( "elementManager" , true ) ;
}
li . appendChild ( text ) ;
const removeButton = span ( "X" ) ;
removeButton . className = "elementRemoveButton" ;
removeButton . onclick = ( ) => {
if ( confirm ( "Are you sure you want to delete that element?" ) ) {
delete elements [ element . name ] ;
Storage . filter ( "elements" , a => a . name != element . name ) ;
li . style . display = "none" ;
document . getElementById ( "customElementsList/" + key ) . remove ( ) ;
if ( document . getElementById ( "customElementsList" ) . children . length == 0 ) {
document . getElementById ( "noCustomElementsMessage" ) . style . display = "" ;
}
}
}
const emptySpan = span ( " " ) ;
emptySpan . className = "elementRemoveButton" ;
li . appendChild ( emptySpan ) ;
li . appendChild ( removeButton ) ;
list . appendChild ( li ) ;
}
} else {
list . style . display = "none" ;
message . style . display = "" ;
}
} ,
onClose : ( ) => {
const settings = Storage . get ( "settings" , { allowFreeRemoval : false , clearElements : false } , true ) ;
const elements _ = Storage . get ( "elements" , [ ] ) . map ( e => e . name ) ;
2023-08-02 10:03:19 -04:00
const lastFreeRemoval = Storage . get ( "lastFreeRemoval" , false ) ;
2023-07-30 18:46:46 -04:00
if ( settings . allowFreeRemoval && ! lastFreeRemoval ) {
for ( const li of document . getElementById ( "elementsList" ) . children ) {
const name = li . querySelector ( "span" ) . innerText
if ( ! elements _ . includes ( name ) && li . getElementsByClassName ( "elementRemoveButton" ) . length == 0 ) {
const removeButton = span ( "X" ) ;
removeButton . className = "elementRemoveButton" ;
removeButton . onclick = ( ) => {
if ( confirm ( "Are you sure you want to delete that element?" ) ) {
delete elements [ name ] ;
li . style . display = "none" ;
Storage . append ( "deletedElements" , name ) ;
document . getElementById ( "noDeletedElementsMessage" ) . style . display = "none" ;
const li2 = document . createElement ( "li" ) ;
li2 . innerText = name ;
li2 . onclick = ( ) => {
if ( confirm ( "Are you sure you want to re-add '" + name + "'?" ) ) {
Storage . filter ( "deletedElements" , a => a != name ) ;
li2 . style . display = "none" ;
}
}
document . getElementById ( "deletedElementsList" ) . appendChild ( li2 ) ;
}
}
const emptySpan = span ( " " )
emptySpan . className = "elementRemoveButton" ;
li . appendChild ( emptySpan ) ;
li . appendChild ( removeButton )
}
}
} else if ( ! settings . allowFreeRemoval && lastFreeRemoval ) {
for ( const li of document . getElementById ( "elementsList" ) . children ) {
if ( ! elements _ . includes ( li . querySelector ( "span" ) . innerText ) )
li . querySelectorAll ( ".elementRemoveButton" ) . forEach ( e => e . remove ( ) ) ;
}
}
2023-08-02 10:03:19 -04:00
Storage . set ( "lastFreeRemoval" , settings . allowFreeRemoval ) ;
2023-07-30 18:46:46 -04:00
openMenu ( "elementsManager" , true ) ;
}
}
runAfterLoadList . push ( cssInject , loadChanges ) ;
} else {
2023-08-02 08:56:51 -04:00
enabledMods . unshift ( "mods/betterMenuScreens.js" ) ;
2023-07-30 18:46:46 -04:00
localStorage . setItem ( "enabledMods" , JSON . stringify ( enabledMods ) ) ;
window . location . reload ( ) ;
}