diff --git a/code/_globalvars/_regexes.dm b/code/_globalvars/_regexes.dm index d0098e3711f..af98089fe4e 100644 --- a/code/_globalvars/_regexes.dm +++ b/code/_globalvars/_regexes.dm @@ -1,2 +1,3 @@ GLOBAL_DATUM_INIT(filename_forbidden_chars, /regex, regex(@{""|[\\\n\t/?%*:|<>]|\.\."}, "g")) +GLOBAL_DATUM_INIT(is_color, /regex, regex("^#\[0-9a-fA-F]{6}$")) GLOBAL_PROTECT(filename_forbidden_chars) diff --git a/code/game/dna/mutations/mutation_powers.dm b/code/game/dna/mutations/mutation_powers.dm index 75efba0c78a..f9cb5574c82 100644 --- a/code/game/dna/mutations/mutation_powers.dm +++ b/code/game/dna/mutations/mutation_powers.dm @@ -818,9 +818,10 @@ M.change_gender(FEMALE) if(eyes_organ) - var/new_eyes = input("Please select eye color.", "Character Generation", eyes_organ.eye_color) as null|color - if(new_eyes) - M.change_eye_color(new_eyes) + var/new_eyes = tgui_input_color(user, "Please select eye color.", "Character Generation", eyes_organ.eye_color) + if(isnull(new_eyes)) + return + M.change_eye_color(new_eyes) if(istype(head_organ)) //Alt heads. @@ -838,14 +839,14 @@ if(new_style) M.change_hair(new_style) - var/new_hair = input("Please select hair color.", "Character Generation", head_organ.hair_colour) as null|color - if(new_hair) + var/new_hair = tgui_input_color(user, "Please select hair color.", "Character Generation", head_organ.hair_colour) + if(!isnull(new_hair)) M.change_hair_color(new_hair) var/datum/sprite_accessory/hair_style = GLOB.hair_styles_public_list[head_organ.h_style] if(hair_style.secondary_theme && !hair_style.no_sec_colour) - new_hair = input("Please select secondary hair color.", "Character Generation", head_organ.sec_hair_colour) as null|color - if(new_hair) + new_hair = tgui_input_color(user, "Please select secondary hair color.", "Character Generation", head_organ.sec_hair_colour) + if(!isnull(new_hair)) M.change_hair_color(new_hair, TRUE) // facial hair @@ -855,25 +856,25 @@ if(new_style) M.change_facial_hair(new_style) - var/new_facial = input("Please select facial hair color.", "Character Generation", head_organ.facial_colour) as null|color - if(new_facial) + var/new_facial = tgui_input_color(user, "Please select facial hair color.", "Character Generation", head_organ.facial_colour) + if(!isnull(new_facial)) M.change_facial_hair_color(new_facial) var/datum/sprite_accessory/facial_hair_style = GLOB.facial_hair_styles_list[head_organ.f_style] if(facial_hair_style.secondary_theme && !facial_hair_style.no_sec_colour) - new_facial = input("Please select secondary facial hair color.", "Character Generation", head_organ.sec_facial_colour) as null|color - if(new_facial) + new_facial = tgui_input_color(user, "Please select secondary facial hair color.", "Character Generation", head_organ.sec_facial_colour) + if(!isnull(new_facial)) M.change_facial_hair_color(new_facial, TRUE) //Head accessory. if(head_organ.dna.species.bodyflags & HAS_HEAD_ACCESSORY) var/list/valid_head_accessories = M.generate_valid_head_accessories() var/new_head_accessory = tgui_input_list(user, "Please select head accessory style", "Character Generation", valid_head_accessories) - if(new_head_accessory) + if(!isnull(new_head_accessory)) M.change_head_accessory(new_head_accessory) - var/new_head_accessory_colour = input("Please select head accessory colour.", "Character Generation", head_organ.headacc_colour) as null|color - if(new_head_accessory_colour) + var/new_head_accessory_colour = tgui_input_color(user, "Please select head accessory color.", "Character Generation", head_organ.headacc_colour) + if(!isnull(new_head_accessory_colour)) M.change_head_accessory_color(new_head_accessory_colour) @@ -882,7 +883,7 @@ var/list/valid_body_accessories = M.generate_valid_body_accessories() if(length(valid_body_accessories) > 1) //By default valid_body_accessories will always have at the very least a 'none' entry populating the list, even if the user's species is not present in any of the list items. var/new_body_accessory = tgui_input_list(user, "Please select body accessory style", "Character Generation", valid_body_accessories) - if(new_body_accessory) + if(!isnull(new_body_accessory)) M.change_body_accessory(new_body_accessory) if(istype(head_organ)) @@ -890,32 +891,32 @@ if(M.dna.species.bodyflags & HAS_HEAD_MARKINGS) var/list/valid_head_markings = M.generate_valid_markings("head") var/new_marking = tgui_input_list(user, "Please select head marking style", "Character Generation", valid_head_markings) - if(new_marking) + if(!isnull(new_marking)) M.change_markings(new_marking, "head") - var/new_marking_colour = input("Please select head marking colour.", "Character Generation", M.m_colours["head"]) as null|color - if(new_marking_colour) + var/new_marking_colour = tgui_input_color(user, "Please select head marking color.", "Character Generation", M.m_colours["head"]) + if(!isnull(new_marking_colour)) M.change_marking_color(new_marking_colour, "head") //Body markings. if(M.dna.species.bodyflags & HAS_BODY_MARKINGS) var/list/valid_body_markings = M.generate_valid_markings("body") var/new_marking = tgui_input_list(user, "Please select body marking style", "Character Generation", valid_body_markings) - if(new_marking) + if(!isnull(new_marking)) M.change_markings(new_marking, "body") - var/new_marking_colour = input("Please select body marking colour.", "Character Generation", M.m_colours["body"]) as null|color - if(new_marking_colour) + var/new_marking_colour = tgui_input_color(user, "Please select body marking color.", "Character Generation", M.m_colours["body"]) + if(!isnull(new_marking_colour)) M.change_marking_color(new_marking_colour, "body") //Tail markings. if(M.dna.species.bodyflags & HAS_TAIL_MARKINGS) var/list/valid_tail_markings = M.generate_valid_markings("tail") var/new_marking = tgui_input_list("Please select tail marking style", "Character Generation", valid_tail_markings) - if(new_marking) + if(!isnull(new_marking)) M.change_markings(new_marking, "tail") - var/new_marking_colour = input("Please select tail marking colour.", "Character Generation", M.m_colours["tail"]) as null|color - if(new_marking_colour) + var/new_marking_colour = tgui_input_color(user, "Please select tail marking color.", "Character Generation", M.m_colours["tail"]) + if(!isnull(new_marking_colour)) M.change_marking_color(new_marking_colour, "tail") //Skin tone. @@ -944,8 +945,8 @@ //Skin colour. if(M.dna.species.bodyflags & HAS_SKIN_COLOR) - var/new_body_colour = input("Please select body colour.", "Character Generation", M.skin_colour) as null|color - if(new_body_colour) + var/new_body_colour = tgui_input_color(user, "Please select body color.", "Character Generation", M.skin_colour) + if(!isnull(new_body_colour)) M.change_skin_color(new_body_colour) M.update_dna() diff --git a/code/game/machinery/dye_generator.dm b/code/game/machinery/dye_generator.dm index 29cff395050..a1207bac1c7 100644 --- a/code/game/machinery/dye_generator.dm +++ b/code/game/machinery/dye_generator.dm @@ -44,7 +44,9 @@ src.add_fingerprint(user) if(stat & (BROKEN|NOPOWER)) return - var/temp = input(usr, "Choose a dye color", "Dye Color") as color + var/temp = tgui_input_color(user, "Please select a dye color", "Dye Color") + if(isnull(temp)) + return dye_color = temp set_light(2, l_color = temp) diff --git a/code/game/objects/items/crayons.dm b/code/game/objects/items/crayons.dm index 13fb3a5cf09..b398722ace5 100644 --- a/code/game/objects/items/crayons.dm +++ b/code/game/objects/items/crayons.dm @@ -292,7 +292,9 @@ if(!Adjacent(usr) || usr.incapacitated()) return if(href_list["color"]) - var/temp = input(usr, "Please select colour.", "Crayon colour") as color + var/temp = tgui_input_color(usr, "Please select crayon color.", "Crayon color") + if(isnull(temp)) + return colour = temp update_window(usr) else @@ -328,7 +330,9 @@ if("Change Drawing") ..() if("Change Color") - colour = input(user,"Choose Color") as color + colour = tgui_input_color(user,"Please select a paint color.","Spray Can Color") + if(isnull(colour)) + return update_icon() /obj/item/toy/crayon/spraycan/afterattack(atom/target, mob/user as mob, proximity) diff --git a/code/game/objects/structures/curtains.dm b/code/game/objects/structures/curtains.dm index 7d2ed5496b7..c569526f0b6 100644 --- a/code/game/objects/structures/curtains.dm +++ b/code/game/objects/structures/curtains.dm @@ -41,7 +41,7 @@ /obj/structure/curtain/attackby(obj/item/W, mob/user) if(istype(W, /obj/item/toy/crayon)) - color = input(user, "Choose Color") as color + color = tgui_input_color(user,"Please choose a color.", "Curtain Color") return return ..() diff --git a/code/game/turfs/simulated/floor/light_floor.dm b/code/game/turfs/simulated/floor/light_floor.dm index dfc49b4bd4d..5d3e3982e24 100644 --- a/code/game/turfs/simulated/floor/light_floor.dm +++ b/code/game/turfs/simulated/floor/light_floor.dm @@ -69,8 +69,8 @@ if(!I.use_tool(src, user, 0, volume = I.tool_volume)) return if(!light_broken) - var/new_color = input(user, "Select a bulb color", "Select a bulb color", color) as color|null - if(!new_color) + var/new_color = tgui_input_color(user, "Select a bulb color", "Select a bulb color", color) + if(isnull(new_color)) return // Cancel if they walked away diff --git a/code/modules/admin/admin_verbs.dm b/code/modules/admin/admin_verbs.dm index eb38ae84804..b43f852278e 100644 --- a/code/modules/admin/admin_verbs.dm +++ b/code/modules/admin/admin_verbs.dm @@ -1103,8 +1103,8 @@ GLOBAL_LIST_INIT(view_runtimes_verbs, list( return message = strip_html(message, 500) - var/message_color = input(src, "Input your message color:", "Color Selector") as color|null - if(!message_color) + var/message_color = tgui_input_color(src, "Input your message color:", "Admin Message - Color Selector") + if(isnull(message_color)) return show_blurb(about_to_be_banned, 15, message, null, "center", "center", message_color, null, null, 1) diff --git a/code/modules/client/preference/link_processing.dm b/code/modules/client/preference/link_processing.dm index b75a04e59c5..7e51f38c49f 100644 --- a/code/modules/client/preference/link_processing.dm +++ b/code/modules/client/preference/link_processing.dm @@ -329,17 +329,16 @@ if("hair") if(!(S.bodyflags & BALD)) - var/input = "Choose your character's hair colour:" - var/new_hair = input(user, input, "Character Preference", active_character.h_colour) as color|null - if(new_hair) + var/new_hair = tgui_input_color(user, "Choose your character's hair color.", "Character Preference", active_character.h_colour) + if(!isnull(new_hair)) active_character.h_colour = new_hair if("secondary_hair") if(!(S.bodyflags & BALD)) var/datum/sprite_accessory/hair_style = GLOB.hair_styles_public_list[active_character.h_style] if(hair_style.secondary_theme && !hair_style.no_sec_colour) - var/new_hair = input(user, "Choose your character's secondary hair colour:", "Character Preference", active_character.h_sec_colour) as color|null - if(new_hair) + var/new_hair = tgui_input_color(user, "Choose your character's secondary hair color.", "Character Preference", active_character.h_sec_colour) + if(!isnull(new_hair)) active_character.h_sec_colour = new_hair if("h_style") @@ -387,9 +386,10 @@ active_character.h_grad_offset_y = clamp(text2num(expl[2]) || 0, -16, 16) if("h_grad_colour") - var/result = input(user, "Choose your character's hair gradient colour:", "Character Preference", active_character.h_grad_colour) as color|null - if(result) - active_character.h_grad_colour = result + var/result = tgui_input_color(user, "Choose your character's hair gradient color:", "Character Preference", active_character.h_grad_colour) + if(isnull(result)) + return + active_character.h_grad_colour = result if("h_grad_alpha") var/result = tgui_input_number(user, "Choose your character's hair gradient alpha (0-255):", "Character Preference", active_character.h_grad_alpha, 255) @@ -398,16 +398,17 @@ active_character.h_grad_alpha = clamp(result, 0, 255) if("runechat_color") - var/result = input(user, "Choose your character's runechat color:", "Character Preference", active_character.runechat_color) as color|null - if(result) - active_character.runechat_color = result + var/result = tgui_input_color(user, "Choose your character's runechat color:", "Character Preference", active_character.runechat_color) + if(isnull(result)) + return + active_character.runechat_color = result if("headaccessory") if(S.bodyflags & HAS_HEAD_ACCESSORY) //Species with head accessories. - var/input = "Choose the colour of your your character's head accessory:" - var/new_head_accessory = input(user, input, "Character Preference", active_character.hacc_colour) as color|null - if(new_head_accessory) - active_character.hacc_colour = new_head_accessory + var/new_head_accessory = tgui_input_color(user, "Choose the color of your your character's head accessory.", "Character Preference", active_character.hacc_colour) + if(isnull(new_head_accessory)) + return + active_character.hacc_colour = new_head_accessory if("ha_style") if(S.bodyflags & HAS_HEAD_ACCESSORY) //Species with head accessories. @@ -485,10 +486,10 @@ if("m_head_colour") if(S.bodyflags & HAS_HEAD_MARKINGS) //Species with head markings. - var/input = "Choose the colour of your your character's head markings:" - var/new_markings = input(user, input, "Character Preference", active_character.m_colours["head"]) as color|null - if(new_markings) - active_character.m_colours["head"] = new_markings + var/new_markings = tgui_input_color(user, "Choose the color of your your character's head markings.", "Character Preference", active_character.m_colours["head"]) + if(isnull(new_markings)) + return + active_character.m_colours["head"] = new_markings if("m_style_body") if(S.bodyflags & HAS_BODY_MARKINGS) //Species with body markings/tattoos. @@ -508,10 +509,10 @@ if("m_body_colour") if(S.bodyflags & HAS_BODY_MARKINGS) //Species with body markings/tattoos. - var/input = "Choose the colour of your your character's body markings:" - var/new_markings = input(user, input, "Character Preference", active_character.m_colours["body"]) as color|null - if(new_markings) - active_character.m_colours["body"] = new_markings + var/new_markings = tgui_input_color(user, "Choose the color of your your character's body markings.", "Character Preference", active_character.m_colours["body"]) + if(isnull(new_markings)) + return + active_character.m_colours["body"] = new_markings if("m_style_tail") if(S.bodyflags & HAS_TAIL_MARKINGS) //Species with tail markings. @@ -537,10 +538,10 @@ if("m_tail_colour") if(S.bodyflags & HAS_TAIL_MARKINGS) //Species with tail markings. - var/input = "Choose the colour of your your character's tail markings:" - var/new_markings = input(user, input, "Character Preference", active_character.m_colours["tail"]) as color|null - if(new_markings) - active_character.m_colours["tail"] = new_markings + var/new_markings = tgui_input_color(user, "Choose the color of your your character's tail markings.", "Character Preference", active_character.m_colours["tail"]) + if(isnull(new_markings)) + return + active_character.m_colours["tail"] = new_markings if("body_accessory") var/list/possible_body_accessories = list() @@ -565,17 +566,19 @@ if("facial") if(!(S.bodyflags & SHAVED)) - var/new_facial = input(user, "Choose your character's facial-hair colour:", "Character Preference", active_character.f_colour) as color|null - if(new_facial) - active_character.f_colour = new_facial + var/new_facial = tgui_input_color(user, "Choose your character's facial-hair color:", "Character Preference", active_character.f_colour) + if(isnull(new_facial)) + return + active_character.f_colour = new_facial if("secondary_facial") if(!(S.bodyflags & SHAVED)) var/datum/sprite_accessory/facial_hair_style = GLOB.facial_hair_styles_list[active_character.f_style] if(facial_hair_style.secondary_theme && !facial_hair_style.no_sec_colour) - var/new_facial = input(user, "Choose your character's secondary facial-hair colour:", "Character Preference", active_character.f_sec_colour) as color|null - if(new_facial) - active_character.f_sec_colour = new_facial + var/new_facial = tgui_input_color(user, "Choose your character's secondary facial-hair color:", "Character Preference", active_character.f_sec_colour) + if(isnull(new_facial)) + return + active_character.f_sec_colour = new_facial if("f_style") var/list/valid_facial_hairstyles = list() @@ -658,34 +661,37 @@ active_character.socks = new_socks if("eyes") - var/new_eyes = input(user, "Choose your character's eye colour:", "Character Preference", active_character.e_colour) as color|null - if(new_eyes) - active_character.e_colour = new_eyes + var/new_eyes = tgui_input_color(user, "Choose your character's eye color:", "Character Preference", active_character.e_colour) + if(isnull(new_eyes)) + return + active_character.e_colour = new_eyes if("s_tone") if(S.bodyflags & HAS_SKIN_TONE) var/new_s_tone = input(user, "Choose your character's skin-tone:\n(Light 1 - 220 Dark)", "Character Preference") as num|null - if(!new_s_tone) + if(isnull(new_s_tone)) return active_character.s_tone = 35 - max(min(round(new_s_tone), 220), 1) else if(S.bodyflags & HAS_ICON_SKIN_TONE) var/const/MAX_LINE_ENTRIES = 4 var/prompt = "Choose your character's skin tone: 1-[length(S.icon_skin_tones)]\n(Light to Dark)" var/skin_c = tgui_input_number(user, prompt, "Character Preference", active_character.s_tone, length(S.icon_skin_tones), 1) - if(!skin_c) + if(isnull(skin_c)) return active_character.s_tone = skin_c if("skin") if((S.bodyflags & HAS_SKIN_COLOR) || GLOB.body_accessory_by_species[active_character.species] || check_rights(R_ADMIN, 0, user)) - var/new_skin = input(user, "Choose your character's skin colour: ", "Character Preference", active_character.s_colour) as color|null - if(new_skin) - active_character.s_colour = new_skin + var/new_skin = tgui_input_color(user, "Choose your character's skin color: ", "Character Preference", active_character.s_colour) + if(isnull(new_skin)) + return + active_character.s_colour = new_skin if("ooccolor") - var/new_ooccolor = input(user, "Choose your OOC colour:", "Game Preference", ooccolor) as color|null - if(new_ooccolor) - ooccolor = new_ooccolor + var/new_ooccolor = tgui_input_color(user, "Choose your OOC color:", "Game Preference", ooccolor) + if(isnull(new_ooccolor)) + return + ooccolor = new_ooccolor if("bag") var/new_backbag = tgui_input_list(user, "Choose your character's style of bag", "Character Preference", GLOB.backbaglist) @@ -1015,8 +1021,9 @@ toggles2 ^= PREFTOGGLE_2_AFKWATCH if("UIcolor") - var/UI_style_color_new = input(user, "Choose your UI color, dark colors are not recommended!", UI_style_color) as color|null - if(!UI_style_color_new) return + var/UI_style_color_new = tgui_input_color(user, "Choose your UI color, dark colors are not recommended!", "Game Preference", UI_style_color) + if(isnull(UI_style_color_new)) + return UI_style_color = UI_style_color_new if(ishuman(usr)) //mid-round preference changes, for aesthetics @@ -1151,9 +1158,10 @@ screentip_mode = desired_screentip_mode if("screentip_color") - var/screentip_color_new = input(user, "Choose your screentip color", screentip_color) as color|null - if(screentip_color_new) - screentip_color = screentip_color_new + var/screentip_color_new = tgui_input_color(user, "Choose your screentip color", "Game Preference", screentip_color) + if(isnull(screentip_color_new)) + return + screentip_color = screentip_color_new if("edit_2fa") // Do this async so we arent holding up a topic() call diff --git a/code/modules/client/preference/loadout/gear_tweaks.dm b/code/modules/client/preference/loadout/gear_tweaks.dm index 459279c6175..37f90fb753c 100644 --- a/code/modules/client/preference/loadout/gear_tweaks.dm +++ b/code/modules/client/preference/loadout/gear_tweaks.dm @@ -33,7 +33,7 @@ /datum/gear_tweak/color/get_metadata(user, metadata) if(valid_colors) return input(user, "Choose an item color.", "Character Preference", metadata) as null|anything in valid_colors - return input(user, "Choose an item color.", "Global Preference", metadata) as color|null + return tgui_input_color(user, "Choose an item color.", "Global Preference", metadata) /datum/gear_tweak/color/tweak_item(obj/item/I, metadata) if(valid_colors && !(metadata in valid_colors)) diff --git a/code/modules/client/preference/preferences_toggles.dm b/code/modules/client/preference/preferences_toggles.dm index 9f4adb3e96d..081f8981d94 100644 --- a/code/modules/client/preference/preferences_toggles.dm +++ b/code/modules/client/preference/preferences_toggles.dm @@ -510,8 +510,8 @@ blackbox_message = "Set Own OOC" /datum/preference_toggle/special_toggle/set_ooc_color/set_toggles(client/user) - var/new_ooccolor = input(usr, "Please select your OOC color.", "OOC color", user.prefs.ooccolor) as color|null - if(new_ooccolor) + var/new_ooccolor = tgui_input_color(usr, "Please select your OOC color.", "OOC Color", user.prefs.ooccolor) + if(!isnull(new_ooccolor)) user.prefs.ooccolor = new_ooccolor to_chat(usr, "Your OOC color has been set to [new_ooccolor].") else diff --git a/code/modules/customitems/item_defines.dm b/code/modules/customitems/item_defines.dm index 59308ef3757..04a85751fb6 100644 --- a/code/modules/customitems/item_defines.dm +++ b/code/modules/customitems/item_defines.dm @@ -100,8 +100,8 @@ /obj/item/fluff/tattoo_gun/elliot_cybernetic_tat/attack_self(mob/user as mob) if(!used) - var/ink_color = input("Please select an ink color.", "Tattoo Ink Color", rgb(tattoo_r, tattoo_g, tattoo_b)) as color|null - if(ink_color && !(user.incapacitated() || used)) + var/ink_color = tgui_input_color(user, "Please select an ink color.", "Tattoo Ink Color", rgb(tattoo_r, tattoo_g, tattoo_b)) + if(!isnull(ink_color) && !(user.incapacitated() || used)) tattoo_r = color2R(ink_color) tattoo_g = color2G(ink_color) tattoo_b = color2B(ink_color) diff --git a/code/modules/mob/living/carbon/human/species/machine.dm b/code/modules/mob/living/carbon/human/species/machine.dm index 38b70dcfa4d..9243510ddb4 100644 --- a/code/modules/mob/living/carbon/human/species/machine.dm +++ b/code/modules/mob/living/carbon/human/species/machine.dm @@ -152,11 +152,11 @@ if(!head_organ) return if(!robohead.is_monitor) //If they've got a prosthetic head and it isn't a monitor, they've no screen to adjust. Instead, let them change the colour of their optics! - var/optic_colour = input(H, "Select optic colour", H.m_colours["head"]) as color|null + var/optic_colour = tgui_input_color(H, "Please select an optic color", "Select Optic Color", H.m_colours["head"]) if(H.incapacitated(TRUE, TRUE)) - to_chat(H, "You were interrupted while changing the colour of your optics.") + to_chat(H, "You were interrupted while changing the color of your optics.") return - if(optic_colour) + if(!isnull(optic_colour)) H.change_markings(optic_colour, "head") else if(robohead.is_monitor) //Means that the character's head is a monitor (has a screen). Time to customize. @@ -175,7 +175,7 @@ var/new_style = tgui_input_list(H, "Select a monitor display", "Monitor Display", hair) if(!new_style) return - var/new_color = input("Please select hair color.", "Monitor Color", head_organ.hair_colour) as null|color + var/new_color = tgui_input_color(H, "Please select hair color.", "Monitor Color", head_organ.hair_colour) if(H.incapacitated(TRUE, TRUE)) to_chat(H, "You were interrupted while changing your monitor display.") @@ -183,7 +183,7 @@ if(new_style) H.change_hair(new_style, 1) // The 1 is to enable custom sprites - if(new_color) + if(!isnull(new_color)) H.change_hair_color(new_color) /datum/species/machine/spec_electrocute_act(mob/living/carbon/human/H, shock_damage, source, siemens_coeff, flags) diff --git a/code/modules/mod/modules/modules_general.dm b/code/modules/mod/modules/modules_general.dm index ae880b5a2ea..3ef82f70ca5 100644 --- a/code/modules/mod/modules/modules_general.dm +++ b/code/modules/mod/modules/modules_general.dm @@ -251,8 +251,8 @@ /obj/item/mod/module/flashlight/configure_edit(key, value) switch(key) if("light_color") - value = input(usr, "Pick new light color", "Flashlight Color") as color|null - if(!value) + value = tgui_input_color(usr, "Pick new light color", "Flashlight Color", light_color) + if(isnull(value)) return if(is_color_dark(value, 50)) to_chat(mod.wearer, ("That is too dark")) diff --git a/code/modules/projectiles/guns/energy/kinetic_accelerator.dm b/code/modules/projectiles/guns/energy/kinetic_accelerator.dm index ad85a7e67f7..d995e7346fd 100644 --- a/code/modules/projectiles/guns/energy/kinetic_accelerator.dm +++ b/code/modules/projectiles/guns/energy/kinetic_accelerator.dm @@ -657,4 +657,4 @@ desc = "Causes kinetic accelerator bolts to have an adjustable-colored tracer trail and explosion. Use in-hand to change color." /obj/item/borg/upgrade/modkit/tracer/adjustable/attack_self(mob/user) - bolt_color = input(user,"","Choose Color",bolt_color) as color|null + bolt_color = tgui_input_color(user, "Please select a tracer color", "PKA Tracer Color", bolt_color) diff --git a/code/modules/tgui/modules/appearance_changer.dm b/code/modules/tgui/modules/appearance_changer.dm index ba291eb65a4..5b9730486e1 100644 --- a/code/modules/tgui/modules/appearance_changer.dm +++ b/code/modules/tgui/modules/appearance_changer.dm @@ -72,8 +72,8 @@ if("skin_color") if(can_change_skin_color()) - var/new_skin = input(usr, "Choose your character's skin colour: ", "Skin Color", owner.skin_colour) as color|null - if(new_skin && (!..()) && owner.change_skin_color(new_skin)) + var/new_skin = tgui_input_color(usr, "Choose your character's skin colour: ", "Skin Color", owner.skin_colour) + if(!isnull(new_skin) && (!..()) && owner.change_skin_color(new_skin)) update_dna() if("hair") @@ -83,32 +83,32 @@ if("hair_color") if(can_change(APPEARANCE_HAIR_COLOR)) - var/new_hair = input("Please select hair color.", "Hair Color", head_organ.hair_colour) as color|null - if(new_hair && (!..()) && owner.change_hair_color(new_hair)) + var/new_hair = tgui_input_color(usr, "Please select hair color.", "Hair Color", head_organ.hair_colour) + if(!isnull(new_hair) && (!..()) && owner.change_hair_color(new_hair)) update_dna() if("secondary_hair_color") if(can_change(APPEARANCE_SECONDARY_HAIR_COLOR)) - var/new_hair = input("Please select secondary hair color.", "Secondary Hair Color", head_organ.sec_hair_colour) as color|null - if(new_hair && (!..()) && owner.change_hair_color(new_hair, 1)) + var/new_hair = tgui_input_color(usr, "Please select secondary hair color.", "Secondary Hair Color", head_organ.sec_hair_colour) + if(!isnull(new_hair) && (!..()) && owner.change_hair_color(new_hair, 1)) update_dna() if("hair_gradient") if(can_change(APPEARANCE_HAIR) && length(valid_hairstyles)) var/new_style = tgui_input_list(usr, "Please select gradient style", "Hair Gradient", GLOB.hair_gradients_list) - if(new_style) + if(!isnull(new_style)) owner.change_hair_gradient(style = new_style) if("hair_gradient_offset") if(can_change(APPEARANCE_HAIR) && length(valid_hairstyles)) var/new_offset = input("Please enter gradient offset as a comma-separated value (x,y). Example:\n0,0 (no offset)\n5,0 (5 pixels to the right)", "Hair Gradient", "[head_organ.h_grad_offset_x],[head_organ.h_grad_offset_y]") as null|text - if(new_offset) + if(!isnull(new_offset)) owner.change_hair_gradient(offset_raw = new_offset) if("hair_gradient_colour") if(can_change(APPEARANCE_HAIR) && length(valid_hairstyles)) - var/new_color = input("Please select gradient color.", "Hair Gradient", head_organ.h_grad_colour) as null|color - if(new_color) + var/new_color = tgui_input_color(usr, "Please select gradient color.", "Hair Gradient", head_organ.h_grad_colour) + if(!isnull(new_color)) owner.change_hair_gradient(color = new_color) if("hair_gradient_alpha") @@ -124,26 +124,26 @@ if("facial_hair_color") if(can_change(APPEARANCE_FACIAL_HAIR_COLOR)) - var/new_facial = input("Please select facial hair color.", "Facial Hair Color", head_organ.facial_colour) as color|null - if(new_facial && (!..()) && owner.change_facial_hair_color(new_facial)) + var/new_facial = tgui_input_color(usr, "Please select facial hair color.", "Facial Hair Color", head_organ.facial_colour) + if(!isnull(new_facial) && (!..()) && owner.change_facial_hair_color(new_facial)) update_dna() if("secondary_facial_hair_color") if(can_change(APPEARANCE_SECONDARY_FACIAL_HAIR_COLOR)) - var/new_facial = input("Please select secondary facial hair color.", "Secondary Facial Hair Color", head_organ.sec_facial_colour) as color|null - if(new_facial && (!..()) && owner.change_facial_hair_color(new_facial, 1)) + var/new_facial = tgui_input_color(usr, "Please select secondary facial hair color.", "Secondary Facial Hair Color", head_organ.sec_facial_colour) + if(!isnull(new_facial) && (!..()) && owner.change_facial_hair_color(new_facial, 1)) update_dna() if("eye_color") if(can_change(APPEARANCE_EYE_COLOR)) var/obj/item/organ/internal/eyes/eyes_organ = owner.get_int_organ(/obj/item/organ/internal/eyes) - var/new_eyes = input("Please select eye color.", "Eye Color", eyes_organ.eye_color) as color|null - if(new_eyes && (!..()) && owner.change_eye_color(new_eyes)) + var/new_eyes = tgui_input_color(usr, "Please select eye color.", "Eye Color", eyes_organ.eye_color) + if(!isnull(new_eyes) && (!..()) && owner.change_eye_color(new_eyes)) update_dna() if("runechat_color") - var/new_runechat_color = input("Please select runechat color.", "Runechat Color", owner.dna.chat_color) as color|null - if(new_runechat_color && (!..())) + var/new_runechat_color = tgui_input_color("Please select runechat color.", "Runechat Color", owner.dna.chat_color) + if(!isnull(new_runechat_color) && (!..())) owner.change_runechat_color(new_runechat_color) if("head_accessory") @@ -153,8 +153,8 @@ if("head_accessory_color") if(can_change_head_accessory()) - var/new_head_accessory = input("Please select head accessory color.", "Head Accessory Color", head_organ.headacc_colour) as color|null - if(new_head_accessory && (!..()) && owner.change_head_accessory_color(new_head_accessory)) + var/new_head_accessory = tgui_input_color(usr, "Please select head accessory color.", "Head Accessory Color", head_organ.headacc_colour) + if(!isnull(new_head_accessory) && (!..()) && owner.change_head_accessory_color(new_head_accessory)) update_dna() if("head_marking") @@ -164,7 +164,7 @@ if("head_marking_color") if(can_change_markings("head")) - var/new_markings = input("Please select head marking color.", "Marking Color", owner.m_colours["head"]) as color|null + var/new_markings = tgui_input_color(usr, "Please select head marking color.", "Marking Color", owner.m_colours["head"]) if(new_markings && (!..()) && owner.change_marking_color(new_markings, "head")) update_dna() @@ -175,7 +175,7 @@ if("body_marking_color") if(can_change_markings("body")) - var/new_markings = input("Please select body marking color.", "Marking Color", owner.m_colours["body"]) as color|null + var/new_markings = tgui_input_color(usr, "Please select body marking color.", "Marking Color", owner.m_colours["body"]) if(new_markings && (!..()) && owner.change_marking_color(new_markings, "body")) update_dna() @@ -186,7 +186,7 @@ if("tail_marking_color") if(can_change_markings("tail")) - var/new_markings = input("Please select tail marking color.", "Marking Color", owner.m_colours["tail"]) as color|null + var/new_markings = tgui_input_color(usr, "Please select tail marking color.", "Marking Color", owner.m_colours["tail"]) if(new_markings && (!..()) && owner.change_marking_color(new_markings, "tail")) update_dna() diff --git a/code/modules/tgui/tgui_input/color_input.dm b/code/modules/tgui/tgui_input/color_input.dm new file mode 100644 index 00000000000..7ba4b9ed3e0 --- /dev/null +++ b/code/modules/tgui/tgui_input/color_input.dm @@ -0,0 +1,132 @@ +/** + * Creates a TGUI color picker window and returns the user's response. + * + * This proc should be used to create a color picker that the caller will wait for a response from. + * Arguments: + * * user - The user to show the picker to. + * * title - The of the picker modal, shown on the top of the TGUI window. + * * timeout - The timeout of the picker, after which the modal will close and qdel itself. Set to zero for no timeout. + * * autofocus - The bool that controls if this picker should grab window focus. + */ +/proc/tgui_input_color(mob/user, message, title, default = "#000000", timeout = 0, autofocus = TRUE, ui_state = GLOB.always_state) + if(!user) + user = usr + if(!istype(user)) + if(!isclient(user)) + CRASH("We passed something that wasn't a user/client in a TGUI Input Color! The passed thing was [user]!") + var/client/client = user + user = client.mob + + if(isnull(user.client)) + return + + // Client does NOT have tgui_input on: Returns regular input + if(user.client?.prefs?.toggles2 & PREFTOGGLE_2_DISABLE_TGUI_INPUT) + return input(user, message, title, default) as color|null + + var/datum/tgui_input_color/picker = new(user, message, title, default, timeout, autofocus, ui_state) + picker.ui_interact(user) + picker.wait() + if(picker) + . = picker.choice + qdel(picker) + +/** + * tgui_input_color + * + * Datum used for instantiating and using a TGUI-controlled color picker. + */ +/datum/tgui_input_color + /// The title of the TGUI window + var/title + /// The message to show the user + var/message + /// The default choice, used if there is an existing value + var/default + /// The color the user selected, null if no selection has been made + var/choice + /// The time at which the tgui_input_color was created, for displaying timeout progress. + var/start_time + /// The lifespan of the tgui_input_color, after which the window will close and delete itself. + var/timeout + /// The bool that controls if this modal should grab window focus + var/autofocus + /// Boolean field describing if the tgui_input_color was closed by the user. + var/closed + /// The attached timer that handles this objects timeout deletion + var/deletion_timer + /// The TGUI UI state that will be returned in ui_state(). Default: always_state + var/datum/ui_state/state + +/datum/tgui_input_color/New(mob/user, message, title, default, timeout, autofocus, ui_state) + src.autofocus = autofocus + src.title = title + src.default = default + src.message = message + src.state = ui_state + + if(timeout) + src.timeout = timeout + start_time = world.time + deletion_timer = QDEL_IN(src, timeout) + +/datum/tgui_input_color/Destroy(force, ...) + SStgui.close_uis(src) + state = null + deltimer(deletion_timer) + return ..() + +/** + * Waits for a user's response to the tgui_input_color's prompt before returning. Returns early if + * the window was closed by the user. + */ +/datum/tgui_input_color/proc/wait() + while(!choice && !closed && !QDELETED(src)) + stoplag(1) + +/datum/tgui_input_color/ui_interact(mob/user, datum/tgui/ui) + ui = SStgui.try_update_ui(user, src, ui) + if(!ui) + ui = new(user, src, "ColorPickerModal") + ui.open() + ui.set_autoupdate(timeout > 0) + +/datum/tgui_input_color/ui_close(mob/user) + closed = TRUE + +/datum/tgui_input_color/ui_state(mob/user) + return state + +/datum/tgui_input_color/ui_static_data(mob/user) + var/list/data = list() + data["autofocus"] = autofocus + data["large_buttons"] = !user.client?.prefs || (user.client.prefs.toggles2 & PREFTOGGLE_2_LARGE_INPUT_BUTTONS) + data["swapped_buttons"] = !user.client?.prefs || (user.client.prefs.toggles2 & PREFTOGGLE_2_SWAP_INPUT_BUTTONS) + data["title"] = title + data["default_color"] = default + data["message"] = message + return data + +/datum/tgui_input_color/ui_data(mob/user) + var/list/data = list() + if(timeout) + data["timeout"] = CLAMP01((timeout - (world.time - start_time) - 1 SECONDS) / (timeout - 1 SECONDS)) + return data + +/datum/tgui_input_color/ui_act(action, list/params) + . = ..() + if(.) + return + + switch(action) + if("submit") + if(!findtext(params["entry"], GLOB.is_color)) + return + choice = params["entry"] + closed = TRUE + SStgui.close_uis(src) + return TRUE + if("cancel") + closed = TRUE + SStgui.close_uis(src) + return TRUE diff --git a/paradise.dme b/paradise.dme index 421676544b8..6b5a1211936 100644 --- a/paradise.dme +++ b/paradise.dme @@ -2953,6 +2953,7 @@ #include "code\modules\tgui\states\strippable_state.dm" #include "code\modules\tgui\states\viewer_state.dm" #include "code\modules\tgui\tgui_input\alert_input.dm" +#include "code\modules\tgui\tgui_input\color_input.dm" #include "code\modules\tgui\tgui_input\keycombo_input.dm" #include "code\modules\tgui\tgui_input\list_input.dm" #include "code\modules\tgui\tgui_input\number_input.dm" diff --git a/tgui/packages/common/color.ts b/tgui/packages/common/color.ts new file mode 100644 index 00000000000..84570dba2f7 --- /dev/null +++ b/tgui/packages/common/color.ts @@ -0,0 +1,338 @@ +/** + * @file + * @copyright 2020 Aleksej Komarov + * @license MIT + */ + +const EPSILON = 0.0001; + +export class Color { + r: number; + g: number; + b: number; + a: number; + + constructor(r = 0, g = 0, b = 0, a = 1) { + this.r = r; + this.g = g; + this.b = b; + this.a = a; + } + + toString() { + return `rgba(${this.r | 0}, ${this.g | 0}, ${this.b | 0}, ${this.a | 0})`; + } + + /** + * Creates a color from the CSS hex color notation. + */ + static fromHex(hex: string): Color { + return new Color(parseInt(hex.substr(1, 2), 16), parseInt(hex.substr(3, 2), 16), parseInt(hex.substr(5, 2), 16)); + } + + /** + * Linear interpolation of two colors. + */ + static lerp(c1: Color, c2: Color, n: number): Color { + return new Color( + (c2.r - c1.r) * n + c1.r, + (c2.g - c1.g) * n + c1.g, + (c2.b - c1.b) * n + c1.b, + (c2.a - c1.a) * n + c1.a + ); + } + + /** + * Loops up the color in the provided list of colors + * with linear interpolation. + */ + static lookup(value: number, colors: Color[] = []): Color { + const len = colors.length; + if (len < 2) { + throw new Error('Needs at least two colors!'); + } + const scaled = value * (len - 1); + if (value < EPSILON) { + return colors[0]; + } + if (value >= 1 - EPSILON) { + return colors[len - 1]; + } + const ratio = scaled % 1; + const index = scaled | 0; + return Color.lerp(colors[index], colors[index + 1], ratio); + } +} + +/* + * MIT License + * https://github.com/omgovich/react-colorful/ + * + * Copyright (c) 2020 Vlad Shilov + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +const round = (number: number, digits = 0, base = Math.pow(10, digits)): number => { + return Math.round(base * number) / base; +}; + +export interface RgbColor { + r: number; + g: number; + b: number; +} + +export interface RgbaColor extends RgbColor { + a: number; +} + +export interface HslColor { + h: number; + s: number; + l: number; +} + +export interface HslaColor extends HslColor { + a: number; +} + +export interface HsvColor { + h: number; + s: number; + v: number; +} + +export interface HsvaColor extends HsvColor { + a: number; +} + +export type ObjectColor = RgbColor | HslColor | HsvColor | RgbaColor | HslaColor | HsvaColor; + +export type AnyColor = string | ObjectColor; + +/** + * Valid CSS units. + * https://developer.mozilla.org/en-US/docs/Web/CSS/angle + */ +const angleUnits: Record = { + grad: 360 / 400, + turn: 360, + rad: 360 / (Math.PI * 2), +}; + +export const hexToHsva = (hex: string): HsvaColor => rgbaToHsva(hexToRgba(hex)); + +export const hexToRgba = (hex: string): RgbaColor => { + if (hex[0] === '#') hex = hex.substring(1); + + if (hex.length < 6) { + return { + r: parseInt(hex[0] + hex[0], 16), + g: parseInt(hex[1] + hex[1], 16), + b: parseInt(hex[2] + hex[2], 16), + a: hex.length === 4 ? round(parseInt(hex[3] + hex[3], 16) / 255, 2) : 1, + }; + } + + return { + r: parseInt(hex.substring(0, 2), 16), + g: parseInt(hex.substring(2, 4), 16), + b: parseInt(hex.substring(4, 6), 16), + a: hex.length === 8 ? round(parseInt(hex.substring(6, 8), 16) / 255, 2) : 1, + }; +}; + +export const parseHue = (value: string, unit = 'deg'): number => { + return Number(value) * (angleUnits[unit] || 1); +}; + +export const hslaStringToHsva = (hslString: string): HsvaColor => { + const matcher = + /hsla?\(?\s*(-?\d*\.?\d+)(deg|rad|grad|turn)?[,\s]+(-?\d*\.?\d+)%?[,\s]+(-?\d*\.?\d+)%?,?\s*[/\s]*(-?\d*\.?\d+)?(%)?\s*\)?/i; + const match = matcher.exec(hslString); + + if (!match) return { h: 0, s: 0, v: 0, a: 1 }; + + return hslaToHsva({ + h: parseHue(match[1], match[2]), + s: Number(match[3]), + l: Number(match[4]), + a: match[5] === undefined ? 1 : Number(match[5]) / (match[6] ? 100 : 1), + }); +}; + +export const hslStringToHsva = hslaStringToHsva; + +export const hslaToHsva = ({ h, s, l, a }: HslaColor): HsvaColor => { + s *= (l < 50 ? l : 100 - l) / 100; + + return { + h: h, + s: s > 0 ? ((2 * s) / (l + s)) * 100 : 0, + v: l + s, + a, + }; +}; + +export const hsvaToHex = (hsva: HsvaColor): string => rgbaToHex(hsvaToRgba(hsva)); + +export const hsvaToHsla = ({ h, s, v, a }: HsvaColor): HslaColor => { + const hh = ((200 - s) * v) / 100; + + return { + h: round(h), + s: round(hh > 0 && hh < 200 ? ((s * v) / 100 / (hh <= 100 ? hh : 200 - hh)) * 100 : 0), + l: round(hh / 2), + a: round(a, 2), + }; +}; + +export const hsvaToHslString = (hsva: HsvaColor): string => { + const { h, s, l } = hsvaToHsla(hsva); + return `hsl(${h}, ${s}%, ${l}%)`; +}; + +export const hsvaToHsvString = (hsva: HsvaColor): string => { + const { h, s, v } = roundHsva(hsva); + return `hsv(${h}, ${s}%, ${v}%)`; +}; + +export const hsvaToHsvaString = (hsva: HsvaColor): string => { + const { h, s, v, a } = roundHsva(hsva); + return `hsva(${h}, ${s}%, ${v}%, ${a})`; +}; + +export const hsvaToHslaString = (hsva: HsvaColor): string => { + const { h, s, l, a } = hsvaToHsla(hsva); + return `hsla(${h}, ${s}%, ${l}%, ${a})`; +}; + +export const hsvaToRgba = ({ h, s, v, a }: HsvaColor): RgbaColor => { + h = (h / 360) * 6; + s = s / 100; + v = v / 100; + + const hh = Math.floor(h), + b = v * (1 - s), + c = v * (1 - (h - hh) * s), + d = v * (1 - (1 - h + hh) * s), + module = hh % 6; + + return { + r: [v, c, b, b, d, v][module] * 255, + g: [d, v, v, c, b, b][module] * 255, + b: [b, b, d, v, v, c][module] * 255, + a: round(a, 2), + }; +}; + +export const hsvaToRgbString = (hsva: HsvaColor): string => { + const { r, g, b } = hsvaToRgba(hsva); + return `rgb(${round(r)}, ${round(g)}, ${round(b)})`; +}; + +export const hsvaToRgbaString = (hsva: HsvaColor): string => { + const { r, g, b, a } = hsvaToRgba(hsva); + return `rgba(${round(r)}, ${round(g)}, ${round(b)}, ${round(a, 2)})`; +}; + +export const hsvaStringToHsva = (hsvString: string): HsvaColor => { + const matcher = + /hsva?\(?\s*(-?\d*\.?\d+)(deg|rad|grad|turn)?[,\s]+(-?\d*\.?\d+)%?[,\s]+(-?\d*\.?\d+)%?,?\s*[/\s]*(-?\d*\.?\d+)?(%)?\s*\)?/i; + const match = matcher.exec(hsvString); + + if (!match) return { h: 0, s: 0, v: 0, a: 1 }; + + return roundHsva({ + h: parseHue(match[1], match[2]), + s: Number(match[3]), + v: Number(match[4]), + a: match[5] === undefined ? 1 : Number(match[5]) / (match[6] ? 100 : 1), + }); +}; + +export const hsvStringToHsva = hsvaStringToHsva; + +export const rgbaStringToHsva = (rgbaString: string): HsvaColor => { + const matcher = + /rgba?\(?\s*(-?\d*\.?\d+)(%)?[,\s]+(-?\d*\.?\d+)(%)?[,\s]+(-?\d*\.?\d+)(%)?,?\s*[/\s]*(-?\d*\.?\d+)?(%)?\s*\)?/i; + const match = matcher.exec(rgbaString); + + if (!match) return { h: 0, s: 0, v: 0, a: 1 }; + + return rgbaToHsva({ + r: Number(match[1]) / (match[2] ? 100 / 255 : 1), + g: Number(match[3]) / (match[4] ? 100 / 255 : 1), + b: Number(match[5]) / (match[6] ? 100 / 255 : 1), + a: match[7] === undefined ? 1 : Number(match[7]) / (match[8] ? 100 : 1), + }); +}; + +export const rgbStringToHsva = rgbaStringToHsva; + +const format = (number: number) => { + const hex = number.toString(16); + return hex.length < 2 ? '0' + hex : hex; +}; + +export const rgbaToHex = ({ r, g, b, a }: RgbaColor): string => { + const alphaHex = a < 1 ? format(round(a * 255)) : ''; + return '#' + format(round(r)) + format(round(g)) + format(round(b)) + alphaHex; +}; + +export const rgbaToHsva = ({ r, g, b, a }: RgbaColor): HsvaColor => { + const max = Math.max(r, g, b); + const delta = max - Math.min(r, g, b); + + // prettier-ignore + const hh = delta + ? max === r + ? (g - b) / delta + : max === g + ? 2 + (b - r) / delta + : 4 + (r - g) / delta + : 0; + + return { + h: 60 * (hh < 0 ? hh + 6 : hh), + s: max ? (delta / max) * 100 : 0, + v: (max / 255) * 100, + a, + }; +}; + +export const roundHsva = (hsva: HsvaColor): HsvaColor => ({ + h: round(hsva.h), + s: round(hsva.s), + v: round(hsva.v), + a: round(hsva.a, 2), +}); + +export const rgbaToRgb = ({ r, g, b }: RgbaColor): RgbColor => ({ r, g, b }); + +export const hslaToHsl = ({ h, s, l }: HslaColor): HslColor => ({ h, s, l }); + +export const hsvaToHsv = (hsva: HsvaColor): HsvColor => { + const { h, s, v } = roundHsva(hsva); + return { h, s, v }; +}; + +const hexMatcher = /^#?([0-9A-F]{3,8})$/i; + +export const validHex = (value: string, alpha?: boolean): boolean => { + const match = hexMatcher.exec(value); + const length = match ? match[1].length : 0; + + return ( + length === 3 || // '#rgb' format + length === 6 || // '#rrggbb' format + (!!alpha && length === 4) || // '#rgba' format + (!!alpha && length === 8) // '#rrggbbaa' format + ); +}; diff --git a/tgui/packages/tgui/components/Interactive.tsx b/tgui/packages/tgui/components/Interactive.tsx new file mode 100644 index 00000000000..c2d1b899b24 --- /dev/null +++ b/tgui/packages/tgui/components/Interactive.tsx @@ -0,0 +1,138 @@ +/** + * MIT License + * https://github.com/omgovich/react-colorful/ + * + * Copyright (c) 2020 Vlad Shilov + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +import { clamp } from 'common/math'; +import { Component, InfernoNode, createRef, RefObject } from 'inferno'; + +export interface Interaction { + left: number; + top: number; +} + +// Finds the proper window object to fix iframe embedding issues +const getParentWindow = (node?: HTMLDivElement | null): Window => { + return (node && node.ownerDocument.defaultView) || self; +}; + +// Returns a relative position of the pointer inside the node's bounding box +const getRelativePosition = (node: HTMLDivElement, event: MouseEvent): Interaction => { + const rect = node.getBoundingClientRect(); + const pointer = event as MouseEvent; + return { + left: clamp((pointer.pageX - (rect.left + getParentWindow(node).pageXOffset)) / rect.width, 0, 1), + top: clamp((pointer.pageY - (rect.top + getParentWindow(node).pageYOffset)) / rect.height, 0, 1), + }; +}; + +export interface InteractiveProps { + onMove: (interaction: Interaction) => void; + onKey: (offset: Interaction) => void; + children: InfernoNode[]; + style?: any; +} + +export class Interactive extends Component { + containerRef: RefObject; + props: InteractiveProps; + + constructor(props: InteractiveProps) { + super(); + this.props = props; + this.containerRef = createRef(); + } + + handleMoveStart = (event: MouseEvent) => { + const el = this.containerRef?.current; + if (!el) return; + + // Prevent text selection + event.preventDefault(); + el.focus(); + this.props.onMove(getRelativePosition(el, event)); + this.toggleDocumentEvents(true); + }; + + handleMove = (event: MouseEvent) => { + // Prevent text selection + event.preventDefault(); + + // If user moves the pointer outside of the window or iframe bounds and release it there, + // `mouseup`/`touchend` won't be fired. In order to stop the picker from following the cursor + // after the user has moved the mouse/finger back to the document, we check `event.buttons` + // and `event.touches`. It allows us to detect that the user is just moving his pointer + // without pressing it down + const isDown = event.buttons > 0; + + if (isDown && this.containerRef?.current) { + this.props.onMove(getRelativePosition(this.containerRef.current, event)); + } else { + this.toggleDocumentEvents(false); + } + }; + + handleMoveEnd = () => { + this.toggleDocumentEvents(false); + }; + + handleKeyDown = (event: KeyboardEvent) => { + const keyCode = event.which || event.keyCode; + + // Ignore all keys except arrow ones + if (keyCode < 37 || keyCode > 40) return; + // Do not scroll page by arrow keys when document is focused on the element + event.preventDefault(); + // Send relative offset to the parent component. + // We use codes (37←, 38↑, 39→, 40↓) instead of keys ('ArrowRight', 'ArrowDown', etc) + // to reduce the size of the library + this.props.onKey({ + left: keyCode === 39 ? 0.05 : keyCode === 37 ? -0.05 : 0, + top: keyCode === 40 ? 0.05 : keyCode === 38 ? -0.05 : 0, + }); + }; + + toggleDocumentEvents(state?: boolean) { + const el = this.containerRef?.current; + const parentWindow = getParentWindow(el); + + // Add or remove additional pointer event listeners + const toggleEvent = state ? parentWindow.addEventListener : parentWindow.removeEventListener; + toggleEvent('mousemove', this.handleMove); + toggleEvent('mouseup', this.handleMoveEnd); + } + + componentDidMount() { + this.toggleDocumentEvents(true); + } + + componentWillUnmount() { + this.toggleDocumentEvents(false); + } + + render() { + return ( +
+ {this.props.children} +
+ ); + } +} diff --git a/tgui/packages/tgui/components/Pointer.tsx b/tgui/packages/tgui/components/Pointer.tsx new file mode 100644 index 00000000000..9ee8e7549df --- /dev/null +++ b/tgui/packages/tgui/components/Pointer.tsx @@ -0,0 +1,38 @@ +/** + * MIT License + * https://github.com/omgovich/react-colorful/ + * + * Copyright (c) 2020 Vlad Shilov + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +import { classes } from 'common/react'; +import { InfernoNode } from 'inferno'; + +interface PointerProps { + className?: string; + top?: number; + left: number; + color: string; +} + +export const Pointer = ({ className, color, left, top = 0.5 }: PointerProps): InfernoNode => { + const nodeClassName = classes(['react-colorful__pointer', className]); + + const style = { + top: `${top * 100}%`, + left: `${left * 100}%`, + }; + + return ( +
+
+
+ ); +}; diff --git a/tgui/packages/tgui/components/index.js b/tgui/packages/tgui/components/index.js index 1db4cb5c898..ca3b218616b 100644 --- a/tgui/packages/tgui/components/index.js +++ b/tgui/packages/tgui/components/index.js @@ -21,6 +21,7 @@ export { DraggableControl } from './DraggableControl'; export { Dropdown } from './Dropdown'; export { Flex } from './Flex'; export { Grid } from './Grid'; +export { Interactive } from './Interactive'; export { Image } from './Image'; export { DmIcon } from './DmIcon'; export { Icon } from './Icon'; @@ -33,6 +34,7 @@ export { Modal } from './Modal'; export { NanoMap } from './NanoMap'; export { NoticeBox } from './NoticeBox'; export { NumberInput } from './NumberInput'; +export { Pointer } from './Pointer'; export { Popper } from './Popper'; export { ProgressBar } from './ProgressBar'; export { RestrictedInput } from './RestrictedInput'; diff --git a/tgui/packages/tgui/interfaces/ColorPickerModal.tsx b/tgui/packages/tgui/interfaces/ColorPickerModal.tsx new file mode 100644 index 00000000000..781be69af64 --- /dev/null +++ b/tgui/packages/tgui/interfaces/ColorPickerModal.tsx @@ -0,0 +1,596 @@ +/* eslint-disable react/state-in-constructor */ +/** + * @file + * @copyright 2023 itsmeow + * @license MIT + */ + +import { Loader } from './common/Loader'; +import { useBackend, useLocalState } from '../backend'; +import { Autofocus, Box, Flex, Section, Stack, Pointer, NumberInput, Tooltip } from '../components'; +import { Window } from '../layouts'; +import { clamp } from 'common/math'; +import { hexToHsva, HsvaColor, hsvaToHex, hsvaToHslString, hsvaToRgba, rgbaToHsva, validHex } from 'common/color'; +import { Interaction, Interactive } from 'tgui/components/Interactive'; +import { classes } from 'common/react'; +import { Component, FocusEvent, FormEvent, InfernoNode } from 'inferno'; +import { logger } from 'tgui/logging'; +import { InputButtons } from './common/InputButtons'; + +type ColorPickerData = { + autofocus: boolean; + buttons: string[]; + message: string; + large_buttons: boolean; + swapped_buttons: boolean; + timeout: number; + title: string; + default_color: string; +}; + +export const ColorPickerModal = (_, context) => { + const { data } = useBackend(context); + const { timeout, message, title, autofocus, default_color = '#000000' } = data; + let [selectedColor, setSelectedColor] = useLocalState( + context, + 'color_picker_choice', + hexToHsva(default_color) + ); + + return ( + + {!!timeout && } + + + {message && ( + +
+ + {message} + +
+
+ )} + +
+ {!!autofocus && } + +
+
+ + + +
+
+
+ ); +}; + +export const ColorSelector = ( + { color, setColor, defaultColor }: { color: HsvaColor; setColor; defaultColor: string }, + context +) => { + const handleChange = (params: Partial) => { + setColor((current: HsvaColor) => { + return Object.assign({}, current, params); + }); + }; + const rgb = hsvaToRgba(color); + const hexColor = hsvaToHex(color); + return ( + + + + +
+ + +
+
+ + + Current + + + Previous + +
+ + + + + + +
+
+
+ + + + + + Hex: + + + { + logger.info(value); + setColor(hexToHsva(value)); + }} + prefixed + /> + + + + + + + + H: + + + + + + handleChange({ h: v })} max={360} unit="°" /> + + + + + + + S: + + + + + + handleChange({ s: v })} unit="%" /> + + + + + + + V: + + + + + + handleChange({ v: v })} unit="%" /> + + + + + + + + R: + + + + + + { + rgb.r = v; + handleChange(rgbaToHsva(rgb)); + }} + max={255} + /> + + + + + + + G: + + + + + + { + rgb.g = v; + handleChange(rgbaToHsva(rgb)); + }} + max={255} + /> + + + + + + + B: + + + + + + { + rgb.b = v; + handleChange(rgbaToHsva(rgb)); + }} + max={255} + /> + + + + + +
+ ); +}; + +const TextSetter = ({ + value, + callback, + min = 0, + max = 100, + unit, +}: { + value: number; + callback: any; + min?: number; + max?: number; + unit?: string; +}) => { + return ( + + ); +}; + +/** + * MIT License + * https://github.com/omgovich/react-colorful/ + * + * Copyright (c) 2020 Vlad Shilov + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +interface HexColorInputProps extends Omit { + /** Enables `#` prefix displaying */ + prefixed?: boolean; + /** Allows `#rgba` and `#rrggbbaa` color formats */ + alpha?: boolean; +} + +/** Adds "#" symbol to the beginning of the string */ +const prefix = (value: string) => '#' + value; + +export const HexColorInput = (props: HexColorInputProps): InfernoNode => { + const { prefixed, alpha, color, fluid, onChange, ...rest } = props; + + /** Escapes all non-hexadecimal characters including "#" */ + const escape = (value: string) => value.replace(/([^0-9A-F]+)/gi, '').substring(0, alpha ? 8 : 6); + + /** Validates hexadecimal strings */ + const validate = (value: string) => validHex(value, alpha); + + return ( + + ); +}; + +interface ColorInputBaseProps { + fluid?: boolean; + color: string; + onChange: (newColor: string) => void; + /** Blocks typing invalid characters and limits string length */ + escape: (value: string) => string; + /** Checks that value is valid color string */ + validate: (value: string) => boolean; + /** Processes value before displaying it in the input */ + format?: (value: string) => string; +} + +export class ColorInput extends Component { + props: ColorInputBaseProps; + state: { localValue: string }; + + constructor(props: ColorInputBaseProps) { + super(); + this.props = props; + this.state = { localValue: this.props.escape(this.props.color) }; + } + + // Trigger `onChange` handler only if the input value is a valid color + handleInput = (e: FormEvent) => { + const inputValue = this.props.escape(e.currentTarget.value); + this.setState({ localValue: inputValue }); + }; + + // Take the color from props if the last typed color (in local state) is not valid + handleBlur = (e: FocusEvent) => { + if (e.currentTarget) { + if (!this.props.validate(e.currentTarget.value)) { + this.setState({ localValue: this.props.escape(this.props.color) }); // return to default; + } else { + this.props.onChange(this.props.escape ? this.props.escape(e.currentTarget.value) : e.currentTarget.value); + } + } + }; + + componentDidUpdate(prevProps, prevState): void { + if (prevProps.color !== this.props.color) { + // Update the local state when `color` property value is changed + this.setState({ localValue: this.props.escape(this.props.color) }); + } + } + + render() { + return ( + +
.
+ +
+ ); + } +} + +const SaturationValue = ({ hsva, onChange }) => { + const handleMove = (interaction: Interaction) => { + onChange({ + s: interaction.left * 100, + v: 100 - interaction.top * 100, + }); + }; + + const handleKey = (offset: Interaction) => { + // Saturation and brightness always fit into [0, 100] range + onChange({ + s: clamp(hsva.s + offset.left * 100, 0, 100), + v: clamp(hsva.v - offset.top * 100, 0, 100), + }); + }; + + const containerStyle = { + 'background-color': `${hsvaToHslString({ h: hsva.h, s: 100, v: 100, a: 1 })} !important`, + }; + + return ( +
+ + + +
+ ); +}; + +const Hue = ({ + className, + hue, + onChange, +}: { + className?: string; + hue: number; + onChange: (newHue: { h: number }) => void; +}) => { + const handleMove = (interaction: Interaction) => { + onChange({ h: 360 * interaction.left }); + }; + + const handleKey = (offset: Interaction) => { + // Hue measured in degrees of the color circle ranging from 0 to 360 + onChange({ + h: clamp(hue + offset.left * 360, 0, 360), + }); + }; + + const nodeClassName = classes(['react-colorful__hue', className]); + + return ( +
+ + + +
+ ); +}; + +const Saturation = ({ + className, + color, + onChange, +}: { + className?: string; + color: HsvaColor; + onChange: (newSaturation: { s: number }) => void; +}) => { + const handleMove = (interaction: Interaction) => { + onChange({ s: 100 * interaction.left }); + }; + + const handleKey = (offset: Interaction) => { + // Hue measured in degrees of the color circle ranging from 0 to 100 + onChange({ + s: clamp(color.s + offset.left * 100, 0, 100), + }); + }; + + const nodeClassName = classes(['react-colorful__saturation', className]); + + return ( +
+ + + +
+ ); +}; + +const Value = ({ + className, + color, + onChange, +}: { + className?: string; + color: HsvaColor; + onChange: (newValue: { v: number }) => void; +}) => { + const handleMove = (interaction: Interaction) => { + onChange({ v: 100 * interaction.left }); + }; + + const handleKey = (offset: Interaction) => { + onChange({ + v: clamp(color.v + offset.left * 100, 0, 100), + }); + }; + + const nodeClassName = classes(['react-colorful__value', className]); + + return ( +
+ + + +
+ ); +}; + +const RGBSlider = ({ + className, + color, + onChange, + target, +}: { + className?: string; + color: HsvaColor; + onChange: (newValue: HsvaColor) => void; + target: string; +}) => { + const rgb = hsvaToRgba(color); + + const setNewTarget = (value: number) => { + rgb[target] = value; + onChange(rgbaToHsva(rgb)); + }; + + const handleMove = (interaction: Interaction) => { + setNewTarget(255 * interaction.left); + }; + + const handleKey = (offset: Interaction) => { + setNewTarget(clamp(rgb[target] + offset.left * 255, 0, 255)); + }; + + const nodeClassName = classes([`react-colorful__${target}`, className]); + + let selected = + target === 'r' + ? `rgb(${Math.round(rgb.r)},0,0)` + : target === 'g' + ? `rgb(0,${Math.round(rgb.g)},0)` + : `rgb(0,0,${Math.round(rgb.b)})`; + + return ( +
+ + + +
+ ); +}; diff --git a/tgui/packages/tgui/styles/interfaces/ColorPicker.scss b/tgui/packages/tgui/styles/interfaces/ColorPicker.scss new file mode 100644 index 00000000000..d18030eaea4 --- /dev/null +++ b/tgui/packages/tgui/styles/interfaces/ColorPicker.scss @@ -0,0 +1,140 @@ +/** + * MIT License + * https://github.com/omgovich/react-colorful/ + * + * Copyright (c) 2020 Vlad Shilov + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +@use '../colors.scss'; +@use '../base.scss'; + +.react-colorful { + position: relative; + display: flex; + flex-direction: column; + width: 200px; + height: 200px; + user-select: none; + cursor: default; +} + +.react-colorful__saturation_value { + position: relative; + flex-grow: 1; + border-color: transparent; /* Fixes https://github.com/omgovich/react-colorful/issues/139 */ + border-bottom: 12px solid #000; + border-radius: 8px 8px 0 0; + background-image: linear-gradient(to top, rgba(0, 0, 0, 255), rgba(0, 0, 0, 0)), + linear-gradient(to right, rgba(255, 255, 255, 255), rgba(255, 255, 255, 0)); +} + +.react-colorful__pointer-fill, +.react-colorful__alpha-gradient { + content: ''; + position: absolute; + left: 0; + top: 0; + right: 0; + bottom: 0; + pointer-events: none; + border-radius: inherit; +} + +/* Improve elements rendering on light backgrounds */ +.react-colorful__alpha-gradient, +.react-colorful__saturation_value { + box-shadow: inset 0 0 0 1px rgba(0, 0, 0, 0.05); +} + +.react-colorful__hue, +.react-colorful__r, +.react-colorful__g, +.react-colorful__b, +.react-colorful__alpha, +.react-colorful__saturation, +.react-colorful__value { + position: relative; + height: 24px; +} + +.react-colorful__hue { + background: linear-gradient(to right, #f00 0%, #ff0 17%, #0f0 33%, #0ff 50%, #00f 67%, #f0f 83%, #f00 100%); +} + +.react-colorful__r { + background: linear-gradient(to right, #000, #f00); +} + +.react-colorful__g { + background: linear-gradient(to right, #000, #0f0); +} + +.react-colorful__b { + background: linear-gradient(to right, #000, #00f); +} + +/* Round bottom corners of the last element: `Hue` for `ColorPicker` or `Alpha` for `AlphaColorPicker` */ +.react-colorful__last-control { + border-radius: 0 0 8px 8px; +} + +.react-colorful__interactive { + position: absolute; + left: 0; + top: 0; + right: 0; + bottom: 0; + border-radius: inherit; + outline: none; + /* Don't trigger the default scrolling behavior when the event is originating from this element */ + touch-action: none; +} + +.react-colorful__pointer { + position: absolute; + z-index: 1; + box-sizing: border-box; + width: 28px; + height: 28px; + transform: translate(-50%, -50%); + background-color: #cfcfcf; + border: 2px solid #cfcfcf; + border-radius: 50%; + box-shadow: 0 2px 5px rgba(0, 0, 0, 0.4); +} + +.react-colorful__interactive:focus .react-colorful__pointer { + transform: translate(-50%, -50%) scale(1.1); + background-color: #fff; + border-color: #fff; +} + +/* Chessboard-like pattern for alpha related elements */ +.react-colorful__alpha, +.react-colorful__alpha-pointer { + background-color: #fff; + background-image: url('data:image/svg+xml,'); +} + +.react-colorful__saturation-pointer, +.react-colorful__value-pointer, +.react-colorful__hue-pointer, +.react-colorful__r-pointer, +.react-colorful__g-pointer, +.react-colorful__b-pointer { + z-index: 1; + width: 20px; + height: 20px; +} + +/* Display the saturation value pointer over the hue one */ +.react-colorful__saturation_value-pointer { + z-index: 3; +} diff --git a/tgui/packages/tgui/styles/main.scss b/tgui/packages/tgui/styles/main.scss index 338c83c03fe..6c9131e9d6d 100644 --- a/tgui/packages/tgui/styles/main.scss +++ b/tgui/packages/tgui/styles/main.scss @@ -49,6 +49,7 @@ @include meta.load-css('./interfaces/AlertModal.scss'); @include meta.load-css('./interfaces/BrigCells.scss'); @include meta.load-css('./interfaces/CameraConsole.scss'); +@include meta.load-css('./interfaces/ColorPicker.scss'); @include meta.load-css('./interfaces/Contractor.scss'); @include meta.load-css('./interfaces/CrewMonitor.scss'); @include meta.load-css('./interfaces/ExosuitFabricator.scss'); diff --git a/tgui/public/tgui-panel.bundle.js b/tgui/public/tgui-panel.bundle.js index 7376322b852..110bb8cf898 100644 --- a/tgui/public/tgui-panel.bundle.js +++ b/tgui/public/tgui-panel.bundle.js @@ -1,30 +1,30 @@ -(function(){(function(){var xn={96376:function(y,n,t){"use strict";n.__esModule=!0,n.createPopper=void 0,n.popperGenerator=g;var e=u(t(74758)),r=u(t(28811)),o=u(t(98309)),a=u(t(44896)),s=u(t(33118)),i=u(t(10579)),c=u(t(56500)),p=u(t(17633));n.detectOverflow=p.default;var f=t(75573);function u(h){return h&&h.__esModule?h:{default:h}}var v={placement:"bottom",modifiers:[],strategy:"absolute"};function l(){for(var h=arguments.length,m=new Array(h),E=0;E0&&(0,r.round)(u.width)/c.offsetWidth||1,l=c.offsetHeight>0&&(0,r.round)(u.height)/c.offsetHeight||1);var g=(0,e.isElement)(c)?(0,o.default)(c):window,d=g.visualViewport,h=!(0,a.default)()&&f,m=(u.left+(h&&d?d.offsetLeft:0))/v,E=(u.top+(h&&d?d.offsetTop:0))/l,I=u.width/v,O=u.height/l;return{width:I,height:O,top:E,right:m+I,bottom:E+O,left:m,x:m,y:E}}},49035:function(y,n,t){"use strict";n.__esModule=!0,n.default=O;var e=t(46206),r=h(t(87991)),o=h(t(79752)),a=h(t(98309)),s=h(t(44896)),i=h(t(40600)),c=h(t(16599)),p=t(75573),f=h(t(37786)),u=h(t(57819)),v=h(t(4206)),l=h(t(12972)),g=h(t(81666)),d=t(63618);function h(C){return C&&C.__esModule?C:{default:C}}function m(C,S){var b=(0,f.default)(C,!1,S==="fixed");return b.top=b.top+C.clientTop,b.left=b.left+C.clientLeft,b.bottom=b.top+C.clientHeight,b.right=b.left+C.clientWidth,b.width=C.clientWidth,b.height=C.clientHeight,b.x=b.left,b.y=b.top,b}function E(C,S,b){return S===e.viewport?(0,g.default)((0,r.default)(C,b)):(0,p.isElement)(S)?m(S,b):(0,g.default)((0,o.default)((0,i.default)(C)))}function I(C){var S=(0,a.default)((0,u.default)(C)),b=["absolute","fixed"].indexOf((0,c.default)(C).position)>=0,T=b&&(0,p.isHTMLElement)(C)?(0,s.default)(C):C;return(0,p.isElement)(T)?S.filter(function(P){return(0,p.isElement)(P)&&(0,v.default)(P,T)&&(0,l.default)(P)!=="body"}):[]}function O(C,S,b,T){var P=S==="clippingParents"?I(C):[].concat(S),N=[].concat(P,[b]),M=N[0],w=N.reduce(function(D,V){var F=E(C,V,T);return D.top=(0,d.max)(F.top,D.top),D.right=(0,d.min)(F.right,D.right),D.bottom=(0,d.min)(F.bottom,D.bottom),D.left=(0,d.max)(F.left,D.left),D},E(C,M,T));return w.width=w.right-w.left,w.height=w.bottom-w.top,w.x=w.left,w.y=w.top,w}},74758:function(y,n,t){"use strict";n.__esModule=!0,n.default=v;var e=f(t(37786)),r=f(t(13390)),o=f(t(12972)),a=t(75573),s=f(t(79697)),i=f(t(40600)),c=f(t(10798)),p=t(63618);function f(l){return l&&l.__esModule?l:{default:l}}function u(l){var g=l.getBoundingClientRect(),d=(0,p.round)(g.width)/l.offsetWidth||1,h=(0,p.round)(g.height)/l.offsetHeight||1;return d!==1||h!==1}function v(l,g,d){d===void 0&&(d=!1);var h=(0,a.isHTMLElement)(g),m=(0,a.isHTMLElement)(g)&&u(g),E=(0,i.default)(g),I=(0,e.default)(l,m,d),O={scrollLeft:0,scrollTop:0},C={x:0,y:0};return(h||!h&&!d)&&(((0,o.default)(g)!=="body"||(0,c.default)(E))&&(O=(0,r.default)(g)),(0,a.isHTMLElement)(g)?(C=(0,e.default)(g,!0),C.x+=g.clientLeft,C.y+=g.clientTop):E&&(C.x=(0,s.default)(E))),{x:I.left+O.scrollLeft-C.x,y:I.top+O.scrollTop-C.y,width:I.width,height:I.height}}},16599:function(y,n,t){"use strict";n.__esModule=!0,n.default=o;var e=r(t(95115));function r(a){return a&&a.__esModule?a:{default:a}}function o(a){return(0,e.default)(a).getComputedStyle(a)}},40600:function(y,n,t){"use strict";n.__esModule=!0,n.default=r;var e=t(75573);function r(o){return(((0,e.isElement)(o)?o.ownerDocument:o.document)||window.document).documentElement}},79752:function(y,n,t){"use strict";n.__esModule=!0,n.default=c;var e=i(t(40600)),r=i(t(16599)),o=i(t(79697)),a=i(t(43750)),s=t(63618);function i(p){return p&&p.__esModule?p:{default:p}}function c(p){var f,u=(0,e.default)(p),v=(0,a.default)(p),l=(f=p.ownerDocument)==null?void 0:f.body,g=(0,s.max)(u.scrollWidth,u.clientWidth,l?l.scrollWidth:0,l?l.clientWidth:0),d=(0,s.max)(u.scrollHeight,u.clientHeight,l?l.scrollHeight:0,l?l.clientHeight:0),h=-v.scrollLeft+(0,o.default)(p),m=-v.scrollTop;return(0,r.default)(l||u).direction==="rtl"&&(h+=(0,s.max)(u.clientWidth,l?l.clientWidth:0)-g),{width:g,height:d,x:h,y:m}}},3073:function(y,n){"use strict";n.__esModule=!0,n.default=t;function t(e){return{scrollLeft:e.scrollLeft,scrollTop:e.scrollTop}}},28811:function(y,n,t){"use strict";n.__esModule=!0,n.default=o;var e=r(t(37786));function r(a){return a&&a.__esModule?a:{default:a}}function o(a){var s=(0,e.default)(a),i=a.offsetWidth,c=a.offsetHeight;return Math.abs(s.width-i)<=1&&(i=s.width),Math.abs(s.height-c)<=1&&(c=s.height),{x:a.offsetLeft,y:a.offsetTop,width:i,height:c}}},12972:function(y,n){"use strict";n.__esModule=!0,n.default=t;function t(e){return e?(e.nodeName||"").toLowerCase():null}},13390:function(y,n,t){"use strict";n.__esModule=!0,n.default=i;var e=s(t(43750)),r=s(t(95115)),o=t(75573),a=s(t(3073));function s(c){return c&&c.__esModule?c:{default:c}}function i(c){return c===(0,r.default)(c)||!(0,o.isHTMLElement)(c)?(0,e.default)(c):(0,a.default)(c)}},44896:function(y,n,t){"use strict";n.__esModule=!0,n.default=v;var e=p(t(95115)),r=p(t(12972)),o=p(t(16599)),a=t(75573),s=p(t(87031)),i=p(t(57819)),c=p(t(35366));function p(l){return l&&l.__esModule?l:{default:l}}function f(l){return!(0,a.isHTMLElement)(l)||(0,o.default)(l).position==="fixed"?null:l.offsetParent}function u(l){var g=/firefox/i.test((0,c.default)()),d=/Trident/i.test((0,c.default)());if(d&&(0,a.isHTMLElement)(l)){var h=(0,o.default)(l);if(h.position==="fixed")return null}var m=(0,i.default)(l);for((0,a.isShadowRoot)(m)&&(m=m.host);(0,a.isHTMLElement)(m)&&["html","body"].indexOf((0,r.default)(m))<0;){var E=(0,o.default)(m);if(E.transform!=="none"||E.perspective!=="none"||E.contain==="paint"||["transform","perspective"].indexOf(E.willChange)!==-1||g&&E.willChange==="filter"||g&&E.filter&&E.filter!=="none")return m;m=m.parentNode}return null}function v(l){for(var g=(0,e.default)(l),d=f(l);d&&(0,s.default)(d)&&(0,o.default)(d).position==="static";)d=f(d);return d&&((0,r.default)(d)==="html"||(0,r.default)(d)==="body"&&(0,o.default)(d).position==="static")?g:d||u(l)||g}},57819:function(y,n,t){"use strict";n.__esModule=!0,n.default=s;var e=a(t(12972)),r=a(t(40600)),o=t(75573);function a(i){return i&&i.__esModule?i:{default:i}}function s(i){return(0,e.default)(i)==="html"?i:i.assignedSlot||i.parentNode||((0,o.isShadowRoot)(i)?i.host:null)||(0,r.default)(i)}},24426:function(y,n,t){"use strict";n.__esModule=!0,n.default=i;var e=s(t(57819)),r=s(t(10798)),o=s(t(12972)),a=t(75573);function s(c){return c&&c.__esModule?c:{default:c}}function i(c){return["html","body","#document"].indexOf((0,o.default)(c))>=0?c.ownerDocument.body:(0,a.isHTMLElement)(c)&&(0,r.default)(c)?c:i((0,e.default)(c))}},87991:function(y,n,t){"use strict";n.__esModule=!0,n.default=i;var e=s(t(95115)),r=s(t(40600)),o=s(t(79697)),a=s(t(89331));function s(c){return c&&c.__esModule?c:{default:c}}function i(c,p){var f=(0,e.default)(c),u=(0,r.default)(c),v=f.visualViewport,l=u.clientWidth,g=u.clientHeight,d=0,h=0;if(v){l=v.width,g=v.height;var m=(0,a.default)();(m||!m&&p==="fixed")&&(d=v.offsetLeft,h=v.offsetTop)}return{width:l,height:g,x:d+(0,o.default)(c),y:h}}},95115:function(y,n){"use strict";n.__esModule=!0,n.default=t;function t(e){if(e==null)return window;if(e.toString()!=="[object Window]"){var r=e.ownerDocument;return r&&r.defaultView||window}return e}},43750:function(y,n,t){"use strict";n.__esModule=!0,n.default=o;var e=r(t(95115));function r(a){return a&&a.__esModule?a:{default:a}}function o(a){var s=(0,e.default)(a),i=s.pageXOffset,c=s.pageYOffset;return{scrollLeft:i,scrollTop:c}}},79697:function(y,n,t){"use strict";n.__esModule=!0,n.default=s;var e=a(t(37786)),r=a(t(40600)),o=a(t(43750));function a(i){return i&&i.__esModule?i:{default:i}}function s(i){return(0,e.default)((0,r.default)(i)).left+(0,o.default)(i).scrollLeft}},75573:function(y,n,t){"use strict";n.__esModule=!0,n.isElement=o,n.isHTMLElement=a,n.isShadowRoot=s;var e=r(t(95115));function r(i){return i&&i.__esModule?i:{default:i}}function o(i){var c=(0,e.default)(i).Element;return i instanceof c||i instanceof Element}function a(i){var c=(0,e.default)(i).HTMLElement;return i instanceof c||i instanceof HTMLElement}function s(i){if(typeof ShadowRoot=="undefined")return!1;var c=(0,e.default)(i).ShadowRoot;return i instanceof c||i instanceof ShadowRoot}},89331:function(y,n,t){"use strict";n.__esModule=!0,n.default=o;var e=r(t(35366));function r(a){return a&&a.__esModule?a:{default:a}}function o(){return!/^((?!chrome|android).)*safari/i.test((0,e.default)())}},10798:function(y,n,t){"use strict";n.__esModule=!0,n.default=o;var e=r(t(16599));function r(a){return a&&a.__esModule?a:{default:a}}function o(a){var s=(0,e.default)(a),i=s.overflow,c=s.overflowX,p=s.overflowY;return/auto|scroll|overlay|hidden/.test(i+p+c)}},87031:function(y,n,t){"use strict";n.__esModule=!0,n.default=o;var e=r(t(12972));function r(a){return a&&a.__esModule?a:{default:a}}function o(a){return["table","td","th"].indexOf((0,e.default)(a))>=0}},98309:function(y,n,t){"use strict";n.__esModule=!0,n.default=i;var e=s(t(24426)),r=s(t(57819)),o=s(t(95115)),a=s(t(10798));function s(c){return c&&c.__esModule?c:{default:c}}function i(c,p){var f;p===void 0&&(p=[]);var u=(0,e.default)(c),v=u===((f=c.ownerDocument)==null?void 0:f.body),l=(0,o.default)(u),g=v?[l].concat(l.visualViewport||[],(0,a.default)(u)?u:[]):u,d=p.concat(g);return v?d:d.concat(i((0,r.default)(g)))}},46206:function(y,n){"use strict";n.__esModule=!0,n.write=n.viewport=n.variationPlacements=n.top=n.start=n.right=n.reference=n.read=n.popper=n.placements=n.modifierPhases=n.main=n.left=n.end=n.clippingParents=n.bottom=n.beforeWrite=n.beforeRead=n.beforeMain=n.basePlacements=n.auto=n.afterWrite=n.afterRead=n.afterMain=void 0;var t=n.top="top",e=n.bottom="bottom",r=n.right="right",o=n.left="left",a=n.auto="auto",s=n.basePlacements=[t,e,r,o],i=n.start="start",c=n.end="end",p=n.clippingParents="clippingParents",f=n.viewport="viewport",u=n.popper="popper",v=n.reference="reference",l=n.variationPlacements=s.reduce(function(P,N){return P.concat([N+"-"+i,N+"-"+c])},[]),g=n.placements=[].concat(s,[a]).reduce(function(P,N){return P.concat([N,N+"-"+i,N+"-"+c])},[]),d=n.beforeRead="beforeRead",h=n.read="read",m=n.afterRead="afterRead",E=n.beforeMain="beforeMain",I=n.main="main",O=n.afterMain="afterMain",C=n.beforeWrite="beforeWrite",S=n.write="write",b=n.afterWrite="afterWrite",T=n.modifierPhases=[d,h,m,E,I,O,C,S,b]},95996:function(y,n,t){"use strict";n.__esModule=!0;var e={popperGenerator:!0,detectOverflow:!0,createPopperBase:!0,createPopper:!0,createPopperLite:!0};n.popperGenerator=n.detectOverflow=n.createPopperLite=n.createPopperBase=n.createPopper=void 0;var r=t(46206);Object.keys(r).forEach(function(c){c==="default"||c==="__esModule"||Object.prototype.hasOwnProperty.call(e,c)||c in n&&n[c]===r[c]||(n[c]=r[c])});var o=t(39805);Object.keys(o).forEach(function(c){c==="default"||c==="__esModule"||Object.prototype.hasOwnProperty.call(e,c)||c in n&&n[c]===o[c]||(n[c]=o[c])});var a=t(96376);n.popperGenerator=a.popperGenerator,n.detectOverflow=a.detectOverflow,n.createPopperBase=a.createPopper;var s=t(83312);n.createPopper=s.createPopper;var i=t(2473);n.createPopperLite=i.createPopper},19975:function(y,n,t){"use strict";n.__esModule=!0,n.default=void 0;var e=o(t(12972)),r=t(75573);function o(c){return c&&c.__esModule?c:{default:c}}function a(c){var p=c.state;Object.keys(p.elements).forEach(function(f){var u=p.styles[f]||{},v=p.attributes[f]||{},l=p.elements[f];!(0,r.isHTMLElement)(l)||!(0,e.default)(l)||(Object.assign(l.style,u),Object.keys(v).forEach(function(g){var d=v[g];d===!1?l.removeAttribute(g):l.setAttribute(g,d===!0?"":d)}))})}function s(c){var p=c.state,f={popper:{position:p.options.strategy,left:"0",top:"0",margin:"0"},arrow:{position:"absolute"},reference:{}};return Object.assign(p.elements.popper.style,f.popper),p.styles=f,p.elements.arrow&&Object.assign(p.elements.arrow.style,f.arrow),function(){Object.keys(p.elements).forEach(function(u){var v=p.elements[u],l=p.attributes[u]||{},g=Object.keys(p.styles.hasOwnProperty(u)?p.styles[u]:f[u]),d=g.reduce(function(h,m){return h[m]="",h},{});!(0,r.isHTMLElement)(v)||!(0,e.default)(v)||(Object.assign(v.style,d),Object.keys(l).forEach(function(h){v.removeAttribute(h)}))})}}var i=n.default={name:"applyStyles",enabled:!0,phase:"write",fn:a,effect:s,requires:["computeStyles"]}},52744:function(y,n,t){"use strict";n.__esModule=!0,n.default=void 0;var e=u(t(83104)),r=u(t(28811)),o=u(t(4206)),a=u(t(44896)),s=u(t(41199)),i=t(28595),c=u(t(43286)),p=u(t(81447)),f=t(46206);function u(h){return h&&h.__esModule?h:{default:h}}var v=function(){function h(m,E){return m=typeof m=="function"?m(Object.assign({},E.rects,{placement:E.placement})):m,(0,c.default)(typeof m!="number"?m:(0,p.default)(m,f.basePlacements))}return h}();function l(h){var m,E=h.state,I=h.name,O=h.options,C=E.elements.arrow,S=E.modifiersData.popperOffsets,b=(0,e.default)(E.placement),T=(0,s.default)(b),P=[f.left,f.right].indexOf(b)>=0,N=P?"height":"width";if(!(!C||!S)){var M=v(O.padding,E),w=(0,r.default)(C),D=T==="y"?f.top:f.left,V=T==="y"?f.bottom:f.right,F=E.rects.reference[N]+E.rects.reference[T]-S[T]-E.rects.popper[N],$=S[T]-E.rects.reference[T],Y=(0,a.default)(C),j=Y?T==="y"?Y.clientHeight||0:Y.clientWidth||0:0,x=F/2-$/2,U=M[D],K=j-w[N]-M[V],z=j/2-w[N]/2+x,W=(0,i.within)(U,z,K),et=T;E.modifiersData[I]=(m={},m[et]=W,m.centerOffset=W-z,m)}}function g(h){var m=h.state,E=h.options,I=E.element,O=I===void 0?"[data-popper-arrow]":I;O!=null&&(typeof O=="string"&&(O=m.elements.popper.querySelector(O),!O)||(0,o.default)(m.elements.popper,O)&&(m.elements.arrow=O))}var d=n.default={name:"arrow",enabled:!0,phase:"main",fn:l,effect:g,requires:["popperOffsets"],requiresIfExists:["preventOverflow"]}},59894:function(y,n,t){"use strict";n.__esModule=!0,n.default=void 0,n.mapToStyles=l;var e=t(46206),r=f(t(44896)),o=f(t(95115)),a=f(t(40600)),s=f(t(16599)),i=f(t(83104)),c=f(t(45)),p=t(63618);function f(h){return h&&h.__esModule?h:{default:h}}var u={top:"auto",right:"auto",bottom:"auto",left:"auto"};function v(h,m){var E=h.x,I=h.y,O=m.devicePixelRatio||1;return{x:(0,p.round)(E*O)/O||0,y:(0,p.round)(I*O)/O||0}}function l(h){var m,E=h.popper,I=h.popperRect,O=h.placement,C=h.variation,S=h.offsets,b=h.position,T=h.gpuAcceleration,P=h.adaptive,N=h.roundOffsets,M=h.isFixed,w=S.x,D=w===void 0?0:w,V=S.y,F=V===void 0?0:V,$=typeof N=="function"?N({x:D,y:F}):{x:D,y:F};D=$.x,F=$.y;var Y=S.hasOwnProperty("x"),j=S.hasOwnProperty("y"),x=e.left,U=e.top,K=window;if(P){var z=(0,r.default)(E),W="clientHeight",et="clientWidth";if(z===(0,o.default)(E)&&(z=(0,a.default)(E),(0,s.default)(z).position!=="static"&&b==="absolute"&&(W="scrollHeight",et="scrollWidth")),z=z,O===e.top||(O===e.left||O===e.right)&&C===e.end){U=e.bottom;var lt=M&&z===K&&K.visualViewport?K.visualViewport.height:z[W];F-=lt-I.height,F*=T?1:-1}if(O===e.left||(O===e.top||O===e.bottom)&&C===e.end){x=e.right;var ct=M&&z===K&&K.visualViewport?K.visualViewport.width:z[et];D-=ct-I.width,D*=T?1:-1}}var J=Object.assign({position:b},P&&u),X=N===!0?v({x:D,y:F},(0,o.default)(E)):{x:D,y:F};if(D=X.x,F=X.y,T){var at;return Object.assign({},J,(at={},at[U]=j?"0":"",at[x]=Y?"0":"",at.transform=(K.devicePixelRatio||1)<=1?"translate("+D+"px, "+F+"px)":"translate3d("+D+"px, "+F+"px, 0)",at))}return Object.assign({},J,(m={},m[U]=j?F+"px":"",m[x]=Y?D+"px":"",m.transform="",m))}function g(h){var m=h.state,E=h.options,I=E.gpuAcceleration,O=I===void 0?!0:I,C=E.adaptive,S=C===void 0?!0:C,b=E.roundOffsets,T=b===void 0?!0:b,P={placement:(0,i.default)(m.placement),variation:(0,c.default)(m.placement),popper:m.elements.popper,popperRect:m.rects.popper,gpuAcceleration:O,isFixed:m.options.strategy==="fixed"};m.modifiersData.popperOffsets!=null&&(m.styles.popper=Object.assign({},m.styles.popper,l(Object.assign({},P,{offsets:m.modifiersData.popperOffsets,position:m.options.strategy,adaptive:S,roundOffsets:T})))),m.modifiersData.arrow!=null&&(m.styles.arrow=Object.assign({},m.styles.arrow,l(Object.assign({},P,{offsets:m.modifiersData.arrow,position:"absolute",adaptive:!1,roundOffsets:T})))),m.attributes.popper=Object.assign({},m.attributes.popper,{"data-popper-placement":m.placement})}var d=n.default={name:"computeStyles",enabled:!0,phase:"beforeWrite",fn:g,data:{}}},36692:function(y,n,t){"use strict";n.__esModule=!0,n.default=void 0;var e=r(t(95115));function r(i){return i&&i.__esModule?i:{default:i}}var o={passive:!0};function a(i){var c=i.state,p=i.instance,f=i.options,u=f.scroll,v=u===void 0?!0:u,l=f.resize,g=l===void 0?!0:l,d=(0,e.default)(c.elements.popper),h=[].concat(c.scrollParents.reference,c.scrollParents.popper);return v&&h.forEach(function(m){m.addEventListener("scroll",p.update,o)}),g&&d.addEventListener("resize",p.update,o),function(){v&&h.forEach(function(m){m.removeEventListener("scroll",p.update,o)}),g&&d.removeEventListener("resize",p.update,o)}}var s=n.default={name:"eventListeners",enabled:!0,phase:"write",fn:function(){function i(){}return i}(),effect:a,data:{}}},23798:function(y,n,t){"use strict";n.__esModule=!0,n.default=void 0;var e=p(t(71376)),r=p(t(83104)),o=p(t(86459)),a=p(t(17633)),s=p(t(9041)),i=t(46206),c=p(t(45));function p(l){return l&&l.__esModule?l:{default:l}}function f(l){if((0,r.default)(l)===i.auto)return[];var g=(0,e.default)(l);return[(0,o.default)(l),g,(0,o.default)(g)]}function u(l){var g=l.state,d=l.options,h=l.name;if(!g.modifiersData[h]._skip){for(var m=d.mainAxis,E=m===void 0?!0:m,I=d.altAxis,O=I===void 0?!0:I,C=d.fallbackPlacements,S=d.padding,b=d.boundary,T=d.rootBoundary,P=d.altBoundary,N=d.flipVariations,M=N===void 0?!0:N,w=d.allowedAutoPlacements,D=g.options.placement,V=(0,r.default)(D),F=V===D,$=C||(F||!M?[(0,e.default)(D)]:f(D)),Y=[D].concat($).reduce(function(tt,k){return tt.concat((0,r.default)(k)===i.auto?(0,s.default)(g,{placement:k,boundary:b,rootBoundary:T,padding:S,flipVariations:M,allowedAutoPlacements:w}):k)},[]),j=g.rects.reference,x=g.rects.popper,U=new Map,K=!0,z=Y[0],W=0;W=0,X=J?"width":"height",at=(0,a.default)(g,{placement:et,boundary:b,rootBoundary:T,altBoundary:P,padding:S}),dt=J?ct?i.right:i.left:ct?i.bottom:i.top;j[X]>x[X]&&(dt=(0,e.default)(dt));var pt=(0,e.default)(dt),_=[];if(E&&_.push(at[lt]<=0),O&&_.push(at[dt]<=0,at[pt]<=0),_.every(function(tt){return tt})){z=et,K=!1;break}U.set(et,_)}if(K)for(var rt=M?3:1,it=function(){function tt(k){var q=Y.find(function(nt){var ht=U.get(nt);if(ht)return ht.slice(0,k).every(function(Et){return Et})});if(q)return z=q,"break"}return tt}(),mt=rt;mt>0;mt--){var ot=it(mt);if(ot==="break")break}g.placement!==z&&(g.modifiersData[h]._skip=!0,g.placement=z,g.reset=!0)}}var v=n.default={name:"flip",enabled:!0,phase:"main",fn:u,requiresIfExists:["offset"],data:{_skip:!1}}},83761:function(y,n,t){"use strict";n.__esModule=!0,n.default=void 0;var e=t(46206),r=o(t(17633));function o(p){return p&&p.__esModule?p:{default:p}}function a(p,f,u){return u===void 0&&(u={x:0,y:0}),{top:p.top-f.height-u.y,right:p.right-f.width+u.x,bottom:p.bottom-f.height+u.y,left:p.left-f.width-u.x}}function s(p){return[e.top,e.right,e.bottom,e.left].some(function(f){return p[f]>=0})}function i(p){var f=p.state,u=p.name,v=f.rects.reference,l=f.rects.popper,g=f.modifiersData.preventOverflow,d=(0,r.default)(f,{elementContext:"reference"}),h=(0,r.default)(f,{altBoundary:!0}),m=a(d,v),E=a(h,l,g),I=s(m),O=s(E);f.modifiersData[u]={referenceClippingOffsets:m,popperEscapeOffsets:E,isReferenceHidden:I,hasPopperEscaped:O},f.attributes.popper=Object.assign({},f.attributes.popper,{"data-popper-reference-hidden":I,"data-popper-escaped":O})}var c=n.default={name:"hide",enabled:!0,phase:"main",requiresIfExists:["preventOverflow"],fn:i}},39805:function(y,n,t){"use strict";n.__esModule=!0,n.preventOverflow=n.popperOffsets=n.offset=n.hide=n.flip=n.eventListeners=n.computeStyles=n.arrow=n.applyStyles=void 0;var e=u(t(19975));n.applyStyles=e.default;var r=u(t(52744));n.arrow=r.default;var o=u(t(59894));n.computeStyles=o.default;var a=u(t(36692));n.eventListeners=a.default;var s=u(t(23798));n.flip=s.default;var i=u(t(83761));n.hide=i.default;var c=u(t(61410));n.offset=c.default;var p=u(t(40107));n.popperOffsets=p.default;var f=u(t(75137));n.preventOverflow=f.default;function u(v){return v&&v.__esModule?v:{default:v}}},61410:function(y,n,t){"use strict";n.__esModule=!0,n.default=void 0,n.distanceAndSkiddingToXY=a;var e=o(t(83104)),r=t(46206);function o(c){return c&&c.__esModule?c:{default:c}}function a(c,p,f){var u=(0,e.default)(c),v=[r.left,r.top].indexOf(u)>=0?-1:1,l=typeof f=="function"?f(Object.assign({},p,{placement:c})):f,g=l[0],d=l[1];return g=g||0,d=(d||0)*v,[r.left,r.right].indexOf(u)>=0?{x:d,y:g}:{x:g,y:d}}function s(c){var p=c.state,f=c.options,u=c.name,v=f.offset,l=v===void 0?[0,0]:v,g=r.placements.reduce(function(E,I){return E[I]=a(I,p.rects,l),E},{}),d=g[p.placement],h=d.x,m=d.y;p.modifiersData.popperOffsets!=null&&(p.modifiersData.popperOffsets.x+=h,p.modifiersData.popperOffsets.y+=m),p.modifiersData[u]=g}var i=n.default={name:"offset",enabled:!0,phase:"main",requires:["popperOffsets"],fn:s}},40107:function(y,n,t){"use strict";n.__esModule=!0,n.default=void 0;var e=r(t(89951));function r(s){return s&&s.__esModule?s:{default:s}}function o(s){var i=s.state,c=s.name;i.modifiersData[c]=(0,e.default)({reference:i.rects.reference,element:i.rects.popper,strategy:"absolute",placement:i.placement})}var a=n.default={name:"popperOffsets",enabled:!0,phase:"read",fn:o,data:{}}},75137:function(y,n,t){"use strict";n.__esModule=!0,n.default=void 0;var e=t(46206),r=l(t(83104)),o=l(t(41199)),a=l(t(28066)),s=t(28595),i=l(t(28811)),c=l(t(44896)),p=l(t(17633)),f=l(t(45)),u=l(t(34780)),v=t(63618);function l(h){return h&&h.__esModule?h:{default:h}}function g(h){var m=h.state,E=h.options,I=h.name,O=E.mainAxis,C=O===void 0?!0:O,S=E.altAxis,b=S===void 0?!1:S,T=E.boundary,P=E.rootBoundary,N=E.altBoundary,M=E.padding,w=E.tether,D=w===void 0?!0:w,V=E.tetherOffset,F=V===void 0?0:V,$=(0,p.default)(m,{boundary:T,rootBoundary:P,padding:M,altBoundary:N}),Y=(0,r.default)(m.placement),j=(0,f.default)(m.placement),x=!j,U=(0,o.default)(Y),K=(0,a.default)(U),z=m.modifiersData.popperOffsets,W=m.rects.reference,et=m.rects.popper,lt=typeof F=="function"?F(Object.assign({},m.rects,{placement:m.placement})):F,ct=typeof lt=="number"?{mainAxis:lt,altAxis:lt}:Object.assign({mainAxis:0,altAxis:0},lt),J=m.modifiersData.offset?m.modifiersData.offset[m.placement]:null,X={x:0,y:0};if(z){if(C){var at,dt=U==="y"?e.top:e.left,pt=U==="y"?e.bottom:e.right,_=U==="y"?"height":"width",rt=z[U],it=rt+$[dt],mt=rt-$[pt],ot=D?-et[_]/2:0,tt=j===e.start?W[_]:et[_],k=j===e.start?-et[_]:-W[_],q=m.elements.arrow,nt=D&&q?(0,i.default)(q):{width:0,height:0},ht=m.modifiersData["arrow#persistent"]?m.modifiersData["arrow#persistent"].padding:(0,u.default)(),Et=ht[dt],ut=ht[pt],yt=(0,s.within)(0,W[_],nt[_]),Pt=x?W[_]/2-ot-yt-Et-ct.mainAxis:tt-yt-Et-ct.mainAxis,bt=x?-W[_]/2+ot+yt+ut+ct.mainAxis:k+yt+ut+ct.mainAxis,st=m.elements.arrow&&(0,c.default)(m.elements.arrow),gt=st?U==="y"?st.clientTop||0:st.clientLeft||0:0,It=(at=J==null?void 0:J[U])!=null?at:0,Lt=rt+Pt-It-gt,Vt=rt+bt-It,Ot=(0,s.within)(D?(0,v.min)(it,Lt):it,rt,D?(0,v.max)(mt,Vt):mt);z[U]=Ot,X[U]=Ot-rt}if(b){var ft,St=U==="x"?e.top:e.left,At=U==="x"?e.bottom:e.right,Tt=z[K],Nt=K==="y"?"height":"width",Ft=Tt+$[St],jt=Tt-$[At],$t=[e.top,e.left].indexOf(Y)!==-1,Ht=(ft=J==null?void 0:J[K])!=null?ft:0,Yt=$t?Ft:Tt-W[Nt]-et[Nt]-Ht+ct.altAxis,Wt=$t?Tt+W[Nt]+et[Nt]-Ht-ct.altAxis:jt,te=D&&$t?(0,s.withinMaxClamp)(Yt,Tt,Wt):(0,s.within)(D?Yt:Ft,Tt,D?Wt:jt);z[K]=te,X[K]=te-Tt}m.modifiersData[I]=X}}var d=n.default={name:"preventOverflow",enabled:!0,phase:"main",fn:g,requiresIfExists:["offset"]}},2473:function(y,n,t){"use strict";n.__esModule=!0,n.defaultModifiers=n.createPopper=void 0;var e=t(96376);n.popperGenerator=e.popperGenerator,n.detectOverflow=e.detectOverflow;var r=i(t(36692)),o=i(t(40107)),a=i(t(59894)),s=i(t(19975));function i(f){return f&&f.__esModule?f:{default:f}}var c=n.defaultModifiers=[r.default,o.default,a.default,s.default],p=n.createPopper=(0,e.popperGenerator)({defaultModifiers:c})},83312:function(y,n,t){"use strict";n.__esModule=!0;var e={createPopper:!0,createPopperLite:!0,defaultModifiers:!0,popperGenerator:!0,detectOverflow:!0};n.defaultModifiers=n.createPopperLite=n.createPopper=void 0;var r=t(96376);n.popperGenerator=r.popperGenerator,n.detectOverflow=r.detectOverflow;var o=d(t(36692)),a=d(t(40107)),s=d(t(59894)),i=d(t(19975)),c=d(t(61410)),p=d(t(23798)),f=d(t(75137)),u=d(t(52744)),v=d(t(83761)),l=t(2473);n.createPopperLite=l.createPopper;var g=t(39805);Object.keys(g).forEach(function(E){E==="default"||E==="__esModule"||Object.prototype.hasOwnProperty.call(e,E)||E in n&&n[E]===g[E]||(n[E]=g[E])});function d(E){return E&&E.__esModule?E:{default:E}}var h=n.defaultModifiers=[o.default,a.default,s.default,i.default,c.default,p.default,f.default,u.default,v.default],m=n.createPopperLite=n.createPopper=(0,r.popperGenerator)({defaultModifiers:h})},9041:function(y,n,t){"use strict";n.__esModule=!0,n.default=i;var e=s(t(45)),r=t(46206),o=s(t(17633)),a=s(t(83104));function s(c){return c&&c.__esModule?c:{default:c}}function i(c,p){p===void 0&&(p={});var f=p,u=f.placement,v=f.boundary,l=f.rootBoundary,g=f.padding,d=f.flipVariations,h=f.allowedAutoPlacements,m=h===void 0?r.placements:h,E=(0,e.default)(u),I=E?d?r.variationPlacements:r.variationPlacements.filter(function(S){return(0,e.default)(S)===E}):r.basePlacements,O=I.filter(function(S){return m.indexOf(S)>=0});O.length===0&&(O=I);var C=O.reduce(function(S,b){return S[b]=(0,o.default)(c,{placement:b,boundary:v,rootBoundary:l,padding:g})[(0,a.default)(b)],S},{});return Object.keys(C).sort(function(S,b){return C[S]-C[b]})}},89951:function(y,n,t){"use strict";n.__esModule=!0,n.default=i;var e=s(t(83104)),r=s(t(45)),o=s(t(41199)),a=t(46206);function s(c){return c&&c.__esModule?c:{default:c}}function i(c){var p=c.reference,f=c.element,u=c.placement,v=u?(0,e.default)(u):null,l=u?(0,r.default)(u):null,g=p.x+p.width/2-f.width/2,d=p.y+p.height/2-f.height/2,h;switch(v){case a.top:h={x:g,y:p.y-f.height};break;case a.bottom:h={x:g,y:p.y+p.height};break;case a.right:h={x:p.x+p.width,y:d};break;case a.left:h={x:p.x-f.width,y:d};break;default:h={x:p.x,y:p.y}}var m=v?(0,o.default)(v):null;if(m!=null){var E=m==="y"?"height":"width";switch(l){case a.start:h[m]=h[m]-(p[E]/2-f[E]/2);break;case a.end:h[m]=h[m]+(p[E]/2-f[E]/2);break;default:}}return h}},10579:function(y,n){"use strict";n.__esModule=!0,n.default=t;function t(e){var r;return function(){return r||(r=new Promise(function(o){Promise.resolve().then(function(){r=void 0,o(e())})})),r}}},17633:function(y,n,t){"use strict";n.__esModule=!0,n.default=v;var e=u(t(49035)),r=u(t(40600)),o=u(t(37786)),a=u(t(89951)),s=u(t(81666)),i=t(46206),c=t(75573),p=u(t(43286)),f=u(t(81447));function u(l){return l&&l.__esModule?l:{default:l}}function v(l,g){g===void 0&&(g={});var d=g,h=d.placement,m=h===void 0?l.placement:h,E=d.strategy,I=E===void 0?l.strategy:E,O=d.boundary,C=O===void 0?i.clippingParents:O,S=d.rootBoundary,b=S===void 0?i.viewport:S,T=d.elementContext,P=T===void 0?i.popper:T,N=d.altBoundary,M=N===void 0?!1:N,w=d.padding,D=w===void 0?0:w,V=(0,p.default)(typeof D!="number"?D:(0,f.default)(D,i.basePlacements)),F=P===i.popper?i.reference:i.popper,$=l.rects.popper,Y=l.elements[M?F:P],j=(0,e.default)((0,c.isElement)(Y)?Y:Y.contextElement||(0,r.default)(l.elements.popper),C,b,I),x=(0,o.default)(l.elements.reference),U=(0,a.default)({reference:x,element:$,strategy:"absolute",placement:m}),K=(0,s.default)(Object.assign({},$,U)),z=P===i.popper?K:x,W={top:j.top-z.top+V.top,bottom:z.bottom-j.bottom+V.bottom,left:j.left-z.left+V.left,right:z.right-j.right+V.right},et=l.modifiersData.offset;if(P===i.popper&&et){var lt=et[m];Object.keys(W).forEach(function(ct){var J=[i.right,i.bottom].indexOf(ct)>=0?1:-1,X=[i.top,i.bottom].indexOf(ct)>=0?"y":"x";W[ct]+=lt[X]*J})}return W}},81447:function(y,n){"use strict";n.__esModule=!0,n.default=t;function t(e,r){return r.reduce(function(o,a){return o[a]=e,o},{})}},28066:function(y,n){"use strict";n.__esModule=!0,n.default=t;function t(e){return e==="x"?"y":"x"}},83104:function(y,n,t){"use strict";n.__esModule=!0,n.default=r;var e=t(46206);function r(o){return o.split("-")[0]}},34780:function(y,n){"use strict";n.__esModule=!0,n.default=t;function t(){return{top:0,right:0,bottom:0,left:0}}},41199:function(y,n){"use strict";n.__esModule=!0,n.default=t;function t(e){return["top","bottom"].indexOf(e)>=0?"x":"y"}},71376:function(y,n){"use strict";n.__esModule=!0,n.default=e;var t={left:"right",right:"left",bottom:"top",top:"bottom"};function e(r){return r.replace(/left|right|bottom|top/g,function(o){return t[o]})}},86459:function(y,n){"use strict";n.__esModule=!0,n.default=e;var t={start:"end",end:"start"};function e(r){return r.replace(/start|end/g,function(o){return t[o]})}},45:function(y,n){"use strict";n.__esModule=!0,n.default=t;function t(e){return e.split("-")[1]}},63618:function(y,n){"use strict";n.__esModule=!0,n.round=n.min=n.max=void 0;var t=n.max=Math.max,e=n.min=Math.min,r=n.round=Math.round},56500:function(y,n){"use strict";n.__esModule=!0,n.default=t;function t(e){var r=e.reduce(function(o,a){var s=o[a.name];return o[a.name]=s?Object.assign({},s,a,{options:Object.assign({},s.options,a.options),data:Object.assign({},s.data,a.data)}):a,o},{});return Object.keys(r).map(function(o){return r[o]})}},43286:function(y,n,t){"use strict";n.__esModule=!0,n.default=o;var e=r(t(34780));function r(a){return a&&a.__esModule?a:{default:a}}function o(a){return Object.assign({},(0,e.default)(),a)}},33118:function(y,n,t){"use strict";n.__esModule=!0,n.default=o;var e=t(46206);function r(a){var s=new Map,i=new Set,c=[];a.forEach(function(f){s.set(f.name,f)});function p(f){i.add(f.name);var u=[].concat(f.requires||[],f.requiresIfExists||[]);u.forEach(function(v){if(!i.has(v)){var l=s.get(v);l&&p(l)}}),c.push(f)}return a.forEach(function(f){i.has(f.name)||p(f)}),c}function o(a){var s=r(a);return e.modifierPhases.reduce(function(i,c){return i.concat(s.filter(function(p){return p.phase===c}))},[])}},81666:function(y,n){"use strict";n.__esModule=!0,n.default=t;function t(e){return Object.assign({},e,{left:e.x,top:e.y,right:e.x+e.width,bottom:e.y+e.height})}},35366:function(y,n){"use strict";n.__esModule=!0,n.default=t;function t(){var e=navigator.userAgentData;return e!=null&&e.brands&&Array.isArray(e.brands)?e.brands.map(function(r){return r.brand+"/"+r.version}).join(" "):navigator.userAgent}},28595:function(y,n,t){"use strict";n.__esModule=!0,n.within=r,n.withinMaxClamp=o;var e=t(63618);function r(a,s,i){return(0,e.max)(a,(0,e.min)(s,i))}function o(a,s,i){var c=r(a,s,i);return c>i?i:c}},22734:function(y){"use strict";/*! @license DOMPurify 2.5.4 | (c) Cure53 and other contributors | Released under the Apache license 2.0 and Mozilla Public License 2.0 | github.com/cure53/DOMPurify/blob/2.5.4/LICENSE */(function(n,t){y.exports=t()})(void 0,function(){"use strict";function n(st){"@babel/helpers - typeof";return n=typeof Symbol=="function"&&typeof Symbol.iterator=="symbol"?function(gt){return typeof gt}:function(gt){return gt&&typeof Symbol=="function"&>.constructor===Symbol&>!==Symbol.prototype?"symbol":typeof gt},n(st)}function t(st,gt){return t=Object.setPrototypeOf||function(){function It(Lt,Vt){return Lt.__proto__=Vt,Lt}return It}(),t(st,gt)}function e(){if(typeof Reflect=="undefined"||!Reflect.construct||Reflect.construct.sham)return!1;if(typeof Proxy=="function")return!0;try{return Boolean.prototype.valueOf.call(Reflect.construct(Boolean,[],function(){})),!0}catch(st){return!1}}function r(st,gt,It){return e()?r=Reflect.construct:r=function(){function Lt(Vt,Ot,ft){var St=[null];St.push.apply(St,Ot);var At=Function.bind.apply(Vt,St),Tt=new At;return ft&&t(Tt,ft.prototype),Tt}return Lt}(),r.apply(null,arguments)}function o(st){return a(st)||s(st)||i(st)||p()}function a(st){if(Array.isArray(st))return c(st)}function s(st){if(typeof Symbol!="undefined"&&st[Symbol.iterator]!=null||st["@@iterator"]!=null)return Array.from(st)}function i(st,gt){if(st){if(typeof st=="string")return c(st,gt);var It=Object.prototype.toString.call(st).slice(8,-1);if(It==="Object"&&st.constructor&&(It=st.constructor.name),It==="Map"||It==="Set")return Array.from(st);if(It==="Arguments"||/^(?:Ui|I)nt(?:8|16|32)(?:Clamped)?Array$/.test(It))return c(st,gt)}}function c(st,gt){(gt==null||gt>st.length)&&(gt=st.length);for(var It=0,Lt=new Array(gt);It1?It-1:0),Vt=1;Vt/gm),mt=h(/\${[\w\W]*}/gm),ot=h(/^data-[\-\w.\u00B7-\uFFFF]/),tt=h(/^aria-[\-\w]+$/),k=h(/^(?:(?:(?:f|ht)tps?|mailto|tel|callto|cid|xmpp):|[^a-z]|[a-z+.\-]+(?:[^a-z+.\-:]|$))/i),q=h(/^(?:\w+script|data):/i),nt=h(/[\u0000-\u0020\u00A0\u1680\u180E\u2000-\u2029\u205F\u3000]/g),ht=h(/^html$/i),Et=h(/^[a-z][.\w]*(-[.\w]+)+$/i),ut=function(){function st(){return typeof window=="undefined"?null:window}return st}(),yt=function(){function st(gt,It){if(n(gt)!=="object"||typeof gt.createPolicy!="function")return null;var Lt=null,Vt="data-tt-policy-suffix";It.currentScript&&It.currentScript.hasAttribute(Vt)&&(Lt=It.currentScript.getAttribute(Vt));var Ot="dompurify"+(Lt?"#"+Lt:"");try{return gt.createPolicy(Ot,{createHTML:function(){function ft(St){return St}return ft}(),createScriptURL:function(){function ft(St){return St}return ft}()})}catch(ft){return null}}return st}();function Pt(){var st=arguments.length>0&&arguments[0]!==void 0?arguments[0]:ut(),gt=function(){function B(R){return Pt(R)}return B}();if(gt.version="2.5.4",gt.removed=[],!st||!st.document||st.document.nodeType!==9)return gt.isSupported=!1,gt;var It=st.document,Lt=st.document,Vt=st.DocumentFragment,Ot=st.HTMLTemplateElement,ft=st.Node,St=st.Element,At=st.NodeFilter,Tt=st.NamedNodeMap,Nt=Tt===void 0?st.NamedNodeMap||st.MozNamedAttrMap:Tt,Ft=st.HTMLFormElement,jt=st.DOMParser,$t=st.trustedTypes,Ht=St.prototype,Yt=K(Ht,"cloneNode"),Wt=K(Ht,"nextSibling"),te=K(Ht,"childNodes"),Ce=K(Ht,"parentNode");if(typeof Ot=="function"){var Me=Lt.createElement("template");Me.content&&Me.content.ownerDocument&&(Lt=Me.content.ownerDocument)}var _t=yt($t,It),Re=_t?_t.createHTML(""):"",ve=Lt,se=ve.implementation,sn=ve.createNodeIterator,cn=ve.createDocumentFragment,On=ve.getElementsByTagName,ln=It.importNode,fn={};try{fn=U(Lt).documentMode?Lt.documentMode:{}}catch(B){}var ne={};gt.isSupported=typeof Ce=="function"&&se&&se.createHTMLDocument!==void 0&&fn!==9;var He=rt,we=it,Le=mt,An=ot,dn=tt,Pn=q,vn=nt,Be=Et,he=k,Gt=null,re=x({},[].concat(o(z),o(W),o(et),o(ct),o(X))),Xt=null,hn=x({},[].concat(o(at),o(dt),o(pt),o(_))),zt=Object.seal(Object.create(null,{tagNameCheck:{writable:!0,configurable:!1,enumerable:!0,value:null},attributeNameCheck:{writable:!0,configurable:!1,enumerable:!0,value:null},allowCustomizedBuiltInElements:{writable:!0,configurable:!1,enumerable:!0,value:!1}})),ce=null,De=null,pn=!0,We=!0,gn=!1,mn=!0,le=!1,Fe=!0,fe=!1,xe=!1,Oe=!1,kt=!1,Ve=!1,je=!1,ze=!0,ke=!1,pe="user-content-",Xe=!0,Ae=!1,be={},ge=null,Qe=x({},["annotation-xml","audio","colgroup","desc","foreignobject","head","iframe","math","mi","mn","mo","ms","mtext","noembed","noframes","noscript","plaintext","script","style","svg","template","thead","title","video","xmp"]),yn=null,Ze=x({},["audio","video","img","source","image","track"]),Je=null,oe=x({},["alt","class","for","id","label","name","pattern","placeholder","role","summary","title","value","style","xmlns"]),Ue="http://www.w3.org/1998/Math/MathML",$e="http://www.w3.org/2000/svg",ue="http://www.w3.org/1999/xhtml",Te=ue,_e=!1,qe=null,tn=x({},[Ue,$e,ue],P),me,Sn=["application/xhtml+xml","text/html"],Nn="text/html",Qt,Ie=null,En=255,Mn=Lt.createElement("form"),Cn=function(){function B(R){return R instanceof RegExp||R instanceof Function}return B}(),ee=function(){function B(R){Ie&&Ie===R||((!R||n(R)!=="object")&&(R={}),R=U(R),me=Sn.indexOf(R.PARSER_MEDIA_TYPE)===-1?me=Nn:me=R.PARSER_MEDIA_TYPE,Qt=me==="application/xhtml+xml"?P:T,Gt="ALLOWED_TAGS"in R?x({},R.ALLOWED_TAGS,Qt):re,Xt="ALLOWED_ATTR"in R?x({},R.ALLOWED_ATTR,Qt):hn,qe="ALLOWED_NAMESPACES"in R?x({},R.ALLOWED_NAMESPACES,P):tn,Je="ADD_URI_SAFE_ATTR"in R?x(U(oe),R.ADD_URI_SAFE_ATTR,Qt):oe,yn="ADD_DATA_URI_TAGS"in R?x(U(Ze),R.ADD_DATA_URI_TAGS,Qt):Ze,ge="FORBID_CONTENTS"in R?x({},R.FORBID_CONTENTS,Qt):Qe,ce="FORBID_TAGS"in R?x({},R.FORBID_TAGS,Qt):{},De="FORBID_ATTR"in R?x({},R.FORBID_ATTR,Qt):{},be="USE_PROFILES"in R?R.USE_PROFILES:!1,pn=R.ALLOW_ARIA_ATTR!==!1,We=R.ALLOW_DATA_ATTR!==!1,gn=R.ALLOW_UNKNOWN_PROTOCOLS||!1,mn=R.ALLOW_SELF_CLOSE_IN_ATTR!==!1,le=R.SAFE_FOR_TEMPLATES||!1,Fe=R.SAFE_FOR_XML!==!1,fe=R.WHOLE_DOCUMENT||!1,kt=R.RETURN_DOM||!1,Ve=R.RETURN_DOM_FRAGMENT||!1,je=R.RETURN_TRUSTED_TYPE||!1,Oe=R.FORCE_BODY||!1,ze=R.SANITIZE_DOM!==!1,ke=R.SANITIZE_NAMED_PROPS||!1,Xe=R.KEEP_CONTENT!==!1,Ae=R.IN_PLACE||!1,he=R.ALLOWED_URI_REGEXP||he,Te=R.NAMESPACE||ue,zt=R.CUSTOM_ELEMENT_HANDLING||{},R.CUSTOM_ELEMENT_HANDLING&&Cn(R.CUSTOM_ELEMENT_HANDLING.tagNameCheck)&&(zt.tagNameCheck=R.CUSTOM_ELEMENT_HANDLING.tagNameCheck),R.CUSTOM_ELEMENT_HANDLING&&Cn(R.CUSTOM_ELEMENT_HANDLING.attributeNameCheck)&&(zt.attributeNameCheck=R.CUSTOM_ELEMENT_HANDLING.attributeNameCheck),R.CUSTOM_ELEMENT_HANDLING&&typeof R.CUSTOM_ELEMENT_HANDLING.allowCustomizedBuiltInElements=="boolean"&&(zt.allowCustomizedBuiltInElements=R.CUSTOM_ELEMENT_HANDLING.allowCustomizedBuiltInElements),le&&(We=!1),Ve&&(kt=!0),be&&(Gt=x({},o(X)),Xt=[],be.html===!0&&(x(Gt,z),x(Xt,at)),be.svg===!0&&(x(Gt,W),x(Xt,dt),x(Xt,_)),be.svgFilters===!0&&(x(Gt,et),x(Xt,dt),x(Xt,_)),be.mathMl===!0&&(x(Gt,ct),x(Xt,pt),x(Xt,_))),R.ADD_TAGS&&(Gt===re&&(Gt=U(Gt)),x(Gt,R.ADD_TAGS,Qt)),R.ADD_ATTR&&(Xt===hn&&(Xt=U(Xt)),x(Xt,R.ADD_ATTR,Qt)),R.ADD_URI_SAFE_ATTR&&x(Je,R.ADD_URI_SAFE_ATTR,Qt),R.FORBID_CONTENTS&&(ge===Qe&&(ge=U(ge)),x(ge,R.FORBID_CONTENTS,Qt)),Xe&&(Gt["#text"]=!0),fe&&x(Gt,["html","head","body"]),Gt.table&&(x(Gt,["tbody"]),delete ce.tbody),d&&d(R),Ie=R)}return B}(),Pe=x({},["mi","mo","mn","ms","mtext"]),en=x({},["foreignobject","annotation-xml"]),Rn=x({},["title","style","font","a","script"]),Ge=x({},W);x(Ge,et),x(Ge,lt);var Ye=x({},ct);x(Ye,J);var bn=function(){function B(R){var G=Ce(R);(!G||!G.tagName)&&(G={namespaceURI:Te,tagName:"template"});var H=T(R.tagName),Q=T(G.tagName);return qe[R.namespaceURI]?R.namespaceURI===$e?G.namespaceURI===ue?H==="svg":G.namespaceURI===Ue?H==="svg"&&(Q==="annotation-xml"||Pe[Q]):!!Ge[H]:R.namespaceURI===Ue?G.namespaceURI===ue?H==="math":G.namespaceURI===$e?H==="math"&&en[Q]:!!Ye[H]:R.namespaceURI===ue?G.namespaceURI===$e&&!en[Q]||G.namespaceURI===Ue&&!Pe[Q]?!1:!Ye[H]&&(Rn[H]||!Ge[H]):!!(me==="application/xhtml+xml"&&qe[R.namespaceURI]):!1}return B}(),Zt=function(){function B(R){b(gt.removed,{element:R});try{R.parentNode.removeChild(R)}catch(G){try{R.outerHTML=Re}catch(H){R.remove()}}}return B}(),ye=function(){function B(R,G){try{b(gt.removed,{attribute:G.getAttributeNode(R),from:G})}catch(H){b(gt.removed,{attribute:null,from:G})}if(G.removeAttribute(R),R==="is"&&!Xt[R])if(kt||Ve)try{Zt(G)}catch(H){}else try{G.setAttribute(R,"")}catch(H){}}return B}(),Tn=function(){function B(R){var G,H;if(Oe)R=""+R;else{var Q=N(R,/^[\r\n\t ]+/);H=Q&&Q[0]}me==="application/xhtml+xml"&&Te===ue&&(R=''+R+"");var vt=_t?_t.createHTML(R):R;if(Te===ue)try{G=new jt().parseFromString(vt,me)}catch(Rt){}if(!G||!G.documentElement){G=se.createDocument(Te,"template",null);try{G.documentElement.innerHTML=_e?Re:vt}catch(Rt){}}var Ct=G.body||G.documentElement;return R&&H&&Ct.insertBefore(Lt.createTextNode(H),Ct.childNodes[0]||null),Te===ue?On.call(G,fe?"html":"body")[0]:fe?G.documentElement:Ct}return B}(),Ke=function(){function B(R){return sn.call(R.ownerDocument||R,R,At.SHOW_ELEMENT|At.SHOW_COMMENT|At.SHOW_TEXT|At.SHOW_PROCESSING_INSTRUCTION|At.SHOW_CDATA_SECTION,null,!1)}return B}(),nn=function(){function B(R){return R instanceof Ft&&(typeof R.__depth!="undefined"&&typeof R.__depth!="number"||typeof R.__removalCount!="undefined"&&typeof R.__removalCount!="number"||typeof R.nodeName!="string"||typeof R.textContent!="string"||typeof R.removeChild!="function"||!(R.attributes instanceof Nt)||typeof R.removeAttribute!="function"||typeof R.setAttribute!="function"||typeof R.namespaceURI!="string"||typeof R.insertBefore!="function"||typeof R.hasChildNodes!="function")}return B}(),Ne=function(){function B(R){return n(ft)==="object"?R instanceof ft:R&&n(R)==="object"&&typeof R.nodeType=="number"&&typeof R.nodeName=="string"}return B}(),ae=function(){function B(R,G,H){ne[R]&&C(ne[R],function(Q){Q.call(gt,G,H,Ie)})}return B}(),rn=function(){function B(R){var G;if(ae("beforeSanitizeElements",R,null),nn(R)||V(/[\u0080-\uFFFF]/,R.nodeName))return Zt(R),!0;var H=Qt(R.nodeName);if(ae("uponSanitizeElement",R,{tagName:H,allowedTags:Gt}),R.hasChildNodes()&&!Ne(R.firstElementChild)&&(!Ne(R.content)||!Ne(R.content.firstElementChild))&&V(/<[/\w]/g,R.innerHTML)&&V(/<[/\w]/g,R.textContent)||H==="select"&&V(/